300 lines
10 KiB
Python
300 lines
10 KiB
Python
# -*- coding: utf-8 -*- #
|
|
# Copyright 2024 Google LLC. All Rights Reserved.
|
|
#
|
|
# 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.
|
|
"""Cloud Backup Plans client."""
|
|
|
|
from __future__ import absolute_import
|
|
from __future__ import division
|
|
from __future__ import unicode_literals
|
|
|
|
import collections
|
|
|
|
from googlecloudsdk.api_lib.backupdr import util
|
|
from googlecloudsdk.calliope import exceptions
|
|
from googlecloudsdk.command_lib.backupdr import util as command_util
|
|
|
|
# TODO: b/416214401 - Add type annotations.
|
|
|
|
|
|
class BackupPlansClient(util.BackupDrClientBase):
|
|
"""Cloud Backup Plans client."""
|
|
|
|
def __init__(self):
|
|
super(BackupPlansClient, self).__init__()
|
|
self.service = self.client.projects_locations_backupPlans
|
|
|
|
def _ParseBackupRules(self, backup_rules):
|
|
backup_rules_message = []
|
|
for backup_rule in backup_rules:
|
|
standard_schedule = self.messages.StandardSchedule()
|
|
standard_schedule.timeZone = (
|
|
'UTC' if 'time-zone' not in backup_rule else backup_rule['time-zone']
|
|
)
|
|
standard_schedule.backupWindow = self.messages.BackupWindow(
|
|
startHourOfDay=backup_rule['backup-window-start'],
|
|
endHourOfDay=backup_rule['backup-window-end'],
|
|
)
|
|
standard_schedule.recurrenceType = (
|
|
self.messages.StandardSchedule.RecurrenceTypeValueValuesEnum(
|
|
backup_rule['recurrence']
|
|
)
|
|
)
|
|
if 'hourly-frequency' in backup_rule:
|
|
standard_schedule.hourlyFrequency = backup_rule['hourly-frequency']
|
|
if 'days-of-week' in backup_rule:
|
|
standard_schedule.daysOfWeek = [
|
|
self.messages.StandardSchedule.DaysOfWeekValueListEntryValuesEnum(
|
|
day
|
|
)
|
|
for day in backup_rule['days-of-week']
|
|
]
|
|
if 'week-day-of-month' in backup_rule:
|
|
week_day_of_month = backup_rule['week-day-of-month'].split('-')
|
|
standard_schedule.weekDayOfMonth = self.messages.WeekDayOfMonth(
|
|
weekOfMonth=self.messages.WeekDayOfMonth.WeekOfMonthValueValuesEnum(
|
|
week_day_of_month[0]
|
|
),
|
|
dayOfWeek=self.messages.WeekDayOfMonth.DayOfWeekValueValuesEnum(
|
|
week_day_of_month[1]
|
|
),
|
|
)
|
|
if 'days-of-month' in backup_rule:
|
|
standard_schedule.daysOfMonth = backup_rule['days-of-month']
|
|
if 'months' in backup_rule:
|
|
standard_schedule.months = [
|
|
self.messages.StandardSchedule.MonthsValueListEntryValuesEnum(month)
|
|
for month in backup_rule['months']
|
|
]
|
|
backup_rule_message = self.messages.BackupRule(
|
|
ruleId=backup_rule['rule-id'],
|
|
backupRetentionDays=backup_rule['retention-days'],
|
|
standardSchedule=standard_schedule,
|
|
)
|
|
backup_rules_message.append(backup_rule_message)
|
|
return backup_rules_message
|
|
|
|
def Create(
|
|
self,
|
|
resource,
|
|
backup_vault,
|
|
resource_type,
|
|
backup_rules,
|
|
log_retention_days,
|
|
description,
|
|
labels,
|
|
max_custom_on_demand_retention_days,
|
|
):
|
|
"""Creates a Backup Plan.
|
|
|
|
Args:
|
|
resource: The Backup Plan resource.
|
|
backup_vault: The Backup Vault resource.
|
|
resource_type: The resource type of the Backup Plan.
|
|
backup_rules: The backup rules of the Backup Plan.
|
|
log_retention_days: The log retention days of the Backup Plan.
|
|
description: The description of the Backup Plan.
|
|
labels: The labels of the Backup Plan.
|
|
max_custom_on_demand_retention_days: The custom on demand retention days
|
|
limit of the Backup Plan.
|
|
|
|
Returns:
|
|
The created Backup Plan.
|
|
"""
|
|
parent = resource.Parent().RelativeName()
|
|
backup_plan_id = resource.Name()
|
|
backup_plan = self.messages.BackupPlan(
|
|
resourceType=resource_type,
|
|
backupVault=backup_vault,
|
|
)
|
|
if description is not None:
|
|
backup_plan.description = description
|
|
if labels is not None:
|
|
backup_plan.labels = self.messages.BackupPlan.LabelsValue(
|
|
additionalProperties=[
|
|
self.messages.BackupPlan.LabelsValue.AdditionalProperty(
|
|
key=key, value=value
|
|
)
|
|
for key, value in labels.items()
|
|
]
|
|
)
|
|
backup_plan.backupRules = self._ParseBackupRules(backup_rules)
|
|
if log_retention_days is not None:
|
|
backup_plan.logRetentionDays = log_retention_days
|
|
if max_custom_on_demand_retention_days is not None:
|
|
backup_plan.maxCustomOnDemandRetentionDays = (
|
|
int(max_custom_on_demand_retention_days)
|
|
)
|
|
request = self.messages.BackupdrProjectsLocationsBackupPlansCreateRequest(
|
|
parent=parent,
|
|
backupPlan=backup_plan,
|
|
backupPlanId=backup_plan_id,
|
|
)
|
|
return self.service.Create(request)
|
|
|
|
def Describe(self, resource):
|
|
"""Describes a Backup Plan.
|
|
|
|
Args:
|
|
resource: The Backup Plan resource.
|
|
|
|
Returns:
|
|
The described Backup Plan.
|
|
"""
|
|
request = self.messages.BackupdrProjectsLocationsBackupPlansGetRequest(
|
|
name=resource.RelativeName()
|
|
)
|
|
return self.service.Get(request)
|
|
|
|
def ParseUpdate(
|
|
self,
|
|
description,
|
|
new_backup_rules_from_file,
|
|
update_backup_rules,
|
|
add_backup_rules,
|
|
remove_backup_rules,
|
|
current_backup_plan,
|
|
log_retention_days,
|
|
max_custom_on_demand_retention_days,
|
|
):
|
|
"""Parses the update request for a Backup Plan.
|
|
|
|
Args:
|
|
description: The description of the Backup Plan.
|
|
new_backup_rules_from_file: The backup rules to update from file in the
|
|
Backup Plan.
|
|
update_backup_rules: The backup rules to update in the Backup Plan.
|
|
add_backup_rules: The backup rules to add to the Backup Plan.
|
|
remove_backup_rules: The backup rules to remove from the Backup Plan.
|
|
current_backup_plan: The current Backup Plan.
|
|
log_retention_days: The log retention days of the Backup Plan.
|
|
max_custom_on_demand_retention_days: The custom on demand retention days
|
|
limit of the Backup Plan.
|
|
|
|
Returns:
|
|
The updated Backup Plan.
|
|
|
|
Raises:
|
|
InvalidArgumentException: If the backup rules are invalid.
|
|
ValueError: If the backup plan is not found.
|
|
"""
|
|
if current_backup_plan is None:
|
|
raise ValueError('Could not find the backup plan.')
|
|
updated_backup_plan = self.messages.BackupPlan(
|
|
resourceType=current_backup_plan.resourceType
|
|
)
|
|
if description is not None:
|
|
updated_backup_plan.description = description
|
|
if log_retention_days is not None:
|
|
updated_backup_plan.logRetentionDays = log_retention_days
|
|
if max_custom_on_demand_retention_days is not None:
|
|
updated_backup_plan.maxCustomOnDemandRetentionDays = (
|
|
int(max_custom_on_demand_retention_days)
|
|
)
|
|
current_rule_ids = {rule.ruleId for rule in current_backup_plan.backupRules}
|
|
if new_backup_rules_from_file is not None:
|
|
updated_backup_plan.backupRules = self._ParseBackupRules(
|
|
new_backup_rules_from_file
|
|
)
|
|
return updated_backup_plan
|
|
if update_backup_rules is not None:
|
|
rule_ids = collections.Counter(
|
|
[rule['rule-id'] for rule in update_backup_rules]
|
|
)
|
|
duplicate_rule_ids = [
|
|
rule_id for rule_id, count in rule_ids.items() if count > 1
|
|
]
|
|
if duplicate_rule_ids:
|
|
raise exceptions.InvalidArgumentException(
|
|
'rule-id',
|
|
f'Rules {duplicate_rule_ids} found in more than one'
|
|
' --backup-rule flag.',
|
|
)
|
|
not_found_rule_ids = list(set([
|
|
rule['rule-id']
|
|
for rule in update_backup_rules
|
|
if rule['rule-id'] not in current_rule_ids
|
|
]))
|
|
if not_found_rule_ids:
|
|
raise exceptions.InvalidArgumentException(
|
|
'rule-id',
|
|
f'Rules {not_found_rule_ids} not found in the backup plan.'
|
|
' The --backup-rule flag can only be used to modify existing'
|
|
' rules.',
|
|
)
|
|
update_rule_ids = [rule['rule-id'] for rule in update_backup_rules]
|
|
updated_backup_plan.backupRules = [
|
|
rule
|
|
for rule in current_backup_plan.backupRules
|
|
if rule.ruleId not in update_rule_ids
|
|
]
|
|
updated_backup_plan.backupRules.extend(
|
|
self._ParseBackupRules(update_backup_rules)
|
|
)
|
|
else:
|
|
updated_backup_plan.backupRules = current_backup_plan.backupRules
|
|
if add_backup_rules is not None:
|
|
updated_backup_plan.backupRules.extend(
|
|
self._ParseBackupRules(add_backup_rules)
|
|
)
|
|
if remove_backup_rules is not None:
|
|
not_found_rule_ids = list(set([
|
|
rule_id
|
|
for rule_id in remove_backup_rules
|
|
if rule_id not in current_rule_ids
|
|
]))
|
|
if not_found_rule_ids:
|
|
raise exceptions.InvalidArgumentException(
|
|
'rule-id',
|
|
f'Rules {not_found_rule_ids} not found in the backup plan.',
|
|
)
|
|
updated_backup_plan.backupRules = [
|
|
rule for rule in updated_backup_plan.backupRules
|
|
if rule.ruleId not in remove_backup_rules
|
|
]
|
|
return updated_backup_plan
|
|
|
|
def Update(self, resource, backup_plan, update_mask):
|
|
"""Updates a Backup Plan.
|
|
|
|
Args:
|
|
resource: The Backup Plan resource.
|
|
backup_plan: The updated Backup Plan.
|
|
update_mask: The update mask to edit the Backup Plan.
|
|
|
|
Returns:
|
|
The updated Backup Plan.
|
|
"""
|
|
request_id = command_util.GenerateRequestId()
|
|
request = self.messages.BackupdrProjectsLocationsBackupPlansPatchRequest(
|
|
backupPlan=backup_plan,
|
|
name=resource.RelativeName(),
|
|
requestId=request_id,
|
|
updateMask=update_mask,
|
|
)
|
|
return self.service.Patch(request)
|
|
|
|
def Delete(self, resource):
|
|
"""Deletes a Backup Plan.
|
|
|
|
Args:
|
|
resource: The Backup Plan resource.
|
|
|
|
Returns:
|
|
The deleted Backup Plan.
|
|
"""
|
|
request = self.messages.BackupdrProjectsLocationsBackupPlansDeleteRequest(
|
|
name=resource.RelativeName()
|
|
)
|
|
return self.service.Delete(request)
|