From e961d9ad4e7ce193f9264126496246d05a45fdf1 Mon Sep 17 00:00:00 2001 From: hardikl Date: Fri, 3 Apr 2026 19:39:17 +0530 Subject: [PATCH 01/10] feat: adding fcp service and fc interface tools --- descriptions/descriptions.go | 8 ++ docs/examples.md | 20 +++ integration/test/fcp_test.go | 99 +++++++++++++ ontap/ontap.go | 22 +++ rest/fcp.go | 196 +++++++++++++++++++++++++ server/fcp.go | 270 +++++++++++++++++++++++++++++++++++ server/server.go | 10 ++ tool/tool.go | 16 +++ 8 files changed, 641 insertions(+) create mode 100644 integration/test/fcp_test.go create mode 100644 rest/fcp.go create mode 100644 server/fcp.go diff --git a/descriptions/descriptions.go b/descriptions/descriptions.go index 0da2bd7..e38bcc7 100644 --- a/descriptions/descriptions.go +++ b/descriptions/descriptions.go @@ -76,6 +76,14 @@ const CreateNVMeService = `Create NVMe service on a cluster by cluster name.` const UpdateNVMeService = `Update NVMe service on a cluster by cluster name.` const DeleteNVMeService = `Delete NVMe service on a cluster by cluster name.` +const CreateFCPService = `Create FCP service on a cluster by cluster name.` +const UpdateFCPService = `Update FCP service on a cluster by cluster name.` +const DeleteFCPService = `Delete FCP service on a cluster by cluster name.` + +const CreateFCInterface = `Create FC interface on a cluster by cluster name.` +const UpdateFCInterface = `Update FC interface on a cluster by cluster name.` +const DeleteFCInterface = `Delete FC interface on a cluster by cluster name.` + const ListOntapEndpoints = `List ONTAP REST collection endpoints in the catalog. The catalog contains all endpoints — can be large. Prefer search_ontap_endpoints for targeted discovery. Use the optional 'match' parameter to filter by substring or regex pattern (e.g. "snapshot", "lun", ".*nfs.*export.*"). diff --git a/docs/examples.md b/docs/examples.md index 4a9dde7..d46fb39 100644 --- a/docs/examples.md +++ b/docs/examples.md @@ -263,6 +263,26 @@ Expected Response: The nvme service has been successfully updated. --- +### Manage FCP + +- On the umeng-aff300-05-06 cluster, enable fcp service in marketing svm + +Expected Response: The fcp service has been successfully created. + +- On the umeng-aff300-05-06 cluster, create fc interface fc1 in marketing svm at port 0e in node umeng-aff300-01 of fcp data protocol + +Expected Response: The fc interface has been successfully created. + +- On the umeng-aff300-05-06 cluster, delete fc interface fc1 in marketing svm + +Expected Response: The fc interface has been successfully deleted. + +- On the umeng-aff300-05-06 cluster, update fcp service to disable on the marketing svm + +Expected Response: The fcp service has been successfully updated. + +--- + ### Querying Specific Fields **Get volume space and protection details** diff --git a/integration/test/fcp_test.go b/integration/test/fcp_test.go new file mode 100644 index 0000000..71dfcaf --- /dev/null +++ b/integration/test/fcp_test.go @@ -0,0 +1,99 @@ +package main + +import ( + "context" + "crypto/tls" + "log/slog" + "net/http" + "testing" + "time" + + "github.com/netapp/ontap-mcp/config" +) + +func TestFCP(t *testing.T) { + SkipIfMissing(t, CheckTools) + + tests := []struct { + name string + input string + expectedOntapErr string + verifyAPI ontapVerifier + }{ + { + name: "Update FCP service", + input: SarClusterStr + "update fcp service to disable on the marketing svm", + expectedOntapErr: "because it does not exist", + verifyAPI: ontapVerifier{}, + }, + { + name: "Clean FCP service", + input: SarClusterStr + "delete fcp service in marketing svm", + expectedOntapErr: "because it does not exist", + verifyAPI: ontapVerifier{api: "api/protocols/san/fcp/services?svm=marketing", validationFunc: deleteObject}, + }, + { + name: "Create FCP service", + input: SarClusterStr + "enable fcp service in marketing svm", + expectedOntapErr: "", + verifyAPI: ontapVerifier{api: "api/protocols/san/fcp/services?svm=marketing", validationFunc: createObject}, + }, + { + name: "Create FC Interface", + input: SarClusterStr + "create fc interface " + rn("fc1") + " in marketing svm at port 0e in node umeng-aff300-01 of fcp data protocol", + expectedOntapErr: "", + verifyAPI: ontapVerifier{api: "api/network/fc/interfaces?name=" + rn("fc1") + "&svm=marketing", validationFunc: createObject}, + }, + { + name: "Update FC Interface", + input: SarClusterStr + "disable fc interface " + rn("fc1") + " in marketing svm", + expectedOntapErr: "", + verifyAPI: ontapVerifier{}, + }, + { + name: "Clean FC Interface", + input: SarClusterStr + "delete fc interface " + rn("fc1") + " in marketing svm", + expectedOntapErr: "because it does not exist", + verifyAPI: ontapVerifier{api: "api/network/fc/interfaces?name=" + rn("fc1") + "&svm=marketing", validationFunc: deleteObject}, + }, + { + name: "Update FCP service", + input: SarClusterStr + "update fcp service to disable on the marketing svm", + expectedOntapErr: "", + verifyAPI: ontapVerifier{}, + }, + { + name: "Clean FCP service", + input: SarClusterStr + "delete fcp service in marketing svm", + expectedOntapErr: "because it does not exist", + verifyAPI: ontapVerifier{api: "api/protocols/san/fcp/services?svm=marketing", validationFunc: deleteObject}, + }, + } + + cfg, err := config.ReadConfig(ConfigFile) + if err != nil { + t.Fatalf("Error parsing the config: %v", err) + } + + poller := cfg.Pollers[SarCluster] + transport := &http.Transport{ + TLSClientConfig: &tls.Config{ + InsecureSkipVerify: poller.UseInsecureTLS, // #nosec G402 + }, + } + client := &http.Client{Transport: transport, Timeout: 10 * time.Second} + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + slog.Debug("", slog.String("Input", tt.input)) + ctx, cancel := context.WithTimeout(context.Background(), 3*time.Minute) + defer cancel() + if _, err := testAgent.ChatWithResponse(ctx, t, tt.input, tt.expectedOntapErr); err != nil { + t.Fatalf("Error processing input %q: %v", tt.input, err) + } + if tt.verifyAPI.api != "" && !tt.verifyAPI.validationFunc(t, tt.verifyAPI.api, poller, client) { + t.Errorf("Error while accessing the object via prompt %q", tt.input) + } + }) + } +} diff --git a/ontap/ontap.go b/ontap/ontap.go index 958fd3e..c99cf51 100644 --- a/ontap/ontap.go +++ b/ontap/ontap.go @@ -194,6 +194,28 @@ type NVMeService struct { Enabled string `json:"enabled,omitzero" jsonschema:"admin state of the NVMe service"` } +type FCPService struct { + SVM NameAndUUID `json:"svm" jsonschema:"svm name"` + Enabled string `json:"enabled,omitzero" jsonschema:"admin state of the FCP service"` +} + +type FCInterfacePort struct { + Name string `json:"name,omitzero" jsonschema:"FC port name"` + Node NameAndUUID `json:"node,omitzero" jsonschema:"node on which the FC port is located"` +} + +type FCInterfaceLocation struct { + HomePort FCInterfacePort `json:"home_port,omitzero" jsonschema:"home port of the FC interface"` +} + +type FCInterface struct { + SVM NameAndUUID `json:"svm,omitzero" jsonschema:"svm name"` + Name string `json:"name,omitzero" jsonschema:"FC interface name"` + DataProtocol string `json:"data_protocol,omitzero" jsonschema:"data protocol of the FC interface (e.g. fcp)"` + Enabled string `json:"enabled,omitzero" jsonschema:"admin state of the FC interface"` + Location FCInterfaceLocation `json:"location,omitzero" jsonschema:"location of the FC interface"` +} + const ( ASAr2 = "asar2" CDOT = "cdot" diff --git a/rest/fcp.go b/rest/fcp.go new file mode 100644 index 0000000..e801460 --- /dev/null +++ b/rest/fcp.go @@ -0,0 +1,196 @@ +package rest + +import ( + "context" + "fmt" + "net/http" + "net/url" + + "github.com/netapp/ontap-mcp/ontap" +) + +func (c *Client) CreateFCPService(ctx context.Context, fcpService ontap.FCPService) error { + var statusCode int + + responseHeaders := http.Header{} + + builder := c.baseRequestBuilder(`/api/protocols/san/fcp/services`, &statusCode, responseHeaders). + BodyJSON(fcpService) + + if err := c.buildAndExecuteRequest(ctx, builder); err != nil { + return err + } + + return c.checkStatus(statusCode) +} + +func (c *Client) UpdateFCPService(ctx context.Context, svmName string, fcpService ontap.FCPService) error { + var ( + statusCode int + fcpSr ontap.GetData + ) + + responseHeaders := http.Header{} + + params := url.Values{} + params.Set("svm.name", svmName) + + builder := c.baseRequestBuilder(`/api/protocols/san/fcp/services`, &statusCode, responseHeaders). + Params(params). + ToJSON(&fcpSr) + + if err := c.buildAndExecuteRequest(ctx, builder); err != nil { + return err + } + + if fcpSr.NumRecords == 0 { + return fmt.Errorf("failed to get detail of fcp service in svm %s because it does not exist", svmName) + } + + if fcpSr.NumRecords != 1 { + return fmt.Errorf("failed to get fcp service on svm=%s because there are %d matching records", + svmName, fcpSr.NumRecords) + } + + builder = c.baseRequestBuilder(`/api/protocols/san/fcp/services/`+fcpSr.Records[0].Svm.UUID, &statusCode, responseHeaders). + BodyJSON(fcpService). + Patch() + + if err := c.buildAndExecuteRequest(ctx, builder); err != nil { + return err + } + + return c.checkStatus(statusCode) +} + +func (c *Client) DeleteFCPService(ctx context.Context, svmName string) error { + var ( + statusCode int + fcpSr ontap.GetData + ) + + responseHeaders := http.Header{} + + params := url.Values{} + params.Set("svm.name", svmName) + + builder := c.baseRequestBuilder(`/api/protocols/san/fcp/services`, &statusCode, responseHeaders). + Params(params). + ToJSON(&fcpSr) + + if err := c.buildAndExecuteRequest(ctx, builder); err != nil { + return err + } + + if fcpSr.NumRecords == 0 { + return fmt.Errorf("failed to get detail of fcp service in svm %s because it does not exist", svmName) + } + + if fcpSr.NumRecords != 1 { + return fmt.Errorf("failed to get fcp service on svm=%s because there are %d matching records", + svmName, fcpSr.NumRecords) + } + + builder = c.baseRequestBuilder(`/api/protocols/san/fcp/services/`+fcpSr.Records[0].Svm.UUID, &statusCode, responseHeaders). + Delete() + + if err := c.buildAndExecuteRequest(ctx, builder); err != nil { + return err + } + + return c.checkStatus(statusCode) +} + +func (c *Client) CreateFCInterface(ctx context.Context, fcInterface ontap.FCInterface) error { + var statusCode int + + responseHeaders := http.Header{} + + builder := c.baseRequestBuilder(`/api/network/fc/interfaces`, &statusCode, responseHeaders). + BodyJSON(fcInterface) + + if err := c.buildAndExecuteRequest(ctx, builder); err != nil { + return err + } + + return c.checkStatus(statusCode) +} + +func (c *Client) UpdateFCInterface(ctx context.Context, svmName string, name string, fcInterface ontap.FCInterface) error { + var ( + statusCode int + fcIfSr ontap.GetData + ) + + responseHeaders := http.Header{} + + params := url.Values{} + params.Set("svm.name", svmName) + params.Set("name", name) + + builder := c.baseRequestBuilder(`/api/network/fc/interfaces`, &statusCode, responseHeaders). + Params(params). + ToJSON(&fcIfSr) + + if err := c.buildAndExecuteRequest(ctx, builder); err != nil { + return err + } + + if fcIfSr.NumRecords == 0 { + return fmt.Errorf("failed to get detail of fc interface %s in svm %s because it does not exist", name, svmName) + } + + if fcIfSr.NumRecords != 1 { + return fmt.Errorf("failed to update fc interface %s in svm=%s because there are %d matching records", + name, svmName, fcIfSr.NumRecords) + } + + builder = c.baseRequestBuilder(`/api/network/fc/interfaces/`+fcIfSr.Records[0].UUID, &statusCode, responseHeaders). + BodyJSON(fcInterface). + Patch() + + if err := c.buildAndExecuteRequest(ctx, builder); err != nil { + return err + } + + return c.checkStatus(statusCode) +} + +func (c *Client) DeleteFCInterface(ctx context.Context, svmName string, name string) error { + var ( + statusCode int + fcIfSr ontap.GetData + ) + + responseHeaders := http.Header{} + + params := url.Values{} + params.Set("svm.name", svmName) + params.Set("name", name) + + builder := c.baseRequestBuilder(`/api/network/fc/interfaces`, &statusCode, responseHeaders). + Params(params). + ToJSON(&fcIfSr) + + if err := c.buildAndExecuteRequest(ctx, builder); err != nil { + return err + } + + if fcIfSr.NumRecords == 0 { + return fmt.Errorf("failed to get detail of fc interface %s in svm %s because it does not exist", name, svmName) + } + + if fcIfSr.NumRecords != 1 { + return fmt.Errorf("failed to delete fc interface %s in svm=%s because there are %d matching records", + name, svmName, fcIfSr.NumRecords) + } + + builder = c.baseRequestBuilder(`/api/network/fc/interfaces/`+fcIfSr.Records[0].UUID, &statusCode, responseHeaders). + Delete() + + if err := c.buildAndExecuteRequest(ctx, builder); err != nil { + return err + } + + return c.checkStatus(statusCode) +} diff --git a/server/fcp.go b/server/fcp.go new file mode 100644 index 0000000..1450631 --- /dev/null +++ b/server/fcp.go @@ -0,0 +1,270 @@ +package server + +import ( + "context" + "errors" + "fmt" + + "github.com/modelcontextprotocol/go-sdk/mcp" + "github.com/netapp/ontap-mcp/ontap" + "github.com/netapp/ontap-mcp/tool" +) + +func (a *App) CreateFCPService(ctx context.Context, _ *mcp.CallToolRequest, parameters tool.FCPService) (*mcp.CallToolResult, any, error) { + if !a.locks.TryLock(parameters.Cluster) { + return errorResult(fmt.Errorf("another write operation is in progress on cluster %s, please try again", parameters.Cluster)), nil, nil + } + defer a.locks.Unlock(parameters.Cluster) + + fcpServiceCreate, err := newCreateFCPService(parameters) + if err != nil { + return nil, nil, err + } + + client, err := a.getClient(parameters.Cluster) + if err != nil { + return errorResult(err), nil, err + } + err = client.CreateFCPService(ctx, fcpServiceCreate) + + if err != nil { + return errorResult(err), nil, err + } + + return &mcp.CallToolResult{ + Content: []mcp.Content{ + &mcp.TextContent{Text: "FCP service created successfully"}, + }, + }, nil, nil +} + +func (a *App) UpdateFCPService(ctx context.Context, _ *mcp.CallToolRequest, parameters tool.FCPService) (*mcp.CallToolResult, any, error) { + if !a.locks.TryLock(parameters.Cluster) { + return errorResult(fmt.Errorf("another write operation is in progress on cluster %s, please try again", parameters.Cluster)), nil, nil + } + defer a.locks.Unlock(parameters.Cluster) + + fcpServiceUpdate, err := newUpdateFCPService(parameters) + if err != nil { + return nil, nil, err + } + + client, err := a.getClient(parameters.Cluster) + if err != nil { + return errorResult(err), nil, err + } + err = client.UpdateFCPService(ctx, parameters.SVM, fcpServiceUpdate) + + if err != nil { + return errorResult(err), nil, err + } + + return &mcp.CallToolResult{ + Content: []mcp.Content{ + &mcp.TextContent{Text: "FCP service updated successfully"}, + }, + }, nil, nil +} + +func (a *App) DeleteFCPService(ctx context.Context, _ *mcp.CallToolRequest, parameters tool.FCPService) (*mcp.CallToolResult, any, error) { + if !a.locks.TryLock(parameters.Cluster) { + return errorResult(fmt.Errorf("another write operation is in progress on cluster %s, please try again", parameters.Cluster)), nil, nil + } + defer a.locks.Unlock(parameters.Cluster) + + if err := newDeleteFCPService(parameters); err != nil { + return nil, nil, err + } + + client, err := a.getClient(parameters.Cluster) + if err != nil { + return errorResult(err), nil, err + } + err = client.DeleteFCPService(ctx, parameters.SVM) + + if err != nil { + return errorResult(err), nil, err + } + + return &mcp.CallToolResult{ + Content: []mcp.Content{ + &mcp.TextContent{Text: "FCP service deleted successfully"}, + }, + }, nil, nil +} + +func newCreateFCPService(in tool.FCPService) (ontap.FCPService, error) { + out := ontap.FCPService{} + if in.SVM == "" { + return out, errors.New("SVM name is required") + } + + out.SVM = ontap.NameAndUUID{Name: in.SVM} + return out, nil +} + +func newUpdateFCPService(in tool.FCPService) (ontap.FCPService, error) { + out := ontap.FCPService{} + if in.SVM == "" { + return out, errors.New("SVM name is required") + } + if in.Enabled != "" { + out.Enabled = in.Enabled + } + return out, nil +} + +func newDeleteFCPService(in tool.FCPService) error { + if in.SVM == "" { + return errors.New("SVM name is required") + } + return nil +} + +func (a *App) CreateFCInterface(ctx context.Context, _ *mcp.CallToolRequest, parameters tool.FCInterface) (*mcp.CallToolResult, any, error) { + if !a.locks.TryLock(parameters.Cluster) { + return errorResult(fmt.Errorf("another write operation is in progress on cluster %s, please try again", parameters.Cluster)), nil, nil + } + defer a.locks.Unlock(parameters.Cluster) + + fcInterfaceCreate, err := newCreateFCInterface(parameters) + if err != nil { + return nil, nil, err + } + + client, err := a.getClient(parameters.Cluster) + if err != nil { + return errorResult(err), nil, err + } + err = client.CreateFCInterface(ctx, fcInterfaceCreate) + + if err != nil { + return errorResult(err), nil, err + } + + return &mcp.CallToolResult{ + Content: []mcp.Content{ + &mcp.TextContent{Text: "FC interface created successfully"}, + }, + }, nil, nil +} + +func (a *App) UpdateFCInterface(ctx context.Context, _ *mcp.CallToolRequest, parameters tool.FCInterface) (*mcp.CallToolResult, any, error) { + if !a.locks.TryLock(parameters.Cluster) { + return errorResult(fmt.Errorf("another write operation is in progress on cluster %s, please try again", parameters.Cluster)), nil, nil + } + defer a.locks.Unlock(parameters.Cluster) + + fcInterfaceUpdate, err := newUpdateFCInterface(parameters) + if err != nil { + return nil, nil, err + } + + client, err := a.getClient(parameters.Cluster) + if err != nil { + return errorResult(err), nil, err + } + err = client.UpdateFCInterface(ctx, parameters.SVM, parameters.Name, fcInterfaceUpdate) + + if err != nil { + return errorResult(err), nil, err + } + + return &mcp.CallToolResult{ + Content: []mcp.Content{ + &mcp.TextContent{Text: "FC interface updated successfully"}, + }, + }, nil, nil +} + +func (a *App) DeleteFCInterface(ctx context.Context, _ *mcp.CallToolRequest, parameters tool.FCInterface) (*mcp.CallToolResult, any, error) { + if !a.locks.TryLock(parameters.Cluster) { + return errorResult(fmt.Errorf("another write operation is in progress on cluster %s, please try again", parameters.Cluster)), nil, nil + } + defer a.locks.Unlock(parameters.Cluster) + + if err := newDeleteFCInterface(parameters); err != nil { + return nil, nil, err + } + + client, err := a.getClient(parameters.Cluster) + if err != nil { + return errorResult(err), nil, err + } + err = client.DeleteFCInterface(ctx, parameters.SVM, parameters.Name) + + if err != nil { + return errorResult(err), nil, err + } + + return &mcp.CallToolResult{ + Content: []mcp.Content{ + &mcp.TextContent{Text: "FC interface deleted successfully"}, + }, + }, nil, nil +} + +func newCreateFCInterface(in tool.FCInterface) (ontap.FCInterface, error) { + out := ontap.FCInterface{} + if in.SVM == "" { + return out, errors.New("SVM name is required") + } + if in.Name == "" { + return out, errors.New("FC interface name is required") + } + if in.DataProtocol == "" { + return out, errors.New("data protocol is required") + } + if in.HomeNodeName == "" { + return out, errors.New("home node name is required") + } + if in.HomePortName == "" { + return out, errors.New("home port name is required") + } + + out.SVM = ontap.NameAndUUID{Name: in.SVM} + out.Name = in.Name + out.DataProtocol = in.DataProtocol + out.Location = ontap.FCInterfaceLocation{ + HomePort: ontap.FCInterfacePort{ + Name: in.HomePortName, + Node: ontap.NameAndUUID{Name: in.HomeNodeName}, + }, + } + if in.Enabled != "" { + out.Enabled = in.Enabled + } + return out, nil +} + +func newUpdateFCInterface(in tool.FCInterface) (ontap.FCInterface, error) { + out := ontap.FCInterface{} + if in.SVM == "" { + return out, errors.New("SVM name is required") + } + if in.Name == "" { + return out, errors.New("FC interface name is required") + } + if in.Enabled != "" { + out.Enabled = in.Enabled + } + if in.HomeNodeName != "" && in.HomePortName != "" { + out.Location = ontap.FCInterfaceLocation{ + HomePort: ontap.FCInterfacePort{ + Name: in.HomePortName, + Node: ontap.NameAndUUID{Name: in.HomeNodeName}, + }, + } + } + return out, nil +} + +func newDeleteFCInterface(in tool.FCInterface) error { + if in.SVM == "" { + return errors.New("SVM name is required") + } + if in.Name == "" { + return errors.New("FC interface name is required") + } + return nil +} diff --git a/server/server.go b/server/server.go index fdf9040..fa05f31 100644 --- a/server/server.go +++ b/server/server.go @@ -133,6 +133,16 @@ func (a *App) createMCPServer() *mcp.Server { addTool(a, server, "update_nvme_service", descriptions.UpdateNVMeService, updateAnnotation, a.UpdateNVMeService) addTool(a, server, "delete_nvme_service", descriptions.DeleteNVMeService, deleteAnnotation, a.DeleteNVMeService) + // operation on FCP service object + addTool(a, server, "create_fcp_service", descriptions.CreateFCPService, createAnnotation, a.CreateFCPService) + addTool(a, server, "update_fcp_service", descriptions.UpdateFCPService, updateAnnotation, a.UpdateFCPService) + addTool(a, server, "delete_fcp_service", descriptions.DeleteFCPService, deleteAnnotation, a.DeleteFCPService) + + // operation on FC interface object + addTool(a, server, "create_fc_interface", descriptions.CreateFCInterface, createAnnotation, a.CreateFCInterface) + addTool(a, server, "update_fc_interface", descriptions.UpdateFCInterface, updateAnnotation, a.UpdateFCInterface) + addTool(a, server, "delete_fc_interface", descriptions.DeleteFCInterface, deleteAnnotation, a.DeleteFCInterface) + if a.catalog != nil { addTool(a, server, "list_ontap_endpoints", descriptions.ListOntapEndpoints, readOnlyAnnotation, a.ListOntapEndpoints) addTool(a, server, "search_ontap_endpoints", descriptions.SearchOntapEndpoints, readOnlyAnnotation, a.SearchOntapEndpoints) diff --git a/tool/tool.go b/tool/tool.go index 2a82c12..9300004 100644 --- a/tool/tool.go +++ b/tool/tool.go @@ -115,6 +115,22 @@ type NVMeService struct { Enabled string `json:"enabled,omitzero" jsonschema:"admin state of the NVMe service"` } +type FCPService struct { + Cluster string `json:"cluster_name" jsonschema:"cluster name"` + SVM string `json:"svm_name" jsonschema:"SVM name"` + Enabled string `json:"enabled,omitzero" jsonschema:"admin state of the FCP service"` +} + +type FCInterface struct { + Cluster string `json:"cluster_name" jsonschema:"cluster name"` + SVM string `json:"svm_name" jsonschema:"SVM name"` + Name string `json:"name" jsonschema:"FC interface name"` + DataProtocol string `json:"data_protocol,omitzero" jsonschema:"data protocol of the FC interface (e.g. fcp)"` + Enabled string `json:"enabled,omitzero" jsonschema:"admin state of the FC interface"` + HomeNodeName string `json:"home_node_name,omitzero" jsonschema:"name of the home node for the FC interface"` + HomePortName string `json:"home_port_name,omitzero" jsonschema:"name of the home port on the home node for the FC interface"` +} + type OntapGetParams struct { Cluster string `json:"cluster_name" jsonschema:"cluster name, from list_registered_clusters"` Fields string `json:"fields,omitzero" jsonschema:"comma-separated dot-notation fields to return, e.g. \"name,svm.name,space.size\" — use space.* to expand all space sub-fields"` From f41b6ba7f4080db779535fd104c6914e526bf475 Mon Sep 17 00:00:00 2001 From: hardikl Date: Mon, 6 Apr 2026 17:42:34 +0530 Subject: [PATCH 02/10] feat: handled copilot comments --- integration/test/fcp_test.go | 10 +++++----- ontap/ontap.go | 2 +- server/fcp.go | 6 ++++++ 3 files changed, 12 insertions(+), 6 deletions(-) diff --git a/integration/test/fcp_test.go b/integration/test/fcp_test.go index 71dfcaf..966a91a 100644 --- a/integration/test/fcp_test.go +++ b/integration/test/fcp_test.go @@ -30,19 +30,19 @@ func TestFCP(t *testing.T) { name: "Clean FCP service", input: SarClusterStr + "delete fcp service in marketing svm", expectedOntapErr: "because it does not exist", - verifyAPI: ontapVerifier{api: "api/protocols/san/fcp/services?svm=marketing", validationFunc: deleteObject}, + verifyAPI: ontapVerifier{api: "api/protocols/san/fcp/services?svm.name=marketing", validationFunc: deleteObject}, }, { name: "Create FCP service", input: SarClusterStr + "enable fcp service in marketing svm", expectedOntapErr: "", - verifyAPI: ontapVerifier{api: "api/protocols/san/fcp/services?svm=marketing", validationFunc: createObject}, + verifyAPI: ontapVerifier{api: "api/protocols/san/fcp/services?svm.name=marketing", validationFunc: createObject}, }, { name: "Create FC Interface", input: SarClusterStr + "create fc interface " + rn("fc1") + " in marketing svm at port 0e in node umeng-aff300-01 of fcp data protocol", expectedOntapErr: "", - verifyAPI: ontapVerifier{api: "api/network/fc/interfaces?name=" + rn("fc1") + "&svm=marketing", validationFunc: createObject}, + verifyAPI: ontapVerifier{api: "api/network/fc/interfaces?name=" + rn("fc1") + "&svm.name=marketing", validationFunc: createObject}, }, { name: "Update FC Interface", @@ -54,7 +54,7 @@ func TestFCP(t *testing.T) { name: "Clean FC Interface", input: SarClusterStr + "delete fc interface " + rn("fc1") + " in marketing svm", expectedOntapErr: "because it does not exist", - verifyAPI: ontapVerifier{api: "api/network/fc/interfaces?name=" + rn("fc1") + "&svm=marketing", validationFunc: deleteObject}, + verifyAPI: ontapVerifier{api: "api/network/fc/interfaces?name=" + rn("fc1") + "&svm.name=marketing", validationFunc: deleteObject}, }, { name: "Update FCP service", @@ -66,7 +66,7 @@ func TestFCP(t *testing.T) { name: "Clean FCP service", input: SarClusterStr + "delete fcp service in marketing svm", expectedOntapErr: "because it does not exist", - verifyAPI: ontapVerifier{api: "api/protocols/san/fcp/services?svm=marketing", validationFunc: deleteObject}, + verifyAPI: ontapVerifier{api: "api/protocols/san/fcp/services?svm.name=marketing", validationFunc: deleteObject}, }, } diff --git a/ontap/ontap.go b/ontap/ontap.go index c99cf51..b4dfca0 100644 --- a/ontap/ontap.go +++ b/ontap/ontap.go @@ -195,7 +195,7 @@ type NVMeService struct { } type FCPService struct { - SVM NameAndUUID `json:"svm" jsonschema:"svm name"` + SVM NameAndUUID `json:"svm,omitzero" jsonschema:"svm name"` Enabled string `json:"enabled,omitzero" jsonschema:"admin state of the FCP service"` } diff --git a/server/fcp.go b/server/fcp.go index 1450631..af04365 100644 --- a/server/fcp.go +++ b/server/fcp.go @@ -100,6 +100,9 @@ func newCreateFCPService(in tool.FCPService) (ontap.FCPService, error) { } out.SVM = ontap.NameAndUUID{Name: in.SVM} + if in.Enabled != "" { + out.Enabled = in.Enabled + } return out, nil } @@ -248,6 +251,9 @@ func newUpdateFCInterface(in tool.FCInterface) (ontap.FCInterface, error) { if in.Enabled != "" { out.Enabled = in.Enabled } + if in.HomeNodeName == "" || in.HomePortName == "" { + return out, errors.New("home node name and home port name both are required") + } if in.HomeNodeName != "" && in.HomePortName != "" { out.Location = ontap.FCInterfaceLocation{ HomePort: ontap.FCInterfacePort{ From dd159ed3b117d67721c031e6cbfaf6e32cda3661 Mon Sep 17 00:00:00 2001 From: hardikl Date: Mon, 6 Apr 2026 18:38:32 +0530 Subject: [PATCH 03/10] feat: minor change --- server/fcp.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/server/fcp.go b/server/fcp.go index af04365..452ba53 100644 --- a/server/fcp.go +++ b/server/fcp.go @@ -251,7 +251,7 @@ func newUpdateFCInterface(in tool.FCInterface) (ontap.FCInterface, error) { if in.Enabled != "" { out.Enabled = in.Enabled } - if in.HomeNodeName == "" || in.HomePortName == "" { + if (in.HomeNodeName == "" && in.HomePortName != "") || (in.HomeNodeName != "" && in.HomePortName == "") { return out, errors.New("home node name and home port name both are required") } if in.HomeNodeName != "" && in.HomePortName != "" { From 99bc8d7c4431dc8555af3046415f15a0148f61c6 Mon Sep 17 00:00:00 2001 From: hardikl Date: Mon, 6 Apr 2026 19:04:49 +0530 Subject: [PATCH 04/10] feat: handle comment --- integration/test/fcp_test.go | 3 +++ server/fcp.go | 2 +- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/integration/test/fcp_test.go b/integration/test/fcp_test.go index 966a91a..16c1399 100644 --- a/integration/test/fcp_test.go +++ b/integration/test/fcp_test.go @@ -76,6 +76,9 @@ func TestFCP(t *testing.T) { } poller := cfg.Pollers[SarCluster] + if poller == nil { + t.Skipf("Cluster %q not found in %s, skipping FCP tests", SarCluster, ConfigFile) + } transport := &http.Transport{ TLSClientConfig: &tls.Config{ InsecureSkipVerify: poller.UseInsecureTLS, // #nosec G402 diff --git a/server/fcp.go b/server/fcp.go index 452ba53..ac8797f 100644 --- a/server/fcp.go +++ b/server/fcp.go @@ -252,7 +252,7 @@ func newUpdateFCInterface(in tool.FCInterface) (ontap.FCInterface, error) { out.Enabled = in.Enabled } if (in.HomeNodeName == "" && in.HomePortName != "") || (in.HomeNodeName != "" && in.HomePortName == "") { - return out, errors.New("home node name and home port name both are required") + return out, errors.New("both home_node_name and home_port_name must be provided together or both omitted") } if in.HomeNodeName != "" && in.HomePortName != "" { out.Location = ontap.FCInterfaceLocation{ From c7765637f7c0ceb8e791a09384c91f7db328f224 Mon Sep 17 00:00:00 2001 From: hardikl Date: Wed, 8 Apr 2026 21:17:11 +0530 Subject: [PATCH 05/10] feat: update fcp tools validation --- integration/test/fcp_test.go | 30 ------------------------------ rest/fcp.go | 2 +- server/fcp.go | 12 ++++++++++-- 3 files changed, 11 insertions(+), 33 deletions(-) diff --git a/integration/test/fcp_test.go b/integration/test/fcp_test.go index 16c1399..6811ea1 100644 --- a/integration/test/fcp_test.go +++ b/integration/test/fcp_test.go @@ -20,24 +20,6 @@ func TestFCP(t *testing.T) { expectedOntapErr string verifyAPI ontapVerifier }{ - { - name: "Update FCP service", - input: SarClusterStr + "update fcp service to disable on the marketing svm", - expectedOntapErr: "because it does not exist", - verifyAPI: ontapVerifier{}, - }, - { - name: "Clean FCP service", - input: SarClusterStr + "delete fcp service in marketing svm", - expectedOntapErr: "because it does not exist", - verifyAPI: ontapVerifier{api: "api/protocols/san/fcp/services?svm.name=marketing", validationFunc: deleteObject}, - }, - { - name: "Create FCP service", - input: SarClusterStr + "enable fcp service in marketing svm", - expectedOntapErr: "", - verifyAPI: ontapVerifier{api: "api/protocols/san/fcp/services?svm.name=marketing", validationFunc: createObject}, - }, { name: "Create FC Interface", input: SarClusterStr + "create fc interface " + rn("fc1") + " in marketing svm at port 0e in node umeng-aff300-01 of fcp data protocol", @@ -56,18 +38,6 @@ func TestFCP(t *testing.T) { expectedOntapErr: "because it does not exist", verifyAPI: ontapVerifier{api: "api/network/fc/interfaces?name=" + rn("fc1") + "&svm.name=marketing", validationFunc: deleteObject}, }, - { - name: "Update FCP service", - input: SarClusterStr + "update fcp service to disable on the marketing svm", - expectedOntapErr: "", - verifyAPI: ontapVerifier{}, - }, - { - name: "Clean FCP service", - input: SarClusterStr + "delete fcp service in marketing svm", - expectedOntapErr: "because it does not exist", - verifyAPI: ontapVerifier{api: "api/protocols/san/fcp/services?svm.name=marketing", validationFunc: deleteObject}, - }, } cfg, err := config.ReadConfig(ConfigFile) diff --git a/rest/fcp.go b/rest/fcp.go index e801460..3e1f8bb 100644 --- a/rest/fcp.go +++ b/rest/fcp.go @@ -141,7 +141,7 @@ func (c *Client) UpdateFCInterface(ctx context.Context, svmName string, name str } if fcIfSr.NumRecords != 1 { - return fmt.Errorf("failed to update fc interface %s in svm=%s because there are %d matching records", + return fmt.Errorf("failed to get unique fc interface %s in svm=%s because there are %d matching records", name, svmName, fcIfSr.NumRecords) } diff --git a/server/fcp.go b/server/fcp.go index ac8797f..8959db9 100644 --- a/server/fcp.go +++ b/server/fcp.go @@ -111,8 +111,9 @@ func newUpdateFCPService(in tool.FCPService) (ontap.FCPService, error) { if in.SVM == "" { return out, errors.New("SVM name is required") } - if in.Enabled != "" { - out.Enabled = in.Enabled + out.Enabled = in.Enabled + if out.Enabled == "" { + return out, errors.New("at least one field must be provided for update (e.g. enabled)") } return out, nil } @@ -248,8 +249,11 @@ func newUpdateFCInterface(in tool.FCInterface) (ontap.FCInterface, error) { if in.Name == "" { return out, errors.New("FC interface name is required") } + + hasUpdates := false if in.Enabled != "" { out.Enabled = in.Enabled + hasUpdates = true } if (in.HomeNodeName == "" && in.HomePortName != "") || (in.HomeNodeName != "" && in.HomePortName == "") { return out, errors.New("both home_node_name and home_port_name must be provided together or both omitted") @@ -261,6 +265,10 @@ func newUpdateFCInterface(in tool.FCInterface) (ontap.FCInterface, error) { Node: ontap.NameAndUUID{Name: in.HomeNodeName}, }, } + hasUpdates = true + } + if !hasUpdates { + return out, errors.New("at least one supported update field must be provided; only enabled and home-node & home-port are supported for update") } return out, nil } From 268e588a613c7a74ddc80c63ce19b5deeacd604a Mon Sep 17 00:00:00 2001 From: hardikl Date: Mon, 13 Apr 2026 13:29:17 +0530 Subject: [PATCH 06/10] feat: adding svm clean in tests --- integration/test/fcp_test.go | 28 +++++++++++++++++++++++----- 1 file changed, 23 insertions(+), 5 deletions(-) diff --git a/integration/test/fcp_test.go b/integration/test/fcp_test.go index 6811ea1..796a79d 100644 --- a/integration/test/fcp_test.go +++ b/integration/test/fcp_test.go @@ -20,23 +20,41 @@ func TestFCP(t *testing.T) { expectedOntapErr string verifyAPI ontapVerifier }{ + { + name: "Clean SVM", + input: ClusterStr + "delete " + rn("marketing") + " svm", + expectedOntapErr: "because it does not exist", + verifyAPI: ontapVerifier{api: "api/svm/svms?name=" + rn("marketing"), validationFunc: deleteObject}, + }, + { + name: "Create SVM", + input: ClusterStr + "create " + rn("marketing") + " svm", + expectedOntapErr: "", + verifyAPI: ontapVerifier{api: "api/svm/svms?name=" + rn("marketing"), validationFunc: createObject}, + }, { name: "Create FC Interface", - input: SarClusterStr + "create fc interface " + rn("fc1") + " in marketing svm at port 0e in node umeng-aff300-01 of fcp data protocol", + input: SarClusterStr + "create fc interface " + rn("fc1") + " in " + rn("marketing") + " svm at port 0e in node umeng-aff300-01 of fcp data protocol", expectedOntapErr: "", - verifyAPI: ontapVerifier{api: "api/network/fc/interfaces?name=" + rn("fc1") + "&svm.name=marketing", validationFunc: createObject}, + verifyAPI: ontapVerifier{api: "api/network/fc/interfaces?name=" + rn("fc1") + "&svm.name=" + rn("marketing"), validationFunc: createObject}, }, { name: "Update FC Interface", - input: SarClusterStr + "disable fc interface " + rn("fc1") + " in marketing svm", + input: SarClusterStr + "disable fc interface " + rn("fc1") + " in " + rn("marketing") + " svm", expectedOntapErr: "", verifyAPI: ontapVerifier{}, }, { name: "Clean FC Interface", - input: SarClusterStr + "delete fc interface " + rn("fc1") + " in marketing svm", + input: SarClusterStr + "delete fc interface " + rn("fc1") + " in " + rn("marketing") + " svm", + expectedOntapErr: "because it does not exist", + verifyAPI: ontapVerifier{api: "api/network/fc/interfaces?name=" + rn("fc1") + "&svm.name=" + rn("marketing"), validationFunc: deleteObject}, + }, + { + name: "Clean SVM", + input: ClusterStr + "delete " + rn("marketing") + " svm", expectedOntapErr: "because it does not exist", - verifyAPI: ontapVerifier{api: "api/network/fc/interfaces?name=" + rn("fc1") + "&svm.name=marketing", validationFunc: deleteObject}, + verifyAPI: ontapVerifier{api: "api/svm/svms?name=" + rn("marketing"), validationFunc: deleteObject}, }, } From 8a4a384533ad6e1547b03313a95314f3c43d83fa Mon Sep 17 00:00:00 2001 From: hardikl Date: Mon, 13 Apr 2026 14:44:55 +0530 Subject: [PATCH 07/10] feat: sar cluster svm clean tests not supported --- integration/test/fcp_test.go | 28 +++++----------------------- 1 file changed, 5 insertions(+), 23 deletions(-) diff --git a/integration/test/fcp_test.go b/integration/test/fcp_test.go index 796a79d..6811ea1 100644 --- a/integration/test/fcp_test.go +++ b/integration/test/fcp_test.go @@ -20,41 +20,23 @@ func TestFCP(t *testing.T) { expectedOntapErr string verifyAPI ontapVerifier }{ - { - name: "Clean SVM", - input: ClusterStr + "delete " + rn("marketing") + " svm", - expectedOntapErr: "because it does not exist", - verifyAPI: ontapVerifier{api: "api/svm/svms?name=" + rn("marketing"), validationFunc: deleteObject}, - }, - { - name: "Create SVM", - input: ClusterStr + "create " + rn("marketing") + " svm", - expectedOntapErr: "", - verifyAPI: ontapVerifier{api: "api/svm/svms?name=" + rn("marketing"), validationFunc: createObject}, - }, { name: "Create FC Interface", - input: SarClusterStr + "create fc interface " + rn("fc1") + " in " + rn("marketing") + " svm at port 0e in node umeng-aff300-01 of fcp data protocol", + input: SarClusterStr + "create fc interface " + rn("fc1") + " in marketing svm at port 0e in node umeng-aff300-01 of fcp data protocol", expectedOntapErr: "", - verifyAPI: ontapVerifier{api: "api/network/fc/interfaces?name=" + rn("fc1") + "&svm.name=" + rn("marketing"), validationFunc: createObject}, + verifyAPI: ontapVerifier{api: "api/network/fc/interfaces?name=" + rn("fc1") + "&svm.name=marketing", validationFunc: createObject}, }, { name: "Update FC Interface", - input: SarClusterStr + "disable fc interface " + rn("fc1") + " in " + rn("marketing") + " svm", + input: SarClusterStr + "disable fc interface " + rn("fc1") + " in marketing svm", expectedOntapErr: "", verifyAPI: ontapVerifier{}, }, { name: "Clean FC Interface", - input: SarClusterStr + "delete fc interface " + rn("fc1") + " in " + rn("marketing") + " svm", - expectedOntapErr: "because it does not exist", - verifyAPI: ontapVerifier{api: "api/network/fc/interfaces?name=" + rn("fc1") + "&svm.name=" + rn("marketing"), validationFunc: deleteObject}, - }, - { - name: "Clean SVM", - input: ClusterStr + "delete " + rn("marketing") + " svm", + input: SarClusterStr + "delete fc interface " + rn("fc1") + " in marketing svm", expectedOntapErr: "because it does not exist", - verifyAPI: ontapVerifier{api: "api/svm/svms?name=" + rn("marketing"), validationFunc: deleteObject}, + verifyAPI: ontapVerifier{api: "api/network/fc/interfaces?name=" + rn("fc1") + "&svm.name=marketing", validationFunc: deleteObject}, }, } From b40767fae3d9516a206ef19950debb6889db860f Mon Sep 17 00:00:00 2001 From: hardikl Date: Mon, 13 Apr 2026 14:54:29 +0530 Subject: [PATCH 08/10] feat: handled copilot comment --- tool/tool.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tool/tool.go b/tool/tool.go index 9e7671b..816ea72 100644 --- a/tool/tool.go +++ b/tool/tool.go @@ -149,8 +149,8 @@ type FCInterface struct { Name string `json:"name" jsonschema:"FC interface name"` DataProtocol string `json:"data_protocol,omitzero" jsonschema:"data protocol of the FC interface (e.g. fcp)"` Enabled string `json:"enabled,omitzero" jsonschema:"admin state of the FC interface"` - HomeNodeName string `json:"home_node_name,omitzero" jsonschema:"name of the home node for the FC interface"` - HomePortName string `json:"home_port_name,omitzero" jsonschema:"name of the home port on the home node for the FC interface"` + HomeNodeName string `json:"location.home_port.node.name,omitzero" jsonschema:"name of the home node for the FC interface"` + HomePortName string `json:"location.home_port.name,omitzero" jsonschema:"name of the home port on the home node for the FC interface"` } type OntapGetParams struct { From 7bbd7c47748e9abe917f1a92e8ec264124da44d8 Mon Sep 17 00:00:00 2001 From: hardikl Date: Mon, 13 Apr 2026 18:55:34 +0530 Subject: [PATCH 09/10] feat: handled review comments --- integration/test/fcp_test.go | 3 +++ server/fcp.go | 10 +++++----- tool/tool.go | 17 ++++++++++++++++- 3 files changed, 24 insertions(+), 6 deletions(-) diff --git a/integration/test/fcp_test.go b/integration/test/fcp_test.go index 6811ea1..32b860a 100644 --- a/integration/test/fcp_test.go +++ b/integration/test/fcp_test.go @@ -11,6 +11,9 @@ import ( "github.com/netapp/ontap-mcp/config" ) +const SarCluster = "sar" +const SarClusterStr = "On the " + SarCluster + " cluster, " + func TestFCP(t *testing.T) { SkipIfMissing(t, CheckTools) diff --git a/server/fcp.go b/server/fcp.go index 8959db9..84cb421 100644 --- a/server/fcp.go +++ b/server/fcp.go @@ -38,7 +38,7 @@ func (a *App) CreateFCPService(ctx context.Context, _ *mcp.CallToolRequest, para }, nil, nil } -func (a *App) UpdateFCPService(ctx context.Context, _ *mcp.CallToolRequest, parameters tool.FCPService) (*mcp.CallToolResult, any, error) { +func (a *App) UpdateFCPService(ctx context.Context, _ *mcp.CallToolRequest, parameters tool.FCPServiceUpdate) (*mcp.CallToolResult, any, error) { if !a.locks.TryLock(parameters.Cluster) { return errorResult(fmt.Errorf("another write operation is in progress on cluster %s, please try again", parameters.Cluster)), nil, nil } @@ -106,7 +106,7 @@ func newCreateFCPService(in tool.FCPService) (ontap.FCPService, error) { return out, nil } -func newUpdateFCPService(in tool.FCPService) (ontap.FCPService, error) { +func newUpdateFCPService(in tool.FCPServiceUpdate) (ontap.FCPService, error) { out := ontap.FCPService{} if in.SVM == "" { return out, errors.New("SVM name is required") @@ -125,7 +125,7 @@ func newDeleteFCPService(in tool.FCPService) error { return nil } -func (a *App) CreateFCInterface(ctx context.Context, _ *mcp.CallToolRequest, parameters tool.FCInterface) (*mcp.CallToolResult, any, error) { +func (a *App) CreateFCInterface(ctx context.Context, _ *mcp.CallToolRequest, parameters tool.FCInterfaceCreate) (*mcp.CallToolResult, any, error) { if !a.locks.TryLock(parameters.Cluster) { return errorResult(fmt.Errorf("another write operation is in progress on cluster %s, please try again", parameters.Cluster)), nil, nil } @@ -208,7 +208,7 @@ func (a *App) DeleteFCInterface(ctx context.Context, _ *mcp.CallToolRequest, par }, nil, nil } -func newCreateFCInterface(in tool.FCInterface) (ontap.FCInterface, error) { +func newCreateFCInterface(in tool.FCInterfaceCreate) (ontap.FCInterface, error) { out := ontap.FCInterface{} if in.SVM == "" { return out, errors.New("SVM name is required") @@ -256,7 +256,7 @@ func newUpdateFCInterface(in tool.FCInterface) (ontap.FCInterface, error) { hasUpdates = true } if (in.HomeNodeName == "" && in.HomePortName != "") || (in.HomeNodeName != "" && in.HomePortName == "") { - return out, errors.New("both home_node_name and home_port_name must be provided together or both omitted") + return out, errors.New("both location.home_port.node.name and location.home_port.name must be provided together or both omitted") } if in.HomeNodeName != "" && in.HomePortName != "" { out.Location = ontap.FCInterfaceLocation{ diff --git a/tool/tool.go b/tool/tool.go index 67767f2..d0d7ff0 100644 --- a/tool/tool.go +++ b/tool/tool.go @@ -172,17 +172,32 @@ type NVMeSubsystemMap struct { Namespace string `json:"namespace_name" jsonschema:"name for NVMe namespace"` } +type FCPServiceUpdate struct { + Cluster string `json:"cluster_name" jsonschema:"cluster name"` + SVM string `json:"svm_name" jsonschema:"SVM name"` + Enabled string `json:"enabled" jsonschema:"admin state of the FCP service"` +} + type FCPService struct { Cluster string `json:"cluster_name" jsonschema:"cluster name"` SVM string `json:"svm_name" jsonschema:"SVM name"` Enabled string `json:"enabled,omitzero" jsonschema:"admin state of the FCP service"` } +type FCInterfaceCreate struct { + Cluster string `json:"cluster_name" jsonschema:"cluster name"` + SVM string `json:"svm_name" jsonschema:"SVM name"` + Name string `json:"name" jsonschema:"FC interface name"` + DataProtocol string `json:"data_protocol" jsonschema:"data protocol of the FC interface (e.g. fcp)"` + Enabled string `json:"enabled,omitzero" jsonschema:"admin state of the FC interface"` + HomeNodeName string `json:"location.home_port.node.name" jsonschema:"name of the home node for the FC interface"` + HomePortName string `json:"location.home_port.name" jsonschema:"name of the home port on the home node for the FC interface"` +} + type FCInterface struct { Cluster string `json:"cluster_name" jsonschema:"cluster name"` SVM string `json:"svm_name" jsonschema:"SVM name"` Name string `json:"name" jsonschema:"FC interface name"` - DataProtocol string `json:"data_protocol,omitzero" jsonschema:"data protocol of the FC interface (e.g. fcp)"` Enabled string `json:"enabled,omitzero" jsonschema:"admin state of the FC interface"` HomeNodeName string `json:"location.home_port.node.name,omitzero" jsonschema:"name of the home node for the FC interface"` HomePortName string `json:"location.home_port.name,omitzero" jsonschema:"name of the home port on the home node for the FC interface"` From d19ad260e1448abda4e4d5f348b08615203141ec Mon Sep 17 00:00:00 2001 From: hardikl Date: Wed, 15 Apr 2026 20:38:25 +0530 Subject: [PATCH 10/10] feat: handled review comments --- integration/test/fcp_test.go | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/integration/test/fcp_test.go b/integration/test/fcp_test.go index 32b860a..2a28aef 100644 --- a/integration/test/fcp_test.go +++ b/integration/test/fcp_test.go @@ -23,6 +23,12 @@ func TestFCP(t *testing.T) { expectedOntapErr string verifyAPI ontapVerifier }{ + { + name: "Clean FC Interface", + input: SarClusterStr + "delete fc interface " + rn("fc1") + " in marketing svm", + expectedOntapErr: "because it does not exist", + verifyAPI: ontapVerifier{api: "api/network/fc/interfaces?name=" + rn("fc1") + "&svm.name=marketing", validationFunc: deleteObject}, + }, { name: "Create FC Interface", input: SarClusterStr + "create fc interface " + rn("fc1") + " in marketing svm at port 0e in node umeng-aff300-01 of fcp data protocol", @@ -38,7 +44,7 @@ func TestFCP(t *testing.T) { { name: "Clean FC Interface", input: SarClusterStr + "delete fc interface " + rn("fc1") + " in marketing svm", - expectedOntapErr: "because it does not exist", + expectedOntapErr: "", verifyAPI: ontapVerifier{api: "api/network/fc/interfaces?name=" + rn("fc1") + "&svm.name=marketing", validationFunc: deleteObject}, }, }