Skip to content

Commit e13eb19

Browse files
shashank-netappkj-netappAlbinJohns-wkaparna0508alloydsa
authored
Adding Trident-Autogrow
Co-authored-by: kj-netapp <87856291+kj-netapp@users.noreply.github.com> Co-authored-by: Albin Johns <Albin.Johns@netapp.com> Co-authored-by: Aparna Singh <aparna.singh@netapp.com> Co-authored-by: Alloyd Savio Mendonca <167860552+alloydsa@users.noreply.github.com>
1 parent aa30ae5 commit e13eb19

File tree

223 files changed

+79797
-1794
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

223 files changed

+79797
-1794
lines changed

cli/api/types.go

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,10 @@ type MultipleStorageClassResponse struct {
4040
Items []StorageClass `json:"items"`
4141
}
4242

43+
type MultipleAutogrowPolicyResponse struct {
44+
Items []storage.AutogrowPolicyExternal `json:"items"`
45+
}
46+
4347
type MultipleVolumeResponse struct {
4448
Items []storage.VolumeExternal `json:"items"`
4549
}

cli/api/types_test.go

Lines changed: 163 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -245,6 +245,169 @@ func TestMultipleStorageClassResponse(t *testing.T) {
245245
assert.Len(t, result.Items, 2)
246246
}
247247

