Repository: Wind-River/gophercloud
Pull request #48: Support for MaxTxRate/ MaxRxRate for rate limiting
Author: login: msheikh-wind
Created: 2025-05-18T13:46:39Z | Merged: 2025-05-23T12:52:49Z

Update MaxTxRate/ MaxRxRate for rate limiting

Test Plan:
PASS: " make && DEBUG=yes and make docker-build" finish successfully



Diff:
diff --git a/starlingx/inventory/v1/interfaces/requests.go b/starlingx/inventory/v1/interfaces/requests.go
index 40ce25cacf..0f24c9addd 100644
--- a/starlingx/inventory/v1/interfaces/requests.go
+++ b/starlingx/inventory/v1/interfaces/requests.go
@@ -76,6 +76,7 @@ type InterfaceOpts struct {
 	UsesModify       *[]string `json:"usesmodify,omitempty" mapstructure:"usesmodify"`
 	PTPRole          *string   `json:"ptp_role,omitempty" mapstructure:"ptp_role"`
 	MaxTxRate        *int      `json:"max_tx_rate,omitempty" mapstructure:"max_tx_rate"`
+	MaxRxRate        *int      `json:"max_rx_rate,omitempty" mapstructure:"max_rx_rate"`
 }
 
 // ListOptsBuilder allows extensions to add additional parameters to the
diff --git a/starlingx/inventory/v1/interfaces/results.go b/starlingx/inventory/v1/interfaces/results.go
index 8ab680406f..73493d2f05 100644
--- a/starlingx/inventory/v1/interfaces/results.go
+++ b/starlingx/inventory/v1/interfaces/results.go
@@ -114,8 +114,11 @@ type Interface struct {
 	// PTPRole is the configuration of the interface as ptp master, slave, or none.
 	PTPRole *string `json:"ptp_role,omitempty"`
 
-	// VFCount is the number of SRIOV VF interfaces configured.
-	MaxTxRate *int `json:"max_tx_rate,omitempty"`
+	// max_tx_rate for tx rate limit configuration.
+	MaxTxRate int `json:"max_tx_rate"`
+
+	// max_rx_rate for rx rate limit configuration.
+	MaxRxRate int `json:"max_rx_rate"`
 }
 
 // InterfacePage is the page returned by a pager when traversing over a
diff --git a/starlingx/inventory/v1/interfaces/testing/fixtures.go b/starlingx/inventory/v1/interfaces/testing/fixtures.go
index 0cca18877a..3f496dfb78 100644
--- a/starlingx/inventory/v1/interfaces/testing/fixtures.go
+++ b/starlingx/inventory/v1/interfaces/testing/fixtures.go
@@ -42,6 +42,28 @@ var (
 		Users:          []string{},
 		PTPRole:        &PTPRoles[0],
 	}
