Skip to content

Commit 91abbb5

Browse files
committed
fix replication import
1 parent db158fa commit 91abbb5

2 files changed

Lines changed: 223 additions & 4 deletions

File tree

cloudmanager/resource_netapp_cloudmanager_snapmirror.go

Lines changed: 85 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
package cloudmanager
22

33
import (
4+
"fmt"
45
"log"
56
"strings"
67

@@ -16,7 +17,7 @@ func resourceCVOSnapMirror() *schema.Resource {
1617
Exists: resourceCVOSnapMirrorExists,
1718
Update: resourceCVOSnapMirrorUpdate,
1819
Importer: &schema.ResourceImporter{
19-
State: schema.ImportStatePassthrough,
20+
State: resourceCVOSnapMirrorImport,
2021
},
2122
Schema: map[string]*schema.Schema{
2223
"source_working_environment_id": {
@@ -316,3 +317,86 @@ func resourceCVOSnapMirrorUpdate(d *schema.ResourceData, meta interface{}) error
316317

317318
return nil
318319
}
320+
321+
func resourceCVOSnapMirrorImport(d *schema.ResourceData, meta interface{}) ([]*schema.ResourceData, error) {
322+
log.Printf("Importing SnapMirror with ID: %s", d.Id())
323+
324+
client := meta.(*Client)
325+
importID := d.Id()
326+
327+
// Parse the import ID - expect format: client_id:destination_volume_name
328+
parts := strings.Split(importID, ":")
329+
if len(parts) != 2 {
330+
return nil, fmt.Errorf("invalid import ID format. Expected: client_id:destination_volume_name, got: %s", importID)
331+
}
332+
333+
clientID := parts[0]
334+
destinationVolumeName := parts[1]
335+
336+
// Check deployment mode
337+
isSaas, connectorIP, err := client.checkDeploymentMode(d, clientID)
338+
if err != nil {
339+
return nil, fmt.Errorf("failed to check deployment mode during import: %v", err)
340+
}
341+
342+
// Try to find the snapmirror relationship by destination volume name
343+
relationship, err := client.findSnapMirrorByDestinationVolume(destinationVolumeName, clientID, isSaas, connectorIP)
344+
if err != nil {
345+
return nil, fmt.Errorf("failed to find snapmirror relationship during import: %v", err)
346+
}
347+
348+
// Set the ID and populate the required fields
349+
d.SetId(destinationVolumeName)
350+
d.Set("client_id", clientID)
351+
d.Set("source_working_environment_id", relationship.Source.WorkingEnvironmentID)
352+
d.Set("destination_working_environment_id", relationship.Destination.WorkingEnvironmentID)
353+
d.Set("source_volume_name", relationship.Source.VolumeName)
354+
d.Set("destination_volume_name", relationship.Destination.VolumeName)
355+
d.Set("source_svm_name", relationship.Source.SvmName)
356+
d.Set("destination_svm_name", relationship.Destination.SvmName)
357+
d.Set("policy", relationship.Policy)
358+
d.Set("schedule", relationship.Schedule)
359+
d.Set("max_transfer_rate", relationship.MaxTransferRate.Size)
360+
361+
// Set default values for fields that have them in the schema
362+
d.Set("deployment_mode", "Standard")
363+
d.Set("delete_destination_volume", false)
364+
365+
// Set optional fields if they exist, otherwise set appropriate defaults
366+
if relationship.Destination.AggregateName != "" {
367+
d.Set("destination_aggregate_name", relationship.Destination.AggregateName)
368+
}
369+
if relationship.Destination.ProviderVolumeType != "" {
370+
d.Set("provider_volume_type", relationship.Destination.ProviderVolumeType)
371+
}
372+
if relationship.Destination.CapacityTier != "" && relationship.Destination.CapacityTier != "none" {
373+
d.Set("capacity_tier", relationship.Destination.CapacityTier)
374+
} else {
375+
d.Set("capacity_tier", "none")
376+
}
377+
378+
// Try to get additional volume information from the destination working environment
379+
// This is needed because the status API doesn't include all volume details
380+
if relationship.Destination.WorkingEnvironmentID != "" {
381+
volumeRequest := volumeRequest{
382+
WorkingEnvironmentID: relationship.Destination.WorkingEnvironmentID,
383+
Name: relationship.Destination.VolumeName,
384+
}
385+
386+
volumes, err := client.getVolume(volumeRequest, clientID, isSaas, connectorIP)
387+
if err == nil && len(volumes) > 0 {
388+
volume := volumes[0]
389+
if volume.AggregateName != "" {
390+
d.Set("destination_aggregate_name", volume.AggregateName)
391+
}
392+
if volume.ProviderVolumeType != "" {
393+
d.Set("provider_volume_type", volume.ProviderVolumeType)
394+
}
395+
if volume.CapacityTier != "" {
396+
d.Set("capacity_tier", volume.CapacityTier)
397+
}
398+
}
399+
}
400+
401+
return []*schema.ResourceData{d}, nil
402+
}

cloudmanager/snapmirror.go

Lines changed: 138 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -49,11 +49,47 @@ type interClusterLifsAddress struct {
4949
}
5050

5151
type snapMirrorStatusResponse struct {
52-
Destination destination `structs:"destination"`
52+
Source relationshipEndpoint `json:"source"`
53+
Destination relationshipEndpoint `json:"destination"`
54+
Policy string `json:"policy"`
55+
Schedule string `json:"schedule"`
56+
MaxTransferRate sizeUnit `json:"maxTransferRate"`
5357
}
5458

55-
type destination struct {
56-
VolumeName string `structs:"volumeName"`
59+
type relationshipEndpoint struct {
60+
WorkingEnvironmentID string `json:"workingEnvironmentId"`
61+
SvmName string `json:"svmName"`
62+
VolumeName string `json:"volumeName"`
63+
AggregateName string `json:"aggregateName"`
64+
ProviderVolumeType string `json:"providerVolumeType"`
65+
CapacityTier string `json:"capacityTier"`
66+
// Add more fields that might be in the actual response
67+
WorkingEnvironmentType string `json:"workingEnvironmentType"`
68+
WorkingEnvironmentStatus string `json:"workingEnvironmentStatus"`
69+
ClusterName string `json:"clusterName"`
70+
Region string `json:"region"`
71+
AvailabilityZone string `json:"availabilityZone"`
72+
SvmPeerAliasName string `json:"svmPeerAliasName"`
73+
NodeName string `json:"nodeName"`
74+
}
75+
76+
type sizeUnit struct {
77+
Size int `json:"size"`
78+
Unit string `json:"unit"`
79+
}
80+
81+
// Basic relationship structure from all-relationships endpoint
82+
type allRelationshipsResponse struct {
83+
Relationships []basicRelationship `json:"relationships"`
84+
}
85+
86+
type basicRelationship struct {
87+
Source relationshipEndpointBasic `json:"source"`
88+
Target relationshipEndpointBasic `json:"target"`
89+
}
90+
91+
type relationshipEndpointBasic struct {
92+
ID string `json:"id"`
5793
}
5894

5995
func (c *Client) getInterclusterlifs(snapMirror snapMirrorRequest, clientID string, isSaas bool, connectorIP string) (interclusterlif, error) {
@@ -319,6 +355,105 @@ func (c *Client) deleteSnapMirror(snapMirror snapMirrorRequest, clientID string,
319355
return err
320356
}
321357

358+
// getAllSnapMirrorRelationships queries all SnapMirror relationships to find one by destination volume name
359+
func (c *Client) getAllSnapMirrorRelationships(clientID string, isSaas bool, connectorIP string) ([]snapMirrorStatusResponse, error) {
360+
accessTokenResult, err := c.getAccessToken()
361+
if err != nil {
362+
log.Print("in getAllSnapMirrorRelationships request, failed to get AccessToken")
363+
return nil, err
364+
}
365+
c.Token = accessTokenResult.Token
366+
367+
hostType := "CloudManagerHost"
368+
if !isSaas {
369+
hostType = "http://" + connectorIP
370+
}
371+
372+
baseURL := "/occm/api/replication/all-relationships"
373+
374+
statusCode, response, _, err := c.CallAPIMethod("GET", baseURL, nil, c.Token, hostType, clientID)
375+
if err != nil {
376+
log.Print("getAllSnapMirrorRelationships request failed ", statusCode)
377+
return nil, err
378+
}
379+
responseError := apiResponseChecker(statusCode, response, "getAllSnapMirrorRelationships")
380+
if responseError != nil {
381+
return nil, responseError
382+
}
383+
384+
var allRelationships allRelationshipsResponse
385+
if err := json.Unmarshal(response, &allRelationships); err != nil {
386+
log.Print("Failed to unmarshall response from getAllSnapMirrorRelationships ", err)
387+
return nil, err
388+
}
389+
390+
log.Printf("Found %d basic relationships", len(allRelationships.Relationships))
391+
392+
// Now get detailed information for each relationship
393+
var result []snapMirrorStatusResponse
394+
for _, basicRel := range allRelationships.Relationships {
395+
// Query detailed status for this source working environment
396+
detailedRelationships, err := c.getSnapMirrorStatusForSourceWE(basicRel.Source.ID, clientID, isSaas, connectorIP)
397+
if err != nil {
398+
log.Printf("Failed to get detailed status for source WE %s: %v", basicRel.Source.ID, err)
399+
continue
400+
}
401+
402+
// Filter relationships for this specific target
403+
for _, detailed := range detailedRelationships {
404+
if detailed.Destination.WorkingEnvironmentID == basicRel.Target.ID {
405+
result = append(result, detailed)
406+
}
407+
}
408+
}
409+
410+
return result, nil
411+
}
412+
413+
// getSnapMirrorStatusForSourceWE gets detailed snapmirror status for a specific source working environment
414+
func (c *Client) getSnapMirrorStatusForSourceWE(sourceWEID string, clientID string, isSaas bool, connectorIP string) ([]snapMirrorStatusResponse, error) {
415+
hostType := "CloudManagerHost"
416+
if !isSaas {
417+
hostType = "http://" + connectorIP
418+
}
419+
420+
baseURL := fmt.Sprintf("/occm/api/replication/status/%s", sourceWEID)
421+
422+
statusCode, response, _, err := c.CallAPIMethod("GET", baseURL, nil, c.Token, hostType, clientID)
423+
if err != nil {
424+
log.Printf("getSnapMirrorStatusForSourceWE request failed for %s: %d", sourceWEID, statusCode)
425+
return nil, err
426+
}
427+
responseError := apiResponseChecker(statusCode, response, "getSnapMirrorStatusForSourceWE")
428+
if responseError != nil {
429+
return nil, responseError
430+
}
431+
432+
var result []snapMirrorStatusResponse
433+
if err := json.Unmarshal(response, &result); err != nil {
434+
log.Printf("Failed to unmarshall response from getSnapMirrorStatusForSourceWE for %s: %v", sourceWEID, err)
435+
return nil, err
436+
}
437+
438+
return result, nil
439+
}
440+
441+
// findSnapMirrorByDestinationVolume finds a snapmirror relationship by destination volume name
442+
func (c *Client) findSnapMirrorByDestinationVolume(destinationVolumeName string, clientID string, isSaas bool, connectorIP string) (*snapMirrorStatusResponse, error) {
443+
relationships, err := c.getAllSnapMirrorRelationships(clientID, isSaas, connectorIP)
444+
if err != nil {
445+
return nil, err
446+
}
447+
448+
for _, relationship := range relationships {
449+
if relationship.Destination.VolumeName == destinationVolumeName {
450+
return &relationship, nil
451+
}
452+
}
453+
454+
return nil, fmt.Errorf("snapmirror relationship not found for destination volume: %s", destinationVolumeName)
455+
}
456+
322457
func (c *Client) getSnapMirror(snapMirror snapMirrorRequest, vol string, clientID string, isSaas bool, connectorIP string) (string, error) {
323458

324459
var result []snapMirrorStatusResponse

0 commit comments

Comments
 (0)