#region Copyright /* * Copyright © 2014-2016 NetApp, Inc. All Rights Reserved. * * CONFIDENTIALITY NOTICE: THIS SOFTWARE CONTAINS CONFIDENTIAL INFORMATION OF * NETAPP, INC. USE, DISCLOSURE OR REPRODUCTION IS PROHIBITED WITHOUT THE PRIOR * EXPRESS WRITTEN PERMISSION OF NETAPP, INC. */ #endregion #region Using Directives using System.Collections; using System.Collections.Generic; using System.ComponentModel; using System.Management.Automation; using SolidFire.Core.Validation; using System.Management.Automation.Runspaces; using SolidFire.Core; using SolidFire.Element.Api; using System.Linq; using System; #endregion namespace SolidFire.Volume.New { /// /// Used to create a schedule that will autonomously make a snapshot of a volume at a defined interval. /// The snapshot created can be used later as a backup or rollback to ensure the data on a /// volume or group of volumes is consistent for the point in time in which the snapshot was created. /// Note: Creating a snapshot is allowed if cluster fullness is at stage 2 or 3. /// Snapshots are not created when cluster fullness is at stage 4 or 5. /// [Cmdlet(VerbsCommon.New, "SFSchedule")] public class NewSFSchedule : SFCmdlet, IDynamicParameters { #region Private Data private List _processedIDs; private Pipeline _pipeline; // Prepare the DynamicParameter classes so they're ready when Frequency is selected. private TimeIntervalDynamicParameters _contextTimeInterval = new TimeIntervalDynamicParameters(); private DaysOfMonthDynamicParameters _contextDaysOfMonth = new DaysOfMonthDynamicParameters(); private DaysOfWeekDynamicParameters _contextDaysOfWeek = new DaysOfWeekDynamicParameters(); #endregion #region Parameters [Parameter(Position = 0, Mandatory = true, ValueFromPipeline = true, ValueFromPipelineByPropertyName = true, HelpMessage = "Volume IDs to be included in the resulting snapshots.")] public long[] VolumeID { get; set; } [Parameter(Position = 1, Mandatory = true, HelpMessage = "Unique name for the schedule.")] public string ScheduleName { get; set; } [Parameter(Position = 2, Mandatory = true, HelpMessage = "Used to indicate the frequency at which the snapshot will be made.")] [ValidateSet("TimeInterval", "DaysOfWeek", "DaysOfMonth")] public string Frequency { get; set; } private bool? _paused; [Parameter(Position = 8, Mandatory = false, HelpMessage = "Use to pause the schedule.")] public bool Paused { get { return _paused.HasValue ? _paused.Value : false; } set { _paused = value; } } [Parameter(Position = 9, Mandatory = false, HelpMessage = "Name of the new snapshot.")] public string SnapshotName { get; set; } private bool _recurring = true; [Parameter(Position = 10, Mandatory = false, HelpMessage = "Indicates if the schedule will be recurring or not. Default is true.")] public bool Recurring { get { return _recurring; } set { _recurring = value; } } [Parameter(Position = 11, Mandatory = false, HelpMessage = " Include snapshots in replication when paired. Default is false.")] public bool EnableRemoteReplication { get; set; } private ulong? _retentionDays; [Parameter(Position = 12, Mandatory = false, HelpMessage = "Number of days to retain snapshots created by this schedule.")] public ulong RetentionDays { get { return _retentionDays.HasValue ? _retentionDays.Value : 0; } set { _retentionDays = value; } } private ulong? _retentionHours; [Parameter(Position = 13, Mandatory = false, HelpMessage = "Number of hours to retain snapshots created by this schedule.")] public ulong RetentionHours { get { return _retentionHours.HasValue ? _retentionHours.Value : 0; } set { _retentionHours = value; } } private ulong? _retentionMinutes; [Parameter(Position = 14, Mandatory = false, HelpMessage = "Number of minutes to retain snapshots created by this schedule.")] [ValidateRange(0, 59)] public ulong RetentionMinutes { get { return _retentionMinutes.HasValue ? _retentionMinutes.Value : 0; } set { _retentionMinutes = value; } } [Parameter(Position = 15, Mandatory = false, HelpMessage = "Date/Time after which the schedule will be run.")] [SFValidatePattern(SolidFireValidations.ISO8601DateTimeOrEmptyString, "The argument is not in the format of an ISO-8601 Date String.")] public string StartingDate { get; set; } #endregion #region Cmdlet Overrides protected override void BeginProcessing() { base.BeginProcessing(); CheckConnection(minVersionNumber: 8.0f); _pipeline = Runspace.DefaultRunspace.CreateNestedPipeline(); _processedIDs = new List(); } protected override void EndProcessing() { var command = new Command("Get-SFSchedule"); command.Parameters.Add("ScheduleID", _processedIDs); if (SFConnection != null) { command.Parameters.Add("SFConnection", SFConnection); } else if (Target != null) { command.Parameters.Add("Target", Target); } _pipeline.Commands.Add(command); WriteObject(_pipeline.Invoke(), true); base.EndProcessing(); _processedIDs.Clear(); _pipeline = null; } protected override void ProcessRecord() { base.ProcessRecord(); var request = new CreateScheduleRequest(); var schedule = new Schedule() { Name = ScheduleName, Paused = _paused ?? false, StartingDate = StartingDate, Recurring = _recurring }; request.Schedule = schedule; var scheduleInfo = new ScheduleInfo() { EnableRemoteReplication = EnableRemoteReplication, SnapshotName = SnapshotName }; // VolumeIDs can be handed in through either the VolumeID or ScheduleInfo parameters. It will usually be one or the other, not both. // If both parameters are present, VolumeID will trump ScheduleInfo Volume IDs. The reason is because if someone is piping in an // existing Schedule but they specify VolumeID in a parameter, it is assumed they want VolumeID to replace the Schedule's existing Volumes list. // Go through both properties then put the appropriate Volume IDs in the ScheduleInfo.Volumes array. var volumes = new List(); if (VolumeID != null) { volumes.Clear(); volumes.AddRange(VolumeID?.Select(v => v)); } if (volumes.Count == 0) { throw new ParameterBindingException("No VolumeID specified. Unable to Modify schedule without VolumeID or ScheduleInfo.Volumes being passed in"); } scheduleInfo.VolumeIDs = volumes.ToArray(); // ScheduleInfo.Retention must be in HH:mm:ss format. If any values are passed in, build the string. if (_retentionDays.HasValue || _retentionHours.HasValue || _retentionMinutes.HasValue) { scheduleInfo.Retention = VolumeUtilities.BuildTimeString(_retentionDays.Value, _retentionHours.Value, _retentionMinutes.Value); } schedule.ScheduleInfo = scheduleInfo; // Perform dynamic property assignment based on Frequency switch (Frequency) { case "TimeInterval": var timeInterval = new TimeIntervalFrequency() { Hours = (long)_contextTimeInterval.TimeIntervalHours, Days = (long)_contextTimeInterval.TimeIntervalDays, Minutes = (long)_contextTimeInterval.TimeIntervalMinutes }; schedule.Frequency = timeInterval; break; case "DaysOfWeek": var daysOfWeek = new DaysOfWeekFrequency() { Hours = (long)_contextDaysOfWeek.TimeOfDayHour, Minutes = (long)_contextDaysOfWeek.TimeOfDayMinute }; var weekdays = new List(); foreach (var day in _contextDaysOfWeek.DaysOfWeekDays) { weekdays.Add((Weekday)Enum.Parse(typeof(Weekday), day)); } daysOfWeek.Weekdays = weekdays.ToArray(); schedule.Frequency = daysOfWeek; break; case "DaysOfMonth": var daysOfMonth = new DaysOfMonthFrequency() { Hours = (long)_contextDaysOfMonth.TimeOfDayHour, Minutes = (long)_contextDaysOfMonth.TimeOfDayMinute, Monthdays = _contextDaysOfMonth.DaysOfMonthDays.Select(d => (long)d).ToArray() }; schedule.Frequency = daysOfMonth; break; }; var objsFromAPI = SendRequest("CreateSchedule", request); foreach (var obj in objsFromAPI) { _processedIDs.Add(obj.Result.ScheduleID); } } /// /// This is necessary to implement IDynamicParameters. It hands back the corresponding parameter set /// based on the value in the Frequence parameter. /// /// DynamicParameter class with parameters for the selected Frequency public object GetDynamicParameters() { switch (Frequency) { case "TimeInterval": return _contextTimeInterval; case "DaysOfMonth": return _contextDaysOfMonth; case "DaysOfWeek": return _contextDaysOfWeek; } return null; } #endregion } }