From 0e678b60eb172def2549d47c3e72d1410eb6338e Mon Sep 17 00:00:00 2001 From: Andrey Schipilo Date: Mon, 31 Jan 2022 14:02:29 +0300 Subject: [PATCH 1/6] Added target policies, jobs and reports calls --- api/common/utils/poll.go | 97 ++++++++++ api/v11/api_v11_replication.go | 174 +++++++++++++++-- replication.go | 328 ++++++++++++++++++++++++++++++++- 3 files changed, 575 insertions(+), 24 deletions(-) create mode 100644 api/common/utils/poll.go diff --git a/api/common/utils/poll.go b/api/common/utils/poll.go new file mode 100644 index 0000000..60a121a --- /dev/null +++ b/api/common/utils/poll.go @@ -0,0 +1,97 @@ +package utils + +// Polling functionality inspired by https://github.com/kubernetes/apimachinery + +import ( + "context" + "errors" + "time" +) + +var ErrWaitTimeout = errors.New("timed out waiting for the condition") + +type WaitWithContextFunc func(ctx context.Context) <-chan struct{} +type ConditionWithContextFunc func(context.Context) (done bool, err error) + +func PollImmediateWithContext(ctx context.Context, interval, timeout time.Duration, condition ConditionWithContextFunc) error { + return poll(ctx, true, poller(interval, timeout), condition) +} + +func WaitForWithContext(ctx context.Context, wait WaitWithContextFunc, fn ConditionWithContextFunc) error { + waitCtx, cancel := context.WithCancel(context.Background()) + defer cancel() + c := wait(waitCtx) + for { + select { + case _, open := <-c: + ok, err := fn(ctx) + if err != nil { + return err + } + if ok { + return nil + } + if !open { + return ErrWaitTimeout + } + case <-ctx.Done(): + return ErrWaitTimeout + } + } +} + +func poll(ctx context.Context, immediate bool, wait WaitWithContextFunc, condition ConditionWithContextFunc) error { + if immediate { + done, err := condition(ctx) + if err != nil { + return err + } + if done { + return nil + } + } + + select { + case <-ctx.Done(): + return ErrWaitTimeout + default: + return WaitForWithContext(ctx, wait, condition) + } +} + + +func poller(interval, timeout time.Duration) WaitWithContextFunc { + return func(ctx context.Context) <-chan struct{} { + ch := make(chan struct{}) + + go func() { + defer close(ch) + + tick := time.NewTicker(interval) + defer tick.Stop() + + var after <-chan time.Time + if timeout != 0 { + timer := time.NewTimer(timeout) + after = timer.C + defer timer.Stop() + } + + for { + select { + case <-tick.C: + select { + case ch <- struct{}{}: + default: + } + case <-after: + return + case <-ctx.Done(): + return + } + } + }() + + return ch + } +} diff --git a/api/v11/api_v11_replication.go b/api/v11/api_v11_replication.go index 6b88115..18fe506 100644 --- a/api/v11/api_v11_replication.go +++ b/api/v11/api_v11_replication.go @@ -3,34 +3,112 @@ package v11 import ( "context" "fmt" + "strconv" + "github.com/dell/goisilon/api" ) const ( - policiesPath = "/platform/11/sync/policies/" + policiesPath = "/platform/11/sync/policies/" + targetPoliciesPath = "/platform/11/sync/target/policies/" + jobsPath = "/platform/11/sync/jobs/" + reportsPath = "/platform/11/sync/reports" +) + +type JOB_ACTION string + +const ( + RESYNC_PREP JOB_ACTION = "resync_prep" + ALLOW_WRITE JOB_ACTION = "allow_write" + ALLOW_WRITE_REVERT JOB_ACTION = "allow_write_revert" + TEST JOB_ACTION = "test" +) + +type JOB_STATE string + +const ( + SCHEDULED JOB_STATE = "scheduled" + RUNNING JOB_STATE = "running" + PAUSED JOB_STATE = "paused" + FINISHED JOB_STATE = "finished" + FAILED JOB_STATE = "failed" + CANCELED JOB_STATE = "canceled" + NEEDS_ATTENTION JOB_STATE = "needs_attention" + SKIPPED JOB_STATE = "skipped" + PENDING JOB_STATE = "pending" + UNKNOWN JOB_STATE = "unknown" ) +type FAILOVER_FAILBACK_STATE string + +const ( + WRITES_DISABLED FAILOVER_FAILBACK_STATE = "writes_disabled" + ENABLING_WRITES FAILOVER_FAILBACK_STATE = "enabling_writes" + WRITES_ENABLED FAILOVER_FAILBACK_STATE = "writes_enabled" + DISABLING_WRITES FAILOVER_FAILBACK_STATE = "disabling_writes" + CREATING_RESYNC_POLICY FAILOVER_FAILBACK_STATE = "creating_resync_policy" + RESYNC_POLICY_CREATED FAILOVER_FAILBACK_STATE = "resync_policy_created" +) + +var policyNameArg = []byte("policy_name") +var sortArg = []byte("sort") +var reportsPerPolicyArg = []byte("reports_per_policy") + // Policy contains the CloudIQ policy info. type Policy struct { - Action string `json:"action,omitempty"` - Id string `json:"id,omitempty"` - Name string `json:"name,omitempty"` - Enabled bool `json:"enabled,omitempty"` - TargetPath string `json:"target_path,omitempty"` - SourcePath string `json:"source_root_path,omitempty"` - TargetHost string `json:"target_host,omitempty"` - JobDelay int `json:"job_delay,omitempty"` - Schedule string `json:"schedule,omitempty"` + Action string `json:"action,omitempty"` + Id string `json:"id,omitempty"` + Name string `json:"name,omitempty"` + Enabled bool `json:"enabled"` + TargetPath string `json:"target_path,omitempty"` + SourcePath string `json:"source_root_path,omitempty"` + TargetHost string `json:"target_host,omitempty"` + JobDelay int `json:"job_delay,omitempty"` + Schedule string `json:"schedule,omitempty"` + LastJobState JOB_STATE `json:"last_job_state,omitempty"` } type Policies struct { Policy []Policy `json:"policies,omitempty"` } +type Reports struct { + Reports []Report `json:"reports,omitempty"` +} + +type TargetPolicy struct { + Id string `json:"id,omitempty"` + Name string `json:"name,omitempty"` + SourceClusterGuid string `json:"source_cluster_guid,omitempty"` + LastJobState JOB_STATE `json:"last_job_state,omitempty"` + TargetPath string `json:"target_path,omitempty"` + SourceHost string `json:"source_host,omitempty"` + LastSourceCoordinatorIp string `json:"last_source_coordinator_ip,omitempty"` + FailoverFailbackState FAILOVER_FAILBACK_STATE `json:"failover_failback_state,omitempty"` +} + +type TargetPolicies struct { + Policy []TargetPolicy `json:"policies,omitempty"` +} + +type JobRequest struct { + Action JOB_ACTION `json:"action,omitempty"` + Id string `json:"id,omitempty"` // ID or Name of policy + SkipFailover bool `json:"skip_failover,omitempty"` + SkipMap bool `json:"skip_map,omitempty"` + SkipCopy bool `json:"skip_copy,omitempty"` +} + +type Report struct { + Policy Policy `json:"policy,omitempty"` + Id string `json:"id"` + JobId int64 `json:"job_id"` + State JOB_STATE `json:"state,omitempty"` + EndTime int64 `json:"end_time"` +} + // GetPolicyByName returns policy by name -func GetPolicyByName( - ctx context.Context, - client api.Client, name string) (policy *Policy, err error) { +func GetPolicyByName(ctx context.Context, client api.Client, name string) (policy *Policy, err error) { p := &Policies{} err = client.Get(ctx, policiesPath, name, nil, nil, &p) if err != nil { @@ -41,23 +119,83 @@ func GetPolicyByName( return &p.Policy[0], nil } -func CreatePolicy(ctx context.Context, client api.Client, name string, sourcePath string, targetPath string, targetHost string, rpo int) error { - resp := "" +func GetTargetPolicyByName(ctx context.Context, client api.Client, name string) (policy *TargetPolicy, err error) { + p := &TargetPolicies{} + err = client.Get(ctx, targetPoliciesPath, name, nil, nil, &p) + if err != nil || len(p.Policy) == 0 { + return nil, err + } + return &p.Policy[0], nil +} + +func CreatePolicy(ctx context.Context, client api.Client, name string, sourcePath string, targetPath string, targetHost string, rpo int, enabled bool) error { + var policyResp Policy body := &Policy{ Action: "sync", Id: "", Name: name, - Enabled: true, + Enabled: enabled, TargetPath: targetPath, SourcePath: sourcePath, TargetHost: targetHost, JobDelay: rpo, Schedule: "when-source-modified", } - return client.Post(ctx, policiesPath, "", nil, nil, body, resp) + return client.Post(ctx, policiesPath, "", nil, nil, body, &policyResp) } func DeletePolicy(ctx context.Context, client api.Client, name string) error { resp := "" return client.Delete(ctx, policiesPath, name, nil, nil, &resp) -} \ No newline at end of file +} + +func DeleteTargetPolicy(ctx context.Context, client api.Client, id string) error { + resp := "" + return client.Delete(ctx, targetPoliciesPath, id, nil, nil, &resp) +} + +func UpdatePolicy(ctx context.Context, client api.Client, policy *Policy) error { + id := policy.Id + policy.Id = "" + + return client.Put(ctx, policiesPath, id, nil, nil, policy, nil) +} + +func ResetPolicy(ctx context.Context, client api.Client, name string) error { + resp := Policy{} + return client.Post(ctx, policiesPath, name+"/reset", nil, nil, nil, &resp) +} + +func StartSyncIQJob(ctx context.Context, client api.Client, job *JobRequest) (*JobRequest, error) { + var jobResp JobRequest + return &jobResp, client.Post(ctx, jobsPath, "", nil, nil, job, &jobResp) +} + +func GetReport(ctx context.Context, client api.Client, reportName string) (*Report, error) { + r := &Reports{} + err := client.Get(ctx, reportsPath, reportName, nil, nil, &r) + if err != nil || len(r.Reports) == 0 { + return nil, err + } + return &r.Reports[0], nil +} + +func GetReportsByPolicyName(ctx context.Context, client api.Client, policyName string, reportsForPolicy int) (*Reports, error) { + r := &Reports{} + err := client.Get(ctx, reportsPath, "", + api.OrderedValues{ + {policyNameArg, []byte(policyName)}, + {sortArg, []byte("end_time")}, + {reportsPerPolicyArg, []byte(strconv.Itoa(reportsForPolicy))}, + }, + nil, &r) + if err != nil { + return nil, err + } + + if len(r.Reports) == 0 { + return nil, fmt.Errorf("no reports found for policy %s", policyName) + } + + return r, nil +} diff --git a/replication.go b/replication.go index 3d836ff..aa9fe9f 100644 --- a/replication.go +++ b/replication.go @@ -2,21 +2,337 @@ package goisilon import ( "context" + "fmt" + log "github.com/akutz/gournal" + "github.com/dell/goisilon/api/common/utils" apiv11 "github.com/dell/goisilon/api/v11" + "time" +) + +const defaultPoll = 5 * time.Second +const defaultTimeout = 10 * time.Minute // set high timeout, we expect to be canceled via context before + +const ( + RESYNC_PREP apiv11.JOB_ACTION = "resync_prep" + ALLOW_WRITE apiv11.JOB_ACTION = "allow_write" + ALLOW_WRITE_REVERT apiv11.JOB_ACTION = "allow_write_revert" + TEST apiv11.JOB_ACTION = "test" + SCHEDULED apiv11.JOB_STATE = "scheduled" + RUNNING apiv11.JOB_STATE = "running" + PAUSED apiv11.JOB_STATE = "paused" + FINISHED apiv11.JOB_STATE = "finished" + FAILED apiv11.JOB_STATE = "failed" + CANCELED apiv11.JOB_STATE = "canceled" + NEEDS_ATTENTION apiv11.JOB_STATE = "needs_attention" + SKIPPED apiv11.JOB_STATE = "skipped" + PENDING apiv11.JOB_STATE = "pending" + UNKNOWN apiv11.JOB_STATE = "unknown" + WRITES_DISABLED apiv11.FAILOVER_FAILBACK_STATE = "writes_disabled" + ENABLING_WRITES apiv11.FAILOVER_FAILBACK_STATE = "enabling_writes" + WRITES_ENABLED apiv11.FAILOVER_FAILBACK_STATE = "writes_enabled" + DISABLING_WRITES apiv11.FAILOVER_FAILBACK_STATE = "disabling_writes" + CREATING_RESYNC_POLICY apiv11.FAILOVER_FAILBACK_STATE = "creating_resync_policy" + RESYNC_POLICY_CREATED apiv11.FAILOVER_FAILBACK_STATE = "resync_policy_created" ) // Policy is an Isilon Policy type Policy *apiv11.Policy +type TargetPolicy *apiv11.TargetPolicy + + // GetPolicyByName returns a policy with the provided ID. func (c *Client) GetPolicyByName(ctx context.Context, id string) (Policy, error) { - return apiv11.GetPolicyByName(ctx,c.API,id) + return apiv11.GetPolicyByName(ctx, c.API, id) +} + +func (c *Client) GetTargetPolicyByName(ctx context.Context, id string) (TargetPolicy, error) { + return apiv11.GetTargetPolicyByName(ctx, c.API, id) +} + +func (c *Client) CreatePolicy(ctx context.Context, name string, rpo int, sourcePath string, targetPath string, targetHost string, enabled bool) error { + return apiv11.CreatePolicy(ctx, c.API, name, sourcePath, targetPath, targetHost, rpo, enabled) +} + +func (c *Client) DeletePolicy(ctx context.Context, name string) error { + return apiv11.DeletePolicy(ctx, c.API, name) +} + +func (c *Client) DeleteTargetPolicy(ctx context.Context, id string) error { + return apiv11.DeleteTargetPolicy(ctx, c.API, id) +} + +func (c *Client) BreakAssociation(ctx context.Context, targetPolicyName string) error { + tp, err := apiv11.GetTargetPolicyByName(ctx, c.API, targetPolicyName) + if err != nil { + return err + } + + return c.DeleteTargetPolicy(ctx, tp.Id) +} + +func (c *Client) ResetPolicy(ctx context.Context, name string) error { + return apiv11.ResetPolicy(ctx, c.API, name) +} + +func (c *Client) EnablePolicy(ctx context.Context, name string) error { + return c.SetPolicyEnabledField(ctx, name, true) +} + +func (c *Client) DisablePolicy(ctx context.Context, name string) error { + return c.SetPolicyEnabledField(ctx, name, false) } -func (c *Client) CreatePolicy(ctx context.Context, name string, rpo int, sourcePath string, targetPath string, targetHost string) error{ - return apiv11.CreatePolicy(ctx,c.API,name,sourcePath,targetPath,targetHost,rpo) +func (c *Client) SetPolicyEnabledField(ctx context.Context, name string, value bool) error { + pp, err := c.GetPolicyByName(ctx, name) + if err != nil { + return err + } + if pp == nil { + return nil + } + + if pp.Enabled == value { + return nil + } + + p := &apiv11.Policy{ + Id: pp.Id, + Enabled: value, + } + + return apiv11.UpdatePolicy(ctx, c.API, p) +} + +func (c *Client) AllowWrites(ctx context.Context, policyName string) error { + targetPolicy, err := c.GetTargetPolicyByName(ctx, policyName) + if err != nil { + return err + } + if targetPolicy.FailoverFailbackState == WRITES_ENABLED { + return nil + } + + _, err = c.RunActionForPolicy(ctx, policyName, apiv11.ALLOW_WRITE) + if err != nil { + return err + } + + err = c.WaitForTargetPolicyCondition(ctx, policyName, WRITES_ENABLED) + if err != nil { + return err + } + + return nil +} + +func (c *Client) DisallowWrites(ctx context.Context, policyName string) error { + targetPolicy, err := c.GetTargetPolicyByName(ctx, policyName) + if err != nil { + return err + } + if targetPolicy.FailoverFailbackState == WRITES_DISABLED { + return nil + } + + _, err = c.RunActionForPolicy(ctx, policyName, apiv11.ALLOW_WRITE_REVERT) + if err != nil { + return err + } + + err = c.WaitForTargetPolicyCondition(ctx, policyName, WRITES_DISABLED) + if err != nil { + return err + } + + return nil +} + +func (c *Client) ResyncPrep(ctx context.Context, policyName string) error { + targetPolicy, err := c.GetTargetPolicyByName(ctx, policyName) + if err != nil { + return err + } + if targetPolicy.FailoverFailbackState == RESYNC_POLICY_CREATED { + return nil + } + + _, err = c.RunActionForPolicy(ctx, policyName, apiv11.RESYNC_PREP) + if err != nil { + return err + } + + err = c.WaitForTargetPolicyCondition(ctx, policyName, RESYNC_POLICY_CREATED) + if err != nil { + return err + } + + return nil +} + +func (c *Client) RunActionForPolicy(ctx context.Context, policyName string, action apiv11.JOB_ACTION) (*apiv11.JobRequest, error) { + job := &apiv11.JobRequest{ + Id: policyName, + Action: action, + } + + return apiv11.StartSyncIQJob(ctx, c.API, job) } -func (c *Client) DeletePolicy(ctx context.Context, name string) error{ - return apiv11.DeletePolicy(ctx,c.API,name) -} \ No newline at end of file +func (c *Client) StartSyncIQJob(ctx context.Context, job *apiv11.JobRequest) (*apiv11.JobRequest, error) { + return apiv11.StartSyncIQJob(ctx, c.API, job) +} + +func (c *Client) GetReport(ctx context.Context, reportName string) (*apiv11.Report, error) { + return apiv11.GetReport(ctx, c.API, reportName) +} + +func (c *Client) GetReportsByPolicyName(ctx context.Context, policyName string, reportsForPolicy int) (*apiv11.Reports, error) { + return apiv11.GetReportsByPolicyName(ctx, c.API, policyName, reportsForPolicy) +} + +func (c *Client) WaitForPolicyEnabledFieldCondition(ctx context.Context, policyName string, enabled bool) error { + pollErr := utils.PollImmediateWithContext(ctx, defaultPoll, defaultTimeout, + func(iCtx context.Context) (bool, error) { + p, err := c.GetPolicyByName(iCtx, policyName) + if err != nil { + return false, err + } + + if p.Enabled != enabled { + return false, nil + } + + return true, nil + }) + + if pollErr != nil { + return pollErr + } + + return nil +} + +func (c *Client) WaitForPolicyLastJobState(ctx context.Context, policyName string, state apiv11.JOB_STATE) error { + pollErr := utils.PollImmediateWithContext(ctx, defaultPoll, defaultTimeout, + func(iCtx context.Context) (bool, error) { + p, err := c.GetPolicyByName(iCtx, policyName) + if err != nil { + return false, err + } + + if p.LastJobState != state { + return false, nil + } + + return true, nil + }) + + if pollErr != nil { + return pollErr + } + + return nil +} + +func (c *Client) WaitForTargetPolicyCondition(ctx context.Context, policyName string, condition apiv11.FAILOVER_FAILBACK_STATE) error { + pollErr := utils.PollImmediateWithContext(ctx, defaultPoll, defaultTimeout, + func(iCtx context.Context) (bool, error) { + tp, err := c.GetTargetPolicyByName(iCtx, policyName) + if err != nil { + return false, err + } + + if tp.FailoverFailbackState != condition { + return false, nil + } + + return true, nil + }) + + if pollErr != nil { + return pollErr + } + + return nil +} + +func (c *Client) SyncPolicy(ctx context.Context, policyName string) error { + p, err := c.GetPolicyByName(ctx, policyName) + if err != nil { + return err + } + rpo := p.JobDelay + + reportFilter := func(r apiv11.Report) bool { + // Check if report is for sync action and is recent enough + isSync := r.Policy.Action == "sync" + + endTime := time.Unix(r.EndTime, 0) + diff := time.Now().Sub(endTime).Seconds() + + fmt.Println("end time", endTime.String()) + + fmt.Println("diff", diff) + + isRecent := diff < float64(rpo) / 2 + + isFinished := r.State == apiv11.FINISHED + + return isSync && isRecent && isFinished + } + + reports, err := c.GetReportsByPolicyName(ctx, policyName, 5) + if err != nil { + return err + } + + filtered := FilterReports(reports.Reports, reportFilter) + log.Debug(ctx, "filtered reports %v for policy %s", filtered, policyName) + + if len(filtered) == 0 { + // If no reports found matching to our criteria -- run sync job + log.Info(ctx, "No matching reports were found, starting sync job") + jobReq := &apiv11.JobRequest{ + Id: policyName, + } + _, err := c.StartSyncIQJob(ctx, jobReq) + if err != nil { + return err + } + + log.Info(ctx, "Waiting for SyncIQ job to complete") + pollErr := utils.PollImmediateWithContext(ctx, defaultPoll, defaultTimeout, + func(iCtx context.Context) (bool, error) { + reports, err := c.GetReportsByPolicyName(ctx, policyName, 5) + if err != nil { + return false, err + } + + filtered := FilterReports(reports.Reports, reportFilter) + + if len(filtered) == 0 { + return false, nil + } + + return true, nil + }) + if pollErr != nil { + return pollErr + } + } else { + log.Info(ctx, "Matching reports for policy %s were already found", policyName) + } + + return nil +} + +func FilterReports(values []apiv11.Report, filterFunc func(apiv11.Report) bool) []apiv11.Report { + filtered := make([]apiv11.Report, 0) + for _, v := range values { + if filterFunc(v) { + filtered = append(filtered, v) + } + } + return filtered +} From 67417549595c325e7115f0e6817f607def723716 Mon Sep 17 00:00:00 2001 From: Andrey Schipilo Date: Wed, 2 Feb 2022 16:01:11 +0300 Subject: [PATCH 2/6] Proper time diff calculation --- replication.go | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/replication.go b/replication.go index aa9fe9f..2c5e4ed 100644 --- a/replication.go +++ b/replication.go @@ -2,7 +2,6 @@ package goisilon import ( "context" - "fmt" log "github.com/akutz/gournal" "github.com/dell/goisilon/api/common/utils" apiv11 "github.com/dell/goisilon/api/v11" @@ -40,7 +39,6 @@ type Policy *apiv11.Policy type TargetPolicy *apiv11.TargetPolicy - // GetPolicyByName returns a policy with the provided ID. func (c *Client) GetPolicyByName(ctx context.Context, id string) (Policy, error) { return apiv11.GetPolicyByName(ctx, c.API, id) @@ -268,17 +266,20 @@ func (c *Client) SyncPolicy(ctx context.Context, policyName string) error { // Check if report is for sync action and is recent enough isSync := r.Policy.Action == "sync" - endTime := time.Unix(r.EndTime, 0) - diff := time.Now().Sub(endTime).Seconds() - - fmt.Println("end time", endTime.String()) + now := time.Now().Unix() + diff := r.EndTime - now - fmt.Println("diff", diff) + log.Debug(ctx, "end time", r.EndTime) + log.Debug(ctx, "now", now) + log.Debug(ctx, "diff", diff) - isRecent := diff < float64(rpo) / 2 + isRecent := diff < int64(rpo) isFinished := r.State == apiv11.FINISHED + log.Debug(ctx, "sync", isSync) + log.Debug(ctx, "recent", isRecent) + log.Debug(ctx, "finished", isFinished) return isSync && isRecent && isFinished } From 40f4e407a49b389d089d591ad398118094f8c2cc Mon Sep 17 00:00:00 2001 From: Maxim Sklyarov Date: Tue, 8 Feb 2022 11:21:34 +0300 Subject: [PATCH 3/6] call for encrypted replication + gofmt --- acls.go | 4 +-- acls_test.go | 4 +-- api/api_ordered_values.go | 4 +-- api/api_ordered_values_test.go | 4 +-- api/api_test.go | 4 +-- api/common/utils/poll.go | 1 - api/common/utils/utils.go | 4 +-- api/common/utils/utils_test.go | 4 +-- api/v1/api_v1.go | 4 +-- api/v1/api_v1_exports.go | 4 +-- api/v1/api_v1_quotas.go | 4 +-- api/v1/api_v1_snapshots.go | 4 +-- api/v1/api_v1_zones.go | 4 +-- api/v11/api_v11_replication.go | 4 ++- api/v2/api_v2.go | 4 +-- api/v2/api_v2_acls.go | 4 +-- api/v2/api_v2_exports.go | 4 +-- api/v2/api_v2_exports_json_test.go | 4 +-- api/v2/api_v2_exports_test.go | 4 +-- api/v2/api_v2_fs.go | 4 +-- api/v2/api_v2_types.go | 4 +-- api/v3/api_v3.go | 4 +-- api/v3/api_v3_cluster.go | 4 +-- api/v3/api_v3_types.go | 4 +-- client_test.go | 4 +-- cluster.go | 4 +-- cluster_test.go | 4 +-- exports_test.go | 44 +++++++++++++++--------------- quota.go | 4 +-- quota_test.go | 4 +-- replication.go | 4 +-- zones.go | 4 +-- zones_test.go | 4 +-- 33 files changed, 85 insertions(+), 84 deletions(-) diff --git a/acls.go b/acls.go index 6569a89..d8ce160 100644 --- a/acls.go +++ b/acls.go @@ -1,4 +1,4 @@ -/* +/* Copyright (c) 2019 Dell Inc, or its subsidiaries. Licensed under the Apache License, Version 2.0 (the "License"); @@ -12,7 +12,7 @@ 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 goisilon import ( diff --git a/acls_test.go b/acls_test.go index 4f7ee4c..901984e 100755 --- a/acls_test.go +++ b/acls_test.go @@ -1,4 +1,4 @@ -/* +/* Copyright (c) 2019 Dell Inc, or its subsidiaries. Licensed under the Apache License, Version 2.0 (the "License"); @@ -12,7 +12,7 @@ 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 goisilon import ( diff --git a/api/api_ordered_values.go b/api/api_ordered_values.go index 3a38f6d..e036d4d 100644 --- a/api/api_ordered_values.go +++ b/api/api_ordered_values.go @@ -1,4 +1,4 @@ -/* +/* Copyright (c) 2019 Dell Inc, or its subsidiaries. Licensed under the Apache License, Version 2.0 (the "License"); @@ -12,7 +12,7 @@ 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 api import ( diff --git a/api/api_ordered_values_test.go b/api/api_ordered_values_test.go index 44141a0..f9b14e9 100644 --- a/api/api_ordered_values_test.go +++ b/api/api_ordered_values_test.go @@ -1,4 +1,4 @@ -/* +/* Copyright (c) 2019 Dell Inc, or its subsidiaries. Licensed under the Apache License, Version 2.0 (the "License"); @@ -12,7 +12,7 @@ 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 api import ( diff --git a/api/api_test.go b/api/api_test.go index af50cf9..194ac34 100755 --- a/api/api_test.go +++ b/api/api_test.go @@ -1,4 +1,4 @@ -/* +/* Copyright (c) 2019 Dell Inc, or its subsidiaries. Licensed under the Apache License, Version 2.0 (the "License"); @@ -12,7 +12,7 @@ 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 api import ( diff --git a/api/common/utils/poll.go b/api/common/utils/poll.go index 60a121a..d205df8 100644 --- a/api/common/utils/poll.go +++ b/api/common/utils/poll.go @@ -59,7 +59,6 @@ func poll(ctx context.Context, immediate bool, wait WaitWithContextFunc, conditi } } - func poller(interval, timeout time.Duration) WaitWithContextFunc { return func(ctx context.Context) <-chan struct{} { ch := make(chan struct{}) diff --git a/api/common/utils/utils.go b/api/common/utils/utils.go index b650406..e4a13a2 100644 --- a/api/common/utils/utils.go +++ b/api/common/utils/utils.go @@ -1,4 +1,4 @@ -/* +/* Copyright (c) 2019 Dell Inc, or its subsidiaries. Licensed under the Apache License, Version 2.0 (the "License"); @@ -12,7 +12,7 @@ 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 utils // IsStringInSlice checks if a string is an element of a string slice diff --git a/api/common/utils/utils_test.go b/api/common/utils/utils_test.go index ec618e4..2ce431f 100644 --- a/api/common/utils/utils_test.go +++ b/api/common/utils/utils_test.go @@ -1,4 +1,4 @@ -/* +/* Copyright (c) 2019 Dell Inc, or its subsidiaries. Licensed under the Apache License, Version 2.0 (the "License"); @@ -12,7 +12,7 @@ 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 utils import ( diff --git a/api/v1/api_v1.go b/api/v1/api_v1.go index bde9c14..5c91270 100644 --- a/api/v1/api_v1.go +++ b/api/v1/api_v1.go @@ -1,4 +1,4 @@ -/* +/* Copyright (c) 2019 Dell Inc, or its subsidiaries. Licensed under the Apache License, Version 2.0 (the "License"); @@ -12,7 +12,7 @@ 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 v1 import ( diff --git a/api/v1/api_v1_exports.go b/api/v1/api_v1_exports.go index 9238102..fbc535d 100644 --- a/api/v1/api_v1_exports.go +++ b/api/v1/api_v1_exports.go @@ -1,4 +1,4 @@ -/* +/* Copyright (c) 2019 Dell Inc, or its subsidiaries. Licensed under the Apache License, Version 2.0 (the "License"); @@ -12,7 +12,7 @@ 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 v1 import ( diff --git a/api/v1/api_v1_quotas.go b/api/v1/api_v1_quotas.go index 0dd0281..db8db4e 100644 --- a/api/v1/api_v1_quotas.go +++ b/api/v1/api_v1_quotas.go @@ -1,4 +1,4 @@ -/* +/* Copyright (c) 2019 Dell Inc, or its subsidiaries. Licensed under the Apache License, Version 2.0 (the "License"); @@ -12,7 +12,7 @@ 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 v1 import ( diff --git a/api/v1/api_v1_snapshots.go b/api/v1/api_v1_snapshots.go index cbb6858..f3a4cf9 100644 --- a/api/v1/api_v1_snapshots.go +++ b/api/v1/api_v1_snapshots.go @@ -1,4 +1,4 @@ -/* +/* Copyright (c) 2019 Dell Inc, or its subsidiaries. Licensed under the Apache License, Version 2.0 (the "License"); @@ -12,7 +12,7 @@ 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 v1 import ( diff --git a/api/v1/api_v1_zones.go b/api/v1/api_v1_zones.go index 8e69176..6a8f33e 100644 --- a/api/v1/api_v1_zones.go +++ b/api/v1/api_v1_zones.go @@ -1,4 +1,4 @@ -/* +/* Copyright (c) 2019 Dell Inc, or its subsidiaries. Licensed under the Apache License, Version 2.0 (the "License"); @@ -12,7 +12,7 @@ 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 v1 import ( diff --git a/api/v11/api_v11_replication.go b/api/v11/api_v11_replication.go index 18fe506..fd76cb2 100644 --- a/api/v11/api_v11_replication.go +++ b/api/v11/api_v11_replication.go @@ -63,6 +63,7 @@ type Policy struct { TargetPath string `json:"target_path,omitempty"` SourcePath string `json:"source_root_path,omitempty"` TargetHost string `json:"target_host,omitempty"` + TargetCert string `json:"target_certificate_id,omitempty"` JobDelay int `json:"job_delay,omitempty"` Schedule string `json:"schedule,omitempty"` LastJobState JOB_STATE `json:"last_job_state,omitempty"` @@ -128,7 +129,7 @@ func GetTargetPolicyByName(ctx context.Context, client api.Client, name string) return &p.Policy[0], nil } -func CreatePolicy(ctx context.Context, client api.Client, name string, sourcePath string, targetPath string, targetHost string, rpo int, enabled bool) error { +func CreatePolicy(ctx context.Context, client api.Client, name string, sourcePath string, targetPath string, targetHost string, targetCert string, rpo int, enabled bool) error { var policyResp Policy body := &Policy{ Action: "sync", @@ -139,6 +140,7 @@ func CreatePolicy(ctx context.Context, client api.Client, name string, sourcePat SourcePath: sourcePath, TargetHost: targetHost, JobDelay: rpo, + TargetCert: targetCert, Schedule: "when-source-modified", } return client.Post(ctx, policiesPath, "", nil, nil, body, &policyResp) diff --git a/api/v2/api_v2.go b/api/v2/api_v2.go index 08a34e0..98c4fc7 100644 --- a/api/v2/api_v2.go +++ b/api/v2/api_v2.go @@ -1,4 +1,4 @@ -/* +/* Copyright (c) 2019 Dell Inc, or its subsidiaries. Licensed under the Apache License, Version 2.0 (the "License"); @@ -12,7 +12,7 @@ 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 v2 import ( diff --git a/api/v2/api_v2_acls.go b/api/v2/api_v2_acls.go index a03ff1e..d08f4f3 100644 --- a/api/v2/api_v2_acls.go +++ b/api/v2/api_v2_acls.go @@ -1,4 +1,4 @@ -/* +/* Copyright (c) 2019 Dell Inc, or its subsidiaries. Licensed under the Apache License, Version 2.0 (the "License"); @@ -12,7 +12,7 @@ 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 v2 import ( diff --git a/api/v2/api_v2_exports.go b/api/v2/api_v2_exports.go index 602f961..060d140 100644 --- a/api/v2/api_v2_exports.go +++ b/api/v2/api_v2_exports.go @@ -1,4 +1,4 @@ -/* +/* Copyright (c) 2019 Dell Inc, or its subsidiaries. Licensed under the Apache License, Version 2.0 (the "License"); @@ -12,7 +12,7 @@ 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 v2 import ( diff --git a/api/v2/api_v2_exports_json_test.go b/api/v2/api_v2_exports_json_test.go index a10ce98..44351e6 100644 --- a/api/v2/api_v2_exports_json_test.go +++ b/api/v2/api_v2_exports_json_test.go @@ -1,4 +1,4 @@ -/* +/* Copyright (c) 2019 Dell Inc, or its subsidiaries. Licensed under the Apache License, Version 2.0 (the "License"); @@ -12,7 +12,7 @@ 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 v2 var getOneExportJSON = []byte(`{ "exports" : [ diff --git a/api/v2/api_v2_exports_test.go b/api/v2/api_v2_exports_test.go index 6621ed9..49a1658 100644 --- a/api/v2/api_v2_exports_test.go +++ b/api/v2/api_v2_exports_test.go @@ -1,4 +1,4 @@ -/* +/* Copyright (c) 2019 Dell Inc, or its subsidiaries. Licensed under the Apache License, Version 2.0 (the "License"); @@ -12,7 +12,7 @@ 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 v2 import ( diff --git a/api/v2/api_v2_fs.go b/api/v2/api_v2_fs.go index ee7ce9b..a6ffa08 100644 --- a/api/v2/api_v2_fs.go +++ b/api/v2/api_v2_fs.go @@ -1,4 +1,4 @@ -/* +/* Copyright (c) 2019 Dell Inc, or its subsidiaries. Licensed under the Apache License, Version 2.0 (the "License"); @@ -12,7 +12,7 @@ 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 v2 import ( diff --git a/api/v2/api_v2_types.go b/api/v2/api_v2_types.go index a30b42c..1927f1f 100644 --- a/api/v2/api_v2_types.go +++ b/api/v2/api_v2_types.go @@ -1,4 +1,4 @@ -/* +/* Copyright (c) 2019 Dell Inc, or its subsidiaries. Licensed under the Apache License, Version 2.0 (the "License"); @@ -12,7 +12,7 @@ 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 v2 import ( diff --git a/api/v3/api_v3.go b/api/v3/api_v3.go index a738a7b..9978abe 100644 --- a/api/v3/api_v3.go +++ b/api/v3/api_v3.go @@ -1,4 +1,4 @@ -/* +/* Copyright (c) 2019 Dell Inc, or its subsidiaries. Licensed under the Apache License, Version 2.0 (the "License"); @@ -12,7 +12,7 @@ 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 v3 import ( diff --git a/api/v3/api_v3_cluster.go b/api/v3/api_v3_cluster.go index e48da14..276c9b7 100644 --- a/api/v3/api_v3_cluster.go +++ b/api/v3/api_v3_cluster.go @@ -1,4 +1,4 @@ -/* +/* Copyright (c) 2019 Dell Inc, or its subsidiaries. Licensed under the Apache License, Version 2.0 (the "License"); @@ -12,7 +12,7 @@ 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 v3 import ( diff --git a/api/v3/api_v3_types.go b/api/v3/api_v3_types.go index 0ced3e1..cc3c7cc 100644 --- a/api/v3/api_v3_types.go +++ b/api/v3/api_v3_types.go @@ -1,4 +1,4 @@ -/* +/* Copyright (c) 2019 Dell Inc, or its subsidiaries. Licensed under the Apache License, Version 2.0 (the "License"); @@ -12,7 +12,7 @@ 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 v3 type isiStats struct { diff --git a/client_test.go b/client_test.go index 75c4a0c..0d20f17 100644 --- a/client_test.go +++ b/client_test.go @@ -1,4 +1,4 @@ -/* +/* Copyright (c) 2019 Dell Inc, or its subsidiaries. Licensed under the Apache License, Version 2.0 (the "License"); @@ -12,7 +12,7 @@ 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 goisilon import ( diff --git a/cluster.go b/cluster.go index 1235229..9321133 100644 --- a/cluster.go +++ b/cluster.go @@ -1,4 +1,4 @@ -/* +/* Copyright (c) 2019 Dell Inc, or its subsidiaries. Licensed under the Apache License, Version 2.0 (the "License"); @@ -12,7 +12,7 @@ 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 goisilon import ( diff --git a/cluster_test.go b/cluster_test.go index 81be1ae..be9672e 100644 --- a/cluster_test.go +++ b/cluster_test.go @@ -1,4 +1,4 @@ -/* +/* Copyright (c) 2019 Dell Inc, or its subsidiaries. Licensed under the Apache License, Version 2.0 (the "License"); @@ -12,7 +12,7 @@ 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 goisilon import "testing" diff --git a/exports_test.go b/exports_test.go index c9666c4..8561164 100644 --- a/exports_test.go +++ b/exports_test.go @@ -1,4 +1,4 @@ -/* +/* Copyright (c) 2019 Dell Inc, or its subsidiaries. Licensed under the Apache License, Version 2.0 (the "License"); @@ -12,7 +12,7 @@ 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 goisilon import ( @@ -315,20 +315,20 @@ func TestExportClientsAdd(t *testing.T) { } func TestAddExportClientsByID(t *testing.T) { - - // Add the test exports - volumeName1 := "test_get_exports1" - vol, err := client.CreateVolume(defaultCtx, volumeName1) - assertNoError(t, err) - assertNotNil(t, vol) - volumeName1 = vol.Name - t.Logf("created volume: %s", volumeName1) - - exportID, err := client.Export(defaultCtx, volumeName1) - assertNoError(t, err) - t.Logf("created export: %d", exportID) - - exportForClient = exportID + + // Add the test exports + volumeName1 := "test_get_exports1" + vol, err := client.CreateVolume(defaultCtx, volumeName1) + assertNoError(t, err) + assertNotNil(t, vol) + volumeName1 = vol.Name + t.Logf("created volume: %s", volumeName1) + + exportID, err := client.Export(defaultCtx, volumeName1) + assertNoError(t, err) + t.Logf("created export: %d", exportID) + + exportForClient = exportID export, _ := client.GetExportByID(defaultCtx, exportID) fmt.Printf("export '%d' has \n%-20v: '%v'\n%-20v: '%v'\n%-20v: '%v'\n", exportID, "clients", *export.Clients, "read_only_cilents", *export.ReadOnlyClients, "read_write_cilents", *export.ReadWriteClients) @@ -365,19 +365,19 @@ func TestRemoveExportClientsByID(t *testing.T) { func TestRemoveExportClientsByName(t *testing.T) { testRemoveExportClients(t, nil, client.RemoveExportClientsByName) - volumeName1 := "test_get_exports1" - // make sure we clean up when we're done - defer client.Unexport(defaultCtx, volumeName1) - defer client.DeleteVolume(defaultCtx, volumeName1) + volumeName1 := "test_get_exports1" + // make sure we clean up when we're done + defer client.Unexport(defaultCtx, volumeName1) + defer client.DeleteVolume(defaultCtx, volumeName1) } func testRemoveExportClients(t *testing.T, removeExportClientsByIDFunc func(ctx context.Context, id int, clientsToRemove []string) error, removeExportClientsByNameFunc func(ctx context.Context, name string, clientsToRemove []string) error) { - volumeName1 := "test_get_exports1" + volumeName1 := "test_get_exports1" - exportID := exportForClient + exportID := exportForClient export, _ := client.GetExportByName(defaultCtx, volumeName1) exportName := volumeName1 diff --git a/quota.go b/quota.go index 7ebf0e7..1a971de 100644 --- a/quota.go +++ b/quota.go @@ -1,4 +1,4 @@ -/* +/* Copyright (c) 2019 Dell Inc, or its subsidiaries. Licensed under the Apache License, Version 2.0 (the "License"); @@ -12,7 +12,7 @@ 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 goisilon import ( diff --git a/quota_test.go b/quota_test.go index c9cd004..eabbae7 100644 --- a/quota_test.go +++ b/quota_test.go @@ -1,4 +1,4 @@ -/* +/* Copyright (c) 2019 Dell Inc, or its subsidiaries. Licensed under the Apache License, Version 2.0 (the "License"); @@ -12,7 +12,7 @@ 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 goisilon import ( diff --git a/replication.go b/replication.go index 2c5e4ed..10b9a65 100644 --- a/replication.go +++ b/replication.go @@ -48,8 +48,8 @@ func (c *Client) GetTargetPolicyByName(ctx context.Context, id string) (TargetPo return apiv11.GetTargetPolicyByName(ctx, c.API, id) } -func (c *Client) CreatePolicy(ctx context.Context, name string, rpo int, sourcePath string, targetPath string, targetHost string, enabled bool) error { - return apiv11.CreatePolicy(ctx, c.API, name, sourcePath, targetPath, targetHost, rpo, enabled) +func (c *Client) CreatePolicy(ctx context.Context, name string, rpo int, sourcePath string, targetPath string, targetHost string, targetCert string, enabled bool) error { + return apiv11.CreatePolicy(ctx, c.API, name, sourcePath, targetPath, targetHost, targetCert, rpo, enabled) } func (c *Client) DeletePolicy(ctx context.Context, name string) error { diff --git a/zones.go b/zones.go index 230d566..78a47ad 100644 --- a/zones.go +++ b/zones.go @@ -1,4 +1,4 @@ -/* +/* Copyright (c) 2019 Dell Inc, or its subsidiaries. Licensed under the Apache License, Version 2.0 (the "License"); @@ -12,7 +12,7 @@ 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 goisilon import ( diff --git a/zones_test.go b/zones_test.go index dc4d92c..ef6064b 100644 --- a/zones_test.go +++ b/zones_test.go @@ -1,4 +1,4 @@ -/* +/* Copyright (c) 2019 Dell Inc, or its subsidiaries. Licensed under the Apache License, Version 2.0 (the "License"); @@ -12,7 +12,7 @@ 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 goisilon import "testing" From 82d347b4b6fbe83b2f218fd2fdef842ba3ed4410 Mon Sep 17 00:00:00 2001 From: ilya Date: Thu, 17 Feb 2022 13:00:40 +0400 Subject: [PATCH 4/6] added activejob call --- api/v11/api_v11_replication.go | 13 +++++++ replication.go | 66 +++------------------------------- 2 files changed, 18 insertions(+), 61 deletions(-) diff --git a/api/v11/api_v11_replication.go b/api/v11/api_v11_replication.go index fd76cb2..30fe771 100644 --- a/api/v11/api_v11_replication.go +++ b/api/v11/api_v11_replication.go @@ -100,6 +100,10 @@ type JobRequest struct { SkipCopy bool `json:"skip_copy,omitempty"` } +type Jobs struct { + Job []JobRequest `json:"jobs,omitempty"` +} + type Report struct { Policy Policy `json:"policy,omitempty"` Id string `json:"id"` @@ -201,3 +205,12 @@ func GetReportsByPolicyName(ctx context.Context, client api.Client, policyName s return r, nil } + +func GetJobsByPolicyName(ctx context.Context, client api.Client, policyName string) (*JobRequest, error) { + j := &Jobs{} + err := client.Get(ctx, jobsPath, policyName, nil, nil, &j) + if err != nil { + return nil, err + } + return &j.Job[0], nil +} diff --git a/replication.go b/replication.go index 10b9a65..c2ab693 100644 --- a/replication.go +++ b/replication.go @@ -2,7 +2,6 @@ package goisilon import ( "context" - log "github.com/akutz/gournal" "github.com/dell/goisilon/api/common/utils" apiv11 "github.com/dell/goisilon/api/v11" "time" @@ -256,44 +255,8 @@ func (c *Client) WaitForTargetPolicyCondition(ctx context.Context, policyName st } func (c *Client) SyncPolicy(ctx context.Context, policyName string) error { - p, err := c.GetPolicyByName(ctx, policyName) + _, err := c.GetJobsByPolicyName(ctx, policyName) if err != nil { - return err - } - rpo := p.JobDelay - - reportFilter := func(r apiv11.Report) bool { - // Check if report is for sync action and is recent enough - isSync := r.Policy.Action == "sync" - - now := time.Now().Unix() - diff := r.EndTime - now - - log.Debug(ctx, "end time", r.EndTime) - log.Debug(ctx, "now", now) - log.Debug(ctx, "diff", diff) - - isRecent := diff < int64(rpo) - - isFinished := r.State == apiv11.FINISHED - - log.Debug(ctx, "sync", isSync) - log.Debug(ctx, "recent", isRecent) - log.Debug(ctx, "finished", isFinished) - return isSync && isRecent && isFinished - } - - reports, err := c.GetReportsByPolicyName(ctx, policyName, 5) - if err != nil { - return err - } - - filtered := FilterReports(reports.Reports, reportFilter) - log.Debug(ctx, "filtered reports %v for policy %s", filtered, policyName) - - if len(filtered) == 0 { - // If no reports found matching to our criteria -- run sync job - log.Info(ctx, "No matching reports were found, starting sync job") jobReq := &apiv11.JobRequest{ Id: policyName, } @@ -301,33 +264,14 @@ func (c *Client) SyncPolicy(ctx context.Context, policyName string) error { if err != nil { return err } - - log.Info(ctx, "Waiting for SyncIQ job to complete") - pollErr := utils.PollImmediateWithContext(ctx, defaultPoll, defaultTimeout, - func(iCtx context.Context) (bool, error) { - reports, err := c.GetReportsByPolicyName(ctx, policyName, 5) - if err != nil { - return false, err - } - - filtered := FilterReports(reports.Reports, reportFilter) - - if len(filtered) == 0 { - return false, nil - } - - return true, nil - }) - if pollErr != nil { - return pollErr - } - } else { - log.Info(ctx, "Matching reports for policy %s were already found", policyName) } - return nil } +func (c *Client) GetJobsByPolicyName(ctx context.Context, policyName string) (*apiv11.JobRequest, error) { + return apiv11.GetJobsByPolicyName(ctx, c.API, policyName) +} + func FilterReports(values []apiv11.Report, filterFunc func(apiv11.Report) bool) []apiv11.Report { filtered := make([]apiv11.Report, 0) for _, v := range values { From de3994c2108580f432de5be97dd40da8799ff5a6 Mon Sep 17 00:00:00 2001 From: Maxim Sklyarov Date: Thu, 17 Feb 2022 16:52:44 +0300 Subject: [PATCH 5/6] sync/failover/reporotect/deletion reworked --- api/v11/api_v11_replication.go | 30 ++++++++++---- replication.go | 72 +++++++++++++++++++++++++++++++--- 2 files changed, 90 insertions(+), 12 deletions(-) diff --git a/api/v11/api_v11_replication.go b/api/v11/api_v11_replication.go index 30fe771..243a592 100644 --- a/api/v11/api_v11_replication.go +++ b/api/v11/api_v11_replication.go @@ -3,9 +3,8 @@ package v11 import ( "context" "fmt" - "strconv" - "github.com/dell/goisilon/api" + "strconv" ) const ( @@ -24,6 +23,13 @@ const ( TEST JOB_ACTION = "test" ) +type RUNNING_JOB_ACTION string + +const ( + SYNC RUNNING_JOB_ACTION = "sync" + COPY RUNNING_JOB_ACTION = "copy" +) + type JOB_STATE string const ( @@ -100,8 +106,13 @@ type JobRequest struct { SkipCopy bool `json:"skip_copy,omitempty"` } +type Job struct { + Action RUNNING_JOB_ACTION `json:"policy_action,omitempty"` + Id string `json:"id,omitempty"` // ID or Name of policy +} + type Jobs struct { - Job []JobRequest `json:"jobs,omitempty"` + Job []Job `json:"jobs,omitempty"` } type Report struct { @@ -172,8 +183,8 @@ func ResetPolicy(ctx context.Context, client api.Client, name string) error { return client.Post(ctx, policiesPath, name+"/reset", nil, nil, nil, &resp) } -func StartSyncIQJob(ctx context.Context, client api.Client, job *JobRequest) (*JobRequest, error) { - var jobResp JobRequest +func StartSyncIQJob(ctx context.Context, client api.Client, job *JobRequest) (*Job, error) { + var jobResp Job return &jobResp, client.Post(ctx, jobsPath, "", nil, nil, job, &jobResp) } @@ -206,11 +217,16 @@ func GetReportsByPolicyName(ctx context.Context, client api.Client, policyName s return r, nil } -func GetJobsByPolicyName(ctx context.Context, client api.Client, policyName string) (*JobRequest, error) { +func GetJobsByPolicyName(ctx context.Context, client api.Client, policyName string) ([]Job, error) { j := &Jobs{} err := client.Get(ctx, jobsPath, policyName, nil, nil, &j) if err != nil { + if e,ok:= err.(*api.JSONError);ok{ + if e.StatusCode == 404{ + return []Job{}, nil + } + } return nil, err } - return &j.Job[0], nil + return j.Job, nil } diff --git a/replication.go b/replication.go index c2ab693..c77db2f 100644 --- a/replication.go +++ b/replication.go @@ -2,6 +2,7 @@ package goisilon import ( "context" + log "github.com/akutz/gournal" "github.com/dell/goisilon/api/common/utils" apiv11 "github.com/dell/goisilon/api/v11" "time" @@ -167,7 +168,7 @@ func (c *Client) ResyncPrep(ctx context.Context, policyName string) error { return nil } -func (c *Client) RunActionForPolicy(ctx context.Context, policyName string, action apiv11.JOB_ACTION) (*apiv11.JobRequest, error) { +func (c *Client) RunActionForPolicy(ctx context.Context, policyName string, action apiv11.JOB_ACTION) (*apiv11.Job, error) { job := &apiv11.JobRequest{ Id: policyName, Action: action, @@ -176,7 +177,7 @@ func (c *Client) RunActionForPolicy(ctx context.Context, policyName string, acti return apiv11.StartSyncIQJob(ctx, c.API, job) } -func (c *Client) StartSyncIQJob(ctx context.Context, job *apiv11.JobRequest) (*apiv11.JobRequest, error) { +func (c *Client) StartSyncIQJob(ctx context.Context, job *apiv11.JobRequest) (*apiv11.Job, error) { return apiv11.StartSyncIQJob(ctx, c.API, job) } @@ -210,6 +211,28 @@ func (c *Client) WaitForPolicyEnabledFieldCondition(ctx context.Context, policyN return nil } +func (c *Client) WaitForNoActiveJobs(ctx context.Context, policyName string) error { + pollErr := utils.PollImmediateWithContext(ctx, defaultPoll, defaultTimeout, + func(iCtx context.Context) (bool, error) { + p, err := c.GetJobsByPolicyName(iCtx, policyName) + if err != nil { + return false, err + } + + if len(p)!= 0 { + return false, nil + } + + return true, nil + }) + + if pollErr != nil { + return pollErr + } + + return nil +} + func (c *Client) WaitForPolicyLastJobState(ctx context.Context, policyName string, state apiv11.JOB_STATE) error { pollErr := utils.PollImmediateWithContext(ctx, defaultPoll, defaultTimeout, func(iCtx context.Context) (bool, error) { @@ -255,20 +278,59 @@ func (c *Client) WaitForTargetPolicyCondition(ctx context.Context, policyName st } func (c *Client) SyncPolicy(ctx context.Context, policyName string) error { - _, err := c.GetJobsByPolicyName(ctx, policyName) + + // get all running + // if running - wait for it and succeed + // if no running - start new - wait for it and succeed + + var isRunning bool + + policy, err := c.GetPolicyByName(ctx,policyName) if err != nil { + return err + } + if policy.Enabled != true{ + return nil + } + + runningJobs, err := c.GetJobsByPolicyName(ctx, policyName) + if err != nil { + log.Info(ctx,err.Error()) + return err + } + for _, i := range runningJobs { + if i.Action == apiv11.SYNC { + // running job detected. Wait for it to complete. + isRunning = true + } + } + if isRunning { + log.Info(ctx, "found active jobs, waiting for completion") + err = c.WaitForNoActiveJobs(ctx, policyName) + if err != nil { + return err + } + return nil + } else { jobReq := &apiv11.JobRequest{ Id: policyName, } + log.Info(ctx, "found no active sync jobs, starting a new one") _, err := c.StartSyncIQJob(ctx, jobReq) if err != nil { return err } + time.Sleep(3 * time.Second) + err = c.WaitForNoActiveJobs(ctx, policyName) + if err != nil { + return err + } + return nil } - return nil + } -func (c *Client) GetJobsByPolicyName(ctx context.Context, policyName string) (*apiv11.JobRequest, error) { +func (c *Client) GetJobsByPolicyName(ctx context.Context, policyName string) ([]apiv11.Job, error) { return apiv11.GetJobsByPolicyName(ctx, c.API, policyName) } From 7bcc578929cb154c62161ddc4ddd1362cb0f4c4a Mon Sep 17 00:00:00 2001 From: Andrey Schipilo Date: Thu, 24 Feb 2022 12:46:48 +0300 Subject: [PATCH 6/6] Addressed PR comments --- acls.go | 2 +- acls_test.go | 2 +- api/api_ordered_values.go | 2 +- api/api_ordered_values_test.go | 2 +- api/api_test.go | 2 +- api/common/utils/poll.go | 15 ++ api/common/utils/utils.go | 2 +- api/common/utils/utils_test.go | 2 +- api/v1/api_v1.go | 2 +- api/v1/api_v1_exports.go | 2 +- api/v1/api_v1_quotas.go | 2 +- api/v1/api_v1_snapshots.go | 2 +- api/v1/api_v1_zones.go | 2 +- api/v11/api_v11_replication.go | 20 ++- api/v2/api_v2.go | 2 +- api/v2/api_v2_acls.go | 2 +- api/v2/api_v2_exports.go | 2 +- api/v2/api_v2_exports_json_test.go | 2 +- api/v2/api_v2_exports_test.go | 2 +- api/v2/api_v2_fs.go | 2 +- api/v2/api_v2_types.go | 2 +- api/v3/api_v3.go | 2 +- api/v3/api_v3_cluster.go | 2 +- api/v3/api_v3_types.go | 2 +- client_test.go | 2 +- cluster.go | 2 +- cluster_test.go | 2 +- exports_test.go | 2 +- quota.go | 2 +- quota_test.go | 2 +- replication_test.go | 280 +++++++++++++++++++++++++++++ zones.go | 2 +- zones_test.go | 2 +- 33 files changed, 344 insertions(+), 31 deletions(-) create mode 100644 replication_test.go diff --git a/acls.go b/acls.go index d8ce160..6dd962e 100644 --- a/acls.go +++ b/acls.go @@ -1,5 +1,5 @@ /* - Copyright (c) 2019 Dell Inc, or its subsidiaries. + Copyright (c) 2022 Dell Inc, or its subsidiaries. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. diff --git a/acls_test.go b/acls_test.go index 901984e..1a54d86 100755 --- a/acls_test.go +++ b/acls_test.go @@ -1,5 +1,5 @@ /* - Copyright (c) 2019 Dell Inc, or its subsidiaries. + Copyright (c) 2022 Dell Inc, or its subsidiaries. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. diff --git a/api/api_ordered_values.go b/api/api_ordered_values.go index e036d4d..111e89d 100644 --- a/api/api_ordered_values.go +++ b/api/api_ordered_values.go @@ -1,5 +1,5 @@ /* - Copyright (c) 2019 Dell Inc, or its subsidiaries. + Copyright (c) 2022 Dell Inc, or its subsidiaries. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. diff --git a/api/api_ordered_values_test.go b/api/api_ordered_values_test.go index f9b14e9..456d48d 100644 --- a/api/api_ordered_values_test.go +++ b/api/api_ordered_values_test.go @@ -1,5 +1,5 @@ /* - Copyright (c) 2019 Dell Inc, or its subsidiaries. + Copyright (c) 2022 Dell Inc, or its subsidiaries. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. diff --git a/api/api_test.go b/api/api_test.go index 194ac34..896dde5 100755 --- a/api/api_test.go +++ b/api/api_test.go @@ -1,5 +1,5 @@ /* - Copyright (c) 2019 Dell Inc, or its subsidiaries. + Copyright (c) 2022 Dell Inc, or its subsidiaries. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. diff --git a/api/common/utils/poll.go b/api/common/utils/poll.go index d205df8..dea8831 100644 --- a/api/common/utils/poll.go +++ b/api/common/utils/poll.go @@ -1,3 +1,18 @@ +/* + Copyright (c) 2022 Dell Inc, or its subsidiaries. + + 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 utils // Polling functionality inspired by https://github.com/kubernetes/apimachinery diff --git a/api/common/utils/utils.go b/api/common/utils/utils.go index e4a13a2..d0828e6 100644 --- a/api/common/utils/utils.go +++ b/api/common/utils/utils.go @@ -1,5 +1,5 @@ /* - Copyright (c) 2019 Dell Inc, or its subsidiaries. + Copyright (c) 2022 Dell Inc, or its subsidiaries. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. diff --git a/api/common/utils/utils_test.go b/api/common/utils/utils_test.go index 2ce431f..ae48b5b 100644 --- a/api/common/utils/utils_test.go +++ b/api/common/utils/utils_test.go @@ -1,5 +1,5 @@ /* - Copyright (c) 2019 Dell Inc, or its subsidiaries. + Copyright (c) 2022 Dell Inc, or its subsidiaries. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. diff --git a/api/v1/api_v1.go b/api/v1/api_v1.go index 5c91270..47bd04c 100644 --- a/api/v1/api_v1.go +++ b/api/v1/api_v1.go @@ -1,5 +1,5 @@ /* - Copyright (c) 2019 Dell Inc, or its subsidiaries. + Copyright (c) 2022 Dell Inc, or its subsidiaries. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. diff --git a/api/v1/api_v1_exports.go b/api/v1/api_v1_exports.go index fbc535d..b43fb63 100644 --- a/api/v1/api_v1_exports.go +++ b/api/v1/api_v1_exports.go @@ -1,5 +1,5 @@ /* - Copyright (c) 2019 Dell Inc, or its subsidiaries. + Copyright (c) 2022 Dell Inc, or its subsidiaries. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. diff --git a/api/v1/api_v1_quotas.go b/api/v1/api_v1_quotas.go index db8db4e..897d422 100644 --- a/api/v1/api_v1_quotas.go +++ b/api/v1/api_v1_quotas.go @@ -1,5 +1,5 @@ /* - Copyright (c) 2019 Dell Inc, or its subsidiaries. + Copyright (c) 2022 Dell Inc, or its subsidiaries. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. diff --git a/api/v1/api_v1_snapshots.go b/api/v1/api_v1_snapshots.go index f3a4cf9..6ac6b8d 100644 --- a/api/v1/api_v1_snapshots.go +++ b/api/v1/api_v1_snapshots.go @@ -1,5 +1,5 @@ /* - Copyright (c) 2019 Dell Inc, or its subsidiaries. + Copyright (c) 2022 Dell Inc, or its subsidiaries. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. diff --git a/api/v1/api_v1_zones.go b/api/v1/api_v1_zones.go index 6a8f33e..b3b19fe 100644 --- a/api/v1/api_v1_zones.go +++ b/api/v1/api_v1_zones.go @@ -1,5 +1,5 @@ /* - Copyright (c) 2019 Dell Inc, or its subsidiaries. + Copyright (c) 2022 Dell Inc, or its subsidiaries. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. diff --git a/api/v11/api_v11_replication.go b/api/v11/api_v11_replication.go index 243a592..0d698c5 100644 --- a/api/v11/api_v11_replication.go +++ b/api/v11/api_v11_replication.go @@ -1,3 +1,18 @@ +/* + Copyright (c) 2022 Dell Inc, or its subsidiaries. + + 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 v11 import ( @@ -191,9 +206,12 @@ func StartSyncIQJob(ctx context.Context, client api.Client, job *JobRequest) (*J func GetReport(ctx context.Context, client api.Client, reportName string) (*Report, error) { r := &Reports{} err := client.Get(ctx, reportsPath, reportName, nil, nil, &r) - if err != nil || len(r.Reports) == 0 { + if err != nil { return nil, err } + if len(r.Reports) == 0 { + return nil, fmt.Errorf("no reports found with report name %s", reportName) + } return &r.Reports[0], nil } diff --git a/api/v2/api_v2.go b/api/v2/api_v2.go index 98c4fc7..7041c94 100644 --- a/api/v2/api_v2.go +++ b/api/v2/api_v2.go @@ -1,5 +1,5 @@ /* - Copyright (c) 2019 Dell Inc, or its subsidiaries. + Copyright (c) 2022 Dell Inc, or its subsidiaries. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. diff --git a/api/v2/api_v2_acls.go b/api/v2/api_v2_acls.go index d08f4f3..2f3d925 100644 --- a/api/v2/api_v2_acls.go +++ b/api/v2/api_v2_acls.go @@ -1,5 +1,5 @@ /* - Copyright (c) 2019 Dell Inc, or its subsidiaries. + Copyright (c) 2022 Dell Inc, or its subsidiaries. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. diff --git a/api/v2/api_v2_exports.go b/api/v2/api_v2_exports.go index 060d140..ebe7ba1 100644 --- a/api/v2/api_v2_exports.go +++ b/api/v2/api_v2_exports.go @@ -1,5 +1,5 @@ /* - Copyright (c) 2019 Dell Inc, or its subsidiaries. + Copyright (c) 2022 Dell Inc, or its subsidiaries. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. diff --git a/api/v2/api_v2_exports_json_test.go b/api/v2/api_v2_exports_json_test.go index 44351e6..a485717 100644 --- a/api/v2/api_v2_exports_json_test.go +++ b/api/v2/api_v2_exports_json_test.go @@ -1,5 +1,5 @@ /* - Copyright (c) 2019 Dell Inc, or its subsidiaries. + Copyright (c) 2022 Dell Inc, or its subsidiaries. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. diff --git a/api/v2/api_v2_exports_test.go b/api/v2/api_v2_exports_test.go index 49a1658..a3239ff 100644 --- a/api/v2/api_v2_exports_test.go +++ b/api/v2/api_v2_exports_test.go @@ -1,5 +1,5 @@ /* - Copyright (c) 2019 Dell Inc, or its subsidiaries. + Copyright (c) 2022 Dell Inc, or its subsidiaries. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. diff --git a/api/v2/api_v2_fs.go b/api/v2/api_v2_fs.go index a6ffa08..05f6364 100644 --- a/api/v2/api_v2_fs.go +++ b/api/v2/api_v2_fs.go @@ -1,5 +1,5 @@ /* - Copyright (c) 2019 Dell Inc, or its subsidiaries. + Copyright (c) 2022 Dell Inc, or its subsidiaries. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. diff --git a/api/v2/api_v2_types.go b/api/v2/api_v2_types.go index 1927f1f..3da420a 100644 --- a/api/v2/api_v2_types.go +++ b/api/v2/api_v2_types.go @@ -1,5 +1,5 @@ /* - Copyright (c) 2019 Dell Inc, or its subsidiaries. + Copyright (c) 2022 Dell Inc, or its subsidiaries. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. diff --git a/api/v3/api_v3.go b/api/v3/api_v3.go index 9978abe..40f8548 100644 --- a/api/v3/api_v3.go +++ b/api/v3/api_v3.go @@ -1,5 +1,5 @@ /* - Copyright (c) 2019 Dell Inc, or its subsidiaries. + Copyright (c) 2022 Dell Inc, or its subsidiaries. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. diff --git a/api/v3/api_v3_cluster.go b/api/v3/api_v3_cluster.go index 276c9b7..8cc3b7a 100644 --- a/api/v3/api_v3_cluster.go +++ b/api/v3/api_v3_cluster.go @@ -1,5 +1,5 @@ /* - Copyright (c) 2019 Dell Inc, or its subsidiaries. + Copyright (c) 2022 Dell Inc, or its subsidiaries. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. diff --git a/api/v3/api_v3_types.go b/api/v3/api_v3_types.go index cc3c7cc..08fb266 100644 --- a/api/v3/api_v3_types.go +++ b/api/v3/api_v3_types.go @@ -1,5 +1,5 @@ /* - Copyright (c) 2019 Dell Inc, or its subsidiaries. + Copyright (c) 2022 Dell Inc, or its subsidiaries. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. diff --git a/client_test.go b/client_test.go index 0d20f17..6bc93d9 100644 --- a/client_test.go +++ b/client_test.go @@ -1,5 +1,5 @@ /* - Copyright (c) 2019 Dell Inc, or its subsidiaries. + Copyright (c) 2022 Dell Inc, or its subsidiaries. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. diff --git a/cluster.go b/cluster.go index 9321133..6129f46 100644 --- a/cluster.go +++ b/cluster.go @@ -1,5 +1,5 @@ /* - Copyright (c) 2019 Dell Inc, or its subsidiaries. + Copyright (c) 2022 Dell Inc, or its subsidiaries. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. diff --git a/cluster_test.go b/cluster_test.go index be9672e..70d2a61 100644 --- a/cluster_test.go +++ b/cluster_test.go @@ -1,5 +1,5 @@ /* - Copyright (c) 2019 Dell Inc, or its subsidiaries. + Copyright (c) 2022 Dell Inc, or its subsidiaries. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. diff --git a/exports_test.go b/exports_test.go index 8561164..4e9a192 100644 --- a/exports_test.go +++ b/exports_test.go @@ -1,5 +1,5 @@ /* - Copyright (c) 2019 Dell Inc, or its subsidiaries. + Copyright (c) 2022 Dell Inc, or its subsidiaries. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. diff --git a/quota.go b/quota.go index 1a971de..eb4f17a 100644 --- a/quota.go +++ b/quota.go @@ -1,5 +1,5 @@ /* - Copyright (c) 2019 Dell Inc, or its subsidiaries. + Copyright (c) 2022 Dell Inc, or its subsidiaries. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. diff --git a/quota_test.go b/quota_test.go index eabbae7..498fa03 100644 --- a/quota_test.go +++ b/quota_test.go @@ -1,5 +1,5 @@ /* - Copyright (c) 2019 Dell Inc, or its subsidiaries. + Copyright (c) 2022 Dell Inc, or its subsidiaries. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. diff --git a/replication_test.go b/replication_test.go new file mode 100644 index 0000000..d647a6d --- /dev/null +++ b/replication_test.go @@ -0,0 +1,280 @@ +package goisilon_test + +import ( + "context" + "fmt" + "github.com/dell/goisilon" + "github.com/dell/goisilon/api" + "github.com/stretchr/testify/suite" + "testing" + "time" +) + +type ReplicationTestSuite struct { + suite.Suite + localClient *goisilon.Client + remoteClient *goisilon.Client + localEndpoint string + remoteEndpoint string +} + +func (suite *ReplicationTestSuite) SetupSuite() { + lc, err := goisilon.NewClientWithArgs( + context.Background(), + "https://10.225.111.21:8080", + true, + 1, + "admin", + "", + "dangerous", + "/ifs/data/test-goisilon", + "0777", 0) + if err != nil { + panic(err) + } + suite.localClient = lc + suite.localEndpoint = "10.225.111.21" + + rc, err := goisilon.NewClientWithArgs( + context.Background(), + "https://10.225.111.70:8080", + true, + 1, + "admin", + "", + "dangerous", + "/ifs/data/test-goisilon", + "0777", 0) + if err != nil { + panic(err) + } + suite.remoteClient = rc + suite.remoteEndpoint = "10.225.111.70" +} + +func (suite *ReplicationTestSuite) TearDownSuite() { +} + +func (suite *ReplicationTestSuite) TestUnplannedFailoverScenario() { + ctx, cancel := context.WithTimeout(context.Background(), 5*time.Minute) + defer cancel() + + volumeName := "replicated" + + // *** SIMULATE CREATE_VOLUME CALL *** // + + // Create volume that would serve as VG + volume, err := suite.localClient.CreateVolume(ctx, volumeName) + suite.NoError(err) + suite.NotNil(volume) + + // defer func() { + // err := suite.localClient.DeleteVolume(ctx, volumeName) + // suite.NoError(err) + // }() + + res, err := suite.localClient.GetVolume(context.Background(), "", volumeName) + suite.NoError(err) + fmt.Println("local", res) + + err = suite.localClient.CreatePolicy(ctx, + volumeName, + 300, + "/ifs/data/test-goisilon/replicated", + "/ifs/data/test-goisilon/replicated", + suite.remoteEndpoint, + true) + // suite.NoError(err) + + p, err := suite.localClient.GetPolicyByName(ctx, volumeName) + suite.NoError(err) + suite.NotNil(p) + fmt.Println("local policy", p) + + err = suite.localClient.WaitForPolicyLastJobState(ctx, volumeName, goisilon.FINISHED) + suite.NoError(err) + + // defer func() { + // err := suite.localClient.DeletePolicy(ctx, volumeName) + // suite.NoError(err) + // }() + + // *** SIMULATE EXECUTE_ACTION UNPLANNED_FAILOVER CALL *** + + err = suite.remoteClient.BreakAssociation(ctx, volumeName) + suite.NoError(err) + + + // *** SIMULATE EXECUTE_ACTION REPROTECT CALL *** + // In driver EXECUTE_ACTION reprotect will be called on another side, but here we just talk to remote client + + local := suite.remoteClient + remote := suite.localClient + + pp, err := remote.GetPolicyByName(ctx, volumeName) + suite.NoError(err) + + if pp.Enabled { + // Disable policy on remote + err = remote.DisablePolicy(ctx, volumeName) + suite.NoError(err) + + err = remote.WaitForPolicyEnabledFieldCondition(ctx, volumeName, false) + suite.NoError(err) + + // Run reset on the policy + err = remote.ResetPolicy(ctx, volumeName) + suite.NoError(err) + + + // Create policy on local (actually get it before creating it) + err = local.CreatePolicy(ctx, + volumeName, + 300, + "/ifs/data/test-goisilon/replicated", + "/ifs/data/test-goisilon/replicated", + suite.localEndpoint, + true) + suite.NoError(err) + + err = local.WaitForPolicyEnabledFieldCondition(ctx, volumeName, true) + suite.NoError(err) + + + } else { + err = local.EnablePolicy(ctx, volumeName) + suite.NoError(err) + + err = local.WaitForPolicyEnabledFieldCondition(ctx, volumeName, true) + suite.NoError(err) + } + + + tp, err := remote.GetTargetPolicyByName(ctx, volumeName) + if err != nil { + if e, ok := err.(*api.JSONError); ok { + if e.StatusCode != 404 { + suite.NoError(err) + } + } + } + + if tp != nil { + err = remote.DisallowWrites(ctx, volumeName) + suite.NoError(err) + } +} + +func (suite *ReplicationTestSuite) TestReplication() { + ctx, cancel := context.WithTimeout(context.Background(), 5*time.Minute) + defer cancel() + + volumeName := "replicated" + + // *** SIMULATE CREATE_VOLUME CALL *** // + + // Create volume that would serve as VG + volume, err := suite.localClient.CreateVolume(ctx, volumeName) + suite.NoError(err) + suite.NotNil(volume) + + // defer func() { + // err := suite.localClient.DeleteVolume(ctx, volumeName) + // suite.NoError(err) + // }() + + res, err := suite.localClient.GetVolume(context.Background(), "", volumeName) + suite.NoError(err) + fmt.Println("local", res) + + err = suite.localClient.CreatePolicy(ctx, + volumeName, + 300, + "/ifs/data/test-goisilon/replicated", + "/ifs/data/test-goisilon/replicated", + suite.remoteEndpoint, + true) + suite.NoError(err) + + p, err := suite.localClient.GetPolicyByName(ctx, volumeName) + suite.NoError(err) + suite.NotNil(p) + fmt.Println("local policy", p) + + err = suite.localClient.WaitForPolicyLastJobState(ctx, volumeName, goisilon.FINISHED) + suite.NoError(err) + + // defer func() { + // err := suite.localClient.DeletePolicy(ctx, volumeName) + // suite.NoError(err) + // }() + + // *** SIMULATE EXECUTE_ACTION FAILOVER CALL *** + + err = suite.localClient.SyncPolicy(ctx, volumeName) + suite.NoError(err) + + // Create Remote Policy + err = suite.remoteClient.CreatePolicy(ctx, + volumeName, + 300, + "/ifs/data/test-goisilon/replicated", + "/ifs/data/test-goisilon/replicated", + suite.remoteEndpoint, + false) + suite.NoError(err) + + rp, err := suite.remoteClient.GetPolicyByName(ctx, volumeName) + suite.NoError(err) + suite.NotNil(rp) + suite.Equal(rp.Enabled, false) + fmt.Println("remote policy", rp) + + err = suite.remoteClient.WaitForPolicyLastJobState(ctx, volumeName, goisilon.UNKNOWN) + suite.NoError(err) + + // defer func() { + // err := suite.remoteClient.DeletePolicy(ctx, volumeName) + // suite.NoError(err) + // }() + + // Allow writes on remote + err = suite.remoteClient.AllowWrites(ctx, volumeName) + suite.NoError(err) + + // Disable policy on local + err = suite.localClient.DisablePolicy(ctx, volumeName) + suite.NoError(err) + + err = suite.localClient.WaitForPolicyEnabledFieldCondition(ctx, volumeName, false) + suite.NoError(err) + + // Disable writes on local (if we can) + tp, err := suite.localClient.GetTargetPolicyByName(ctx, volumeName) + if err != nil { + if e, ok := err.(*api.JSONError); ok { + if e.StatusCode != 404 { + suite.NoError(err) + } + } + } + + fmt.Println("local target policy", tp) + + if tp != nil { + err = suite.localClient.DisallowWrites(ctx, volumeName) + suite.NoError(err) + } + + // *** SIMULATE EXECUTE_ACTION REPROTECT CALL *** + // In driver EXECUTE_ACTION reprotect will be called on another side, but here we just talk to remote client + err = suite.remoteClient.EnablePolicy(ctx, volumeName) + suite.NoError(err) + + err = suite.remoteClient.WaitForPolicyEnabledFieldCondition(ctx, volumeName, true) + suite.NoError(err) +} + +func TestReplicationSuite(t *testing.T) { + suite.Run(t, new(ReplicationTestSuite)) +} diff --git a/zones.go b/zones.go index 78a47ad..ff26e57 100644 --- a/zones.go +++ b/zones.go @@ -1,5 +1,5 @@ /* - Copyright (c) 2019 Dell Inc, or its subsidiaries. + Copyright (c) 2022 Dell Inc, or its subsidiaries. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. diff --git a/zones_test.go b/zones_test.go index ef6064b..63448b9 100644 --- a/zones_test.go +++ b/zones_test.go @@ -1,5 +1,5 @@ /* - Copyright (c) 2019 Dell Inc, or its subsidiaries. + Copyright (c) 2022 Dell Inc, or its subsidiaries. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License.