Skip to content

Commit

Permalink
Merge pull request #696 from adrianchiris/followup-pr-646
Browse files Browse the repository at this point in the history
Followup pr 646
  • Loading branch information
SchSeba committed May 13, 2024
2 parents 8326f50 + b18f1cb commit 0ca3629
Show file tree
Hide file tree
Showing 24 changed files with 1,070 additions and 2 deletions.
1 change: 1 addition & 0 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -117,6 +117,7 @@ sync-manifests-%: manifests
sed '2{/---/d}' $(CRD_BASES)/sriovnetwork.openshift.io_sriovnetworknodestates.yaml | awk 'NF' > manifests/$*/sriov-network-operator-sriovnetworknodestate.crd.yaml
sed '2{/---/d}' $(CRD_BASES)/sriovnetwork.openshift.io_sriovoperatorconfigs.yaml | awk 'NF' > manifests/$*/sriov-network-operator-sriovoperatorconfig.crd.yaml
sed '2{/---/d}' $(CRD_BASES)/sriovnetwork.openshift.io_sriovnetworks.yaml | awk 'NF' > manifests/$*/sriov-network-operator-sriovnetwork.crd.yaml
sed '2{/---/d}' $(CRD_BASES)/sriovnetwork.openshift.io_ovsnetworks.yaml | awk 'NF' > manifests/$*/sriov-network-operator-ovsnetwork.yaml
@echo ""
@echo "*************************************************************************************************************************************************"
@echo "* Please manually update the sriov-network-operator.v4.7.0.clusterserviceversion.yaml and image-references files in the manifests/$* directory *"
Expand Down
9 changes: 9 additions & 0 deletions PROJECT
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,15 @@ resources:
kind: SriovOperatorConfig
path: github.com/k8snetworkplumbingwg/sriov-network-operator/api/v1
version: v1
- api:
crdVersion: v1
namespaced: true
controller: true
domain: openshift.io
group: sriovnetwork
kind: OVSNetwork
path: github.com/k8snetworkplumbingwg/sriov-network-operator/api/v1
version: v1
version: "3"
plugins:
manifests.sdk.operatorframework.io/v2: {}
Expand Down
38 changes: 38 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,8 @@ The SR-IOV network operator introduces following new CRDs:

- SriovNetwork

- OVSNetwork

- SriovNetworkNodeState

