Skip to content

Commit 966a48c

Browse files
authored
[Az.RecoveryServices.Backup] Added support for non-UTC timezones with standard policies (#20483)
* Added support for non-UTC timezones with standard policy for workloadType IaasVM, MSSql, AzureFiles. resolved review comments * added non-UTC policy tests added CRR DES test case updated help text added example * resolving review comments
1 parent bc60c42 commit 966a48c

File tree

24 files changed

+5471
-41
lines changed

24 files changed

+5471
-41
lines changed

src/RecoveryServices/RecoveryServices.Backup.Helpers/Conversions/ConversionHelpers.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -856,6 +856,7 @@ public static void GetPSSubProtectionPolicy(AzureVmWorkloadPolicy azureVmWorkloa
856856
}
857857
else if (string.Compare(subProtectionPolicy.PolicyType, "Log") == 0)
858858
{
859+
// timeZone paased as input but not used in below method calls
859860
azureVmWorkloadPolicyModel.LogBackupSchedulePolicy = PolicyHelpers.GetPSLogSchedulePolicy((ServiceClientModel.LogSchedulePolicy)
860861
subProtectionPolicy.SchedulePolicy,
861862
((ServiceClientModel.AzureVmWorkloadProtectionPolicy)serviceClientResponse.Properties).Settings.TimeZone);

src/RecoveryServices/RecoveryServices.Backup.Helpers/Conversions/RetentionPolicyConversions.cs

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -261,7 +261,7 @@ private static DailyRetentionSchedule GetPSLTRDailySchedule(ServiceClientModel.D
261261
DailyRetentionSchedule psDaily = new DailyRetentionSchedule();
262262

263263
psDaily.DurationCountInDays = GetRetentionDurationInDays(serviceClientDaily.RetentionDuration);
264-
psDaily.RetentionTimes = ParseDateTimesToUTC(serviceClientDaily.RetentionTimes, timeZone);
264+
psDaily.RetentionTimes = ParseDateTimesToLocal(serviceClientDaily.RetentionTimes, timeZone);
265265

266266
return psDaily;
267267
}
@@ -277,7 +277,7 @@ private static WeeklyRetentionSchedule GetPSLTRWeeklySchedule(ServiceClientModel
277277
WeeklyRetentionSchedule psWeekly = new WeeklyRetentionSchedule();
278278

279279
psWeekly.DurationCountInWeeks = GetRetentionDurationInWeeks(serviceClientWeekly.RetentionDuration);
280-
psWeekly.RetentionTimes = ParseDateTimesToUTC(serviceClientWeekly.RetentionTimes, timeZone);
280+
psWeekly.RetentionTimes = ParseDateTimesToLocal(serviceClientWeekly.RetentionTimes, timeZone);
281281
psWeekly.DaysOfTheWeek =
282282
HelperUtils.EnumListConverter<ServiceClientModel.DayOfWeek?, DayOfWeek>(
283283
serviceClientWeekly.DaysOfTheWeek);
@@ -296,7 +296,7 @@ private static MonthlyRetentionSchedule GetPSLTRMonthlySchedule(ServiceClientMod
296296
MonthlyRetentionSchedule psMonthly = new MonthlyRetentionSchedule();
297297

298298
psMonthly.DurationCountInMonths = GetRetentionDurationInMonths(serviceClientMonthly.RetentionDuration);
299-
psMonthly.RetentionTimes = ParseDateTimesToUTC(serviceClientMonthly.RetentionTimes, timeZone);
299+
psMonthly.RetentionTimes = ParseDateTimesToLocal(serviceClientMonthly.RetentionTimes, timeZone);
300300
psMonthly.RetentionScheduleFormatType =
301301
serviceClientMonthly.RetentionScheduleFormatType.ToEnum<RetentionScheduleFormat>();
302302
psMonthly.RetentionScheduleDaily = GetPSLTRDailyRetentionFormat(serviceClientMonthly.RetentionScheduleDaily);
@@ -316,7 +316,7 @@ private static YearlyRetentionSchedule GetPSLTRYearlySchedule(ServiceClientModel
316316
YearlyRetentionSchedule psYearly = new YearlyRetentionSchedule();
317317

318318
psYearly.DurationCountInYears = GetRetentionDurationInYears(serviceClientYearly.RetentionDuration);
319-
psYearly.RetentionTimes = ParseDateTimesToUTC(serviceClientYearly.RetentionTimes, timeZone);
319+
psYearly.RetentionTimes = ParseDateTimesToLocal(serviceClientYearly.RetentionTimes, timeZone);
320320
psYearly.RetentionScheduleFormatType =
321321
serviceClientYearly.RetentionScheduleFormatType.ToEnum<RetentionScheduleFormat>();
322322
psYearly.RetentionScheduleDaily = GetPSLTRDailyRetentionFormat(serviceClientYearly.RetentionScheduleDaily);

src/RecoveryServices/RecoveryServices.Backup.Helpers/Conversions/SchedulePolicyConversions.cs

Lines changed: 43 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,7 @@ public static SimpleSchedulePolicy GetPSSimpleSchedulePolicy(
4343
psPolicy.ScheduleRunDays = HelperUtils.EnumListConverter<ServiceClientModel.DayOfWeek?, DayOfWeek>(serviceClientPolicy.ScheduleRunDays);
4444

4545
psPolicy.ScheduleRunFrequency = (ScheduleRunType)Enum.Parse(typeof(ScheduleRunType), serviceClientPolicy.ScheduleRunFrequency.ToString());
46-
psPolicy.ScheduleRunTimes = ParseDateTimesToUTC(serviceClientPolicy.ScheduleRunTimes, timeZone);
46+
psPolicy.ScheduleRunTimes = ParseDateTimesToLocal(serviceClientPolicy.ScheduleRunTimes, timeZone);
4747

4848
if (psPolicy.ScheduleRunFrequency == ScheduleRunType.Weekly)
4949
{
@@ -115,7 +115,7 @@ public static SimpleSchedulePolicyV2 GetPSSimpleSchedulePolicyV2(
115115
if (serviceClientPolicy.WeeklySchedule != null)
116116
{
117117
psPolicy.WeeklySchedule.ScheduleRunDays = HelperUtils.EnumListConverter<ServiceClientModel.DayOfWeek?, DayOfWeek>(serviceClientPolicy.WeeklySchedule.ScheduleRunDays);
118-
psPolicy.WeeklySchedule.ScheduleRunTimes = ParseDateTimesToUTC(serviceClientPolicy.WeeklySchedule.ScheduleRunTimes, timeZone);
118+
psPolicy.WeeklySchedule.ScheduleRunTimes = ParseDateTimesToLocal(serviceClientPolicy.WeeklySchedule.ScheduleRunTimes, timeZone);
119119

120120
offset = psPolicy.WeeklySchedule.ScheduleRunTimes[0].DayOfWeek.GetHashCode() - serviceClientPolicy.WeeklySchedule.ScheduleRunTimes[0].Value.DayOfWeek.GetHashCode();
121121
}
@@ -155,7 +155,7 @@ public static SimpleSchedulePolicyV2 GetPSSimpleSchedulePolicyV2(
155155
else
156156
{
157157
psPolicy.DailySchedule = new DailySchedule();
158-
psPolicy.DailySchedule.ScheduleRunTimes = (serviceClientPolicy.DailySchedule != null) ? ParseDateTimesToUTC(serviceClientPolicy.DailySchedule.ScheduleRunTimes, timeZone) : null;
158+
psPolicy.DailySchedule.ScheduleRunTimes = (serviceClientPolicy.DailySchedule != null) ? ParseDateTimesToLocal(serviceClientPolicy.DailySchedule.ScheduleRunTimes, timeZone) : null;
159159
}
160160

161161
psPolicy.ScheduleRunTimeZone = timeZone;
@@ -190,6 +190,15 @@ public static LogSchedulePolicy GetPSLogSchedulePolicy(
190190

191191
#region PStoServiceClientObject conversions
192192

193+
/// <summary>
194+
/// Helper function to parse utc time from local time.
195+
/// </summary>
196+
public static List<DateTime> ParseDateTimesToLocal(IList<DateTime?> localTimes, string timeZone)
197+
{
198+
List<DateTime> utcTimes = ParseDateTimesToUTC(localTimes, timeZone);
199+
return ParseDateTimesFromUTCToTimeZone(utcTimes, timeZone);
200+
}
201+
193202
/// <summary>
194203
/// Helper function to parse utc time from local time.
195204
/// </summary>
@@ -222,6 +231,37 @@ public static List<DateTime> ParseDateTimesToUTC(IList<DateTime?> localTimes, st
222231
return utcTimes;
223232
}
224233

234+
public static List<DateTime> ParseDateTimesFromUTCToTimeZone(List<DateTime> utcTimes, string timeZone)
235+
{
236+
if (utcTimes == null || utcTimes.Count == 0)
237+
{
238+
return null;
239+
}
240+
241+
List<DateTime> localTimes = new List<DateTime>();
242+
DateTime temp;
243+
244+
foreach (DateTime utcTime in utcTimes)
245+
{
246+
if (utcTime == null)
247+
{
248+
throw new ArgumentNullException("Policy date time object is null");
249+
}
250+
251+
temp = utcTime;
252+
if (!string.IsNullOrEmpty(timeZone) && timeZone != "UTC")
253+
{
254+
TimeZoneInfo timeZoneInfo = TimeZoneConverter.TZConvert.GetTimeZoneInfo(timeZone);
255+
temp = TimeZoneInfo.ConvertTimeFromUtc(temp, timeZoneInfo);
256+
temp = DateTime.SpecifyKind(temp, DateTimeKind.Utc);
257+
}
258+
localTimes.Add(temp);
259+
}
260+
261+
return localTimes;
262+
}
263+
264+
225265
/// <summary>
226266
/// Helper function to convert service simple schedule policy from ps schedule policy.
227267
/// </summary>

src/RecoveryServices/RecoveryServices.Backup.Helpers/Validations/PolicyValidations.cs

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -23,8 +23,14 @@ namespace Microsoft.Azure.Commands.RecoveryServices.Backup.Helpers
2323
/// Backup policy validation helpers.
2424
/// </summary>
2525
public partial class PolicyHelpers
26-
{
27-
26+
{
27+
/// <summary>
28+
/// validates LTRP with tiering policy
29+
/// </summary>
30+
/// <param name="ltrPolicy"></param>
31+
/// <param name="tieringPolicy"></param>
32+
/// <param name="isPreviousTieringPolicy"> is an existing tiering policy</param>
33+
/// <exception cref="ArgumentException"></exception>
2834
public static void ValidateLongTermRetentionPolicyWithTieringPolicy(LongTermRetentionPolicy ltrPolicy, TieringPolicy tieringPolicy, bool isPreviousTieringPolicy = false)
2935
{
3036
// To enable Archive(either TierRecommended or TierAfter), Monthly or Yearly retention needs to be set
@@ -83,7 +89,7 @@ public static void ValidateLongTermRetentionPolicyWithTieringPolicy(LongTermRete
8389
}
8490
}
8591

86-
public static void ValidateFullBackupRetentionPolicyWithTieringPolicy(LongTermRetentionPolicy ltrPolicy, TieringPolicy tieringPolicy, bool isPreviousTieringPolicy = false) // check resx messages
92+
public static void ValidateFullBackupRetentionPolicyWithTieringPolicy(LongTermRetentionPolicy ltrPolicy, TieringPolicy tieringPolicy, bool isPreviousTieringPolicy = false)
8793
{
8894
if (tieringPolicy != null && tieringPolicy.TieringMode != TieringMode.DoNotTier)
8995
{

src/RecoveryServices/RecoveryServices.Backup.Models/CommonModels/PolicyRetentionObjects.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -176,7 +176,7 @@ public void Validate(ScheduleRunType ScheduleRunFrequency = 0)
176176
public override string ToString()
177177
{
178178
return string.Format("IsDailyScheduleEnabled:{0}, IsWeeklyScheduleEnabled:{1}, " +
179-
"IsMonthlyScheduleEnabled:{2}, IsYearlyScheduleEnabled:{3}" +
179+
"IsMonthlyScheduleEnabled:{2}, IsYearlyScheduleEnabled:{3} " +
180180
"DailySchedule: {4}, WeeklySchedule: {5}, MonthlySchedule:{6}, YearlySchedule:{7}",
181181
IsDailyScheduleEnabled, IsWeeklyScheduleEnabled,
182182
IsMonthlyScheduleEnabled, IsYearlyScheduleEnabled,

src/RecoveryServices/RecoveryServices.Backup.Models/CommonModels/PolicyScheduleObjects.cs

Lines changed: 19 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -159,7 +159,7 @@ public override void Validate()
159159
DateTime windowStartTime = (DateTime)ScheduleWindowStartTime;
160160

161161
// If ScheduleWindowDuration is greator than (23:30 - ScheduleWindowStartTime) then throw exception
162-
// if non-UTC times are allowed then this exception needs to change
162+
// if non-UTC times (timeZones already allowed) are allowed then this exception needs to change
163163
if (windowStartTime.Minute % 30 != 0 || windowStartTime.Second != 0 || windowStartTime.Millisecond != 0)
164164
{
165165
throw new ArgumentException(Resources.InvalidScheduleTimeInScheduleException);
@@ -171,18 +171,19 @@ public override string ToString()
171171
{
172172
if (ScheduleRunFrequency == ScheduleRunType.Hourly)
173173
{
174-
return string.Format("scheduleRunType:{0}, ScheduleInterval:{1}, ScheduleWindowStartTime:{2}, ScheduleWindowDuration:{3}, ScheduleRunTimeZone:{4}",
174+
return string.Format("scheduleRunType:{0}, ScheduleInterval:{1}, ScheduleWindowStartTime:{2}, ScheduleWindowDuration:{3}, ScheduleRunTimeZone: {4}",
175175
ScheduleRunFrequency,
176176
ScheduleInterval,
177177
ScheduleWindowStartTime.ToString(),
178178
ScheduleWindowDuration,
179179
ScheduleRunTimeZone);
180180
}
181181

182-
return string.Format("scheduleRunType:{0}, ScheduleRunDays:{1}, ScheduleRunTimes:{2}",
182+
return string.Format("scheduleRunType:{0}, ScheduleRunDays:{1}, ScheduleRunTimes:{2}, ScheduleRunTimeZone: {3}",
183183
ScheduleRunFrequency,
184184
TraceUtils.GetString(ScheduleRunDays),
185-
TraceUtils.GetString(ScheduleRunTimes));
185+
TraceUtils.GetString(ScheduleRunTimes),
186+
ScheduleRunTimeZone);
186187
}
187188
}
188189

@@ -199,7 +200,7 @@ public class SimpleSchedulePolicyV2 : SchedulePolicyBase
199200
/// <summary>
200201
/// Hourly Schedule for Enhanced policy.
201202
/// </summary>
202-
public HourlySchedule HourlySchedule { get; set; } // comment this to hide hourly support
203+
public HourlySchedule HourlySchedule { get; set; }
203204

204205
/// <summary>
205206
/// Daily Schedule for Enhanced policy.
@@ -289,20 +290,21 @@ public override string ToString()
289290
{
290291
if(WeeklySchedule != null)
291292
{
292-
return string.Format("scheduleRunType:{0}, ScheduleRunDays:{1}, ScheduleRunTimes:{2}",
293+
return string.Format("scheduleRunType:{0}, ScheduleRunDays:{1}, ScheduleRunTimes:{2}, ScheduleRunTimeZone: {3}",
293294
ScheduleRunFrequency,
294295
TraceUtils.GetString(WeeklySchedule.ScheduleRunDays),
295-
TraceUtils.GetString(WeeklySchedule.ScheduleRunTimes));
296+
TraceUtils.GetString(WeeklySchedule.ScheduleRunTimes), ScheduleRunTimeZone);
296297
}
297298
else if (DailySchedule != null)
298299
{
299-
return string.Format("scheduleRunType:{0}, ScheduleRunTimes:{1}",
300+
return string.Format("scheduleRunType:{0}, ScheduleRunTimes:{1}, ScheduleRunTimeZone: {2}",
300301
ScheduleRunFrequency,
301-
TraceUtils.GetString(DailySchedule.ScheduleRunTimes));
302+
TraceUtils.GetString(DailySchedule.ScheduleRunTimes),
303+
ScheduleRunTimeZone);
302304
}
303305
else if (HourlySchedule != null)
304306
{
305-
return string.Format("scheduleRunType:{0}, ScheduleInterval:{1}, ScheduleWindowStartTime:{2}, ScheduleWindowDuration:{3}, ScheduleRunTimeZone:{4}",
307+
return string.Format("scheduleRunType:{0}, ScheduleInterval:{1}, ScheduleWindowStartTime:{2}, ScheduleWindowDuration:{3}, ScheduleRunTimeZone: {4}",
306308
ScheduleRunFrequency,
307309
HourlySchedule.Interval,
308310
HourlySchedule.WindowStartTime.ToString(),
@@ -317,6 +319,13 @@ public override string ToString()
317319
public class LogSchedulePolicy : SchedulePolicyBase
318320
{
319321
public int? ScheduleFrequencyInMins { get; set; }
322+
323+
public override string ToString()
324+
{
325+
return string.Format("ScheduleFrequencyInMins: {0}",
326+
ScheduleFrequencyInMins);
327+
328+
}
320329
}
321330

322331
public class SQLSchedulePolicy : SchedulePolicyBase

src/RecoveryServices/RecoveryServices.Backup.Models/Properties/Resources.Designer.cs

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/RecoveryServices/RecoveryServices.Backup.Models/Properties/Resources.resx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -289,7 +289,7 @@
289289
<value>Please pass Job or List of Jobs as input. Your input is of type: {0}</value>
290290
</data>
291291
<data name="ScheduleTimeNotInUTCTimeZoneException" xml:space="preserve">
292-
<value>ScheduleRunTimes in Schedule Policy should be in UTC Timezone</value>
292+
<value>ScheduleRunTimes in Schedule Policy should be in UTC Time, however policy can be created in a different time zone by providing the timezone in ScheduleRunTimeZone attribute</value>
293293
</data>
294294
<data name="InvalidProtectionItemException" xml:space="preserve">
295295
<value>Invalid Item - either NULL or NOT of type: {0}</value>

src/RecoveryServices/RecoveryServices.Backup.Providers/Providers/AzureFilesPsBackupProvider.cs

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -415,9 +415,9 @@ private RestAzureNS.AzureOperationResponse<ProtectionPolicyResource> CreateorMod
415415
azureFileShareProtectionPolicy.SchedulePolicy = PolicyHelpers.GetServiceClientSimpleSchedulePolicy(
416416
(CmdletModel.SimpleSchedulePolicy)((AzureFileSharePolicy)policy).SchedulePolicy);
417417

418-
// timeZone in case of Hourly should be customizable
418+
// timeZone should be customizable
419419
string timeZone = ((CmdletModel.SimpleSchedulePolicy)((AzureFileSharePolicy)policy).SchedulePolicy).ScheduleRunTimeZone;
420-
if (ScheduleRunFrequency == CmdletModel.ScheduleRunType.Hourly && timeZone != null)
420+
if (timeZone != null)
421421
{
422422
azureFileShareProtectionPolicy.TimeZone = timeZone;
423423
}
@@ -462,9 +462,9 @@ private RestAzureNS.AzureOperationResponse<ProtectionPolicyResource> CreateorMod
462462
azureFileShareProtectionPolicy.SchedulePolicy = PolicyHelpers.GetServiceClientSimpleSchedulePolicy(
463463
(CmdletModel.SimpleSchedulePolicy)schedulePolicy);
464464

465-
// timeZone in case of Hourly should be customizable
465+
// timeZone should be customizable
466466
string timeZone = ((CmdletModel.SimpleSchedulePolicy)schedulePolicy).ScheduleRunTimeZone;
467-
if (ScheduleRunFrequency == CmdletModel.ScheduleRunType.Hourly && timeZone != null)
467+
if (timeZone != null)
468468
{
469469
azureFileShareProtectionPolicy.TimeZone = timeZone;
470470
}

0 commit comments

Comments
 (0)