+	PlatformInterfaceHerp = interfaces.Interface{
+		ID:             "7499f727-e19b-4e9b-a571-5919bad20dc3",
+		Name:           "Herp",
+		Type:           interfaces.IFTypeEthernet,
+		Class:          interfaces.IFClassPlatform,
+		MTU:            1500,
+		MaxTxRate:      30,
+		MaxRxRate:      40,
+		VID:            nil,
+		IPv4Mode:       &IPv4Modes[0],
+		IPv4Pool:       nil,
+		IPv6Mode:       &IPv6Modes[0],
+		IPv6Pool:       nil,
+		Networks:       nil,
+		DataNetworks:   nil,
+		AEMode:         nil,
+		AETransmitHash: nil,
+		VFCount:        &VFCounts[0],
+		Uses:           []string{"data0"},
+		Users:          []string{},
+		PTPRole:        &PTPRoles[0],
+	}
 	InterfaceDerp = interfaces.Interface{
 		ID:             "a5965fee-dc60-40dc-a234-edf87f1f9380",
 		Name:           "Derp",
@@ -285,6 +307,44 @@ const InterfaceSingleBody = `
 }
 `
 
+const PlatformInterfaceSingleBody = `
+{
+      "aemode": null,
+      "forihostid": 2,
+      "ifclass": "platform",
+      "ifname": "Herp",
+      "iftype": "ethernet",
+      "ihost_uuid": "f73dda8e-be3c-4704-ad1e-ed99e44b846e",
+      "imac": "08:00:27:25:6a:20",
+      "imtu": 1500,
+      "max_tx_rate": 30,
+      "max_rx_rate": 40,
+      "ipv4_mode": "static",
+      "ipv6_mode": "static",
+      "links": [
+        {
+          "href": "http://192.168.204.2:6385/v1/iinterfaces/67f0631b-4616-4308-af36-820633f6a70e",
+          "rel": "self"
+        },
+        {
+          "href": "http://192.168.204.2:6385/iinterfaces/67f0631b-4616-4308-af36-820633f6a70e",
+          "rel": "bookmark"
+        }
+      ],
+      "schedpolicy": null,
+      "sriov_numvfs": 0,
+      "sriov_vf_driver": null,
+      "txhashpolicy": null,
+      "used_by": [],
+      "uses": [
+        "data0"
+      ],
+      "uuid": "7499f727-e19b-4e9b-a571-5919bad20dc3",
+      "vlan_id": null,
+      "ptp_role": "master"
+}
+`
+
 func HandleInterfaceListSuccessfully(t *testing.T) {
 	th.Mux.HandleFunc("/ihosts/f73dda8e-be3c-4704-ad1e-ed99e44b846e/iinterfaces", func(w http.ResponseWriter, r *http.Request) {
 		th.TestMethod(t, r, "GET")
@@ -348,3 +408,30 @@ func HandleInterfaceCreationSuccessfully(t *testing.T, response string) {
 		fmt.Fprintf(w, response)
 	})
 }
+
+func HandlePlatformInterfaceCreationSuccessfully(t *testing.T, response string) {
+	th.Mux.HandleFunc("/iinterfaces", func(w http.ResponseWriter, r *http.Request) {
+		th.TestMethod(t, r, "POST")
+		th.TestHeader(t, r, "X-Auth-Token", client.TokenID)
+		th.TestJSONRequest(t, r, `{
+          "ifclass": "platform",
+          "ifname": "Herp",
+          "iftype": "ethernet",
+          "ihost_uuid": "f73dda8e-be3c-4704-ad1e-ed99e44b846e",
+          "imtu": 1500,
+          "max_tx_rate": 30,
+          "max_rx_rate": 40,
+          "ipv4_mode": "pool",
+          "ipv4_pool": "dbfa4c2e-4526-4aaf-b07b-a3da7aeb6c26",
+          "ipv6_mode": "pool",
+          "ipv6_pool": "934d8341-5114-46d2-9560-7c47618892c7",
+          "uses": [],
+          "usesmodify": [],
+          "ptp_role": "master"
+        }`)
+
+		w.WriteHeader(http.StatusAccepted)
+		w.Header().Add("Content-Type", "application/json")
+		fmt.Fprintf(w, response)
+	})
+}
diff --git a/starlingx/inventory/v1/interfaces/testing/requests_test.go b/starlingx/inventory/v1/interfaces/testing/requests_test.go
index de93643011..b4525b8d57 100644
--- a/starlingx/inventory/v1/interfaces/testing/requests_test.go
+++ b/starlingx/inventory/v1/interfaces/testing/requests_test.go
@@ -119,6 +119,53 @@ func TestCreateInterface(t *testing.T) {
 	th.CheckDeepEquals(t, InterfaceDerp, *actual)
 }
 
+func TestCreatePlatformInterface(t *testing.T) {
+	th.SetupHTTP()
+	defer th.TeardownHTTP()
+	HandlePlatformInterfaceCreationSuccessfully(t, PlatformInterfaceSingleBody)
+	interfaceHostID := "f73dda8e-be3c-4704-ad1e-ed99e44b846e"
+	interfaceName := "Herp"
+	interfaceType := interfaces.IFTypeEthernet
+	interfaceClass := interfaces.IFClassPlatform
+	interfaceMTU := 1500
+	interfaceIPv4Mode := interfaces.AddressModePool
+	interfaceIPv4Pool := "dbfa4c2e-4526-4aaf-b07b-a3da7aeb6c26"
+	interfaceIPv6Mode := interfaces.AddressModePool
+	interfaceIPv6Pool := "934d8341-5114-46d2-9560-7c47618892c7"
+	interfaceUses := []string{}
+	interfaceUsers := []string{}
+	ptpRole := interfaces.PTPRoleMaster
+	maxTxRate := 30
+	maxRxRate := 40
+
+	actual, err := interfaces.Create(client.ServiceClient(), interfaces.InterfaceOpts{
+		HostUUID:         &interfaceHostID,
+		Type:             &interfaceType,
+		Name:             &interfaceName,
+		Class:            &interfaceClass,
+		MTU:              &interfaceMTU,
+		VID:              nil,
+		IPv4Mode:         &interfaceIPv4Mode,
+		IPv4Pool:         &interfaceIPv4Pool,
+		IPv6Mode:         &interfaceIPv6Mode,
+		IPv6Pool:         &interfaceIPv6Pool,
+		Networks:         nil,
+		NetworksToAdd:    nil,
+		NetworksToDelete: nil,
+		DataNetworks:     nil,
+		AEMode:           nil,
+		AETransmitHash:   nil,
+		Uses:             &interfaceUses,
+		UsesModify:       &interfaceUsers,
+		PTPRole:          &ptpRole,
+		MaxTxRate:        &maxTxRate,
+		MaxRxRate:        &maxRxRate,
+	}).Extract()
+	th.AssertNoErr(t, err)
+
+	th.CheckDeepEquals(t, PlatformInterfaceHerp, *actual)
+}
+
 func TestUpdateInterface(t *testing.T) {
 	th.SetupHTTP()
 	defer th.TeardownHTTP()


Reviews:
user: login: yjian118
body: Expected to update the unit test in testing sub-dir, and run go test to verify the test cases
state: CHANGES_REQUESTED
author_association: COLLABORATOR
submitted_at: 2025-05-21T15:50:48Z
user: login: wasnio
state: COMMENTED
author_association: COLLABORATOR
submitted_at: 2025-05-22T11:33:52Z
user: login: msheikh-wind
state: COMMENTED
author_association: NONE
submitted_at: 2025-05-22T11:39:37Z
user: login: wasnio
state: COMMENTED
author_association: COLLABORATOR
submitted_at: 2025-05-22T11:49:33Z
user: login: msheikh-wind
state: COMMENTED
author_association: NONE
submitted_at: 2025-05-22T12:41:21Z
user: login: hugonicodemos
state: APPROVED
author_association: COLLABORATOR
submitted_at: 2025-05-23T12:27:14Z
user: login: yjian118
state: APPROVED
author_association: COLLABORATOR
submitted_at: 2025-05-23T12:52:41Z

Review comments:
diff_hunk: @@ -114,8 +114,11 @@ type Interface struct {
 	// PTPRole is the configuration of the interface as ptp master, slave, or none.
 	PTPRole *string `json:"ptp_role,omitempty"`
 
-	// VFCount is the number of SRIOV VF interfaces configured.
-	MaxTxRate *int `json:"max_tx_rate,omitempty"`
+	// max_tx_rate for tx rate limit configuration.
+	MaxTxRate int `json:"max_tx_rate"`
path: starlingx/inventory/v1/interfaces/results.go
user: login: wasnio
body: why did you change from pointer to integer and remove the omitempty?
created_at: 2025-05-22T11:33:52Z
author_association: COLLABORATOR
diff_hunk: @@ -114,8 +114,11 @@ type Interface struct {
 	// PTPRole is the configuration of the interface as ptp master, slave, or none.
 	PTPRole *string `json:"ptp_role,omitempty"`
 
-	// VFCount is the number of SRIOV VF interfaces configured.
-	MaxTxRate *int `json:"max_tx_rate,omitempty"`
+	// max_tx_rate for tx rate limit configuration.
+	MaxTxRate int `json:"max_tx_rate"`
path: starlingx/inventory/v1/interfaces/results.go
user: login: msheikh-wind
body: I added MaxTxRate for all ethernet, vlan, bond, and MaxTxRate was already there for vf. Now this is in common place for all so it will also work for vf. I followed the similar to MTU implementation.
created_at: 2025-05-22T11:39:37Z
author_association: NONE
diff_hunk: @@ -114,8 +114,11 @@ type Interface struct {
 	// PTPRole is the configuration of the interface as ptp master, slave, or none.
 	PTPRole *string `json:"ptp_role,omitempty"`
 
-	// VFCount is the number of SRIOV VF interfaces configured.
-	MaxTxRate *int `json:"max_tx_rate,omitempty"`
+	// max_tx_rate for tx rate limit configuration.
+	MaxTxRate int `json:"max_tx_rate"`
path: starlingx/inventory/v1/interfaces/results.go
user: login: wasnio
body: by sysinv, these values can be null: https://opendev.org/starlingx/config/src/commit/85d6498e0c23ac4b98fab0b339caa4556832c91f/sysinv/sysinv/sysinv/sysinv/objects/interface.py#L151-L152

Are you sure sysinv will always return an integer value? otherwise, the manager will crash failing to json marshal.
created_at: 2025-05-22T11:49:19Z
author_association: COLLABORATOR
diff_hunk: @@ -114,8 +114,11 @@ type Interface struct {
 	// PTPRole is the configuration of the interface as ptp master, slave, or none.
 	PTPRole *string `json:"ptp_role,omitempty"`
 
-	// VFCount is the number of SRIOV VF interfaces configured.
-	MaxTxRate *int `json:"max_tx_rate,omitempty"`
+	// max_tx_rate for tx rate limit configuration.
+	MaxTxRate int `json:"max_tx_rate"`
path: starlingx/inventory/v1/interfaces/results.go
user: login: msheikh-wind
body: Yes, sysinv will always pass int value. also we tested that, if the max_tx_rate/max_rx_rate is not configured in sysinv, then dm is still able to parse properly.  Also existing imtu is defined in same way in sysinv.
'imtu': utils.int_or_none
created_at: 2025-05-22T12:41:21Z
author_association: NONE

PR comments:
user: login: msheikh-wind
created_at: 2025-05-22T11:27:57Z
body: Updated the unit test case and ran the "go test" and result is passed.

Test result:

PASS
ok      github.com/gophercloud/gophercloud/starlingx/inventory/v1/interfaces/testing    0.017s
author_association: NONE