- SriovNetworkNodePolicy
Expand Down Expand Up @@ -101,6 +103,42 @@ spec:
}
```

### OVSNetwork

A custom resource of OVSNetwork could represent the a layer-2 broadcast domain attached to Open vSwitch that works in HW-offloading mode.
It is primarily used to generate a NetworkAttachmentDefinition CR with an OVS CNI plugin configuration.

The OVSNetwork CR also contains the `resourceName` which is aligned with the `resourceName` of SR-IOV device plugin. One OVSNetwork obj maps to one `resourceName`, but one `resourceName` can be shared by different OVSNetwork CRs.

It is expected that `resourceName` contains name of the resource pool which holds Virtual Functions of a NIC in the switchdev mode.
A Physical function of the NIC should be attached to an OVS bridge before any workload which uses OVSNetwork starts.

Example:

```yaml
apiVersion: sriovnetwork.openshift.io/v1
kind: OVSNetwork
metadata:
name: example-network
namespace: example-namespace
spec:
ipam: |
{
"type": "host-local",
"subnet": "10.56.217.0/24",
"rangeStart": "10.56.217.171",
"rangeEnd": "10.56.217.181",
"routes": [{
"dst": "0.0.0.0/0"
}],
"gateway": "10.56.217.1"
}
vlan: 100
bridge: my-bridge
mtu: 2500
resourceName: switchdevnics
```

### SriovNetworkNodeState

The custom resource to represent the SR-IOV interface states of each host, which should only be managed by the operator itself.
Expand Down
67 changes: 65 additions & 2 deletions api/v1/helper.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import (
"encoding/json"
"fmt"
"os"
"path/filepath"
"regexp"
"sort"
"strconv"
Expand Down Expand Up @@ -660,7 +661,7 @@ func (cr *SriovIBNetwork) RenderNetAttDef() (*uns.Unstructured, error) {
data.Data["LogLevelConfigured"] = false
data.Data["LogFileConfigured"] = false

objs, err := render.RenderDir(ManifestsPath, &data)
objs, err := render.RenderDir(filepath.Join(ManifestsPath, "sriov"), &data)
if err != nil {
return nil, err
}
Expand Down Expand Up @@ -778,7 +779,7 @@ func (cr *SriovNetwork) RenderNetAttDef() (*uns.Unstructured, error) {
data.Data["LogFileConfigured"] = (cr.Spec.LogFile != "")
data.Data["LogFile"] = cr.Spec.LogFile

objs, err := render.RenderDir(ManifestsPath, &data)
objs, err := render.RenderDir(filepath.Join(ManifestsPath, "sriov"), &data)
if err != nil {
return nil, err
}
Expand All @@ -794,6 +795,68 @@ func (cr *SriovNetwork) NetworkNamespace() string {
return cr.Spec.NetworkNamespace
}

// RenderNetAttDef renders a net-att-def for sriov CNI
func (cr *OVSNetwork) RenderNetAttDef() (*uns.Unstructured, error) {
logger := log.WithName("RenderNetAttDef")
logger.Info("Start to render OVS CNI NetworkAttachmentDefinition")

// render RawCNIConfig manifests
data := render.MakeRenderData()
data.Data["CniType"] = "ovs"
data.Data["NetworkName"] = cr.Name
if cr.Spec.NetworkNamespace == "" {
data.Data["NetworkNamespace"] = cr.Namespace
} else {
data.Data["NetworkNamespace"] = cr.Spec.NetworkNamespace
}
data.Data["CniResourceName"] = os.Getenv("RESOURCE_PREFIX") + "/" + cr.Spec.ResourceName

if cr.Spec.Capabilities == "" {
data.Data["CapabilitiesConfigured"] = false
} else {
data.Data["CapabilitiesConfigured"] = true
data.Data["CniCapabilities"] = cr.Spec.Capabilities
}

data.Data["Bridge"] = cr.Spec.Bridge
data.Data["VlanTag"] = cr.Spec.Vlan
data.Data["MTU"] = cr.Spec.MTU
if len(cr.Spec.Trunk) > 0 {
trunkConfRaw, _ := json.Marshal(cr.Spec.Trunk)
data.Data["Trunk"] = string(trunkConfRaw)
} else {
data.Data["Trunk"] = ""
}
data.Data["InterfaceType"] = cr.Spec.InterfaceType

if cr.Spec.IPAM != "" {
data.Data["CniIpam"] = SriovCniIpam + ":" + strings.Join(strings.Fields(cr.Spec.IPAM), "")
} else {
data.Data["CniIpam"] = SriovCniIpamEmpty
}

data.Data["MetaPluginsConfigured"] = false
if cr.Spec.MetaPluginsConfig != "" {
data.Data["MetaPluginsConfigured"] = true
data.Data["MetaPlugins"] = cr.Spec.MetaPluginsConfig
}

objs, err := render.RenderDir(filepath.Join(ManifestsPath, "ovs"), &data)
if err != nil {
return nil, err
}
for _, obj := range objs {
raw, _ := json.Marshal(obj)
logger.Info("render NetworkAttachmentDefinition output", "raw", string(raw))
}
return objs[0], nil
}

// NetworkNamespace returns target network namespace for the network
func (cr *OVSNetwork) NetworkNamespace() string {
return cr.Spec.NetworkNamespace
}

// NetFilterMatch -- parse netFilter and check for a match
func NetFilterMatch(netFilter string, netValue string) (isMatch bool) {
logger := log.WithName("NetFilterMatch")
Expand Down
93 changes: 93 additions & 0 deletions api/v1/helper_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -249,6 +249,99 @@ func TestIBRendering(t *testing.T) {
}
}

func TestOVSRendering(t *testing.T) {
testtable := []struct {
tname string
network v1.OVSNetwork
}{
{
tname: "simpleovs",
network: v1.OVSNetwork{
ObjectMeta: metav1.ObjectMeta{
Name: "test",
},
Spec: v1.OVSNetworkSpec{
NetworkNamespace: "testnamespace",
ResourceName: "testresource",
},
},
},
{
tname: "chained",
network: v1.OVSNetwork{
ObjectMeta: metav1.ObjectMeta{
Name: "test",
},
Spec: v1.OVSNetworkSpec{
NetworkNamespace: "testnamespace",
ResourceName: "testresource",
MTU: 1500,
MetaPluginsConfig: `
{
"type": "vrf",
"vrfname": "blue"
}
`,
},
},
},
{
tname: "complexconf",
network: v1.OVSNetwork{
ObjectMeta: metav1.ObjectMeta{
Name: "test",
},
Spec: v1.OVSNetworkSpec{
NetworkNamespace: "testnamespace",
ResourceName: "testresource",
Capabilities: `{"foo": "bar"}`,
Bridge: "test",
Vlan: 100,
MTU: 1500,
Trunk: []*v1.TrunkConfig{
{
ID: func(i uint) *uint { return &i }(120)},
{
MinID: func(i uint) *uint { return &i }(500),
MaxID: func(i uint) *uint { return &i }(550)},
},
InterfaceType: "netdev",
IPAM: `{"type": "foo"}`,
},
},
},
}
for _, tc := range testtable {
t.Run(tc.tname, func(t *testing.T) {
var b bytes.Buffer
w := bufio.NewWriter(&b)
rendered, err := tc.network.RenderNetAttDef()
if err != nil {
t.Fatal("failed rendering network attachment definition", err)
}
encoder := json.NewEncoder(w)
encoder.SetIndent("", " ")
encoder.Encode(rendered)
w.Flush()
gp := filepath.Join("testdata", filepath.FromSlash(t.Name())+".golden")
if *update {
t.Log("update golden file")
if err := os.WriteFile(gp, b.Bytes(), 0644); err != nil {
t.Fatalf("failed to update golden file: %s", err)
}
}
g, err := os.ReadFile(gp)
if err != nil {
t.Fatalf("failed reading .golden: %s", err)
}
t.Log(b.String())
if !bytes.Equal(b.Bytes(), g) {
t.Errorf("bytes do not match .golden file")
}
})
}
}

func TestSriovNetworkNodePolicyApply(t *testing.T) {
testtable := []struct {
tname string
Expand Down
91 changes: 91 additions & 0 deletions api/v1/ovsnetwork_types.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
/*
Copyright 2024.
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 v1

import (
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
)

// OVSNetworkSpec defines the desired state of OVSNetwork
type OVSNetworkSpec struct {
// Namespace of the NetworkAttachmentDefinition custom resource
NetworkNamespace string `json:"networkNamespace,omitempty"`
// OVS Network device plugin endpoint resource name
ResourceName string `json:"resourceName"`
// Capabilities to be configured for this network.
// Capabilities supported: (mac|ips), e.g. '{"mac": true}'
Capabilities string `json:"capabilities,omitempty"`
// IPAM configuration to be used for this network.
IPAM string `json:"ipam,omitempty"`
// MetaPluginsConfig configuration to be used in order to chain metaplugins
MetaPluginsConfig string `json:"metaPlugins,omitempty"`
// name of the OVS bridge, if not set OVS will automatically select bridge
// based on VF PCI address
Bridge string `json:"bridge,omitempty"`
// +kubebuilder:validation:Minimum=0
// +kubebuilder:validation:Maximum=4095
// Vlan to assign for the OVS port
Vlan uint `json:"vlan,omitempty"`
// Mtu for the OVS port
MTU uint `json:"mtu,omitempty"`
// Trunk configuration for the OVS port
Trunk []*TrunkConfig `json:"trunk,omitempty"`
// The type of interface on ovs.
InterfaceType string `json:"interfaceType,omitempty"`
}

// TrunkConfig contains configuration for bridge trunk
type TrunkConfig struct {
// +kubebuilder:validation:Minimum=0
// +kubebuilder:validation:Maximum=4095
MinID *uint `json:"minID,omitempty"`
// +kubebuilder:validation:Minimum=0
// +kubebuilder:validation:Maximum=4095
MaxID *uint `json:"maxID,omitempty"`
// +kubebuilder:validation:Minimum=0
// +kubebuilder:validation:Maximum=4095
ID *uint `json:"id,omitempty"`
}

// OVSNetworkStatus defines the observed state of OVSNetwork
type OVSNetworkStatus struct {
}

//+kubebuilder:object:root=true
//+kubebuilder:subresource:status

// OVSNetwork is the Schema for the ovsnetworks API
type OVSNetwork struct {
metav1.TypeMeta `json:",inline"`
metav1.ObjectMeta `json:"metadata,omitempty"`

Spec OVSNetworkSpec `json:"spec,omitempty"`
Status OVSNetworkStatus `json:"status,omitempty"`
}

//+kubebuilder:object:root=true

// OVSNetworkList contains a list of OVSNetwork
type OVSNetworkList struct {
metav1.TypeMeta `json:",inline"`
metav1.ListMeta `json:"metadata,omitempty"`
Items []OVSNetwork `json:"items"`
}

func init() {
SchemeBuilder.Register(&OVSNetwork{}, &OVSNetworkList{})
}
14 changes: 14 additions & 0 deletions api/v1/testdata/TestOVSRendering/chained.golden
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
{
"apiVersion": "k8s.cni.cncf.io/v1",
"kind": "NetworkAttachmentDefinition",
"metadata": {
"annotations": {
"k8s.v1.cni.cncf.io/resourceName": "/testresource"
},
"name": "test",
"namespace": "testnamespace"
},
"spec": {
"config": "{ \"cniVersion\":\"1.0.0\", \"name\":\"test\",\"plugins\": [ {\"type\":\"ovs\",\"mtu\":1500,\"ipam\":{} },\n{ \"type\": \"vrf\", \"vrfname\": \"blue\" }\n] }"
}
}
14 changes: 14 additions & 0 deletions api/v1/testdata/TestOVSRendering/complexconf.golden
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
{
"apiVersion": "k8s.cni.cncf.io/v1",
"kind": "NetworkAttachmentDefinition",
"metadata": {
"annotations": {
"k8s.v1.cni.cncf.io/resourceName": "/testresource"
},
"name": "test",
"namespace": "testnamespace"
},
"spec": {
"config": "{ \"cniVersion\":\"1.0.0\", \"name\":\"test\",\"type\":\"ovs\",\"capabilities\":{\"foo\": \"bar\"},\"bridge\":\"test\",\"vlan\":100,\"mtu\":1500,\"trunk\":[{\"id\":120},{\"minID\":500,\"maxID\":550}],\"interface_type\":\"netdev\",\"ipam\":{\"type\":\"foo\"} }"
}
}
Loading

0 comments on commit 0ca3629

Please sign in to comment.