Skip to content

Commit 5de5884

Browse files
authored
Merge pull request #222 from halradaideh/fix-replication-import
fix replication import
2 parents d7b8818 + 91abbb5 commit 5de5884

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": {
@@ -342,3 +343,86 @@ func resourceCVOSnapMirrorUpdate(d *schema.ResourceData, meta interface{}) error
342343

343344
return nil
344345
}
346+
347+
func resourceCVOSnapMirrorImport(d *schema.ResourceData, meta interface{}) ([]*schema.ResourceData, error) {
348+
log.Printf("Importing SnapMirror with ID: %s", d.Id())
349+
350+
client := meta.(*Client)
351+
importID := d.Id()
352+
353+
// Parse the import ID - expect format: client_id:destination_volume_name
354+
parts := strings.Split(importID, ":")
355+
if len(parts) != 2 {
356+
return nil, fmt.Errorf("invalid import ID format. Expected: client_id:destination_volume_name, got: %s", importID)
357+
}
358+
359+
clientID := parts[0]
360+
destinationVolumeName := parts[1]
361+
362+
// Check deployment mode
363+
isSaas, connectorIP, err := client.checkDeploymentMode(d, clientID)
364+
if err != nil {
365+
return nil, fmt.Errorf("failed to check deployment mode during import: %v", err)
366+
}
367+
368+
// Try to find the snapmirror relationship by destination volume name
369+
relationship, err := client.findSnapMirrorByDestinationVolume(destinationVolumeName, clientID, isSaas, connectorIP)
370+
if err != nil {
371+
return nil, fmt.Errorf("failed to find snapmirror relationship during import: %v", err)
372+
}
373+
374+
// Set the ID and populate the required fields
375+
d.SetId(destinationVolumeName)
376+
d.Set("client_id", clientID)
377+
d.Set("source_working_environment_id", relationship.Source.WorkingEnvironmentID)
378+
d.Set("destination_working_environment_id", relationship.Destination.WorkingEnvironmentID)
379+
d.Set("source_volume_name", relationship.Source.VolumeName)
380+
d.Set("destination_volume_name", relationship.Destination.VolumeName)
381+
d.Set("source_svm_name", relationship.Source.SvmName)
382+
d.Set("destination_svm_name", relationship.Destination.SvmName)
383+
d.Set("policy", relationship.Policy)
384+
d.Set("schedule", relationship.Schedule)
385+
d.Set("max_transfer_rate", relationship.MaxTransferRate.Size)
386+
387+
// Set default values for fields that have them in the schema
388+
d.Set("deployment_mode", "Standard")
389+
d.Set("delete_destination_volume", false)
390+
391+
// Set optional fields if they exist, otherwise set appropriate defaults
392+
if relationship.Destination.AggregateName != "" {
393+
d.Set("destination_aggregate_name", relationship.Destination.AggregateName)
394+
}
395+
if relationship.Destination.ProviderVolumeType != "" {
396+
d.Set("provider_volume_type", relationship.Destination.ProviderVolumeType)
397+
}
398+
if relationship.Destination.CapacityTier != "" && relationship.Destination.CapacityTier != "none" {
399+
d.Set("capacity_tier", relationship.Destination.CapacityTier)
400+
} else {
401+
d.Set("capacity_tier", "none")
402+
}
403+
404+
// Try to get additional volume information from the destination working environment
405+
// This is needed because the status API doesn't include all volume details
406+
if relationship.Destination.WorkingEnvironmentID != "" {
407+
volumeRequest := volumeRequest{
408+
WorkingEnvironmentID: relationship.Destination.WorkingEnvironmentID,
409+
Name: relationship.Destination.VolumeName,
410+
}
411+
412+
volumes, err := client.getVolume(volumeRequest, clientID, isSaas, connectorIP)
413+
if err == nil && len(volumes) > 0 {
414+
volume := volumes[0]
415+
if volume.AggregateName != "" {
416+
d.Set("destination_aggregate_name", volume.AggregateName)
417+
}
418+
if volume.ProviderVolumeType != "" {
419+
d.Set("provider_volume_type", volume.ProviderVolumeType)
420+
}
421+
if volume.CapacityTier != "" {
422+
d.Set("capacity_tier", volume.CapacityTier)
423+
}
424+
}
425+
}
426+
427+
return []*schema.ResourceData{d}, nil
428+
}

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)