248+
func TestMultipleAutogrowPolicyResponse(t *testing.T) {
249+
tests := []struct {
250+
name string
251+
policies []storage.AutogrowPolicyExternal
252+
}{
253+
{
254+
name: "multiple policies",
255+
policies: []storage.AutogrowPolicyExternal{
256+
{
257+
Name: "policy1",
258+
UsedThreshold: "80",
259+
GrowthAmount: "20",
260+
MaxSize: "1000",
261+
State: storage.AutogrowPolicyStateSuccess,
262+
Volumes: []string{"vol1", "vol2"},
263+
VolumeCount: 2,
264+
},
265+
{
266+
Name: "policy2",
267+
UsedThreshold: "90",
268+
GrowthAmount: "10",
269+
MaxSize: "2000",
270+
State: storage.AutogrowPolicyStateSuccess,
271+
Volumes: []string{"vol3"},
272+
VolumeCount: 1,
273+
},
274+
},
275+
},
276+
{
277+
name: "single policy",
278+
policies: []storage.AutogrowPolicyExternal{
279+
{
280+
Name: "policy1",
281+
UsedThreshold: "85",
282+
GrowthAmount: "15",
283+
MaxSize: "1500",
284+
State: storage.AutogrowPolicyStateFailed,
285+
Volumes: []string{},
286+
VolumeCount: 0,
287+
},
288+
},
289+
},
290+
{
291+
name: "empty policies",
292+
policies: []storage.AutogrowPolicyExternal{},
293+
},
294+
{
295+
name: "policy with empty maxSize",
296+
policies: []storage.AutogrowPolicyExternal{
297+
{
298+
Name: "policy-no-max",
299+
UsedThreshold: "80",
300+
GrowthAmount: "20",
301+
MaxSize: "",
302+
State: storage.AutogrowPolicyStateSuccess,
303+
Volumes: []string{"vol1"},
304+
VolumeCount: 1,
305+
},
306+
},
307+
},
308+
{
309+
name: "policy with deleting state",
310+
policies: []storage.AutogrowPolicyExternal{
311+
{
312+
Name: "policy-deleting",
313+
UsedThreshold: "75",
314+
GrowthAmount: "25",
315+
MaxSize: "3000",
316+
State: storage.AutogrowPolicyStateDeleting,
317+
Volumes: []string{"vol1", "vol2", "vol3"},
318+
VolumeCount: 3,
319+
},
320+
},
321+
},
322+
}
323+
324+
for _, tt := range tests {
325+
t.Run(tt.name, func(t *testing.T) {
326+
response := MultipleAutogrowPolicyResponse{Items: tt.policies}
327+
328+
// Test marshaling
329+
jsonData, err := json.Marshal(response)
330+
require.NoError(t, err)
331+
assert.Contains(t, string(jsonData), "items")
332+
333+
// Verify JSON structure
334+
var jsonMap map[string]interface{}
335+
err = json.Unmarshal(jsonData, &jsonMap)
336+
require.NoError(t, err)
337+
assert.Contains(t, jsonMap, "items")
338+
339+
// Test unmarshaling
340+
var result MultipleAutogrowPolicyResponse
341+
err = json.Unmarshal(jsonData, &result)
342+
require.NoError(t, err)
343+
assert.Len(t, result.Items, len(tt.policies))
344+
345+
// Verify individual policy data
346+
for i, policy := range tt.policies {
347+
assert.Equal(t, policy.Name, result.Items[i].Name)
348+
assert.Equal(t, policy.UsedThreshold, result.Items[i].UsedThreshold)
349+
assert.Equal(t, policy.GrowthAmount, result.Items[i].GrowthAmount)
350+
assert.Equal(t, policy.MaxSize, result.Items[i].MaxSize)
351+
assert.Equal(t, policy.State, result.Items[i].State)
352+
assert.Equal(t, policy.VolumeCount, result.Items[i].VolumeCount)
353+
assert.ElementsMatch(t, policy.Volumes, result.Items[i].Volumes)
354+
}
355+
})
356+
}
357+
}
358+
359+
func TestMultipleAutogrowPolicyResponse_JSONRoundTrip(t *testing.T) {
360+
// Test that data survives JSON encoding/decoding cycle
361+
original := MultipleAutogrowPolicyResponse{
362+
Items: []storage.AutogrowPolicyExternal{
363+
{
364+
Name: "test-policy",
365+
UsedThreshold: "80",
366+
GrowthAmount: "20",
367+
MaxSize: "1000",
368+
State: storage.AutogrowPolicyStateSuccess,
369+
Volumes: []string{"volume-1", "volume-2", "volume-3"},
370+
VolumeCount: 3,
371+
},
372+
},
373+
}
374+
375+
// Marshal to JSON
376+
jsonData, err := json.Marshal(original)
377+
require.NoError(t, err)
378+
379+
// Unmarshal back
380+
var decoded MultipleAutogrowPolicyResponse
381+
err = json.Unmarshal(jsonData, &decoded)
382+
require.NoError(t, err)
383+
384+
// Verify data integrity
385+
require.Len(t, decoded.Items, 1)
386+
assert.Equal(t, original.Items[0].Name, decoded.Items[0].Name)
387+
assert.Equal(t, original.Items[0].UsedThreshold, decoded.Items[0].UsedThreshold)
388+
assert.Equal(t, original.Items[0].GrowthAmount, decoded.Items[0].GrowthAmount)
389+
assert.Equal(t, original.Items[0].MaxSize, decoded.Items[0].MaxSize)
390+
assert.Equal(t, original.Items[0].State, decoded.Items[0].State)
391+
assert.Equal(t, original.Items[0].VolumeCount, decoded.Items[0].VolumeCount)
392+
assert.ElementsMatch(t, original.Items[0].Volumes, decoded.Items[0].Volumes)
393+
}
394+
395+
func TestMultipleAutogrowPolicyResponse_NilItems(t *testing.T) {
396+
// Test with nil Items slice
397+
response := MultipleAutogrowPolicyResponse{Items: nil}
398+
399+
// Test marshaling
400+
jsonData, err := json.Marshal(response)
401+
require.NoError(t, err)
402+
403+
// Test unmarshaling
404+
var result MultipleAutogrowPolicyResponse
405+
err = json.Unmarshal(jsonData, &result)
406+
require.NoError(t, err)
407+
// Note: nil slices become empty slices after JSON round-trip in Go
408+
assert.Empty(t, result.Items)
409+
}
410+
248411
func TestMultipleVolumeResponse(t *testing.T) {
249412
volumes := []storage.VolumeExternal{
250413
{

cli/cmd/get_autogrowpolicy.go

Lines changed: 175 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,175 @@
1+
// Copyright 2026 NetApp, Inc. All Rights Reserved.
2+
3+
package cmd
4+
5+
import (
6+
"encoding/json"
7+
"fmt"
8+
"net/http"
9+
"os"
10+
"sort"
11+
"strconv"
12+
13+
"github.com/olekukonko/tablewriter"
14+
"github.com/spf13/cobra"
15+
16+
"github.com/netapp/trident/cli/api"
17+
"github.com/netapp/trident/config"
18+
"github.com/netapp/trident/frontend/rest"
19+
"github.com/netapp/trident/storage"
20+
"github.com/netapp/trident/utils/errors"
21+
)
22+
23+
func init() {
24+
getCmd.AddCommand(getAutogrowPolicyCmd)
25+
}
26+
27+
var getAutogrowPolicyCmd = &cobra.Command{
28+
Use: "autogrowpolicy [<name>...]",
29+
Short: "Get one or more autogrow policies from Trident",
30+
Aliases: []string{"agp", "autogrowpolicies"},
31+
RunE: func(cmd *cobra.Command, args []string) error {
32+
if OperatingMode == ModeTunnel {
33+
command := []string{"get", "autogrowpolicy"}
34+
out, err := TunnelCommand(append(command, args...))
35+
printOutput(cmd, out, err)
36+
return err
37+
} else {
38+
return autogrowPolicyList(args)
39+
}
40+
},
41+
}
42+
43+
func autogrowPolicyList(autogrowPolicyNames []string) error {
44+
var err error
45+
46+
// If no policies were specified, we'll get all of them
47+
getAll := false
48+
if len(autogrowPolicyNames) == 0 {
49+
getAll = true
50+
autogrowPolicyNames, err = GetAutogrowPolicies()
51+
if err != nil {
52+
return err
53+
}
54+
}
55+
56+
autogrowPolicies := make([]storage.AutogrowPolicyExternal, 0, config.DefaultAutogrowPoliciesForCLI)
57+
58+
// Get the actual policy objects
59+
for _, autogrowPolicyName := range autogrowPolicyNames {
60+
autogrowPolicy, err := GetAutogrowPolicy(autogrowPolicyName)
61+
if err != nil {
62+
if getAll && errors.IsAutogrowPolicyNotFoundError(err) {
63+
continue
64+
}
65+
return err
66+
}
67+
autogrowPolicies = append(autogrowPolicies, autogrowPolicy)
68+
}
69+
70+
WriteAutogrowPolicies(autogrowPolicies)
71+
72+
return nil
73+
}
74+
75+
func GetAutogrowPolicies() ([]string, error) {
76+
url := BaseURL() + "/autogrowpolicy"
77+
78+
response, responseBody, err := api.InvokeRESTAPI("GET", url, nil)
79+
if err != nil {
80+
return nil, err
81+
} else if response.StatusCode != http.StatusOK {
82+
return nil, fmt.Errorf("could not get Autogrow policies: %v",
83+
GetErrorFromHTTPResponse(response, responseBody))
84+
}
85+
86+
var listPoliciesResponse rest.ListAutogrowPoliciesResponse
87+
err = json.Unmarshal(responseBody, &listPoliciesResponse)
88+
if err != nil {
89+
return nil, err
90+
}
91+
92+
return listPoliciesResponse.AutogrowPolicies, nil
93+
}
94+
95+
func GetAutogrowPolicy(autogrowPolicyName string) (storage.AutogrowPolicyExternal, error) {
96+
url := BaseURL() + "/autogrowpolicy/" + autogrowPolicyName
97+
98+
response, responseBody, err := api.InvokeRESTAPI("GET", url, nil)
99+
if err != nil {
100+
return storage.AutogrowPolicyExternal{}, err
101+
} else if response.StatusCode != http.StatusOK {
102+
errorMessage := fmt.Sprintf("could not get autogrow policy %s: %v", autogrowPolicyName,
103+
GetErrorFromHTTPResponse(response, responseBody))
104+
switch response.StatusCode {
105+
case http.StatusNotFound:
106+
return storage.AutogrowPolicyExternal{}, errors.AutogrowPolicyNotFoundError(errorMessage)
107+
default:
108+
return storage.AutogrowPolicyExternal{}, errors.New(errorMessage)
109+
}
110+
}
111+
112+
var getPolicyResponse rest.GetAutogrowPolicyResponse
113+
err = json.Unmarshal(responseBody, &getPolicyResponse)
114+
if err != nil {
115+
return storage.AutogrowPolicyExternal{}, err
116+
}
117+
118+
return getPolicyResponse.AutogrowPolicy, nil
119+
}
120+
121+
func WriteAutogrowPolicies(autogrowPolicies []storage.AutogrowPolicyExternal) {
122+
switch OutputFormat {
123+
case FormatJSON:
124+
WriteJSON(api.MultipleAutogrowPolicyResponse{Items: autogrowPolicies})
125+
case FormatYAML:
126+
WriteYAML(api.MultipleAutogrowPolicyResponse{Items: autogrowPolicies})
127+
case FormatName:
128+
writeAutogrowPolicyNames(autogrowPolicies)
129+
default:
130+
writeAutogrowPolicyTable(autogrowPolicies)
131+
}
132+
}
133+
134+
func writeAutogrowPolicyTable(autogrowPolicies []storage.AutogrowPolicyExternal) {
135+
// Sort autogrowPolicies alphabetically by name
136+
sort.Slice(autogrowPolicies, func(i, j int) bool {
137+
return autogrowPolicies[i].Name < autogrowPolicies[j].Name
138+
})
139+
140+
table := tablewriter.NewWriter(os.Stdout)
141+
table.SetHeader([]string{"Name", "Used Threshold", "Growth Amount", "Max Size", "Volumes"})
142+
143+
for _, agp := range autogrowPolicies {
144+
maxSize := agp.MaxSize
145+
if maxSize == "" {
146+
maxSize = "N/A"
147+
}
148+
149+
growthAmount := agp.GrowthAmount
150+
if growthAmount == "" {
151+
growthAmount = config.DefaultAGPGrowthAmount // Default value
152+
}
153+
154+
table.Append([]string{
155+
agp.Name,
156+
agp.UsedThreshold,
157+
growthAmount,
158+
maxSize,
159+
strconv.Itoa(agp.VolumeCount),
160+
})
161+
}
162+
163+
table.Render()
164+
}
165+
166+
func writeAutogrowPolicyNames(autogrowPolicies []storage.AutogrowPolicyExternal) {
167+
// Sort autogrowPolicies alphabetically by name
168+
sort.Slice(autogrowPolicies, func(i, j int) bool {
169+
return autogrowPolicies[i].Name < autogrowPolicies[j].Name
170+
})
171+
172+
for _, agp := range autogrowPolicies {
173+
fmt.Println(agp.Name)
174+
}
175+
}

0 commit comments

Comments
 (0)