From 02b95d6c772deeb24b45c51fed56cee918794f0c Mon Sep 17 00:00:00 2001 From: Yiming Bao Date: Wed, 16 Oct 2024 21:00:24 +0800 Subject: [PATCH] Add support for sdt --- inttests/GOSCALEIO_TEST.env_example | 6 +- inttests/nvme_host_test.go | 6 + inttests/sdt_test.go | 121 ++++ nvme_host.go | 11 + nvme_host_test.go | 118 +++- sdt.go | 257 +++++++ sdt_test.go | 994 ++++++++++++++++++++++++++++ types/v1/types.go | 89 +++ 8 files changed, 1600 insertions(+), 2 deletions(-) create mode 100644 inttests/sdt_test.go create mode 100644 sdt.go create mode 100644 sdt_test.go diff --git a/inttests/GOSCALEIO_TEST.env_example b/inttests/GOSCALEIO_TEST.env_example index 347e9a7..7905482 100644 --- a/inttests/GOSCALEIO_TEST.env_example +++ b/inttests/GOSCALEIO_TEST.env_example @@ -40,4 +40,8 @@ GOSCALEIO_REPLICATION_TARGET_NAME=replication-target GATEWAY_ENDPOINT=https://1.2.3.4:443 GATEWAY_USERNAME=admin GATEWAY_PASSWORD=Password123 -GATEWAY_INSECURE=true \ No newline at end of file +GATEWAY_INSECURE=true + +# SDT Variables +NVME_TARGET_IP1= +NVME_TARGET_IP2= \ No newline at end of file diff --git a/inttests/nvme_host_test.go b/inttests/nvme_host_test.go index 88a41ce..270668b 100644 --- a/inttests/nvme_host_test.go +++ b/inttests/nvme_host_test.go @@ -67,6 +67,12 @@ func TestNvmeHost(t *testing.T) { assert.Nil(t, err) }) + t.Run("Get Host NVMe Controllers", func(t *testing.T) { + controllers, err := system.GetHostNvmeControllers(types.NvmeHost{ID: hostID}) + assert.Nil(t, err) + assert.NotNil(t, controllers) + }) + t.Cleanup(func() { err := system.DeleteNvmeHost(hostID) assert.Nil(t, err) diff --git a/inttests/sdt_test.go b/inttests/sdt_test.go new file mode 100644 index 0000000..9697c49 --- /dev/null +++ b/inttests/sdt_test.go @@ -0,0 +1,121 @@ +// Copyright © 2024 Dell Inc. or its subsidiaries. All Rights Reserved. +// +// 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 inttests + +import ( + "fmt" + "os" + "testing" + "time" + + types "github.com/dell/goscaleio/types/v1" + "github.com/stretchr/testify/assert" +) + +func TestSdt(t *testing.T) { + system := getSystem() + name := fmt.Sprintf("example-sdt_%v", randString(5)) + newName := fmt.Sprintf("example-sdt_%v", randString(10)) + var sdtID string + + t.Run("Create sdt", func(t *testing.T) { + assert.NotNil(t, system) + pd := getProtectionDomain(t) + assert.NotNil(t, pd) + targetIP1 := os.Getenv("NVME_TARGET_IP1") + targetIP2 := os.Getenv("NVME_TARGET_IP2") + sdtParam := &types.SdtParam{ + Name: name, + IPList: []*types.SdtIP{{IP: targetIP1, Role: "StorageAndHost"}, {IP: targetIP2, Role: "StorageAndHost"}}, + StoragePort: 12200, + NvmePort: 4420, + DiscoveryPort: 8009, + ProtectionDomainID: pd.ProtectionDomain.ID, + } + resp, err := pd.CreateSdt(sdtParam) + assert.Nil(t, err) + assert.NotNil(t, resp.ID) + sdtID = resp.ID + }) + + t.Run("Remove Sdt Target IP", func(t *testing.T) { + targetIP := os.Getenv("NVME_TARGET_IP2") + err := system.RemoveSdtTargetIP(sdtID, targetIP) + assert.Nil(t, err) + }) + + t.Run("Add Sdt Target IP", func(t *testing.T) { + targetIP := os.Getenv("NVME_TARGET_IP2") + err := system.AddSdtTargetIP(sdtID, targetIP, "StorageAndHost") + assert.Nil(t, err) + }) + + t.Run("Get All Sdt", func(t *testing.T) { + hosts, err := system.GetAllSdts() + assert.Nil(t, err) + assert.NotNil(t, hosts) + }) + + t.Run("Get Sdt By ID", func(t *testing.T) { + host, err := system.GetSdtByID(sdtID) + assert.Nil(t, err) + assert.NotNil(t, host) + }) + + t.Run("Rename Sdt", func(t *testing.T) { + err := system.RenameSdt(sdtID, newName) + assert.Nil(t, err) + }) + + t.Run("Set Sdt NvmePort", func(t *testing.T) { + err := system.SetSdtNvmePort(sdtID, 4422) + assert.Nil(t, err) + }) + + t.Run("Set Sdt StoragePort", func(t *testing.T) { + err := system.SetSdtStoragePort(sdtID, 12300) + assert.Nil(t, err) + }) + + t.Run("Set Sdt DiscoveryPort", func(t *testing.T) { + err := system.SetSdtDiscoveryPort(sdtID, 8010) + assert.Nil(t, err) + }) + + t.Run("Modify Sdt IP and Role", func(t *testing.T) { + targetIP := os.Getenv("NVME_TARGET_IP2") + err := system.ModifySdtIPRole(sdtID, targetIP, "StorageOnly") + assert.Nil(t, err) + }) + + t.Run("Rollback Sdt StoragePort", func(t *testing.T) { + err := system.SetSdtStoragePort(sdtID, 12200) + assert.Nil(t, err) + }) + + t.Run("Enter Sdt Maintenance Mode", func(t *testing.T) { + err := system.EnterSdtMaintenanceMode(sdtID) + assert.Nil(t, err) + }) + + t.Run("Exit Sdt Maintenance Mode", func(t *testing.T) { + err := system.ExitSdtMaintenanceMode(sdtID) + assert.Nil(t, err) + time.Sleep(5 * time.Second) + }) + + t.Cleanup(func() { + err := system.DeleteNvmeHost(sdtID) + assert.Nil(t, err) + }) +} diff --git a/nvme_host.go b/nvme_host.go index 38b6c4a..cff523e 100644 --- a/nvme_host.go +++ b/nvme_host.go @@ -158,3 +158,14 @@ func (s *System) DeleteNvmeHost(id string) error { } return nil } + +// GetHostNvmeControllers returns all attached NVMe controllers +func (s *System) GetHostNvmeControllers(host types.NvmeHost) ([]types.NvmeController, error) { + defer TimeSpent("GetHostNvmeControllers", time.Now()) + path := fmt.Sprintf("api/instances/Host::%v/relationships/NvmeController", host.ID) + + var nvmeControllers []types.NvmeController + err := s.client.getJSONWithRetry( + http.MethodGet, path, nil, &nvmeControllers) + return nvmeControllers, err +} diff --git a/nvme_host_test.go b/nvme_host_test.go index f709139..77f4899 100644 --- a/nvme_host_test.go +++ b/nvme_host_test.go @@ -543,7 +543,7 @@ func Test_ChangeNvmeHostMaxNumSysPorts(t *testing.T) { } } -func Test_RemoveNvmeHost(t *testing.T) { +func Test_DeleteNvmeHost(t *testing.T) { type checkFn func(*testing.T, error) check := func(fns ...checkFn) []checkFn { return fns } @@ -606,3 +606,119 @@ func Test_RemoveNvmeHost(t *testing.T) { }) } } + +func Test_GetHostNvmeControllers(t *testing.T) { + type checkFn func(*testing.T, []types.NvmeController, error) + check := func(fns ...checkFn) []checkFn { return fns } + + responseJSON := `[ + { + "name": null, + "isConnected": true, + "sdtId": "cd16ff4300000000", + "hostIp": null, + "hostId": "4d2a627100010000", + "controllerId": 1, + "sysPortId": 2, + "sysPortIp": "10.0.0.22", + "subsystem": "Io", + "isAssigned": true, + "id": "cc00000001000011", + "links": [ + { + "rel": "self", + "href": "/api/instances/NvmeController::cc00000001000011" + } + ] + }]` + + hasNoError := func(t *testing.T, _ []types.NvmeController, err error) { + if err != nil { + t.Fatalf("expected no error") + } + } + + checkLength := func(length int) func(t *testing.T, slice []types.NvmeController, err error) { + return func(t *testing.T, slice []types.NvmeController, _ error) { + assert.Equal(t, length, len(slice)) + } + } + + checkSdtID := func(index int, id string) func(t *testing.T, slice []types.NvmeController, err error) { + return func(t *testing.T, slice []types.NvmeController, _ error) { + assert.Equal(t, slice[index].SdtID, id) + } + } + + checkHostID := func(index int, id string) func(t *testing.T, slice []types.NvmeController, err error) { + return func(t *testing.T, slice []types.NvmeController, _ error) { + assert.Equal(t, slice[index].HostID, id) + } + } + + checkConnected := func(index int, connected bool) func(t *testing.T, slice []types.NvmeController, err error) { + return func(t *testing.T, slice []types.NvmeController, _ error) { + assert.Equal(t, slice[index].IsConnected, connected) + } + } + + hasError := func(t *testing.T, _ []types.NvmeController, err error) { + if err == nil { + t.Fatalf("expected error") + } + } + + tests := map[string]func(t *testing.T) (*httptest.Server, types.System, []checkFn){ + "success": func(_ *testing.T) (*httptest.Server, types.System, []checkFn) { + systemID := "mock-system-id" + id := "mock-id" + url := fmt.Sprintf("/api/instances/Host::%v/relationships/NvmeController", id) + server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + if r.Method == http.MethodGet && strings.EqualFold(r.URL.Path, url) { + w.WriteHeader(http.StatusOK) + _, err := w.Write([]byte(responseJSON)) + if err != nil { + t.Fatalf("Unexpected error: %v", err) + } + return + } + http.NotFound(w, r) + })) + system := types.System{ + ID: systemID, + } + return server, system, check(hasNoError, checkLength(1), checkSdtID(0, "cd16ff4300000000"), checkHostID(0, "4d2a627100010000"), checkConnected(0, true)) + }, + "error response": func(_ *testing.T) (*httptest.Server, types.System, []checkFn) { + systemID := "mock-system-id" + server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, _ *http.Request) { + http.Error(w, "Server Error", http.StatusInternalServerError) + })) + system := types.System{ + ID: systemID, + } + return server, system, check(hasError) + }, + } + + for name, tc := range tests { + t.Run(name, func(t *testing.T) { + server, sys, checkFns := tc(t) + defer server.Close() + + client, _ := NewClientWithArgs(server.URL, "", math.MaxInt64, true, false) + system := System{ + System: &sys, + client: client, + } + host := types.NvmeHost{ + ID: "mock-id", + } + nvmeHostControllers, err := system.GetHostNvmeControllers(host) + + for _, checkFn := range checkFns { + checkFn(t, nvmeHostControllers, err) + } + }) + } +} diff --git a/sdt.go b/sdt.go new file mode 100644 index 0000000..75f375d --- /dev/null +++ b/sdt.go @@ -0,0 +1,257 @@ +// Copyright © 2024 Dell Inc. or its subsidiaries. All Rights Reserved. +// +// 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 goscaleio + +import ( + "fmt" + "net/http" + "time" + + types "github.com/dell/goscaleio/types/v1" +) + +// Sdt defines struct for Sdt +type Sdt struct { + Sdt *types.Sdt + client *Client +} + +// NewSdt returns a new Sdt +func NewSdt(client *Client, sdt *types.Sdt) *Sdt { + return &Sdt{ + Sdt: sdt, + client: client, + } +} + +// GetAllSdts returns all sdt +func (s *System) GetAllSdts() ([]types.Sdt, error) { + defer TimeSpent("GetAllSdts", time.Now()) + + path := "/api/types/Sdt/instances" + + var allSdts []types.Sdt + err := s.client.getJSONWithRetry( + http.MethodGet, path, nil, &allSdts) + if err != nil { + return nil, err + } + + return allSdts, nil +} + +// GetSdtByID returns an sdt searched by id +func (s *System) GetSdtByID(id string) (*types.Sdt, error) { + defer TimeSpent("GetSdtByID", time.Now()) + + path := fmt.Sprintf("api/instances/Sdt::%v", id) + + var sdt types.Sdt + err := s.client.getJSONWithRetry( + http.MethodGet, path, nil, &sdt) + if err != nil { + return nil, err + } + + return &sdt, nil +} + +// CreateSdt creates a new Sdt +func (pd *ProtectionDomain) CreateSdt(param *types.SdtParam) (*types.SdtResp, error) { + defer TimeSpent("CreateSdt", time.Now()) + + if len(param.IPList) == 0 { + return nil, fmt.Errorf("Must provide at least 1 SDT IP") + } + + param.ProtectionDomainID = pd.ProtectionDomain.ID + + resp := &types.SdtResp{} + path := "/api/types/Sdt/instances" + err := pd.client.getJSONWithRetry( + http.MethodPost, path, param, resp) + return resp, err +} + +// RenameSdt changes the name of the sdt. +func (s *System) RenameSdt(id, name string) error { + defer TimeSpent("RenameSdt", time.Now()) + + path := fmt.Sprintf("/api/instances/Sdt::%v/action/renameSdt", id) + + body := types.SdtRenameParam{ + NewName: name, + } + err := s.client.getJSONWithRetry( + http.MethodPost, path, body, nil) + if err != nil { + return err + } + + return nil +} + +// SetSdtNvmePort set the NVMe port for the sdt. +func (s *System) SetSdtNvmePort(id string, port int) error { + defer TimeSpent("SetSdtNvmePort", time.Now()) + + path := fmt.Sprintf("/api/instances/Sdt::%v/action/modifyNvmePort", id) + + body := types.SdtNvmePortParam{ + NewNvmePort: types.IntString(port), + } + err := s.client.getJSONWithRetry( + http.MethodPost, path, body, nil) + if err != nil { + return err + } + + return nil +} + +// SetSdtStoragePort sets the storage port for the sdt. +func (s *System) SetSdtStoragePort(id string, port int) error { + defer TimeSpent("SetSdtStoragePort", time.Now()) + + path := fmt.Sprintf("/api/instances/Sdt::%v/action/modifyStoragePort", id) + + body := types.SdtStoragePortParam{ + NewStoragePort: types.IntString(port), + } + err := s.client.getJSONWithRetry( + http.MethodPost, path, body, nil) + if err != nil { + return err + } + + return nil +} + +// SetSdtDiscoveryPort sets the discovery port for the sdt. +func (s *System) SetSdtDiscoveryPort(id string, port int) error { + defer TimeSpent("SetSdtDiscoveryPort", time.Now()) + + path := fmt.Sprintf("/api/instances/Sdt::%v/action/modifyDiscoveryPort", id) + + body := types.SdtDiscoveryPortParam{ + NewDiscoveryPort: types.IntString(port), + } + err := s.client.getJSONWithRetry( + http.MethodPost, path, body, nil) + if err != nil { + return err + } + + return nil +} + +// AddSdtTargetIP adds target IP and role for the sdt. +func (s *System) AddSdtTargetIP(id, ip, role string) error { + defer TimeSpent("AddSdtTargetIP", time.Now()) + + path := fmt.Sprintf("/api/instances/Sdt::%v/action/addIp", id) + + body := types.SdtIP{ + IP: ip, + Role: role, + } + err := s.client.getJSONWithRetry( + http.MethodPost, path, body, nil) + if err != nil { + return err + } + + return nil +} + +// RemoveSdtTargetIP removes target IP and role from the sdt. +func (s *System) RemoveSdtTargetIP(id, ip string) error { + defer TimeSpent("RemoveSdtTargetIP", time.Now()) + + path := fmt.Sprintf("/api/instances/Sdt::%v/action/removeIp", id) + + body := types.SdtRemoveIPParam{ + IP: ip, + } + err := s.client.getJSONWithRetry( + http.MethodPost, path, body, nil) + if err != nil { + return err + } + + return nil +} + +// ModifySdtIPRole modify target IP role for the sdt. +func (s *System) ModifySdtIPRole(id, ip, role string) error { + defer TimeSpent("ModifySdtIPRole", time.Now()) + + path := fmt.Sprintf("/api/instances/Sdt::%v/action/modifyIpRole", id) + + body := types.SdtIPRoleParam{ + IP: ip, + Role: role, + } + err := s.client.getJSONWithRetry( + http.MethodPost, path, body, nil) + if err != nil { + return err + } + + return nil +} + +// DeleteSdt deletes the sdt +func (s *System) DeleteSdt(id string) error { + defer TimeSpent("DeleteSdt", time.Now()) + + path := fmt.Sprintf("/api/instances/Sdt::%v/action/removeSdt", id) + + param := &types.EmptyPayload{} + err := s.client.getJSONWithRetry( + http.MethodPost, path, param, nil) + if err != nil { + return err + } + return nil +} + +// EnterSdtMaintenanceMode enter sdt maintenance mode +func (s *System) EnterSdtMaintenanceMode(id string) error { + defer TimeSpent("EnterSdtMaintenanceMode", time.Now()) + + path := fmt.Sprintf("/api/instances/Sdt::%v/action/enterMaintenanceMode", id) + + param := &types.EmptyPayload{} + err := s.client.getJSONWithRetry( + http.MethodPost, path, param, nil) + if err != nil { + return err + } + return nil +} + +// ExitSdtMaintenanceMode exit sdt maintenance mode +func (s *System) ExitSdtMaintenanceMode(id string) error { + defer TimeSpent("ExitSdtMaintenanceMode", time.Now()) + + path := fmt.Sprintf("/api/instances/Sdt::%v/action/exitMaintenanceMode", id) + + param := &types.EmptyPayload{} + err := s.client.getJSONWithRetry( + http.MethodPost, path, param, nil) + if err != nil { + return err + } + return nil +} diff --git a/sdt_test.go b/sdt_test.go new file mode 100644 index 0000000..17c86d4 --- /dev/null +++ b/sdt_test.go @@ -0,0 +1,994 @@ +// Copyright © 2024 Dell Inc. or its subsidiaries. All Rights Reserved. +// +// 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 goscaleio + +import ( + "fmt" + "math" + "net/http" + "net/http/httptest" + "strings" + "testing" + + types "github.com/dell/goscaleio/types/v1" + "github.com/stretchr/testify/assert" +) + +func Test_GetAllSdts(t *testing.T) { + type checkFn func(*testing.T, []types.Sdt, error) + check := func(fns ...checkFn) []checkFn { return fns } + + responseJSON := `[ + { + "ipList": [ + { + "role": "StorageAndHost", + "ip": "10.0.0.23" + } + ], + "sdtState": "Normal", + "systemId": "mock-system-id", + "name": "yulan1sdt3", + "protectionDomainId": "5e4640aa00000000", + "storagePort": 12200, + "nvmePort": 4420, + "discoveryPort": 8009, + "certificateInfo": {}, + "mdmConnectionState": "Connected", + "membershipState": "Joined", + "faultSetId": null, + "softwareVersionInfo": "R4_5.2100.0", + "maintenanceState": "NoMaintenance", + "authenticationError": "None", + "persistentDiscoveryControllersNum": 0, + "id": "mock-id", + "links": [ + { + "rel": "self", + "href": "/api/instances/Sdt::mock-id" + }, + { + "rel": "/api/Sdt/relationship/Statistics", + "href": "/api/instances/Sdt::mock-id/relationships/Statistics" + }, + { + "rel": "/api/parent/relationship/protectionDomainId", + "href": "/api/instances/ProtectionDomain::5e4640aa00000000" + } + ] + } +]` + + hasNoError := func(t *testing.T, _ []types.Sdt, err error) { + if err != nil { + t.Fatalf("expected no error") + } + } + + checkLength := func(length int) func(t *testing.T, hosts []types.Sdt, err error) { + return func(t *testing.T, hosts []types.Sdt, _ error) { + assert.Equal(t, length, len(hosts)) + } + } + + hasError := func(t *testing.T, _ []types.Sdt, err error) { + if err == nil { + t.Fatalf("expected error") + } + } + + tests := map[string]func(t *testing.T) (*httptest.Server, types.System, []checkFn){ + "success": func(t *testing.T) (*httptest.Server, types.System, []checkFn) { + systemID := "mock-system-id" + url := "/api/types/Sdt/instances" + server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + if r.Method == http.MethodGet && strings.EqualFold(r.URL.Path, url) { + w.WriteHeader(http.StatusOK) + _, err := w.Write([]byte(responseJSON)) + if err != nil { + t.Fatalf("Unexpected error: %v", err) + } + return + } + http.NotFound(w, r) + })) + system := types.System{ + ID: systemID, + } + return server, system, check(hasNoError, checkLength(1)) + }, + "error response": func(_ *testing.T) (*httptest.Server, types.System, []checkFn) { + systemID := "mock-system-id" + server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + http.NotFound(w, r) + })) + system := types.System{ + ID: systemID, + } + return server, system, check(hasError) + }, + } + + for name, tc := range tests { + t.Run(name, func(t *testing.T) { + server, sys, checkFns := tc(t) + defer server.Close() + + client, _ := NewClientWithArgs(server.URL, "", math.MaxInt64, true, false) + system := System{ + System: &sys, + client: client, + } + sdts, err := system.GetAllSdts() + + for _, checkFn := range checkFns { + checkFn(t, sdts, err) + } + }) + } +} + +func Test_GetSdtByID(t *testing.T) { + type checkFn func(*testing.T, *types.Sdt, error) + check := func(fns ...checkFn) []checkFn { return fns } + + responseJSON := `{ + "ipList": [ + { + "role": "StorageAndHost", + "ip": "10.0.0.23" + } + ], + "sdtState": "Normal", + "systemId": "mock-system-id", + "name": "mock-name", + "protectionDomainId": "mock-pd-id", + "storagePort": 12200, + "nvmePort": 4420, + "discoveryPort": 8009, + "certificateInfo": {}, + "mdmConnectionState": "Connected", + "membershipState": "Joined", + "faultSetId": null, + "softwareVersionInfo": "R4_5.2100.0", + "maintenanceState": "NoMaintenance", + "authenticationError": "None", + "persistentDiscoveryControllersNum": 0, + "id": "mock-id", + "links": [ + { + "rel": "self", + "href": "/api/instances/Sdt::mock-id" + }, + { + "rel": "/api/Sdt/relationship/Statistics", + "href": "/api/instances/Sdt::mock-id/relationships/Statistics" + }, + { + "rel": "/api/parent/relationship/protectionDomainId", + "href": "/api/instances/ProtectionDomain::mock-pd-id" + } + ] + }` + + hasNoError := func(t *testing.T, _ *types.Sdt, err error) { + if err != nil { + t.Fatalf("expected no error") + } + } + + checkName := func(name string) func(t *testing.T, host *types.Sdt, err error) { + return func(t *testing.T, sdt *types.Sdt, _ error) { + assert.Equal(t, sdt.Name, name) + } + } + + hasError := func(t *testing.T, _ *types.Sdt, err error) { + if err == nil { + t.Fatalf("expected error") + } + } + + tests := map[string]func(t *testing.T) (*httptest.Server, types.System, []checkFn){ + "success": func(t *testing.T) (*httptest.Server, types.System, []checkFn) { + systemID := "mock-system-id" + id := "mock-id" + url := fmt.Sprintf("/api/instances/Sdt::%v", id) + server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + if r.Method == http.MethodGet && strings.EqualFold(r.URL.Path, url) { + w.WriteHeader(http.StatusOK) + _, err := w.Write([]byte(responseJSON)) + if err != nil { + t.Fatalf("Unexpected error: %v", err) + } + return + } + http.NotFound(w, r) + })) + system := types.System{ + ID: systemID, + } + return server, system, check(hasNoError, checkName("mock-name")) + }, + "error response": func(_ *testing.T) (*httptest.Server, types.System, []checkFn) { + systemID := "mock-system-id" + server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, _ *http.Request) { + http.Error(w, "Server Error", http.StatusInternalServerError) + })) + system := types.System{ + ID: systemID, + } + return server, system, check(hasError) + }, + } + + for name, tc := range tests { + t.Run(name, func(t *testing.T) { + server, sys, checkFns := tc(t) + defer server.Close() + + client, _ := NewClientWithArgs(server.URL, "", math.MaxInt64, true, false) + system := System{ + System: &sys, + client: client, + } + sdt, err := system.GetSdtByID("mock-id") + + for _, checkFn := range checkFns { + checkFn(t, sdt, err) + } + }) + } +} + +func Test_CreateSdt(t *testing.T) { + type checkFn func(*testing.T, *types.SdtResp, error) + check := func(fns ...checkFn) []checkFn { return fns } + + hasNoError := func(t *testing.T, _ *types.SdtResp, err error) { + if err != nil { + t.Fatalf("expected no error") + } + } + + checkID := func(id string) func(t *testing.T, resp *types.SdtResp, err error) { + return func(t *testing.T, resp *types.SdtResp, _ error) { + assert.Equal(t, resp.ID, id) + } + } + + checkEmptyIPList := func(t *testing.T, _ *types.SdtResp, err error) { + assert.Equal(t, err.Error(), "Must provide at least 1 SDT IP") + } + + hasError := func(t *testing.T, _ *types.SdtResp, err error) { + if err == nil { + t.Fatalf("expected error") + } + } + + tests := map[string]func(t *testing.T) (*httptest.Server, *types.SdtParam, []checkFn){ + "success": func(t *testing.T) (*httptest.Server, *types.SdtParam, []checkFn) { + sdtParam := &types.SdtParam{ + Name: "example-sdt", + IPList: []*types.SdtIP{{IP: "192.168.0.1", Role: "StorageAndHost"}}, + StoragePort: 12200, + NvmePort: 4420, + DiscoveryPort: 8009, + ProtectionDomainID: "mock-pd-id", + } + + url := "/api/types/Sdt/instances" + id := "mock-id" + server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + if r.Method == http.MethodPost && strings.EqualFold(r.URL.Path, url) { + w.WriteHeader(http.StatusOK) + _, err := w.Write([]byte(fmt.Sprintf("{\"id\": \"%v\"}", id))) + if err != nil { + t.Fatalf("Unexpected error: %v", err) + } + return + } + http.NotFound(w, r) + })) + return server, sdtParam, check(hasNoError, checkID(id)) + }, + + "empty IP list": func(_ *testing.T) (*httptest.Server, *types.SdtParam, []checkFn) { + sdtParam := &types.SdtParam{ + Name: "example-sdt", + IPList: []*types.SdtIP{}, + StoragePort: 12200, + NvmePort: 4420, + DiscoveryPort: 8009, + ProtectionDomainID: "mock-pd-id", + } + + server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, _ *http.Request) { + http.Error(w, "Server Error", http.StatusInternalServerError) + })) + return server, sdtParam, check(checkEmptyIPList) + }, + + "error response": func(_ *testing.T) (*httptest.Server, *types.SdtParam, []checkFn) { + sdtParam := &types.SdtParam{ + Name: "example-sdt", + IPList: []*types.SdtIP{{IP: "192.168.0.1", Role: "StorageAndHost"}}, + StoragePort: 12200, + NvmePort: 4420, + DiscoveryPort: 8009, + ProtectionDomainID: "mock-pd-id", + } + + server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, _ *http.Request) { + http.Error(w, "Server Error", http.StatusInternalServerError) + })) + return server, sdtParam, check(hasError) + }, + } + + for name, tc := range tests { + t.Run(name, func(t *testing.T) { + server, sdtParam, checkFns := tc(t) + defer server.Close() + + client, _ := NewClientWithArgs(server.URL, "", math.MaxInt64, true, false) + pd := NewProtectionDomain(client) + resp, err := pd.CreateSdt(sdtParam) + + for _, checkFn := range checkFns { + checkFn(t, resp, err) + } + }) + } +} + +func Test_RenameSdt(t *testing.T) { + type checkFn func(*testing.T, error) + check := func(fns ...checkFn) []checkFn { return fns } + + hasNoError := func(t *testing.T, err error) { + if err != nil { + t.Fatalf("expected no error") + } + } + + hasError := func(t *testing.T, err error) { + if err == nil { + t.Fatalf("expected error") + } + } + + tests := map[string]func(t *testing.T) (*httptest.Server, types.System, []checkFn){ + "success": func(_ *testing.T) (*httptest.Server, types.System, []checkFn) { + systemID := "mock-system-id" + id := "mock-id" + url := fmt.Sprintf("/api/instances/Sdt::%v/action/renameSdt", id) + server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + if r.Method == http.MethodPost && strings.EqualFold(r.URL.Path, url) { + w.WriteHeader(http.StatusOK) + return + } + http.NotFound(w, r) + })) + system := types.System{ + ID: systemID, + } + return server, system, check(hasNoError) + }, + "error response": func(_ *testing.T) (*httptest.Server, types.System, []checkFn) { + systemID := "mock-system-id" + server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, _ *http.Request) { + http.Error(w, "Server Error", http.StatusInternalServerError) + })) + system := types.System{ + ID: systemID, + } + return server, system, check(hasError) + }, + } + + for name, tc := range tests { + t.Run(name, func(t *testing.T) { + server, sys, checkFns := tc(t) + defer server.Close() + + client, _ := NewClientWithArgs(server.URL, "", math.MaxInt64, true, false) + system := System{ + System: &sys, + client: client, + } + err := system.RenameSdt("mock-id", "new-mock-name") + + for _, checkFn := range checkFns { + checkFn(t, err) + } + }) + } +} + +func Test_SetSdtNvmePort(t *testing.T) { + type checkFn func(*testing.T, error) + check := func(fns ...checkFn) []checkFn { return fns } + + hasNoError := func(t *testing.T, err error) { + if err != nil { + t.Fatalf("expected no error") + } + } + + hasError := func(t *testing.T, err error) { + if err == nil { + t.Fatalf("expected error") + } + } + + tests := map[string]func(t *testing.T) (*httptest.Server, types.System, []checkFn){ + "success": func(_ *testing.T) (*httptest.Server, types.System, []checkFn) { + systemID := "mock-system-id" + id := "mock-id" + url := fmt.Sprintf("/api/instances/Sdt::%v/action/modifyNvmePort", id) + server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + if r.Method == http.MethodPost && strings.EqualFold(r.URL.Path, url) { + w.WriteHeader(http.StatusOK) + return + } + http.NotFound(w, r) + })) + system := types.System{ + ID: systemID, + } + return server, system, check(hasNoError) + }, + "error response": func(_ *testing.T) (*httptest.Server, types.System, []checkFn) { + systemID := "mock-system-id" + server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, _ *http.Request) { + http.Error(w, "Server Error", http.StatusInternalServerError) + })) + system := types.System{ + ID: systemID, + } + return server, system, check(hasError) + }, + } + + for name, tc := range tests { + t.Run(name, func(t *testing.T) { + server, sys, checkFns := tc(t) + defer server.Close() + + client, _ := NewClientWithArgs(server.URL, "", math.MaxInt64, true, false) + system := System{ + System: &sys, + client: client, + } + err := system.SetSdtNvmePort("mock-id", 1234) + + for _, checkFn := range checkFns { + checkFn(t, err) + } + }) + } +} + +func Test_SetSdtStoragePort(t *testing.T) { + type checkFn func(*testing.T, error) + check := func(fns ...checkFn) []checkFn { return fns } + + hasNoError := func(t *testing.T, err error) { + if err != nil { + t.Fatalf("expected no error") + } + } + + hasError := func(t *testing.T, err error) { + if err == nil { + t.Fatalf("expected error") + } + } + + tests := map[string]func(t *testing.T) (*httptest.Server, types.System, []checkFn){ + "success": func(_ *testing.T) (*httptest.Server, types.System, []checkFn) { + systemID := "mock-system-id" + id := "mock-id" + url := fmt.Sprintf("/api/instances/Sdt::%v/action/modifyStoragePort", id) + server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + if r.Method == http.MethodPost && strings.EqualFold(r.URL.Path, url) { + w.WriteHeader(http.StatusOK) + return + } + http.NotFound(w, r) + })) + system := types.System{ + ID: systemID, + } + return server, system, check(hasNoError) + }, + "error response": func(_ *testing.T) (*httptest.Server, types.System, []checkFn) { + systemID := "mock-system-id" + server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, _ *http.Request) { + http.Error(w, "Server Error", http.StatusInternalServerError) + })) + system := types.System{ + ID: systemID, + } + return server, system, check(hasError) + }, + } + + for name, tc := range tests { + t.Run(name, func(t *testing.T) { + server, sys, checkFns := tc(t) + defer server.Close() + + client, _ := NewClientWithArgs(server.URL, "", math.MaxInt64, true, false) + system := System{ + System: &sys, + client: client, + } + err := system.SetSdtStoragePort("mock-id", 1234) + + for _, checkFn := range checkFns { + checkFn(t, err) + } + }) + } +} + +func Test_SetSdtDiscoveryPort(t *testing.T) { + type checkFn func(*testing.T, error) + check := func(fns ...checkFn) []checkFn { return fns } + + hasNoError := func(t *testing.T, err error) { + if err != nil { + t.Fatalf("expected no error") + } + } + + hasError := func(t *testing.T, err error) { + if err == nil { + t.Fatalf("expected error") + } + } + + tests := map[string]func(t *testing.T) (*httptest.Server, types.System, []checkFn){ + "success": func(_ *testing.T) (*httptest.Server, types.System, []checkFn) { + systemID := "mock-system-id" + id := "mock-id" + url := fmt.Sprintf("/api/instances/Sdt::%v/action/modifyDiscoveryPort", id) + server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + if r.Method == http.MethodPost && strings.EqualFold(r.URL.Path, url) { + w.WriteHeader(http.StatusOK) + return + } + http.NotFound(w, r) + })) + system := types.System{ + ID: systemID, + } + return server, system, check(hasNoError) + }, + "error response": func(_ *testing.T) (*httptest.Server, types.System, []checkFn) { + systemID := "mock-system-id" + server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, _ *http.Request) { + http.Error(w, "Server Error", http.StatusInternalServerError) + })) + system := types.System{ + ID: systemID, + } + return server, system, check(hasError) + }, + } + + for name, tc := range tests { + t.Run(name, func(t *testing.T) { + server, sys, checkFns := tc(t) + defer server.Close() + + client, _ := NewClientWithArgs(server.URL, "", math.MaxInt64, true, false) + system := System{ + System: &sys, + client: client, + } + err := system.SetSdtDiscoveryPort("mock-id", 1234) + + for _, checkFn := range checkFns { + checkFn(t, err) + } + }) + } +} + +func Test_AddSdtTargetIP(t *testing.T) { + type checkFn func(*testing.T, error) + check := func(fns ...checkFn) []checkFn { return fns } + + hasNoError := func(t *testing.T, err error) { + if err != nil { + t.Fatalf("expected no error") + } + } + + hasError := func(t *testing.T, err error) { + if err == nil { + t.Fatalf("expected error") + } + } + + tests := map[string]func(t *testing.T) (*httptest.Server, types.System, []checkFn){ + "success": func(_ *testing.T) (*httptest.Server, types.System, []checkFn) { + systemID := "mock-system-id" + id := "mock-id" + url := fmt.Sprintf("/api/instances/Sdt::%v/action/addIp", id) + server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + if r.Method == http.MethodPost && strings.EqualFold(r.URL.Path, url) { + w.WriteHeader(http.StatusOK) + return + } + http.NotFound(w, r) + })) + system := types.System{ + ID: systemID, + } + return server, system, check(hasNoError) + }, + "error response": func(_ *testing.T) (*httptest.Server, types.System, []checkFn) { + systemID := "mock-system-id" + server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, _ *http.Request) { + http.Error(w, "Server Error", http.StatusInternalServerError) + })) + system := types.System{ + ID: systemID, + } + return server, system, check(hasError) + }, + } + + for name, tc := range tests { + t.Run(name, func(t *testing.T) { + server, sys, checkFns := tc(t) + defer server.Close() + + client, _ := NewClientWithArgs(server.URL, "", math.MaxInt64, true, false) + system := System{ + System: &sys, + client: client, + } + err := system.AddSdtTargetIP("mock-id", "192.168.0.2", "StorageAndHost") + + for _, checkFn := range checkFns { + checkFn(t, err) + } + }) + } +} + +func Test_RemoveSdtTargetIP(t *testing.T) { + type checkFn func(*testing.T, error) + check := func(fns ...checkFn) []checkFn { return fns } + + hasNoError := func(t *testing.T, err error) { + if err != nil { + t.Fatalf("expected no error") + } + } + + hasError := func(t *testing.T, err error) { + if err == nil { + t.Fatalf("expected error") + } + } + + tests := map[string]func(t *testing.T) (*httptest.Server, types.System, []checkFn){ + "success": func(_ *testing.T) (*httptest.Server, types.System, []checkFn) { + systemID := "mock-system-id" + id := "mock-id" + url := fmt.Sprintf("/api/instances/Sdt::%v/action/removeIp", id) + server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + if r.Method == http.MethodPost && strings.EqualFold(r.URL.Path, url) { + w.WriteHeader(http.StatusOK) + return + } + http.NotFound(w, r) + })) + system := types.System{ + ID: systemID, + } + return server, system, check(hasNoError) + }, + "error response": func(_ *testing.T) (*httptest.Server, types.System, []checkFn) { + systemID := "mock-system-id" + server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, _ *http.Request) { + http.Error(w, "Server Error", http.StatusInternalServerError) + })) + system := types.System{ + ID: systemID, + } + return server, system, check(hasError) + }, + } + + for name, tc := range tests { + t.Run(name, func(t *testing.T) { + server, sys, checkFns := tc(t) + defer server.Close() + + client, _ := NewClientWithArgs(server.URL, "", math.MaxInt64, true, false) + system := System{ + System: &sys, + client: client, + } + err := system.RemoveSdtTargetIP("mock-id", "192.168.0.2") + + for _, checkFn := range checkFns { + checkFn(t, err) + } + }) + } +} + +func Test_ModifySdtIPRole(t *testing.T) { + type checkFn func(*testing.T, error) + check := func(fns ...checkFn) []checkFn { return fns } + + hasNoError := func(t *testing.T, err error) { + if err != nil { + t.Fatalf("expected no error") + } + } + + hasError := func(t *testing.T, err error) { + if err == nil { + t.Fatalf("expected error") + } + } + + tests := map[string]func(t *testing.T) (*httptest.Server, types.System, []checkFn){ + "success": func(_ *testing.T) (*httptest.Server, types.System, []checkFn) { + systemID := "mock-system-id" + id := "mock-id" + url := fmt.Sprintf("/api/instances/Sdt::%v/action/modifyIpRole", id) + server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + if r.Method == http.MethodPost && strings.EqualFold(r.URL.Path, url) { + w.WriteHeader(http.StatusOK) + return + } + http.NotFound(w, r) + })) + system := types.System{ + ID: systemID, + } + return server, system, check(hasNoError) + }, + "error response": func(_ *testing.T) (*httptest.Server, types.System, []checkFn) { + systemID := "mock-system-id" + server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, _ *http.Request) { + http.Error(w, "Server Error", http.StatusInternalServerError) + })) + system := types.System{ + ID: systemID, + } + return server, system, check(hasError) + }, + } + + for name, tc := range tests { + t.Run(name, func(t *testing.T) { + server, sys, checkFns := tc(t) + defer server.Close() + + client, _ := NewClientWithArgs(server.URL, "", math.MaxInt64, true, false) + system := System{ + System: &sys, + client: client, + } + err := system.ModifySdtIPRole("mock-id", "192.168.0.2", "StorageOnly") + + for _, checkFn := range checkFns { + checkFn(t, err) + } + }) + } +} + +func Test_DeleteSdt(t *testing.T) { + type checkFn func(*testing.T, error) + check := func(fns ...checkFn) []checkFn { return fns } + + hasNoError := func(t *testing.T, err error) { + if err != nil { + t.Fatalf("expected no error") + } + } + + hasError := func(t *testing.T, err error) { + if err == nil { + t.Fatalf("expected error") + } + } + + tests := map[string]func(t *testing.T) (*httptest.Server, types.System, []checkFn){ + "success": func(_ *testing.T) (*httptest.Server, types.System, []checkFn) { + systemID := "mock-system-id" + id := "mock-id" + url := fmt.Sprintf("/api/instances/Sdt::%v/action/removeSdt", id) + server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + if r.Method == http.MethodPost && strings.EqualFold(r.URL.Path, url) { + w.WriteHeader(http.StatusOK) + return + } + http.NotFound(w, r) + })) + system := types.System{ + ID: systemID, + } + return server, system, check(hasNoError) + }, + "error response": func(_ *testing.T) (*httptest.Server, types.System, []checkFn) { + systemID := "mock-system-id" + server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, _ *http.Request) { + http.Error(w, "Server Error", http.StatusInternalServerError) + })) + system := types.System{ + ID: systemID, + } + return server, system, check(hasError) + }, + } + + for name, tc := range tests { + t.Run(name, func(t *testing.T) { + server, sys, checkFns := tc(t) + defer server.Close() + + client, _ := NewClientWithArgs(server.URL, "", math.MaxInt64, true, false) + system := System{ + System: &sys, + client: client, + } + err := system.DeleteSdt("mock-id") + + for _, checkFn := range checkFns { + checkFn(t, err) + } + }) + } +} + +func Test_EnterSdtMaintenanceMode(t *testing.T) { + type checkFn func(*testing.T, error) + check := func(fns ...checkFn) []checkFn { return fns } + + hasNoError := func(t *testing.T, err error) { + if err != nil { + t.Fatalf("expected no error") + } + } + + hasError := func(t *testing.T, err error) { + if err == nil { + t.Fatalf("expected error") + } + } + + tests := map[string]func(t *testing.T) (*httptest.Server, types.System, []checkFn){ + "success": func(_ *testing.T) (*httptest.Server, types.System, []checkFn) { + systemID := "mock-system-id" + id := "mock-id" + url := fmt.Sprintf("/api/instances/Sdt::%v/action/enterMaintenanceMode", id) + server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + if r.Method == http.MethodPost && strings.EqualFold(r.URL.Path, url) { + w.WriteHeader(http.StatusOK) + return + } + http.NotFound(w, r) + })) + system := types.System{ + ID: systemID, + } + return server, system, check(hasNoError) + }, + "error response": func(_ *testing.T) (*httptest.Server, types.System, []checkFn) { + systemID := "mock-system-id" + server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, _ *http.Request) { + http.Error(w, "Server Error", http.StatusInternalServerError) + })) + system := types.System{ + ID: systemID, + } + return server, system, check(hasError) + }, + } + + for name, tc := range tests { + t.Run(name, func(t *testing.T) { + server, sys, checkFns := tc(t) + defer server.Close() + + client, _ := NewClientWithArgs(server.URL, "", math.MaxInt64, true, false) + system := System{ + System: &sys, + client: client, + } + err := system.EnterSdtMaintenanceMode("mock-id") + + for _, checkFn := range checkFns { + checkFn(t, err) + } + }) + } +} + +func Test_ExitSdtMaintenanceMode(t *testing.T) { + type checkFn func(*testing.T, error) + check := func(fns ...checkFn) []checkFn { return fns } + + hasNoError := func(t *testing.T, err error) { + if err != nil { + t.Fatalf("expected no error") + } + } + + hasError := func(t *testing.T, err error) { + if err == nil { + t.Fatalf("expected error") + } + } + + tests := map[string]func(t *testing.T) (*httptest.Server, types.System, []checkFn){ + "success": func(_ *testing.T) (*httptest.Server, types.System, []checkFn) { + systemID := "mock-system-id" + id := "mock-id" + url := fmt.Sprintf("/api/instances/Sdt::%v/action/exitMaintenanceMode", id) + server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + if r.Method == http.MethodPost && strings.EqualFold(r.URL.Path, url) { + w.WriteHeader(http.StatusOK) + return + } + http.NotFound(w, r) + })) + system := types.System{ + ID: systemID, + } + return server, system, check(hasNoError) + }, + "error response": func(_ *testing.T) (*httptest.Server, types.System, []checkFn) { + systemID := "mock-system-id" + server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, _ *http.Request) { + http.Error(w, "Server Error", http.StatusInternalServerError) + })) + system := types.System{ + ID: systemID, + } + return server, system, check(hasError) + }, + } + + for name, tc := range tests { + t.Run(name, func(t *testing.T) { + server, sys, checkFns := tc(t) + defer server.Close() + + client, _ := NewClientWithArgs(server.URL, "", math.MaxInt64, true, false) + system := System{ + System: &sys, + client: client, + } + err := system.ExitSdtMaintenanceMode("mock-id") + + for _, checkFn := range checkFns { + checkFn(t, err) + } + }) + } +} diff --git a/types/v1/types.go b/types/v1/types.go index 54b6bad..5302976 100644 --- a/types/v1/types.go +++ b/types/v1/types.go @@ -2095,3 +2095,92 @@ type NvmeHostParam struct { MaxNumPaths IntString `json:"maxNumPaths,omitempty"` MaxNumSysPorts IntString `json:"maxNumSysPorts,omitempty"` } + +// Sdt defines struct for Sdt +type Sdt struct { + ID string `json:"id"` + Name string `json:"name,omitempty"` + ProtectionDomainID string `json:"protectionDomainId"` + IPList []*SdtIP `json:"ipList"` + StoragePort int `json:"storagePort,omitempty"` + NvmePort int `json:"nvmePort,omitempty"` + DiscoveryPort int `json:"discoveryPort,omitempty"` + SdtState string `json:"sdtState"` + SystemID string `json:"systemId"` + MdmConnectionState string `json:"mdmConnectionState"` + MembershipState string `json:"membershipState"` + FaultSetID string `json:"faultSetId,omitempty"` + SoftwareVersionInfo string `json:"softwareVersionInfo,omitempty"` + MaintenanceState string `json:"maintenanceState,omitempty"` + AuthenticationError string `json:"authenticationError,omitempty"` + PersistentDiscoveryControllersNum int `json:"persistentDiscoveryControllersNum,omitempty"` + CertificateInfo CertificateInfo `json:"certificateInfo,omitempty"` + Links []*Link `json:"links"` +} + +// SdtIP defines struct for SdtIP +type SdtIP struct { + IP string `json:"ip"` + Role string `json:"role"` +} + +// SdtParam defines struct for SdtParam +type SdtParam struct { + Name string `json:"name,omitempty"` + IPList []*SdtIP `json:"ips"` + StoragePort IntString `json:"storagePort,omitempty"` + NvmePort IntString `json:"nvmePort,omitempty"` + DiscoveryPort IntString `json:"discoveryPort,omitempty"` + ProtectionDomainID string `json:"protectionDomainId"` +} + +// SdtResp defines struct for response of creating sdt +type SdtResp struct { + ID string `json:"id"` +} + +// SdtRenameParam defines the struct for renaming an sdt +type SdtRenameParam struct { + NewName string `json:"newName"` +} + +// SdtNvmePortParam defines the struct for modifying NVMe port for the sdt +type SdtNvmePortParam struct { + NewNvmePort IntString `json:"newNvmePort"` +} + +// SdtStoragePortParam defines the struct for modifying StoragePort for the sdt +type SdtStoragePortParam struct { + NewStoragePort IntString `json:"newStoragePort"` +} + +// SdtDiscoveryPortParam defines the struct for modifying DiscoveryPort for the sdt +type SdtDiscoveryPortParam struct { + NewDiscoveryPort IntString `json:"newDiscoveryPort"` +} + +// SdtRemoveIPParam defines the struct for removing IP from the sdt +type SdtRemoveIPParam struct { + IP string `json:"ip"` +} + +// SdtIPRoleParam defines the struct for modifying IP Role for the sdt +type SdtIPRoleParam struct { + IP string `json:"ip"` + Role string `json:"newRole"` +} + +// NvmeController defines struct for NVMe controller +type NvmeController struct { + Name string `json:"name"` + IsConnected bool `json:"isConnected"` + SdtID string `json:"sdtId"` + HostIP string `json:"hostIp"` + HostID string `json:"hostId"` + ControllerID int `json:"controllerId"` + SysPortID int `json:"sysPortId"` + SysPortIP string `json:"sysPortIp"` + Subsystem string `json:"subsystem"` + IsAssigned bool `json:"isAssigned"` + ID string `json:"id"` +}