Module cvpysdk.plan
Main file for performing plan operations.
Plans,PlanTypes and Plan are the classes defined in this file.
Plans: Class for representing all the plans in the commcell
PlanTypes : Class for representing plan types
Plan: Class for representing a single plan of the commcell
Plans
__init__(commcell_object) -- initialise object of plans class of the commcell
__str__() -- returns all the plans associated with the commcell
__repr__() -- returns the string for the instance of the plans class
__len__() -- returns the number of plans added to the Commcell
__getitem__() -- returns the name of the plan for the given plan Id
or the details for the given plan name
_get_plans() -- gets all the plans associated with the commcell specified
_get_plan_template() -- gets the Plan subtype's JSON template
add() -- adds a new Plan to the CommCell
has_plan() -- checks if a plan exists with the given name or not
get() -- returns the instance of the Plans class
delete() -- deletes the plan from the commcell
refresh() -- refresh the plans associated with the commcell
add_data_classification_plan()- Adds data classification plan to the commcell
get_supported_solutions() -- returns the supported solutions for plans
Attributes
**all_plans** -- returns the dict consisting of plans and their details
Plan
====
__init__() -- initialise instance of the plan for the commcell
__repr__() -- return the plan name, the instance is associated with
_get_plan_id() -- method to get the plan id, if not specified in __init__
_get_plan_properties() -- get the properties of this plan
_update_plan_props() -- method to update plan properties
_get_associated_entities() -- method to get list of entities associated to a plan
derive_and_add() -- add new plan by deriving from the parent Plan object
plan_name -- returns the name of the plan
plan_id -- returns the ID of the plan
refresh() -- refresh the properties of the plan
associate_user() -- associates users to the plan
modify_schedule() -- modifies the RPO schedules of the plan
add_storage_copy() -- adds a storage pool as a copy to the plan
disable_full_schedule() -- disables the full schedule of a plan
share() -- shares plan with given user by associating given role
schedule() -- create/delete schedule on DC Plan
edit_plan() -- edit plan options
update_security_associations() -- to update security associations of a plan
Plan Attributes
**plan_id** -- returns the id of the plan
**plan_name** -- returns the name of the plan
**sla_in_minutes** -- returns the SLA/RPO of the plan
**plan_type** -- returns the type of the plan
**subtype** -- returns the subtype of the plan
**override_entities** -- returns the override restrictions of the plan
**storage_policy** -- returns the storage policy of the plan
**schedule_policies** -- returns the schedule policy of the plan
**subclient_policy** -- returns the subclient policy of the plan
**associated_entities** -- returns all the backup entities associated with the plan
**operation_window** -- returns the incremental operation window set by the plan
**full_operation_window** -- returns the full operation window set by the plan
**associated_entities** -- returns all the entities associated with the plan
**content_indexing_props** -- returns the DC plan related properties from the plan
**applicable_solutions** -- returns applicable solutions configured on server plan
Expand source code Browse git
# -*- coding: utf-8 -*-
# --------------------------------------------------------------------------
# Copyright Commvault Systems, Inc.
#
# 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.
# --------------------------------------------------------------------------
"""Main file for performing plan operations.
Plans,PlanTypes and Plan are the classes defined in this file.
Plans: Class for representing all the plans in the commcell
PlanTypes : Class for representing plan types
Plan: Class for representing a single plan of the commcell
Plans
=====
__init__(commcell_object) -- initialise object of plans class of the commcell
__str__() -- returns all the plans associated with the commcell
__repr__() -- returns the string for the instance of the plans class
__len__() -- returns the number of plans added to the Commcell
__getitem__() -- returns the name of the plan for the given plan Id
or the details for the given plan name
_get_plans() -- gets all the plans associated with the commcell specified
_get_plan_template() -- gets the Plan subtype's JSON template
add() -- adds a new Plan to the CommCell
has_plan() -- checks if a plan exists with the given name or not
get() -- returns the instance of the Plans class
delete() -- deletes the plan from the commcell
refresh() -- refresh the plans associated with the commcell
add_data_classification_plan()- Adds data classification plan to the commcell
get_supported_solutions() -- returns the supported solutions for plans
Attributes
----------
**all_plans** -- returns the dict consisting of plans and their details
Plan
====
__init__() -- initialise instance of the plan for the commcell
__repr__() -- return the plan name, the instance is associated with
_get_plan_id() -- method to get the plan id, if not specified in __init__
_get_plan_properties() -- get the properties of this plan
_update_plan_props() -- method to update plan properties
_get_associated_entities() -- method to get list of entities associated to a plan
derive_and_add() -- add new plan by deriving from the parent Plan object
plan_name -- returns the name of the plan
plan_id -- returns the ID of the plan
refresh() -- refresh the properties of the plan
associate_user() -- associates users to the plan
modify_schedule() -- modifies the RPO schedules of the plan
add_storage_copy() -- adds a storage pool as a copy to the plan
disable_full_schedule() -- disables the full schedule of a plan
share() -- shares plan with given user by associating given role
schedule() -- create/delete schedule on DC Plan
edit_plan() -- edit plan options
update_security_associations() -- to update security associations of a plan
Plan Attributes
----------------
**plan_id** -- returns the id of the plan
**plan_name** -- returns the name of the plan
**sla_in_minutes** -- returns the SLA/RPO of the plan
**plan_type** -- returns the type of the plan
**subtype** -- returns the subtype of the plan
**override_entities** -- returns the override restrictions of the plan
**storage_policy** -- returns the storage policy of the plan
**schedule_policies** -- returns the schedule policy of the plan
**subclient_policy** -- returns the subclient policy of the plan
**associated_entities** -- returns all the backup entities associated with the plan
**operation_window** -- returns the incremental operation window set by the plan
**full_operation_window** -- returns the full operation window set by the plan
**associated_entities** -- returns all the entities associated with the plan
**content_indexing_props** -- returns the DC plan related properties from the plan
**applicable_solutions** -- returns applicable solutions configured on server plan
"""
from __future__ import unicode_literals
import copy
from enum import Enum
from .exception import SDKException
from .security.security_association import SecurityAssociation
from .activateapps.constants import TargetApps, PlanConstants
from functools import reduce
class PlanTypes(Enum):
"""Class Enum to represent different plan types"""
Any = 0
DLO = 1
MSP = 2
FS = 3
SNAP = 4
VSA = 5
EXCHANGE = 6
DC = 7
EDISCOVERY = 8
ARCHIVER = 9
class Plans(object):
"""Class for representing all the plans in the commcell."""
def __init__(self, commcell_object):
"""Initialize object of Plans class.
Args:
commcell_object (object) -- instance of the Commcell class
Returns:
object - instance of Plans class
"""
self._commcell_object = commcell_object
self._cvpysdk_object = commcell_object._cvpysdk_object
self._services = commcell_object._services
self._update_response_ = commcell_object._update_response_
self._PLANS = self._services['PLANS']
self._plans = None
self.refresh()
def __str__(self):
"""Representation string consisting of all plans of the Commcell.
Returns:
str - string of all the plans for a commcell
"""
representation_string = "{:^5}\t{:^50}\n\n".format('S. No.', 'Plan')
for index, plan in enumerate(self._plans):
sub_str = '{:^5}\t{:30}\n'.format(index + 1, plan)
representation_string += sub_str
return representation_string.strip()
def __repr__(self):
"""Representation string for the instance of the Plans class."""
return "Plans class instance for Commcell: '{0}'".format(
self._commcell_object.commserv_name
)
def __len__(self):
"""Returns the number of the plans added to the Commcell."""
return len(self.all_plans)
def __getitem__(self, value):
"""Returns the name of the plan for the given plan ID or
the details of the plan for given plan Name.
Args:
value (str / int) -- Name or ID of the plan
Returns:
str - name of the plan, if the plan id was given
dict - dict of details of the plan, if plan name was given
Raises:
IndexError:
no plan exists with the given Name / Id
"""
value = str(value)
if value in self.all_plans:
return self.all_plans[value]
else:
try:
return list(filter(lambda x: x[1]['id'] == value, self.all_plans.items()))[0][0]
except IndexError:
raise IndexError('No plan exists with the given Name / Id')
def _get_plans(self):
"""Gets all the plans associated with the commcell
Returns:
dict - consists of all plans in the commcell
{
"plan1_name": plan1_id,
"plan2_name": plan2_id
}
Raises:
SDKException:
if response is empty
if response is not success
"""
flag, response = self._cvpysdk_object.make_request('GET', self._PLANS)
if flag:
plans = {}
if response.json() and 'plans' in response.json():
response_value = response.json()['plans']
for temp in response_value:
temp_name = temp['plan']['planName'].lower()
temp_id = str(temp['plan']['planId']).lower()
plans[temp_name] = temp_id
return plans
else:
response_string = self._update_response_(response.text)
raise SDKException('Response', '101', response_string)
def _get_plan_template(self, plan_sub_type, plan_type="MSP"):
"""Gets the Plan subtype's JSON template.
Args:
plan_sub_type (str) -- Sub-type of plan to add
"Server" - Server Plans
"FSServer" - File System Plans
"Laptop" - Laptop Plans
plan_type (str) -- Type of plan to add
default: "MSP"
Returns:
str - JSON string of the Plan's template
Raises:
SDKException:
if type or subtype of the plan does not exist
if there is a failure in getting the template
"""
if not (isinstance(plan_sub_type, str) and
isinstance(plan_type, str)):
raise SDKException('Plan', '101')
else:
template_url = self._services['GET_PLAN_TEMPLATE'] % (plan_type, plan_sub_type)
flag, response = self._cvpysdk_object.make_request('GET', template_url)
if flag:
if response.json() and 'plan' in response.json():
return response.json()
else:
raise SDKException('Plan', '102', 'Failed to get Plan template')
else:
response_string = self._update_response_(response.text)
raise SDKException('Response', '101', response_string)
@property
def all_plans(self):
"""Returns the dictionary consisting of all the plans added to the Commcell.
dict - consists of all the plans configured on the commcell
{
"plan1_name": plan1_id,
"plan2_name": plan2_id
}
"""
return self._plans
def filter_plans(self, plan_type, company_name = None):
"""
Returns the dictionary consisting of specified type and company plans.
Args:
plan_type (str) -- Type of plan ['DLO', 'Server', 'Laptop', 'Database', 'FSServer', 'FSIBMiVTL', 'Snap', 'VSAServer', 'VSAReplication',
'ExchangeUser', 'ExchangeJournal', 'Office365', 'Dynamics365', 'DataClassification', 'Archiver']
company_name (str) -- To filter plans based on company. For Commcell, company_name = "". Default will return all plans
Returns:
dict - consists of all the plans with specified types configured on the commcell
{
"plan1_name": plan1_id,
"plan2_name": plan2_id
}
Raises:
SDKException:
if input data type is not valid
if invalid plan type is passed as parameter
if failed to get the response
"""
plan_type_subtype = {
"dlo" : ("1", "16777223"),
"server" : ("2", "33554437"),
"laptop" : ("2", "33554439"),
"database" : ("2", "33579013"),
"fsserver" : ("3", "50331655"),
"fsibmivtl" : ("3", "50331653"),
"snap" : ("4", "67108869"),
"vsaserver" : ("5", "83886085"),
"vsareplication" : ("5", "83918853"),
"exchangeuser" : ("6", "100859907"),
"exchangejournal" : ("6", "100794372"),
"office365" : ("6", "100859937"),
"dynamics365" : ("6", "100794391"),
"dataclassification" : ("7", "117506053"),
"archiver" : ("9", "150994951")
}
if not isinstance(plan_type, str):
raise SDKException('Plan', '101')
elif plan_type.lower() not in plan_type_subtype:
raise SDKException('Plan', '102', 'Invalid Plan Type Passed as Parameter')
else:
template_url = self._services['GET_PLANS'] % plan_type_subtype[plan_type.lower()]
flag, response = self._cvpysdk_object.make_request('GET', template_url)
if flag:
result = dict()
if 'plans' in response.json():
for plan in response.json()['plans']:
if company_name is None:
result[plan['plan']['planName']] = plan['plan']['planId']
else:
if plan['plan']['entityInfo']['companyName'].lower() == company_name.lower():
result[plan['plan']['planName']] = plan['plan']['planId']
return result
else:
response_string = self._update_response_(response.text)
raise SDKException('Response', '101', response_string)
def has_plan(self, plan_name):
"""Checks if a plan exists in the commcell with the input plan name.
Args:
plan_name (str) -- name of the plan
Returns:
bool - boolean output whether the plan exists in the commcell or not
Raises:
SDKException:
if type of the plan name argument is not string
"""
if not isinstance(plan_name, str):
raise SDKException('Plan', '101')
return self._plans and plan_name.lower() in self._plans
def get(self, plan_name):
"""Returns a plan object of the specified plan name.
Args:
plan_name (str) -- name of the plan
Returns:
object - instance of the Plan class for the the given plan name
Raises:
SDKException:
if type of the plan name argument is not string
if no plan exists with the given name
"""
if not isinstance(plan_name, str):
raise SDKException('Plan', '101')
else:
plan_name = plan_name.lower()
if self.has_plan(plan_name):
return Plan(
self._commcell_object,
plan_name,
self._plans[plan_name]
)
raise SDKException(
'Plan', '102', 'No plan exists with name: {0}'.format(
plan_name)
)
def delete(self, plan_name):
"""Deletes the plan from the commcell.
Args:
plan_name (str) -- name of the plan to remove from the commcell
Raises:
SDKException:
if type of the plan name argument is not string
if failed to delete plan
if response is empty
if response is not success
if no plan exists with the given name
"""
if not isinstance(plan_name, str):
raise SDKException('Plan', '101')
else:
plan_name = plan_name.lower()
if self.has_plan(plan_name):
plan_id = self._plans[plan_name]
delete_plan = self._services['DELETE_PLAN'] % (plan_id)
flag, response = self._cvpysdk_object.make_request('DELETE', delete_plan)
error_code = 0
if flag:
if 'error' in response.json():
if isinstance(response.json()['error'], list):
error_code = response.json()['error'][0]['status']['errorCode']
else:
error_code = response.json()['errorCode']
if error_code != 0:
o_str = 'Failed to delete plan'
if isinstance(response.json()['error'], list):
error_message = response.json()['error'][0]['status']['errorMessage']
else:
error_message = response.json()['errorMessage']
o_str += '\nError: "{0}"'.format(error_message)
raise SDKException('Plan', '102', o_str)
else:
# initialize the plan again
# so the plan object has all the plan
self.refresh()
else:
response_string = self._update_response_(response.text)
raise SDKException('Response', '101', response_string)
else:
raise SDKException(
'Plan',
'102',
'No plan exists with name: {0}'.format(plan_name)
)
def add(self,
plan_name,
plan_sub_type,
storage_pool_name=None,
sla_in_minutes=1440,
override_entities=None):
"""Adds a new Plan to the CommCell.
Args:
plan_name (str) -- name of the new plan to add
plan_sub_type (str) -- Type of plan to add
"Server" - Server Plans
"FSServer" - File System Plans
"Laptop" - Laptop Plans
"ExchangeUser" - Exchange Mailbox Plan
storage_pool_name (str) -- name of the storage pool to be used for the plan
sla_in_minutes (int) -- Backup SLA in hours
default: 1440
override_entities (dict) -- Specify the entities with respective
inheritance values.
default: None
{
'privateEntities': [1, 4],
'enforcedEntities': [256, 512, 1024]
}
- where,
privateEntities are set when respective entity overriding is required
enforcedEntities are set when respective entity overriding is not
allowed
left blank if overriding is optional
- entity IDs,
1 - Storage
4 - RPO/Schedules
256 - Windows content
512 - Unix content
1024 - Mac content
Returns:
object - instance of the Plan class created by this method
Raises:
SDKException:
if input parameters are incorrect
if Plan already exists
"""
if not (isinstance(plan_name, str) and
isinstance(plan_sub_type, str)):
raise SDKException('Plan', '101')
else:
if self.has_plan(plan_name):
raise SDKException(
'Plan', '102', 'Plan "{0}" already exists'.format(plan_name)
)
if not plan_sub_type == 'ExchangeUser':
storage_pool_obj = self._commcell_object.storage_pools.get(
storage_pool_name)
is_dedupe = True
if 'dedupDBDetailsList' \
not in storage_pool_obj._storage_pool_properties['storagePoolDetails']:
is_dedupe = False
request_json = self._get_plan_template(plan_sub_type, "MSP")
request_json['plan']['summary']['rpoInMinutes'] = sla_in_minutes
request_json['plan']['summary']['description'] = "Created from CvPySDK."
request_json['plan']['summary']['plan']['planName'] = plan_name
template_schedules = [schedule['subTask']['subTaskName'] for schedule in request_json['plan']['schedule']['subTasks']]
if 'Synthetic Fulls' in template_schedules:
synth_full_index = template_schedules.index('Synthetic Fulls')
request_json['plan']['schedule']['subTasks'][synth_full_index]['options']['commonOpts'][
'automaticSchedulePattern'].update({
'minBackupInterval': 0,
'maxBackupIntervalMinutes': 0,
'minSyncInterval': 0,
'minSyncIntervalMinutes': 0
})
request_json['plan']['schedule']['subTasks'][synth_full_index]['options']['commonOpts'][
'automaticSchedulePattern']['ignoreOpWindowPastMaxInterval'] = True
del request_json['plan']['schedule']['task']['taskName']
if plan_sub_type != 'ExchangeUser':
request_json['plan']['storage']['copy'][0]['useGlobalPolicy'] = {
"storagePolicyId": int(storage_pool_obj.storage_pool_id)
}
if is_dedupe:
request_json['plan']['storage']['copy'][0]['dedupeFlags'][
'useGlobalDedupStore'] = 1
else:
del request_json['plan']['storage']['copy'][0]['storagePolicyFlags']
del request_json['plan']['storage']['copy'][0]['dedupeFlags'][
'enableDeduplication']
del request_json['plan']['storage']['copy'][0]['dedupeFlags'][
'enableClientSideDedup']
del request_json['plan']['storage']['copy'][0]['DDBPartitionInfo']
request_json['plan']['storage']['copy'][0]['extendedFlags'] = {
'useGlobalStoragePolicy': 1
}
# Configurations for database and snap addons
if plan_sub_type == "Server" and 'database' in request_json['plan']:
request_json['plan']['database']['storageLog']['copy'][0]['dedupeFlags'][
'useGlobalDedupStore'] = 1
request_json['plan']['database']['storageLog']['copy'][0].pop(
'DDBPartitionInfo', None
)
request_json['plan']['database']['storageLog']['copy'][0]['dedupeFlags'][
'useGlobalPolicy'] = {
"storagePolicyId": int(storage_pool_obj.storage_pool_id)
}
request_json['plan']['storage']['copy'][1]['extendedFlags'] = {
'useGlobalStoragePolicy': 1
}
request_json['plan']['storage']['copy'][1]['useGlobalPolicy'] = {
"storagePolicyId": int(storage_pool_obj.storage_pool_id)
}
# Enable full backup schedule
for subtask in request_json['plan']['schedule']['subTasks']:
if 'flags' in subtask['subTask'] and subtask['subTask']['flags'] == 65536:
import copy
full_schedule = copy.deepcopy(subtask)
del copy
full_schedule['subTask'].update({
'subTaskName': 'Full backup schedule',
'flags': 4194304
})
full_schedule['pattern'].update({
'freq_type': 4,
'freq_interval': 1,
'name': 'Full backup schedule',
'active_end_time': 0
})
full_schedule['options']['backupOpts']['backupLevel'] = 'FULL'
request_json['plan']['schedule']['subTasks'].append(full_schedule)
break
if isinstance(override_entities, dict):
request_json['plan']['summary']['restrictions'] = 0
request_json['plan']['inheritance'] = {
'isSealed': False
}
if 'enforcedEntities' in override_entities:
request_json['plan']['inheritance']['enforcedEntities'] = override_entities[
'enforcedEntities']
if 'privateEntities' in override_entities:
request_json['plan']['inheritance']['privateEntities'] = override_entities[
'privateEntities']
else:
request_json['plan']['summary']['restrictions'] = 1
request_json['plan']['inheritance'] = {
'isSealed': True
}
headers = self._commcell_object._headers.copy()
headers['LookupNames'] = 'False'
flag, response = self._cvpysdk_object.make_request(
'POST', self._PLANS, request_json, headers=headers
)
if flag:
if response.json():
response_value = response.json()
error_message = None
error_code = None
if 'errors' in response_value:
error_code = response_value['errors'][0]['status']['errorCode']
error_message = response_value['errors'][0]['status']['errorMessage']
if error_code > 1:
o_str = 'Failed to create new Plan\nError: "{0}"'.format(
error_message
)
raise SDKException('Plan', '102', o_str)
if 'plan' in response_value:
plan_name = response_value['plan']['summary']['plan']['planName']
# initialize the plans again
# so that the plans object has all the plans
self.refresh()
# with plan delete storage policy associated might be deleted
# initialize storage policy again
self._commcell_object.storage_policies.refresh()
return self.get(plan_name)
else:
o_str = ('Failed to create new plan due to error code: "{0}"\n'
'Please check the documentation for '
'more details on the error').format(error_code)
raise SDKException('Plan', '102', o_str)
else:
raise SDKException('Response', 102)
else:
response_string = self._update_response_(response.text)
raise SDKException('Response', '101', response_string)
def get_eligible_plans(self, entities):
"""Returns dict of plans that are eligible for the specified entities
Args:
entities (dict) - dictionary containing entities as keys and
their respective IDs as values
{
'clientId': id,
'appId': id,
'backupsetId': id
}
Returns:
dict - dict of eligible plans
Raises:
SDKException:
if there is an error in the response
"""
query = ''
for i in entities:
query += '{0}={1}&'.format(i, entities[i])
requset_url = self._services['ELIGIBLE_PLANS'] % query[0:-1]
flag, response = self._cvpysdk_object.make_request('GET', requset_url)
del query
if flag:
plans = {}
if response.json() and 'plans' in response.json():
response_value = response.json()['plans']
for temp in response_value:
temp_name = temp['plan']['planName'].lower()
temp_id = str(temp['plan']['planId']).lower()
plans[temp_name] = temp_id
return plans
else:
response_string = self._update_response_(response.text)
raise SDKException('Response', '101', response_string)
def get_supported_solutions(self):
"""Method to get supported solutions for plans"""
flag, response = self._cvpysdk_object.make_request(
'GET',
self._services['PLAN_SUPPORTED_SOLUTIONS']
)
if not flag:
raise SDKException('Response', '101', self._update_response_(response.text))
if response.json() and 'id' in response.json():
return {solution['name']: solution['id'] for solution in response.json()['id']}
else:
raise SDKException('Response', '102')
def refresh(self):
"""Refresh the plans associated with the Commcell."""
self._plans = self._get_plans()
def add_data_classification_plan(self, plan_name, index_server, target_app=TargetApps.FSO, **kwargs):
"""Adds data classification plan to the commcell
Args:
plan_name (str) -- Name of plan
index_server (str) -- Index server name
target_app (enum) -- Target app for this plan
cvpysdk.activateapps.constants.TargetApps
**kwargs
index_content (bool) -- Speifies whether to index content or not to index server
content_analyzer (list) -- list of Content analyzer client name
entity_list (list) -- list of entities which needs to be extracted
classifier_list (list) -- list of classifier which needs to be classified
enable_ocr (bool) -- specifies whether OCR is enabled or not
ocr_language (int) -- Language to be used when doing OCR
Default : English (Value-1)
Supported Languages:
ENGLISH = 1,
HEBREW = 2,
SPANISH = 3,
FRENCH = 4,
ITALIAN = 5,
DANISH = 6
include_docs (str) -- Include documents type separated by comma
exclude_path (list) -- List of paths which needs to be excluded
min_doc_size (int) -- Minimum document size in MB
max_doc_size (int) -- Maximum document size in MB
Returns:
object - Plan object
Raises:
SDKException:
if input is not valid
if failed to create plan
if failed to find entities/classifier details
"""
extraction_policy_list = []
if not (isinstance(plan_name, str) and
isinstance(index_server, str)):
raise SDKException('Plan', '101')
request_json = self._get_plan_template("DataClassification", "MSP")
request_json['plan']['summary']['description'] = "DC Plan Created from CvPySDK."
request_json['plan']['summary']['plan']['planName'] = plan_name
request_json['plan']['options'] = {
"enableThreatAnalysis": False,
"targetApps": [
target_app.value
]
}
index_server_client_id = self._commcell_object.index_servers.get(index_server).index_server_client_id
request_json['plan']['eDiscoveryInfo']['analyticsIndexServer'] = {
'clientId': index_server_client_id
}
if target_app.value == TargetApps.FSO.value:
del request_json['plan']['ciPolicy']['detail']['ciPolicy']['filters']
request_json['plan']['ciPolicy']['detail']['ciPolicy']['opType'] = PlanConstants.INDEXING_ONLY_METADATA
elif target_app.value == TargetApps.SDG.value:
if 'content_analyzer' not in kwargs:
raise SDKException('Plan', '103')
ca_list = []
for ca in kwargs.get('content_analyzer', []):
ca_client_id = self._commcell_object.content_analyzers.get(ca).client_id
ca_list.append({
'clientId': ca_client_id
})
request_json['plan']['eDiscoveryInfo']['contentAnalyzerClient'] = ca_list
if 'entity_list' not in kwargs and 'classifier_list' not in kwargs:
raise SDKException('Plan', '104')
activate_obj = self._commcell_object.activate
if 'entity_list' in kwargs or 'classifier_list' in kwargs:
entity_mgr_obj = activate_obj.entity_manager()
# classifier is also an activate entity with type alone different so append this to entity list itself
entity_list = []
for entity in kwargs.get('entity_list', []):
entity_list.append(entity)
for entity in kwargs.get('classifier_list', []):
entity_list.append(entity)
for entity in entity_list:
entity_obj = entity_mgr_obj.get(entity)
extraction_policy_list.append(entity_obj.container_details)
request_json['plan']['eePolicy']['policyType'] = 3
request_json['plan']['eePolicy']['flags'] = 8
request_json['plan']['eePolicy']['detail'] = {
"eePolicy": {
"copyPrecedence": 0,
"extractionPolicyType": 6, # container entities
"extractionPolicy": {
"extractionPolicyList": extraction_policy_list
}
}
}
if 'index_content' in kwargs:
request_json['plan']['ciPolicy']['detail']['ciPolicy']['opType'] = kwargs.get(
'index_content', PlanConstants.INDEXING_METADATA_AND_CONTENT)
if 'enable_ocr' in kwargs:
request_json['plan']['ciPolicy']['detail']['ciPolicy']['enableImageExtraction'] = kwargs.get(
'enable_ocr', False)
request_json['plan']['ciPolicy']['detail']['ciPolicy']['ocrLanguages'] = [kwargs.get('ocr_language', 1)]
if 'include_docs' in kwargs:
request_json['plan']['ciPolicy']['detail']['ciPolicy']['filters']['fileFilters']['includeDocTypes'] = kwargs.get(
'include_docs', PlanConstants.DEFAULT_INCLUDE_DOC_TYPES)
if 'min_doc_size' in kwargs:
request_json['plan']['ciPolicy']['detail']['ciPolicy']['filters']['fileFilters']['minDocSize'] = kwargs.get(
'min_doc_size', PlanConstants.DEFAULT_MIN_DOC_SIZE)
if 'max_doc_size' in kwargs:
request_json['plan']['ciPolicy']['detail']['ciPolicy']['filters']['fileFilters'][
'maxDocSize'] = kwargs.get('max_doc_size', PlanConstants.DEFAULT_MAX_DOC_SIZE)
if 'exclude_path' in kwargs:
request_json['plan']['ciPolicy']['detail']['ciPolicy']['filters']['fileFilters'][
'excludePaths'] = kwargs.get('exclude_path', PlanConstants.DEFAULT_EXCLUDE_LIST)
headers = self._commcell_object._headers.copy()
headers['LookupNames'] = 'False'
flag, response = self._cvpysdk_object.make_request(
'POST', self._PLANS, request_json, headers=headers
)
if flag:
if response.json():
response_value = response.json()
error_message = None
error_code = None
if 'errors' in response_value:
error_code = response_value['errors'][0]['status']['errorCode']
error_message = response_value['errors'][0]['status']['errorMessage']
if error_code > 1:
o_str = 'Failed to create new Plan\nError: "{0}"'.format(
error_message
)
raise SDKException('Plan', '102', o_str)
if 'plan' in response_value:
plan_name = response_value['plan']['summary']['plan']['planName']
# initialize the plans again
self.refresh()
return self.get(plan_name)
else:
o_str = ('Failed to create new plan due to error code: "{0}"\n'
'Please check the documentation for '
'more details on the error').format(error_code)
raise SDKException('Plan', '102', o_str)
else:
raise SDKException('Response', 102)
else:
response_string = self._update_response_(response.text)
raise SDKException('Response', '101', response_string)
class Plan(object):
"""Class for performing operations for a specific Plan."""
def __init__(self, commcell_object, plan_name, plan_id=None):
"""Initialize the Plan class instance.
Args:
commcell_object (object) -- instance of the Commcell class
plan_name (str) -- name of the plan
plan_id (str) -- id of the plan
default: None
Returns:
object - instance of the Plan class
"""
self._commcell_object = commcell_object
self._cvpysdk_object = commcell_object._cvpysdk_object
self._services = commcell_object._services
self._update_response_ = commcell_object._update_response_
self._plan_name = plan_name.lower()
self._plan_properties = None
if plan_id:
self._plan_id = str(plan_id)
else:
self._plan_id = self._get_plan_id()
self._PLAN = self._services['PLAN'] % (self.plan_id)
self._ADD_USERS_TO_PLAN = self._services['ADD_USERS_TO_PLAN'] % (self.plan_id)
self._API_SECURITY = self._services['SECURITY_ASSOCIATION']
self._API_SECURITY_ENTITY = self._services['ENTITY_SECURITY_ASSOCIATION']
self._properties = None
self._sla_in_minutes = None
self._operation_window = None
self._full_operation_window = None
self._plan_type = None
self._subtype = None
self._security_associations = {}
self._storage_pool = None
self._child_policies = {
'storagePolicy': None,
'schedulePolicy': {},
'subclientPolicyIds': []
}
self._storage_copies = {}
self._user_group = None
self._client_group = None
self._override_entities = None
self._parent_plan_name = None
self._addons = []
self._associated_entities = {}
self._dc_plan_props = {}
self._plan_entity_type = 158
self._region_id = []
self._applicable_solutions = []
self.refresh()
def __repr__(self):
"""String representation of the instance of this class."""
representation_string = 'Plan class instance for plan: "{0}", of Commcell: "{1}"'
return representation_string.format(
self._plan_name, self._commcell_object.commserv_name
)
def _get_plan_id(self):
"""Gets the plan id associated with this plan.
Returns:
str - id associated with this plan
"""
plans = Plans(self._commcell_object)
return plans.get(self.plan_name).plan_id
def _get_plan_properties(self):
"""Gets the plan properties of this plan.
Returns:
dict - dictionary consisting of the properties of this plan
Raises:
SDKException:
if response is empty
if response is not success
"""
plan_properties_url = '{0}?propertyLevel=30'.format(self._PLAN)
flag, response = self._cvpysdk_object.make_request('GET', plan_properties_url)
if flag:
if response.json() and 'plan' in response.json():
self._plan_properties = response.json()['plan']
if 'planName' in self._plan_properties['summary']['plan']:
self._plan_name = self._plan_properties['summary']['plan']['planName'].lower()
if 'slaInMinutes' in self._plan_properties['summary']:
self._sla_in_minutes = self._plan_properties['summary']['slaInMinutes']
if 'type' in self._plan_properties['summary']:
self._plan_type = self._plan_properties['summary']['type']
if 'subtype' in self._plan_properties['summary']:
self._subtype = self._plan_properties['summary']['subtype']
if 'storage' in self._plan_properties:
if 'copy' in self._plan_properties['storage']:
for copy in self._plan_properties['storage']['copy']:
if 'useGlobalPolicy' in copy:
storage_pool_name = copy['useGlobalPolicy']['storagePolicyName'].lower()
else:
storage_pool_name = copy['library']['libraryName'].lower()
self._storage_copies[copy['StoragePolicyCopy']['copyName']] = {
'storagePool': storage_pool_name,
'retainBackupDataForDays': copy[
'retentionRules']['retainBackupDataForDays'],
'isDefault': False,
'isSnapCopy': False,
}
if 'extendedRetentionRuleOne' in copy['retentionRules']:
self._storage_copies[
copy['StoragePolicyCopy']['copyName']]['extendedRetention'] = (
1,
True,
copy['retentionRules']['extendedRetentionRuleOne']['rule'],
copy['retentionRules']['extendedRetentionRuleOne']['endDays'],
copy['retentionRules']['extendedRetentionRuleOne']['graceDays']
)
if copy['isDefault'] == 1:
self._storage_copies[
copy['StoragePolicyCopy']['copyName']]['isDefault'] = True
if copy['isSnapCopy'] == 1:
self._storage_copies[
copy['StoragePolicyCopy']['copyName']]['isSnapCopy'] = True
if 'storagePolicy' in self._plan_properties['storage']:
self._commcell_object.storage_policies.refresh()
self._child_policies['storagePolicy'] = self._commcell_object.storage_policies.get(
self._plan_properties['storage']['storagePolicy']['storagePolicyName']
)
if self._subtype == 33554439:
if 'clientGroup' in self._plan_properties['autoCreatedEntities']:
self._commcell_object.client_groups.refresh()
self._client_group = self._commcell_object.client_groups.get(
self._plan_properties['autoCreatedEntities']['clientGroup'][
'clientGroupName']
)
if 'localUserGroup' in self._plan_properties['autoCreatedEntities']:
self._user_group = self._plan_properties['autoCreatedEntities'][
'localUserGroup']['userGroupName']
if 'schedule' in self._plan_properties:
if 'task' in self._plan_properties['schedule']:
self._commcell_object.schedule_policies.refresh()
self._child_policies['schedulePolicy'] = {
'data': self._commcell_object.policies.schedule_policies.get(
self._plan_properties['schedule']['task']['taskName']
)
}
# Skip adding database schedules if plan has no database schedule policy
if self._subtype == 33554437 and self._plan_properties.get('database', {}).get('rpoInMinutes'):
self._child_policies['schedulePolicy'].update({
'log': self._commcell_object.policies.schedule_policies.get(
self._plan_properties[
'database']['scheduleLog']['task']['taskName']
)
})
if self._plan_properties['operationWindow']['ruleId'] != 0:
self._operation_window = self._plan_properties['operationWindow']
else:
self._operation_window = None
if self._plan_properties['fullOperationWindow']['ruleId'] != 0:
self._full_operation_window = self._plan_properties['fullOperationWindow']
else:
self._full_operation_window = None
if 'laptop' in self._plan_properties:
if 'backupContent' in self._plan_properties['laptop']['content']:
self._child_policies['subclientPolicyIds'].clear()
for ida in self._plan_properties['laptop']['content']['backupContent']:
if ida['subClientPolicy'].get('backupSetEntity'):
self._child_policies['subclientPolicyIds'].append(
ida['subClientPolicy']['backupSetEntity']['backupsetId']
)
if ('inheritance' in self._plan_properties and
not self._plan_properties['inheritance']['isSealed']):
temp_dict = self._plan_properties['inheritance']
del temp_dict['isSealed']
if 'enforcedEntities' not in temp_dict:
temp_dict['enforcedEntities'] = []
if 'privateEntities' not in temp_dict:
temp_dict['privateEntities'] = []
self._override_entities = temp_dict
if 'parent' in self._plan_properties['summary']:
self._parent_plan_name = self._plan_properties['summary']['parent']['planName']
if 'eePolicy' in self._plan_properties:
extraction_policy = self._plan_properties['eePolicy']
if 'policyEntity' in extraction_policy:
self._dc_plan_props['eePolicyId'] = extraction_policy['policyEntity']['policyId']
if 'detail' in extraction_policy:
self._dc_plan_props['eePolicy'] = extraction_policy['detail']['eePolicy']
if 'ciPolicy' in self._plan_properties:
ci_policy = self._plan_properties['ciPolicy']
if 'policyEntity' in ci_policy:
self._dc_plan_props['ciPolicyId'] = ci_policy['policyEntity']['policyId']
if 'detail' in ci_policy:
self._dc_plan_props['ciPolicy'] = ci_policy['detail']['ciPolicy']
if 'eDiscoveryInfo' in self._plan_properties:
if 'analyticsIndexServer' in self._plan_properties['eDiscoveryInfo']:
self._dc_plan_props['analyticsIndexServer'] = self._plan_properties['eDiscoveryInfo']['analyticsIndexServer']
if 'options' in self._plan_properties:
plan_options = self._plan_properties['options']
if 'targetApps' in plan_options:
self._dc_plan_props['targetApps'] = plan_options['targetApps']
if 'supportedWorkloads' in plan_options:
self._applicable_solutions = [soln['solutionName'] for soln in plan_options['supportedWorkloads'].get('solutions', [])]
if 'securityAssociations' in self._plan_properties:
self._security_associations = {}
for association in self._plan_properties['securityAssociations'].get('associations', []):
temp_key = None
if 'externalGroupName' in association['userOrGroup'][0]:
temp_key = '{0}\\{1}'.format(
association['userOrGroup'][0]['providerDomainName'],
association['userOrGroup'][0]['externalGroupName']
)
elif 'userGroupName' in association['userOrGroup'][0]:
temp_key = association['userOrGroup'][0]['userGroupName']
else:
temp_key = association['userOrGroup'][0]['userName']
if 'role' in association['properties']:
if temp_key in self._security_associations:
self._security_associations[temp_key].append(
association['properties']['role']['roleName']
)
else:
self._security_associations[temp_key] = [association['properties']['role']['roleName']]
if "storageRules" in self._plan_properties:
self._region_id = [x["regions"]["region"][0]["regionId"] for x in self._plan_properties["storageRules"]["rules"]]
self._get_associated_entities()
return self._plan_properties
else:
raise SDKException('Response', '102')
else:
response_string = self._update_response_(response.text)
raise SDKException('Response', '101', response_string)
def derive_and_add(self,
plan_name,
storage_pool_name=None,
sla_in_minutes=None,
override_entities=None):
"""Derives the base plan based on the the inheritance properties to created a derived plan
Args:
plan_name (str) -- name of the new plan to add
storage_pool_name (str) -- name of the storage pool to be used for the plan
default: None : when the name is left to default, it inherits the base plan
storage pool if overriding is optional/not allowed
sla_in_minutes (int) -- Backup SLA in hours
default: None : when the SLA is left to default, it inherits the base plan
SLA if overriding is optional/not allowed
override_entities (dict) -- Specify the entities with respective overriding.
default: None
{
'privateEntities': [1, 4],
'enforcedEntities': [256, 512, 1024]
}
- where,
privateEntities are set when respective entity overriding is must
enforcedEntities are set when respective entity overriding is
not allowed
left blank if overriding is optional
- entity IDs,
1 - Storage
4 - SLA/Schedules
256 - Windows content
512 - Unix content
1024 - Mac content
Returns:
object - instance of the Plan class created by this method
Raises:
SDKException:
if plan name is in incorrect format
if plan already exists
if neccessary arguments are not passed
if inheritance rules are not followed
"""
if not isinstance(plan_name, str):
raise SDKException('Plan', '101', 'Plan name must be string value')
else:
if self._commcell_object.plans.has_plan(plan_name):
raise SDKException(
'Plan', '102', 'Plan "{0}" already exists'.format(
plan_name)
)
if self._override_entities is not None:
request_json = self._commcell_object.plans._get_plan_template(
str(self._subtype), "MSP")
request_json['plan']['summary']['description'] = "Created from CvPySDK."
request_json['plan']['summary']['plan']['planName'] = plan_name
request_json['plan']['summary']['parent'] = {
'planId': int(self._plan_id)
}
is_dedupe = True
if storage_pool_name is not None:
storage_pool_obj = self._commcell_object.storage_pools.get(
storage_pool_name
)
if 'dedupDBDetailsList' \
not in storage_pool_obj._storage_pool_properties['storagePoolDetails']:
is_dedupe = False
storage_pool_id = int(storage_pool_obj.storage_pool_id)
if is_dedupe:
request_json['plan']['storage']['copy'][0]['dedupeFlags'][
'useGlobalDedupStore'] = 1
else:
del request_json['plan']['storage']['copy'][0]['storagePolicyFlags']
del request_json['plan']['storage']['copy'][0]['dedupeFlags'][
'enableDeduplication']
del request_json['plan']['storage']['copy'][0]['dedupeFlags'][
'enableClientSideDedup']
del request_json['plan']['storage']['copy'][0]['DDBPartitionInfo']
request_json['plan']['storage']['copy'][0]['extendedFlags'] = {
'useGlobalStoragePolicy': 1
}
else:
storage_pool_id = None
if 1 in self._override_entities['enforcedEntities']:
if storage_pool_id is None:
request_json['plan']['storage'] = {
"storagePolicyId": self.storage_policy.storage_policy_id
}
snap_copy_id = self.storage_policy.storage_policy_id
else:
raise SDKException(
'Plan', '102', 'Storage is enforced by base plan, cannot be overridden')
elif 1 in self._override_entities['privateEntities']:
if storage_pool_id is not None:
request_json['plan']['storage']['copy'][0]['useGlobalPolicy'] = {
"storagePolicyId": storage_pool_id
}
snap_copy_id = storage_pool_id
else:
raise SDKException('Plan', '102', 'Storage must be input')
else:
if storage_pool_id is not None:
request_json['plan']['storage']['copy'][0]['useGlobalPolicy'] = {
"storagePolicyId": storage_pool_id
}
snap_copy_id = storage_pool_id
else:
request_json['plan']['storage'] = {
"storagePolicyId": self.storage_policy.storage_policy_id
}
snap_copy_id = self.storage_policy.storage_policy_id
if 4 in self._override_entities['enforcedEntities']:
if sla_in_minutes is None:
request_json['plan']['summary']['slaInMinutes'] = self._sla_in_minutes
else:
raise SDKException(
'Plan', '102', 'SLA is enforced by base plan, cannot be overridden')
elif 4 in self._override_entities['privateEntities']:
if sla_in_minutes is not None:
request_json['plan']['summary']['slaInMinutes'] = sla_in_minutes
else:
raise SDKException('Plan', '102', 'SLA must be input')
else:
if sla_in_minutes is not None:
request_json['plan']['summary']['slaInMinutes'] = sla_in_minutes
else:
request_json['plan']['summary']['slaInMinutes'] = self._sla_in_minutes
if isinstance(override_entities, dict):
request_json['plan']['summary']['restrictions'] = 0
request_json['plan']['inheritance'] = {
'isSealed': False
}
for entity in self._override_entities['enforcedEntities']:
from functools import reduce
if override_entities and entity in reduce(
lambda i, j: i + j, override_entities.values()):
raise SDKException(
'Plan', '102', 'Override not allowed')
if 'enforcedEntities' in override_entities:
request_json['plan']['inheritance']['enforcedEntities'] = (
override_entities['enforcedEntities']
)
if 'privateEntities' in override_entities:
request_json['plan']['inheritance']['privateEntities'] = (
override_entities['privateEntities']
)
else:
request_json['plan']['summary']['restrictions'] = 1
request_json['plan']['inheritance'] = {
'isSealed': True
}
if sla_in_minutes is not None:
request_json['plan']['definesSchedule'] = {
'definesEntity': True
}
else:
request_json['plan']['definesSchedule'] = {
'definesEntity': False
}
if isinstance(self._override_entities, dict):
if (4 not in
self._override_entities['enforcedEntities'] +
self._override_entities['privateEntities']):
request_json['plan']['definesSchedule']['overrideEntity'] = 0
elif 4 in self._override_entities['enforcedEntities']:
request_json['plan']['definesSchedule']['overrideEntity'] = 2
elif 4 in self._override_entities['privateEntities']:
request_json['plan']['definesSchedule']['overrideEntity'] = 1
if storage_pool_id is not None:
request_json['plan']['definesStorage'] = {
'definesEntity': True
}
else:
request_json['plan']['definesStorage'] = {
'definesEntity': False
}
if isinstance(self._override_entities, dict):
if (1 not in
self._override_entities['enforcedEntities'] +
self._override_entities['privateEntities']):
request_json['plan']['definesStorage']['overrideEntity'] = 0
elif 1 in self._override_entities['enforcedEntities']:
request_json['plan']['definesStorage']['overrideEntity'] = 2
elif 1 in self._override_entities['privateEntities']:
request_json['plan']['definesStorage']['overrideEntity'] = 1
if self._subtype != 33554437:
temp_defines_key = {
'definesEntity': False
}
if isinstance(self._override_entities, dict):
if (not all(entity in
self._override_entities['enforcedEntities'] +
self._override_entities['privateEntities']
for entity in [256, 512, 1024])):
temp_defines_key['overrideEntity'] = 0
elif all(entity in self._override_entities['enforcedEntities']
for entity in [256, 512, 1024]):
temp_defines_key['overrideEntity'] = 2
elif all(entity in self._override_entities['privateEntities']
for entity in [256, 512, 1024]):
temp_defines_key['overrideEntity'] = 1
request_json['plan']['laptop']['content']['definesSubclientLin'] = temp_defines_key
request_json['plan']['laptop']['content']['definesSubclientMac'] = temp_defines_key
request_json['plan']['laptop']['content']['definesSubclientWin'] = temp_defines_key
if self._subtype == 33554437 and 'snap' in self.addons and 'copy' \
in request_json['plan']['storage']:
request_json['plan']['storage']['copy'][1]['useGlobalPolicy'] = {
'storagePolicyId': snap_copy_id
}
request_json['plan']['storage']['copy'][1]['extendedFlags'] = {
'useGlobalStoragePolicy': 1
}
add_plan_service = self._commcell_object.plans._PLANS
headers = self._commcell_object._headers.copy()
headers['LookupNames'] = 'False'
flag, response = self._cvpysdk_object.make_request(
'POST', add_plan_service, request_json, headers=headers
)
if flag:
if response.json():
response_value = response.json()
error_message = None
error_code = None
if 'errors' in response_value:
error_code = response_value['errors'][0]['status']['errorCode']
error_message = response_value['errors'][0]['status']['errorMessage']
# error_codes 0 - OK, 1 - plan without storage, 84 - restricted plan
if error_code not in [0, 1, 84]:
o_str = 'Failed to create new Plan\nError: "{0}"'.format(
error_message
)
raise SDKException('Plan', '102', o_str)
if 'plan' in response_value:
plan_name = response_value['plan']['summary']['plan']['planName']
# initialize the plans again
# so that the plans object has all the plans
self._commcell_object.plans.refresh()
return self._commcell_object.plans.get(plan_name)
else:
o_str = ('Failed to create new plan due to error code: "{0}"\n'
'Please check the documentation for '
'more details on the error').format(error_code)
raise SDKException('Plan', '102', o_str)
else:
raise SDKException('Response', 102)
else:
response_string = self._update_response_(response.text)
raise SDKException('Response', '101', response_string)
else:
raise SDKException('Plan', '102', 'Inheritance disabled for plan')
def modify_schedule(self, schedule_json, is_full_schedule=False):
"""Modifies the incremental RPO schedule pattern of the plan with the given schedule json
Args:
schedule_json (dict) -- {
pattern : {}, -- Please refer SchedulePattern.create_schedule in schedules.py for the types of
pattern to be sent
eg: {
"freq_type": 'daily',
"active_start_time": time_in_%H/%S (str),
"repeat_days": days_to_repeat (int)
}
options: {} -- Please refer ScheduleOptions.py classes for respective schedule options
eg: {
"maxNumberOfStreams": 0,
"useMaximumStreams": True,
"useScallableResourceManagement": True,
"totalJobsToProcess": 1000,
"allCopies": True,
"mediaAgent": {
"mediaAgentName": "<ANY MEDIAAGENT>"
}
}
}
is_full_schedule (bool) -- Pass True if he schedule to be modified is the full backup schedule
"""
if is_full_schedule:
try:
schedule_id = list(filter(
lambda st: st['subTask']['flags'] == 4194304, self.schedule_policies['data']._subtasks
))[0]['subTask']['subTaskId']
except IndexError:
raise IndexError('Full backup schedule not enabled')
else:
schedule_id = list(filter(
lambda st: st['subTask']['flags'] == 65536, self.schedule_policies['data']._subtasks
))[0]['subTask']['subTaskId']
self.schedule_policies['data'].modify_schedule(
schedule_json,
schedule_id=schedule_id
)
self.refresh()
def add_storage_copy(self, copy_name, storage_pool, retention=30, extended_retention=None):
"""Add a storage copy as backup destination to this plan
Args:
copy_name (str) - name of the copy that is being added
storage_pool (str) - name of the storage pool for the copy to be added
retention (int) - retention period in days for the copy
extended_retention (tuple) - extended retention rules of a copy
Example: [1, True, "EXTENDED_ALLFULL", 0, 0]
Returns:
dict - dictionary of all copies of this plan
"""
if isinstance(copy_name, str) and isinstance(storage_pool, str):
if not self.storage_policy.has_copy(copy_name):
self.storage_policy.create_secondary_copy(
copy_name,
global_policy=storage_pool
)
self.storage_policy.get_copy(copy_name).copy_retention = (retention, 0, 0)
if extended_retention:
self.storage_policy.get_copy(
copy_name).extended_retention_rules = extended_retention
self.refresh()
return self.storage_copies
else:
err_msg = f'Storage Policy copy "{copy_name}" already exists.'
raise SDKException('Storage', '102', err_msg)
else:
raise SDKException(
'Plan', '102', 'Copy name and storage pool name must be a string.'
)
def disable_full_schedule(self):
"""Disable the full backup schedule of the plan"""
try:
self.schedule_policies['data'].delete_schedule(schedule_id=list(filter(
lambda st: st['subTask']['flags'] == 4194304, self.schedule_policies['data']._subtasks
))[0]['subTask']['subTaskId'])
except IndexError:
raise IndexError('Full backup schedule not enabled')
def edit_association(self, entities, new_plan=None):
"""Reassociates or dissociates the entities from this plan
Args:
entities (list) -- list containing entity objects whose plan association must be edited
Eg: [
{
"clientName": "client",
"subclientName": "subclient",
"backupsetName": "backupset",
"appName": "app"
}
]
new_plan (str) -- new plan to which the associated entities must be reassociated with
Raises:
SDKException
if plan not found
"""
req_json = {
'plan': {
'planName': self.plan_name
},
'entities': entities
}
if new_plan is not None:
if self._commcell_object.plans.has_plan(new_plan):
req_json.update({
'planOperationType': 'OVERWRITE',
'newPlan': {
'planName': new_plan
}
})
else:
SDKException('Plan', '102', 'No plan exists with name: {0}'.format(
new_plan)
)
else:
req_json.update({
'planOperationType': 'DELETE'
})
req_url = self._services['ASSOCIATED_ENTITIES'] % (self._plan_id)
flag, response = self._cvpysdk_object.make_request(
'PUT', req_url, req_json
)
if flag:
if 'response' in response.json():
error_code = str(response.json()["response"][0]["errorCode"])
if error_code == "0":
self.refresh()
return
else:
error_message = str(response.json()["errorMessage"])
o_str = 'Failed to edit plan associated entities\nError: "{0}"'
raise SDKException('Plan', '102', o_str.format(error_message))
else:
response_string = self._commcell_object._update_response_(response.text)
raise SDKException('Response', '101', response_string)
def _update_plan_props(self, props):
"""Updates the properties of the plan
Args:
props (dict) -- dictionary containing the properties to be updated
{
'planName': 'NewName'
}
Raises:
SDKException
if there is failure in updating the plan
"""
flag, response = self._cvpysdk_object.make_request(
'PUT', self._PLAN, props
)
if flag:
if response.json():
error_code = str(response.json()["errors"][0]["status"]["errorCode"])
error_message = str(response.json()["errors"][0]["status"]["errorMessage"])
if error_code == "0":
self.refresh()
return (True, error_code)
else:
return (False, error_code, error_message)
else:
raise SDKException('Response', '102')
else:
response_string = self._commcell_object._update_response_(response.text)
raise SDKException('Response', '101', response_string)
def _get_associated_entities(self):
"""Gets all the backup entities associated with the plan.
Returns:
dict - dictionary containing list of entities that are
associated with the plan.
Raises:
SDKException:
if response is empty
if response is not success
"""
request_url = self._services['ASSOCIATED_ENTITIES'] % (self._plan_id)
flag, response = self._cvpysdk_object.make_request(
'GET', request_url
)
if flag:
if response.json() and 'entities' in response.json():
self._associated_entities = response.json()['entities']
else:
response_string = self._commcell_object._update_response_(response.text)
raise SDKException('Response', '101', response_string)
@property
def plan_id(self):
"""Treats the plan id as a read-only attribute."""
return self._plan_id
@property
def plan_name(self):
"""Treats the plan name as a read-only attribute."""
return self._plan_name
@plan_name.setter
def plan_name(self, value):
"""modifies the plan name"""
if isinstance(value, str):
req_json = {
'summary': {
'plan': {
'planName': value
}
}
}
resp = self._update_plan_props(req_json)
if resp[0]:
return
else:
o_str = 'Failed to update the plan name\nError: "{0}"'
raise SDKException('Plan', '102', o_str.format(resp[2]))
else:
raise SDKException(
'Plan', '102', 'Plan name must be a string value'
)
@property
def sla_in_minutes(self):
"""Treats the plan SLA/RPO as a read-only attribute."""
return self._sla_in_minutes
@sla_in_minutes.setter
def sla_in_minutes(self, value):
"""Modifies the plan SLA/RPO"""
if isinstance(value, int):
req_json = {
'summary': {
'slaInMinutes': value
}
}
resp = self._update_plan_props(req_json)
if resp[0]:
return
else:
o_str = 'Failed to update the plan SLA\nError: "{0}"'
raise SDKException('Plan', '102', o_str.format(resp[2]))
else:
raise SDKException(
'Plan', '102', 'Plan SLA must be an int value'
)
@property
def operation_window(self):
"""Treats the plan incremental operation window as a read-only attribute"""
return self._operation_window
@operation_window.setter
def operation_window(self, value):
"""Modifies the incremental operation window of the plan
Args:
value (list) -- list of time slots for setting the backup window
value (None) -- set value to None to clear the operation window
Raises:
SDKException:
if the input is incorrect
if the operation window configuration fails
"""
if isinstance(value, list):
req_json = {
"operationWindow": {
"operations": [
2,
4
],
"dayTime": value
}
}
resp = self._update_plan_props(req_json)
if resp[0]:
self.refresh()
return
else:
o_str = 'Failed to update the operation window\nError: "{0}"'
raise SDKException('Plan', '102', o_str.format(resp[2]))
elif value is None:
self._commcell_object.operation_window.delete_operation_window(
rule_id=self._operation_window['ruleId']
)
self.refresh()
return
else:
raise SDKException(
'Plan', '102', 'Plan operation window must be a list value or None'
)
@property
def full_operation_window(self):
"""Treats the plan full backup operation window as a read-only attribute"""
return self._full_operation_window
@full_operation_window.setter
def full_operation_window(self, value):
"""Modifies the full backup operation window of the plan
Args:
value (list) -- list of time slots for setting the backup window
value (None) -- set value to None to clear the operation window
Raises:
SDKException:
if the input is incorrect
if the operation window configuration fails
"""
if isinstance(value, list):
req_json = {
"operationWindow": {
"operations": [
1,
],
"dayTime": value
}
}
resp = self._update_plan_props(req_json)
if resp[0]:
self.refresh()
return
else:
o_str = 'Failed to update the full operation window\nError: "{0}"'
raise SDKException('Plan', '102', o_str.format(resp[2]))
elif value is None:
self._commcell_object.operation_window.delete_operation_window(
rule_id=self._full_operation_window['ruleId']
)
self.refresh()
return
else:
raise SDKException(
'Plan', '102', 'Plan full operation window must be a list value or None'
)
@property
def plan_type(self):
"""Treats the plan type as a read-only attribute."""
return self._plan_type
@property
def subtype(self):
"""Treats the plan subtype as a read-only attribute."""
return self._subtype
@property
def override_entities(self):
"""Treats the plan override_entities as a read-only attribute."""
return self._override_entities
@override_entities.setter
def override_entities(self, value):
"""Sets the override restrictions for the plan"""
req_json = {
"inheritance": {
"isSealed": False,
"enforcedEntitiesOperationType": 1,
"privateEntitiesOperationType": 1
}
}
if isinstance(value, dict):
req_json['inheritance'].update(value)
resp = self._update_plan_props(req_json)
if resp[0]:
return
else:
o_str = 'Failed to update the plan override restrictions\nError: "{0}"'
raise SDKException('Plan', '102', o_str.format(resp[2]))
else:
raise SDKException(
'Plan', '102', 'Override restrictions must be defined in a dict'
)
@property
def storage_policy(self):
"""Treats the plan storage policy as a read-only attribute"""
return self._child_policies['storagePolicy']
@property
def storage_copies(self):
"""Treats the plan storage policy as a read-only attribute"""
return self._storage_copies
@property
def schedule_policies(self):
"""Treats the plan schedule policies as read-only attribute"""
return self._child_policies['schedulePolicy']
@property
def addons(self):
"""Treats the plan addons as read-only attribute"""
for addon in self._plan_properties.get('summary', {}).get('addons', []):
self._addons.append(
addon
)
return self._addons
@property
def subclient_policy(self):
"""Treats the plan subclient policy as a read-only attribute"""
return self._child_policies['subclientPolicyIds']
@property
def associated_entities(self):
"""getter for the backup entities associated with the plan"""
return self._associated_entities
@property
def parent_plan(self):
"""getter for the parent plan of a derived plan"""
return self._commcell_object.plans.get(self._parent_plan_name)
@property
def security_associations(self):
"""getter for the plan's security associations
Eg:
{
'sample_user_group_name': 'role_name'
}
"""
return self._security_associations
def update_security_associations(self, associations_list, is_user = True, request_type = None, external_group = False):
"""
Adds the security association on the plan object
Args:
associations_list (list) -- list of users to be associated
Example:
associations_list = [
{
'user_name': user1,
'role_name': role1
},
{
'user_name': user2,
'role_name': role2
}
]
is_user (bool) -- True or False. set is_user = False, If associations_list made up of user groups
request_type (str) -- eg : 'OVERWRITE' or 'UPDATE' or 'DELETE', Default will be OVERWRITE operation
external_group (bool) -- True or False, set external_group = True. If Security associations is being done on External User Groups
Raises:
SDKException:
if association is not of List type
"""
if not isinstance(associations_list, list):
raise SDKException('Plan', '102')
SecurityAssociation(self._commcell_object, self)._add_security_association(associations_list,
is_user, request_type, external_group)
@property
def content_indexing_props(self):
"""returns the DC plan related CI properties from Plan"""
return self._dc_plan_props
@property
def properties(self):
"""Returns the configured properties for the Plan"""
return self._plan_properties
@property
def region_id(self):
"""Returns the Backup destination region id"""
return self._region_id
def refresh(self):
"""Refresh the properties of the Plan."""
self._properties = self._get_plan_properties()
def associate_user(self, userlist):
"""associates the users to the plan.
# TODO: Need to handle user groups.
Arguments:
userlist(list) - list of users to be associated to the plans.
Raises:
SDKException:
if response is empty
if response is not success
"""
users_list = []
for user in userlist:
if self._commcell_object.users.has_user(user):
temp = self._commcell_object.users.get(user)
temp_dict = {
'sendInvite': True,
'user': {
'userName': temp.user_name,
'userId': int(temp.user_id)
}
}
users_list.append(temp_dict)
request_json = {
"userOperationType": 1,
"users": users_list
}
flag, response = self._cvpysdk_object.make_request(
'PUT', self._ADD_USERS_TO_PLAN, request_json
)
if flag:
if response.json() and 'errors' in response.json():
for error in response.json()["errors"]:
error_code = error["status"]["errorCode"]
if error_code == 0:
pass
else:
o_str = 'Failed to add users with error code: "{0}"'
raise SDKException('Plan', '102', o_str.format(error_code))
else:
raise SDKException('Response', '102')
else:
response_string = self._update_response_(response.text)
raise SDKException('Response', '101', response_string)
def share(self, user_or_group_name, role_name, is_user=True, ops_type=1):
"""Shares plan with given user or group by associating with given role
Args:
user_or_group_name (str) -- User or Group name to which we are sharing
role_name (str) -- Role name which needs to associated with
ops_type (int) -- Operation type
Default : 1 (Add)
Supported : 1 (Add)
3 (Delete)
Returns:
None
Raises:
SDKException:
if input is not valid
if failed to do sharing
if user/group/role not exists on commcell
if failed to get exisitng association details
"""
if not isinstance(user_or_group_name, str) or not isinstance(role_name, str):
raise SDKException('Plan', '101')
if ops_type not in [1, 3]:
raise SDKException('Plan', '102', "Sharing operation type provided is not supported")
if is_user:
if not self._commcell_object.users.has_user(user_or_group_name):
raise SDKException('Plan', '102', "User doesn't exists in the commcell")
if not self._commcell_object.roles.has_role(role_name=role_name):
raise SDKException('Plan', '102', "Role doesn't exists in the commcell")
request_json = copy.deepcopy(PlanConstants.PLAN_SHARE_REQUEST_JSON)
association_response = None
if ops_type == 1 and len(self.security_associations) > 1:
association_request_json = copy.deepcopy(PlanConstants.PLAN_SHARE_REQUEST_JSON)
del association_request_json['securityAssociations']
association_request_json['entityAssociated']['entity'][0]['entityId'] = int(self._plan_id)
flag, response = self._cvpysdk_object.make_request(
'GET', self._API_SECURITY_ENTITY %
(self._plan_entity_type, int(
self._plan_id)), association_request_json)
if flag:
if response.json() and 'securityAssociations' in response.json():
association_response = response.json(
)['securityAssociations'][0]['securityAssociations']['associations']
else:
raise SDKException('Plan', '102', 'Failed to get existing security associations')
else:
response_string = self._commcell_object._update_response_(response.text)
raise SDKException('Response', '101', response_string)
external_user = False
if '\\' in user_or_group_name:
external_user = True
if is_user:
user_obj = self._commcell_object.users.get(user_or_group_name)
user_id = user_obj.user_id
request_json['securityAssociations']['associations'][0]['userOrGroup'][0]['userId'] = int(user_id)
request_json['securityAssociations']['associations'][0]['userOrGroup'][0]['_type_'] = 13
request_json['securityAssociations']['associations'][0]['userOrGroup'][0]['userName'] = user_or_group_name
elif external_user:
request_json['securityAssociations']['associations'][0]['userOrGroup'][0]['groupId'] = 0
request_json['securityAssociations']['associations'][0]['userOrGroup'][0]['_type_'] = 62
request_json['securityAssociations']['associations'][0]['userOrGroup'][0][
'externalGroupName'] = user_or_group_name
else:
grp_obj = self._commcell_object.user_groups.get(user_or_group_name)
grp_id = grp_obj.user_group_id
request_json['securityAssociations']['associations'][0]['userOrGroup'][0]['userGroupId'] = int(grp_id)
request_json['securityAssociations']['associations'][0]['userOrGroup'][0]['_type_'] = 15
request_json['securityAssociations']['associations'][0]['userOrGroup'][0][
'userGroupName'] = user_or_group_name
request_json['entityAssociated']['entity'][0]['entityId'] = int(self._plan_id)
request_json['securityAssociations']['associationsOperationType'] = ops_type
role_obj = self._commcell_object.roles.get(role_name)
request_json['securityAssociations']['associations'][0]['properties']['role']['roleId'] = role_obj.role_id
request_json['securityAssociations']['associations'][0]['properties']['role']['roleName'] = role_obj.role_name
# Associate existing associations to the request
if ops_type == 1 and len(self.security_associations) > 1:
request_json['securityAssociations']['associations'].extend(association_response)
flag, response = self._cvpysdk_object.make_request(
'POST', self._API_SECURITY, request_json
)
if flag:
if response.json() and 'response' in response.json():
response_json = response.json()['response'][0]
error_code = response_json['errorCode']
if error_code != 0:
error_message = response_json['errorString']
raise SDKException(
'Plan',
'102', error_message)
self.refresh()
else:
raise SDKException('Plan', '105')
else:
response_string = self._commcell_object._update_response_(response.text)
raise SDKException('Response', '101', response_string)
def schedule(self, schedule_name, pattern_json, ops_type=2):
"""Creates or modifies the schedule associated with plan
Args:
schedule_name (str) -- Schedule name
pattern_json (dict) -- Schedule pattern dict (Refer to Create_schedule_pattern in schedule.py)
ops_type (int) -- Operation type
Default : 2 (Add)
Supported : 2 (Add/Modify)
Raises:
SDKException:
if input is not valid
if failed to create/modify schedule
if plan is not of type Data classification plan
"""
if not isinstance(schedule_name, str) or not isinstance(pattern_json, dict):
raise SDKException('Plan', '101')
if self.plan_type not in [PlanTypes.DC.value]:
raise SDKException('Plan', '102', "Add/Modify Schedule is supported only for DC Plan via CvpySDK")
if ops_type not in [2]:
raise SDKException('Plan', '102', "Schedule operation type provided is not supported")
request_json = copy.deepcopy(PlanConstants.PLAN_SCHEDULE_REQUEST_JSON[self.plan_type])
request_json['summary']['plan']['planId'] = int(self.plan_id)
request_json['schedule']['associations'][0]['entityId'] = int(self.plan_id)
request_json['schedule']['task']['taskName'] = f"Cvpysdk created Schedule policy for plan - {self.plan_name}"
request_json['schedule']['subTasks'][0]['subTask'][
'subTaskName'] = schedule_name
request_json['schedule']['subTasks'][0]['pattern'] = pattern_json
request_json['schedule']['subTasks'][0]['options']['adminOpts']['contentIndexingOption']['operationType'] = ops_type
if self._dc_plan_props['targetApps'][0] == TargetApps.FS.value:
request_json['schedule']['subTasks'][0]['subTask']['operationType'] = 5022
self._update_plan_props(request_json)
def edit_plan(self, **kwargs):
"""Edit plan options
Args:
**kwargs for Data Classification Plan
index_content (bool) -- Speifies whether to index content or not to index server
content_analyzer (list) -- list of Content analyzer client name
entity_list (list) -- list of entities which needs to be extracted
classifier_list (list) -- list of classifier which needs to be classified
enable_ocr (bool) -- specifies whether OCR is enabled or not
ocr_language (int) -- Language to be used when doing OCR
Default : English (Value-1)
Supported Languages:
ENGLISH = 1,
HEBREW = 2,
SPANISH = 3,
FRENCH = 4,
ITALIAN = 5,
DANISH = 6
include_docs (str) -- Include documents type separated by comma
exclude_path (list) -- List of paths which needs to be excluded
min_doc_size (int) -- Minimum document size in MB
max_doc_size (int) -- Maximum document size in MB
"""
if self.plan_type != PlanTypes.DC.value:
raise SDKException('Plan', '102', "Function Not supported for this plan type")
extraction_policy_list = []
request_json = None
if self.plan_type == PlanTypes.DC.value:
request_json = copy.deepcopy(PlanConstants.PLAN_UPDATE_REQUEST_JSON[self.plan_type])
request_json['summary']['plan']['planId'] = int(self.plan_id)
if TargetApps.SDG.value in self.content_indexing_props['targetApps']:
request_json['eePolicyInfo']['eePolicy']['detail']['eePolicy'] = self.content_indexing_props['eePolicy']
request_json['ciPolicyInfo']['ciPolicy']['detail']['ciPolicy'] = self.content_indexing_props['ciPolicy']
activate_obj = self._commcell_object.activate
if 'content_analyzer' in kwargs:
ca_list = []
for ca in kwargs.get('content_analyzer', []):
ca_client_id = self._commcell_object.content_analyzers.get(ca).client_id
ca_list.append({
'clientId': ca_client_id
})
request_json['eDiscoveryInfo'] = {
'contentAnalyzerClient': ca_list}
if 'entity_list' in kwargs or 'classifier_list' in kwargs:
entity_mgr_obj = activate_obj.entity_manager()
# classifier is also an activate entity with type alone different so
# append this to entity list itself
entity_list = []
for entity in kwargs.get('entity_list', []):
entity_list.append(entity)
for entity in kwargs.get('classifier_list', []):
entity_list.append(entity)
for entity in entity_list:
entity_obj = entity_mgr_obj.get(entity)
extraction_policy_list.append(entity_obj.container_details)
request_json['eePolicyInfo']['eePolicy']['detail']['eePolicy']['extractionPolicy']['extractionPolicyList'] = extraction_policy_list
if 'enable_ocr' in kwargs:
request_json['ciPolicyInfo']['ciPolicy']['detail']['ciPolicy']['enableImageExtraction'] = kwargs.get(
'enable_ocr', False)
request_json['ciPolicyInfo']['ciPolicy']['detail']['ciPolicy']['ocrLanguages'] = [
kwargs.get('ocr_language', 1)]
if 'include_docs' in kwargs:
request_json['ciPolicyInfo']['ciPolicy']['detail']['ciPolicy']['filters']['fileFilters']['includeDocTypes'] = kwargs.get(
'include_docs', PlanConstants.DEFAULT_INCLUDE_DOC_TYPES)
if 'min_doc_size' in kwargs:
request_json['ciPolicyInfo']['ciPolicy']['detail']['ciPolicy']['filters']['fileFilters']['minDocSize'] = kwargs.get(
'min_doc_size', PlanConstants.DEFAULT_MIN_DOC_SIZE)
if 'max_doc_size' in kwargs:
request_json['ciPolicyInfo']['ciPolicy']['detail']['ciPolicy']['filters']['fileFilters'][
'maxDocSize'] = kwargs.get('max_doc_size', PlanConstants.DEFAULT_MAX_DOC_SIZE)
if 'exclude_path' in kwargs:
request_json['ciPolicyInfo']['ciPolicy']['detail']['ciPolicy']['filters']['fileFilters'][
'excludePaths'] = kwargs.get('exclude_path', PlanConstants.DEFAULT_EXCLUDE_LIST)
if 'index_content' in kwargs:
request_json['ciPolicyInfo']['ciPolicy']['detail']['ciPolicy']['opType'] = kwargs.get(
'index_content', PlanConstants.INDEXING_METADATA_AND_CONTENT)
elif TargetApps.FSO.value in self.content_indexing_props['targetApps']:
# currently we dont have any thing to update in DC plan for FSO app so throw exception
raise SDKException('Plan', '102', 'No attributes to Edit for DC Plan with TargetApps as : FSO')
self._update_plan_props(request_json)
def policy_subclient_ids(self):
"""Returns Policy subclient IDs of the plan
Returns:
dict : OS and its associated subclient ID
example:
{
'Windows' : windows_subclient_policy_subclient_id,
'Linux' : linux_subclient_policy_subclient_id,
'Mac' : mac_subclient_policy_subclient_id
}
"""
result = dict()
for backupset_id in self.subclient_policy:
url = self._commcell_object._services['ADD_SUBCLIENT'] + '?clientId=2&applicationId=1030&backupsetid=' + str(backupset_id)
flag, response = self._commcell_object._cvpysdk_object.make_request('GET', url)
if flag:
if response.json() and 'subClientProperties' in response.json():
subclient_id = response.json()['subClientProperties'][0]['subClientEntity']['subclientId']
backupset_name = response.json()['subClientProperties'][0]['subClientEntity']['backupsetName']
os = backupset_name.split()[-3]
result[os] = subclient_id
else:
raise SDKException('Plan', 102, 'Failed to get subclient Ids.')
else:
raise SDKException('Plan', 102, response.text)
return result
def update_content_policy(self, content):
"""
Args:
content (dict) : dictionary with backup content details.
example:
content = {
"windowsIncludedPaths": ["Desktop"],
"windowsExcludedPaths": ["Music"],
"windowsFilterToExcludePaths": ["Videos"],
"unixIncludedPaths": ["Desktop"],
"unixExcludedPaths": ["Music"],
"unixFilterToExcludePaths": ["Videos"],
"macIncludedPaths": ["Desktop"],
"macExcludedPaths": ["Music"],
"macFilterToExcludePaths": ["Videos"],
"backupSystemState": True,
"useVSSForSystemState": True,
"backupSystemStateOnlyWithFullBackup": False
}
For unix and mac, replace key name with respective os name, **IncludedPaths, **ExcludedPaths, **FilterToExcludePaths
"""
request_json = {
'backupContent' : content
}
request_url = self._commcell_object._services['V4_SERVER_PLAN'] % self.plan_id
flag, response = self._commcell_object._cvpysdk_object.make_request('PUT', request_url, request_json)
if flag:
if response.json():
if response.json()['errorCode']:
raise SDKException('Plan', 102, response.json()['errorMessage'])
else:
raise SDKException('Plan', 102, 'Failed to update backup content')
else:
raise SDKException('Plan', 102, response.text)
def update_backup_content(self, content, request_type = 'OVERWRITE'):
"""
Args:
content (dict) : dictionary with backup content details.
example:
content = {
'Windows' : {
'Content' : ['\\%Pictures%', '\\%Desktop%'],
'Exclude' : ['\\%Documents%'],
'Backup System State' : True
},
'Linux' : {
'Content' : ['/%Pictures%'],
'Exclude' : ['/%Documents%']
},
'Mac' : {
'Content' : ['/%Pictures%'],
'Exclude' : ['/%Documents%']
}
}
request_type (str) : Supported values 'OVERWRITE' (default), 'UPDATE', 'DELETE'.
For plans created from SP32, Please use below format of content
example:
content = {
"windowsIncludedPaths": ["Desktop"],
"windowsExcludedPaths": ["Music"],
"windowsFilterToExcludePaths": ["Videos"],
"backupSystemState": True,
"useVSSForSystemState": True,
"backupSystemStateOnlyWithFullBackup": False
}
For unix and mac, replace key name with respective os name, **IncludedPaths, **ExcludedPaths, **FilterToExcludePaths
"""
update_request_type = {
"OVERWRITE": 1,
"UPDATE": 2,
"DELETE": 3
}
subclients = self.policy_subclient_ids()
if not subclients:
self.update_content_policy(content)
return
for os, value in content.items():
request_json = {
"subClientProperties": {
"fsExcludeFilterOperationType": update_request_type.get(request_type, 1),
"fsContentOperationType" : update_request_type.get(request_type, 1)
}
}
request_url = self._commcell_object._services['SUBCLIENT'] % subclients[os]
contents = list()
for key, val in value.items():
if key.lower() == 'content':
for path in val: contents.append({"path" : path})
if key.lower() == 'exclude':
for path in val: contents.append({"excludePath" : path})
if os == 'Windows' and key == 'Backup System State':
request_json['subClientProperties']['fsSubClientProp'] = {'backupSystemState' : val}
if contents:
request_json['subClientProperties']['content'] = contents
flag, response = self._commcell_object._cvpysdk_object.make_request('POST', request_url, request_json)
if flag:
if response.json() and 'response' in response.json():
errorCode = response.json()['response'][0].get('errorCode')
if errorCode:
raise SDKException('Plan', 102, 'Failed to Change Content of Plan.')
else:
raise SDKException('Plan', 102, 'Failed to get subclient Ids.')
else:
raise SDKException('Plan', 102, response.text)
@property
def applicable_solutions(self):
"""Method to read applicable solutions"""
return self._applicable_solutions
@applicable_solutions.setter
def applicable_solutions(self, solutions: list = list()):
"""Method to update applicable solutions of plan
Args:
solutions (list) : List of Applicable Solutions
example:
["File Servers", "Databases"] : FS and DB will be set as a applicable solutions
[] : Passing empty list will reset applicable solutions to ALL
"""
request_url = self._commcell_object._services['APPLICABLE_SOLNS_ENABLE' if solutions else 'APPLICABLE_SOLNS_DISABLE'] % self.plan_id
if solutions:
supported_solutions = self._commcell_object.plans.get_supported_solutions()
request_json = {"solutions": [{"id": supported_solutions[soln_name], "name": soln_name} for soln_name in solutions]}
else:
request_json = None
flag, response = self._commcell_object._cvpysdk_object.make_request('PUT', request_url, request_json)
if not flag:
raise SDKException('Response', '101', self._update_response_(response.text))
if not response.json() or response.json()['errorCode']:
raise SDKException('Plan', 102, 'Failed to update Applicable Solutions for Plan')
self.refresh()
Classes
class Plan (commcell_object, plan_name, plan_id=None)
-
Class for performing operations for a specific Plan.
Initialize the Plan class instance.
Args
commcell_object (object) – instance of the Commcell class
plan_name (str) – name of the plan
plan_id (str) – id of the plan default: None
Returns
object - instance of the Plan class
Expand source code Browse git
class Plan(object): """Class for performing operations for a specific Plan.""" def __init__(self, commcell_object, plan_name, plan_id=None): """Initialize the Plan class instance. Args: commcell_object (object) -- instance of the Commcell class plan_name (str) -- name of the plan plan_id (str) -- id of the plan default: None Returns: object - instance of the Plan class """ self._commcell_object = commcell_object self._cvpysdk_object = commcell_object._cvpysdk_object self._services = commcell_object._services self._update_response_ = commcell_object._update_response_ self._plan_name = plan_name.lower() self._plan_properties = None if plan_id: self._plan_id = str(plan_id) else: self._plan_id = self._get_plan_id() self._PLAN = self._services['PLAN'] % (self.plan_id) self._ADD_USERS_TO_PLAN = self._services['ADD_USERS_TO_PLAN'] % (self.plan_id) self._API_SECURITY = self._services['SECURITY_ASSOCIATION'] self._API_SECURITY_ENTITY = self._services['ENTITY_SECURITY_ASSOCIATION'] self._properties = None self._sla_in_minutes = None self._operation_window = None self._full_operation_window = None self._plan_type = None self._subtype = None self._security_associations = {} self._storage_pool = None self._child_policies = { 'storagePolicy': None, 'schedulePolicy': {}, 'subclientPolicyIds': [] } self._storage_copies = {} self._user_group = None self._client_group = None self._override_entities = None self._parent_plan_name = None self._addons = [] self._associated_entities = {} self._dc_plan_props = {} self._plan_entity_type = 158 self._region_id = [] self._applicable_solutions = [] self.refresh() def __repr__(self): """String representation of the instance of this class.""" representation_string = 'Plan class instance for plan: "{0}", of Commcell: "{1}"' return representation_string.format( self._plan_name, self._commcell_object.commserv_name ) def _get_plan_id(self): """Gets the plan id associated with this plan. Returns: str - id associated with this plan """ plans = Plans(self._commcell_object) return plans.get(self.plan_name).plan_id def _get_plan_properties(self): """Gets the plan properties of this plan. Returns: dict - dictionary consisting of the properties of this plan Raises: SDKException: if response is empty if response is not success """ plan_properties_url = '{0}?propertyLevel=30'.format(self._PLAN) flag, response = self._cvpysdk_object.make_request('GET', plan_properties_url) if flag: if response.json() and 'plan' in response.json(): self._plan_properties = response.json()['plan'] if 'planName' in self._plan_properties['summary']['plan']: self._plan_name = self._plan_properties['summary']['plan']['planName'].lower() if 'slaInMinutes' in self._plan_properties['summary']: self._sla_in_minutes = self._plan_properties['summary']['slaInMinutes'] if 'type' in self._plan_properties['summary']: self._plan_type = self._plan_properties['summary']['type'] if 'subtype' in self._plan_properties['summary']: self._subtype = self._plan_properties['summary']['subtype'] if 'storage' in self._plan_properties: if 'copy' in self._plan_properties['storage']: for copy in self._plan_properties['storage']['copy']: if 'useGlobalPolicy' in copy: storage_pool_name = copy['useGlobalPolicy']['storagePolicyName'].lower() else: storage_pool_name = copy['library']['libraryName'].lower() self._storage_copies[copy['StoragePolicyCopy']['copyName']] = { 'storagePool': storage_pool_name, 'retainBackupDataForDays': copy[ 'retentionRules']['retainBackupDataForDays'], 'isDefault': False, 'isSnapCopy': False, } if 'extendedRetentionRuleOne' in copy['retentionRules']: self._storage_copies[ copy['StoragePolicyCopy']['copyName']]['extendedRetention'] = ( 1, True, copy['retentionRules']['extendedRetentionRuleOne']['rule'], copy['retentionRules']['extendedRetentionRuleOne']['endDays'], copy['retentionRules']['extendedRetentionRuleOne']['graceDays'] ) if copy['isDefault'] == 1: self._storage_copies[ copy['StoragePolicyCopy']['copyName']]['isDefault'] = True if copy['isSnapCopy'] == 1: self._storage_copies[ copy['StoragePolicyCopy']['copyName']]['isSnapCopy'] = True if 'storagePolicy' in self._plan_properties['storage']: self._commcell_object.storage_policies.refresh() self._child_policies['storagePolicy'] = self._commcell_object.storage_policies.get( self._plan_properties['storage']['storagePolicy']['storagePolicyName'] ) if self._subtype == 33554439: if 'clientGroup' in self._plan_properties['autoCreatedEntities']: self._commcell_object.client_groups.refresh() self._client_group = self._commcell_object.client_groups.get( self._plan_properties['autoCreatedEntities']['clientGroup'][ 'clientGroupName'] ) if 'localUserGroup' in self._plan_properties['autoCreatedEntities']: self._user_group = self._plan_properties['autoCreatedEntities'][ 'localUserGroup']['userGroupName'] if 'schedule' in self._plan_properties: if 'task' in self._plan_properties['schedule']: self._commcell_object.schedule_policies.refresh() self._child_policies['schedulePolicy'] = { 'data': self._commcell_object.policies.schedule_policies.get( self._plan_properties['schedule']['task']['taskName'] ) } # Skip adding database schedules if plan has no database schedule policy if self._subtype == 33554437 and self._plan_properties.get('database', {}).get('rpoInMinutes'): self._child_policies['schedulePolicy'].update({ 'log': self._commcell_object.policies.schedule_policies.get( self._plan_properties[ 'database']['scheduleLog']['task']['taskName'] ) }) if self._plan_properties['operationWindow']['ruleId'] != 0: self._operation_window = self._plan_properties['operationWindow'] else: self._operation_window = None if self._plan_properties['fullOperationWindow']['ruleId'] != 0: self._full_operation_window = self._plan_properties['fullOperationWindow'] else: self._full_operation_window = None if 'laptop' in self._plan_properties: if 'backupContent' in self._plan_properties['laptop']['content']: self._child_policies['subclientPolicyIds'].clear() for ida in self._plan_properties['laptop']['content']['backupContent']: if ida['subClientPolicy'].get('backupSetEntity'): self._child_policies['subclientPolicyIds'].append( ida['subClientPolicy']['backupSetEntity']['backupsetId'] ) if ('inheritance' in self._plan_properties and not self._plan_properties['inheritance']['isSealed']): temp_dict = self._plan_properties['inheritance'] del temp_dict['isSealed'] if 'enforcedEntities' not in temp_dict: temp_dict['enforcedEntities'] = [] if 'privateEntities' not in temp_dict: temp_dict['privateEntities'] = [] self._override_entities = temp_dict if 'parent' in self._plan_properties['summary']: self._parent_plan_name = self._plan_properties['summary']['parent']['planName'] if 'eePolicy' in self._plan_properties: extraction_policy = self._plan_properties['eePolicy'] if 'policyEntity' in extraction_policy: self._dc_plan_props['eePolicyId'] = extraction_policy['policyEntity']['policyId'] if 'detail' in extraction_policy: self._dc_plan_props['eePolicy'] = extraction_policy['detail']['eePolicy'] if 'ciPolicy' in self._plan_properties: ci_policy = self._plan_properties['ciPolicy'] if 'policyEntity' in ci_policy: self._dc_plan_props['ciPolicyId'] = ci_policy['policyEntity']['policyId'] if 'detail' in ci_policy: self._dc_plan_props['ciPolicy'] = ci_policy['detail']['ciPolicy'] if 'eDiscoveryInfo' in self._plan_properties: if 'analyticsIndexServer' in self._plan_properties['eDiscoveryInfo']: self._dc_plan_props['analyticsIndexServer'] = self._plan_properties['eDiscoveryInfo']['analyticsIndexServer'] if 'options' in self._plan_properties: plan_options = self._plan_properties['options'] if 'targetApps' in plan_options: self._dc_plan_props['targetApps'] = plan_options['targetApps'] if 'supportedWorkloads' in plan_options: self._applicable_solutions = [soln['solutionName'] for soln in plan_options['supportedWorkloads'].get('solutions', [])] if 'securityAssociations' in self._plan_properties: self._security_associations = {} for association in self._plan_properties['securityAssociations'].get('associations', []): temp_key = None if 'externalGroupName' in association['userOrGroup'][0]: temp_key = '{0}\\{1}'.format( association['userOrGroup'][0]['providerDomainName'], association['userOrGroup'][0]['externalGroupName'] ) elif 'userGroupName' in association['userOrGroup'][0]: temp_key = association['userOrGroup'][0]['userGroupName'] else: temp_key = association['userOrGroup'][0]['userName'] if 'role' in association['properties']: if temp_key in self._security_associations: self._security_associations[temp_key].append( association['properties']['role']['roleName'] ) else: self._security_associations[temp_key] = [association['properties']['role']['roleName']] if "storageRules" in self._plan_properties: self._region_id = [x["regions"]["region"][0]["regionId"] for x in self._plan_properties["storageRules"]["rules"]] self._get_associated_entities() return self._plan_properties else: raise SDKException('Response', '102') else: response_string = self._update_response_(response.text) raise SDKException('Response', '101', response_string) def derive_and_add(self, plan_name, storage_pool_name=None, sla_in_minutes=None, override_entities=None): """Derives the base plan based on the the inheritance properties to created a derived plan Args: plan_name (str) -- name of the new plan to add storage_pool_name (str) -- name of the storage pool to be used for the plan default: None : when the name is left to default, it inherits the base plan storage pool if overriding is optional/not allowed sla_in_minutes (int) -- Backup SLA in hours default: None : when the SLA is left to default, it inherits the base plan SLA if overriding is optional/not allowed override_entities (dict) -- Specify the entities with respective overriding. default: None { 'privateEntities': [1, 4], 'enforcedEntities': [256, 512, 1024] } - where, privateEntities are set when respective entity overriding is must enforcedEntities are set when respective entity overriding is not allowed left blank if overriding is optional - entity IDs, 1 - Storage 4 - SLA/Schedules 256 - Windows content 512 - Unix content 1024 - Mac content Returns: object - instance of the Plan class created by this method Raises: SDKException: if plan name is in incorrect format if plan already exists if neccessary arguments are not passed if inheritance rules are not followed """ if not isinstance(plan_name, str): raise SDKException('Plan', '101', 'Plan name must be string value') else: if self._commcell_object.plans.has_plan(plan_name): raise SDKException( 'Plan', '102', 'Plan "{0}" already exists'.format( plan_name) ) if self._override_entities is not None: request_json = self._commcell_object.plans._get_plan_template( str(self._subtype), "MSP") request_json['plan']['summary']['description'] = "Created from CvPySDK." request_json['plan']['summary']['plan']['planName'] = plan_name request_json['plan']['summary']['parent'] = { 'planId': int(self._plan_id) } is_dedupe = True if storage_pool_name is not None: storage_pool_obj = self._commcell_object.storage_pools.get( storage_pool_name ) if 'dedupDBDetailsList' \ not in storage_pool_obj._storage_pool_properties['storagePoolDetails']: is_dedupe = False storage_pool_id = int(storage_pool_obj.storage_pool_id) if is_dedupe: request_json['plan']['storage']['copy'][0]['dedupeFlags'][ 'useGlobalDedupStore'] = 1 else: del request_json['plan']['storage']['copy'][0]['storagePolicyFlags'] del request_json['plan']['storage']['copy'][0]['dedupeFlags'][ 'enableDeduplication'] del request_json['plan']['storage']['copy'][0]['dedupeFlags'][ 'enableClientSideDedup'] del request_json['plan']['storage']['copy'][0]['DDBPartitionInfo'] request_json['plan']['storage']['copy'][0]['extendedFlags'] = { 'useGlobalStoragePolicy': 1 } else: storage_pool_id = None if 1 in self._override_entities['enforcedEntities']: if storage_pool_id is None: request_json['plan']['storage'] = { "storagePolicyId": self.storage_policy.storage_policy_id } snap_copy_id = self.storage_policy.storage_policy_id else: raise SDKException( 'Plan', '102', 'Storage is enforced by base plan, cannot be overridden') elif 1 in self._override_entities['privateEntities']: if storage_pool_id is not None: request_json['plan']['storage']['copy'][0]['useGlobalPolicy'] = { "storagePolicyId": storage_pool_id } snap_copy_id = storage_pool_id else: raise SDKException('Plan', '102', 'Storage must be input') else: if storage_pool_id is not None: request_json['plan']['storage']['copy'][0]['useGlobalPolicy'] = { "storagePolicyId": storage_pool_id } snap_copy_id = storage_pool_id else: request_json['plan']['storage'] = { "storagePolicyId": self.storage_policy.storage_policy_id } snap_copy_id = self.storage_policy.storage_policy_id if 4 in self._override_entities['enforcedEntities']: if sla_in_minutes is None: request_json['plan']['summary']['slaInMinutes'] = self._sla_in_minutes else: raise SDKException( 'Plan', '102', 'SLA is enforced by base plan, cannot be overridden') elif 4 in self._override_entities['privateEntities']: if sla_in_minutes is not None: request_json['plan']['summary']['slaInMinutes'] = sla_in_minutes else: raise SDKException('Plan', '102', 'SLA must be input') else: if sla_in_minutes is not None: request_json['plan']['summary']['slaInMinutes'] = sla_in_minutes else: request_json['plan']['summary']['slaInMinutes'] = self._sla_in_minutes if isinstance(override_entities, dict): request_json['plan']['summary']['restrictions'] = 0 request_json['plan']['inheritance'] = { 'isSealed': False } for entity in self._override_entities['enforcedEntities']: from functools import reduce if override_entities and entity in reduce( lambda i, j: i + j, override_entities.values()): raise SDKException( 'Plan', '102', 'Override not allowed') if 'enforcedEntities' in override_entities: request_json['plan']['inheritance']['enforcedEntities'] = ( override_entities['enforcedEntities'] ) if 'privateEntities' in override_entities: request_json['plan']['inheritance']['privateEntities'] = ( override_entities['privateEntities'] ) else: request_json['plan']['summary']['restrictions'] = 1 request_json['plan']['inheritance'] = { 'isSealed': True } if sla_in_minutes is not None: request_json['plan']['definesSchedule'] = { 'definesEntity': True } else: request_json['plan']['definesSchedule'] = { 'definesEntity': False } if isinstance(self._override_entities, dict): if (4 not in self._override_entities['enforcedEntities'] + self._override_entities['privateEntities']): request_json['plan']['definesSchedule']['overrideEntity'] = 0 elif 4 in self._override_entities['enforcedEntities']: request_json['plan']['definesSchedule']['overrideEntity'] = 2 elif 4 in self._override_entities['privateEntities']: request_json['plan']['definesSchedule']['overrideEntity'] = 1 if storage_pool_id is not None: request_json['plan']['definesStorage'] = { 'definesEntity': True } else: request_json['plan']['definesStorage'] = { 'definesEntity': False } if isinstance(self._override_entities, dict): if (1 not in self._override_entities['enforcedEntities'] + self._override_entities['privateEntities']): request_json['plan']['definesStorage']['overrideEntity'] = 0 elif 1 in self._override_entities['enforcedEntities']: request_json['plan']['definesStorage']['overrideEntity'] = 2 elif 1 in self._override_entities['privateEntities']: request_json['plan']['definesStorage']['overrideEntity'] = 1 if self._subtype != 33554437: temp_defines_key = { 'definesEntity': False } if isinstance(self._override_entities, dict): if (not all(entity in self._override_entities['enforcedEntities'] + self._override_entities['privateEntities'] for entity in [256, 512, 1024])): temp_defines_key['overrideEntity'] = 0 elif all(entity in self._override_entities['enforcedEntities'] for entity in [256, 512, 1024]): temp_defines_key['overrideEntity'] = 2 elif all(entity in self._override_entities['privateEntities'] for entity in [256, 512, 1024]): temp_defines_key['overrideEntity'] = 1 request_json['plan']['laptop']['content']['definesSubclientLin'] = temp_defines_key request_json['plan']['laptop']['content']['definesSubclientMac'] = temp_defines_key request_json['plan']['laptop']['content']['definesSubclientWin'] = temp_defines_key if self._subtype == 33554437 and 'snap' in self.addons and 'copy' \ in request_json['plan']['storage']: request_json['plan']['storage']['copy'][1]['useGlobalPolicy'] = { 'storagePolicyId': snap_copy_id } request_json['plan']['storage']['copy'][1]['extendedFlags'] = { 'useGlobalStoragePolicy': 1 } add_plan_service = self._commcell_object.plans._PLANS headers = self._commcell_object._headers.copy() headers['LookupNames'] = 'False' flag, response = self._cvpysdk_object.make_request( 'POST', add_plan_service, request_json, headers=headers ) if flag: if response.json(): response_value = response.json() error_message = None error_code = None if 'errors' in response_value: error_code = response_value['errors'][0]['status']['errorCode'] error_message = response_value['errors'][0]['status']['errorMessage'] # error_codes 0 - OK, 1 - plan without storage, 84 - restricted plan if error_code not in [0, 1, 84]: o_str = 'Failed to create new Plan\nError: "{0}"'.format( error_message ) raise SDKException('Plan', '102', o_str) if 'plan' in response_value: plan_name = response_value['plan']['summary']['plan']['planName'] # initialize the plans again # so that the plans object has all the plans self._commcell_object.plans.refresh() return self._commcell_object.plans.get(plan_name) else: o_str = ('Failed to create new plan due to error code: "{0}"\n' 'Please check the documentation for ' 'more details on the error').format(error_code) raise SDKException('Plan', '102', o_str) else: raise SDKException('Response', 102) else: response_string = self._update_response_(response.text) raise SDKException('Response', '101', response_string) else: raise SDKException('Plan', '102', 'Inheritance disabled for plan') def modify_schedule(self, schedule_json, is_full_schedule=False): """Modifies the incremental RPO schedule pattern of the plan with the given schedule json Args: schedule_json (dict) -- { pattern : {}, -- Please refer SchedulePattern.create_schedule in schedules.py for the types of pattern to be sent eg: { "freq_type": 'daily', "active_start_time": time_in_%H/%S (str), "repeat_days": days_to_repeat (int) } options: {} -- Please refer ScheduleOptions.py classes for respective schedule options eg: { "maxNumberOfStreams": 0, "useMaximumStreams": True, "useScallableResourceManagement": True, "totalJobsToProcess": 1000, "allCopies": True, "mediaAgent": { "mediaAgentName": "<ANY MEDIAAGENT>" } } } is_full_schedule (bool) -- Pass True if he schedule to be modified is the full backup schedule """ if is_full_schedule: try: schedule_id = list(filter( lambda st: st['subTask']['flags'] == 4194304, self.schedule_policies['data']._subtasks ))[0]['subTask']['subTaskId'] except IndexError: raise IndexError('Full backup schedule not enabled') else: schedule_id = list(filter( lambda st: st['subTask']['flags'] == 65536, self.schedule_policies['data']._subtasks ))[0]['subTask']['subTaskId'] self.schedule_policies['data'].modify_schedule( schedule_json, schedule_id=schedule_id ) self.refresh() def add_storage_copy(self, copy_name, storage_pool, retention=30, extended_retention=None): """Add a storage copy as backup destination to this plan Args: copy_name (str) - name of the copy that is being added storage_pool (str) - name of the storage pool for the copy to be added retention (int) - retention period in days for the copy extended_retention (tuple) - extended retention rules of a copy Example: [1, True, "EXTENDED_ALLFULL", 0, 0] Returns: dict - dictionary of all copies of this plan """ if isinstance(copy_name, str) and isinstance(storage_pool, str): if not self.storage_policy.has_copy(copy_name): self.storage_policy.create_secondary_copy( copy_name, global_policy=storage_pool ) self.storage_policy.get_copy(copy_name).copy_retention = (retention, 0, 0) if extended_retention: self.storage_policy.get_copy( copy_name).extended_retention_rules = extended_retention self.refresh() return self.storage_copies else: err_msg = f'Storage Policy copy "{copy_name}" already exists.' raise SDKException('Storage', '102', err_msg) else: raise SDKException( 'Plan', '102', 'Copy name and storage pool name must be a string.' ) def disable_full_schedule(self): """Disable the full backup schedule of the plan""" try: self.schedule_policies['data'].delete_schedule(schedule_id=list(filter( lambda st: st['subTask']['flags'] == 4194304, self.schedule_policies['data']._subtasks ))[0]['subTask']['subTaskId']) except IndexError: raise IndexError('Full backup schedule not enabled') def edit_association(self, entities, new_plan=None): """Reassociates or dissociates the entities from this plan Args: entities (list) -- list containing entity objects whose plan association must be edited Eg: [ { "clientName": "client", "subclientName": "subclient", "backupsetName": "backupset", "appName": "app" } ] new_plan (str) -- new plan to which the associated entities must be reassociated with Raises: SDKException if plan not found """ req_json = { 'plan': { 'planName': self.plan_name }, 'entities': entities } if new_plan is not None: if self._commcell_object.plans.has_plan(new_plan): req_json.update({ 'planOperationType': 'OVERWRITE', 'newPlan': { 'planName': new_plan } }) else: SDKException('Plan', '102', 'No plan exists with name: {0}'.format( new_plan) ) else: req_json.update({ 'planOperationType': 'DELETE' }) req_url = self._services['ASSOCIATED_ENTITIES'] % (self._plan_id) flag, response = self._cvpysdk_object.make_request( 'PUT', req_url, req_json ) if flag: if 'response' in response.json(): error_code = str(response.json()["response"][0]["errorCode"]) if error_code == "0": self.refresh() return else: error_message = str(response.json()["errorMessage"]) o_str = 'Failed to edit plan associated entities\nError: "{0}"' raise SDKException('Plan', '102', o_str.format(error_message)) else: response_string = self._commcell_object._update_response_(response.text) raise SDKException('Response', '101', response_string) def _update_plan_props(self, props): """Updates the properties of the plan Args: props (dict) -- dictionary containing the properties to be updated { 'planName': 'NewName' } Raises: SDKException if there is failure in updating the plan """ flag, response = self._cvpysdk_object.make_request( 'PUT', self._PLAN, props ) if flag: if response.json(): error_code = str(response.json()["errors"][0]["status"]["errorCode"]) error_message = str(response.json()["errors"][0]["status"]["errorMessage"]) if error_code == "0": self.refresh() return (True, error_code) else: return (False, error_code, error_message) else: raise SDKException('Response', '102') else: response_string = self._commcell_object._update_response_(response.text) raise SDKException('Response', '101', response_string) def _get_associated_entities(self): """Gets all the backup entities associated with the plan. Returns: dict - dictionary containing list of entities that are associated with the plan. Raises: SDKException: if response is empty if response is not success """ request_url = self._services['ASSOCIATED_ENTITIES'] % (self._plan_id) flag, response = self._cvpysdk_object.make_request( 'GET', request_url ) if flag: if response.json() and 'entities' in response.json(): self._associated_entities = response.json()['entities'] else: response_string = self._commcell_object._update_response_(response.text) raise SDKException('Response', '101', response_string) @property def plan_id(self): """Treats the plan id as a read-only attribute.""" return self._plan_id @property def plan_name(self): """Treats the plan name as a read-only attribute.""" return self._plan_name @plan_name.setter def plan_name(self, value): """modifies the plan name""" if isinstance(value, str): req_json = { 'summary': { 'plan': { 'planName': value } } } resp = self._update_plan_props(req_json) if resp[0]: return else: o_str = 'Failed to update the plan name\nError: "{0}"' raise SDKException('Plan', '102', o_str.format(resp[2])) else: raise SDKException( 'Plan', '102', 'Plan name must be a string value' ) @property def sla_in_minutes(self): """Treats the plan SLA/RPO as a read-only attribute.""" return self._sla_in_minutes @sla_in_minutes.setter def sla_in_minutes(self, value): """Modifies the plan SLA/RPO""" if isinstance(value, int): req_json = { 'summary': { 'slaInMinutes': value } } resp = self._update_plan_props(req_json) if resp[0]: return else: o_str = 'Failed to update the plan SLA\nError: "{0}"' raise SDKException('Plan', '102', o_str.format(resp[2])) else: raise SDKException( 'Plan', '102', 'Plan SLA must be an int value' ) @property def operation_window(self): """Treats the plan incremental operation window as a read-only attribute""" return self._operation_window @operation_window.setter def operation_window(self, value): """Modifies the incremental operation window of the plan Args: value (list) -- list of time slots for setting the backup window value (None) -- set value to None to clear the operation window Raises: SDKException: if the input is incorrect if the operation window configuration fails """ if isinstance(value, list): req_json = { "operationWindow": { "operations": [ 2, 4 ], "dayTime": value } } resp = self._update_plan_props(req_json) if resp[0]: self.refresh() return else: o_str = 'Failed to update the operation window\nError: "{0}"' raise SDKException('Plan', '102', o_str.format(resp[2])) elif value is None: self._commcell_object.operation_window.delete_operation_window( rule_id=self._operation_window['ruleId'] ) self.refresh() return else: raise SDKException( 'Plan', '102', 'Plan operation window must be a list value or None' ) @property def full_operation_window(self): """Treats the plan full backup operation window as a read-only attribute""" return self._full_operation_window @full_operation_window.setter def full_operation_window(self, value): """Modifies the full backup operation window of the plan Args: value (list) -- list of time slots for setting the backup window value (None) -- set value to None to clear the operation window Raises: SDKException: if the input is incorrect if the operation window configuration fails """ if isinstance(value, list): req_json = { "operationWindow": { "operations": [ 1, ], "dayTime": value } } resp = self._update_plan_props(req_json) if resp[0]: self.refresh() return else: o_str = 'Failed to update the full operation window\nError: "{0}"' raise SDKException('Plan', '102', o_str.format(resp[2])) elif value is None: self._commcell_object.operation_window.delete_operation_window( rule_id=self._full_operation_window['ruleId'] ) self.refresh() return else: raise SDKException( 'Plan', '102', 'Plan full operation window must be a list value or None' ) @property def plan_type(self): """Treats the plan type as a read-only attribute.""" return self._plan_type @property def subtype(self): """Treats the plan subtype as a read-only attribute.""" return self._subtype @property def override_entities(self): """Treats the plan override_entities as a read-only attribute.""" return self._override_entities @override_entities.setter def override_entities(self, value): """Sets the override restrictions for the plan""" req_json = { "inheritance": { "isSealed": False, "enforcedEntitiesOperationType": 1, "privateEntitiesOperationType": 1 } } if isinstance(value, dict): req_json['inheritance'].update(value) resp = self._update_plan_props(req_json) if resp[0]: return else: o_str = 'Failed to update the plan override restrictions\nError: "{0}"' raise SDKException('Plan', '102', o_str.format(resp[2])) else: raise SDKException( 'Plan', '102', 'Override restrictions must be defined in a dict' ) @property def storage_policy(self): """Treats the plan storage policy as a read-only attribute""" return self._child_policies['storagePolicy'] @property def storage_copies(self): """Treats the plan storage policy as a read-only attribute""" return self._storage_copies @property def schedule_policies(self): """Treats the plan schedule policies as read-only attribute""" return self._child_policies['schedulePolicy'] @property def addons(self): """Treats the plan addons as read-only attribute""" for addon in self._plan_properties.get('summary', {}).get('addons', []): self._addons.append( addon ) return self._addons @property def subclient_policy(self): """Treats the plan subclient policy as a read-only attribute""" return self._child_policies['subclientPolicyIds'] @property def associated_entities(self): """getter for the backup entities associated with the plan""" return self._associated_entities @property def parent_plan(self): """getter for the parent plan of a derived plan""" return self._commcell_object.plans.get(self._parent_plan_name) @property def security_associations(self): """getter for the plan's security associations Eg: { 'sample_user_group_name': 'role_name' } """ return self._security_associations def update_security_associations(self, associations_list, is_user = True, request_type = None, external_group = False): """ Adds the security association on the plan object Args: associations_list (list) -- list of users to be associated Example: associations_list = [ { 'user_name': user1, 'role_name': role1 }, { 'user_name': user2, 'role_name': role2 } ] is_user (bool) -- True or False. set is_user = False, If associations_list made up of user groups request_type (str) -- eg : 'OVERWRITE' or 'UPDATE' or 'DELETE', Default will be OVERWRITE operation external_group (bool) -- True or False, set external_group = True. If Security associations is being done on External User Groups Raises: SDKException: if association is not of List type """ if not isinstance(associations_list, list): raise SDKException('Plan', '102') SecurityAssociation(self._commcell_object, self)._add_security_association(associations_list, is_user, request_type, external_group) @property def content_indexing_props(self): """returns the DC plan related CI properties from Plan""" return self._dc_plan_props @property def properties(self): """Returns the configured properties for the Plan""" return self._plan_properties @property def region_id(self): """Returns the Backup destination region id""" return self._region_id def refresh(self): """Refresh the properties of the Plan.""" self._properties = self._get_plan_properties() def associate_user(self, userlist): """associates the users to the plan. # TODO: Need to handle user groups. Arguments: userlist(list) - list of users to be associated to the plans. Raises: SDKException: if response is empty if response is not success """ users_list = [] for user in userlist: if self._commcell_object.users.has_user(user): temp = self._commcell_object.users.get(user) temp_dict = { 'sendInvite': True, 'user': { 'userName': temp.user_name, 'userId': int(temp.user_id) } } users_list.append(temp_dict) request_json = { "userOperationType": 1, "users": users_list } flag, response = self._cvpysdk_object.make_request( 'PUT', self._ADD_USERS_TO_PLAN, request_json ) if flag: if response.json() and 'errors' in response.json(): for error in response.json()["errors"]: error_code = error["status"]["errorCode"] if error_code == 0: pass else: o_str = 'Failed to add users with error code: "{0}"' raise SDKException('Plan', '102', o_str.format(error_code)) else: raise SDKException('Response', '102') else: response_string = self._update_response_(response.text) raise SDKException('Response', '101', response_string) def share(self, user_or_group_name, role_name, is_user=True, ops_type=1): """Shares plan with given user or group by associating with given role Args: user_or_group_name (str) -- User or Group name to which we are sharing role_name (str) -- Role name which needs to associated with ops_type (int) -- Operation type Default : 1 (Add) Supported : 1 (Add) 3 (Delete) Returns: None Raises: SDKException: if input is not valid if failed to do sharing if user/group/role not exists on commcell if failed to get exisitng association details """ if not isinstance(user_or_group_name, str) or not isinstance(role_name, str): raise SDKException('Plan', '101') if ops_type not in [1, 3]: raise SDKException('Plan', '102', "Sharing operation type provided is not supported") if is_user: if not self._commcell_object.users.has_user(user_or_group_name): raise SDKException('Plan', '102', "User doesn't exists in the commcell") if not self._commcell_object.roles.has_role(role_name=role_name): raise SDKException('Plan', '102', "Role doesn't exists in the commcell") request_json = copy.deepcopy(PlanConstants.PLAN_SHARE_REQUEST_JSON) association_response = None if ops_type == 1 and len(self.security_associations) > 1: association_request_json = copy.deepcopy(PlanConstants.PLAN_SHARE_REQUEST_JSON) del association_request_json['securityAssociations'] association_request_json['entityAssociated']['entity'][0]['entityId'] = int(self._plan_id) flag, response = self._cvpysdk_object.make_request( 'GET', self._API_SECURITY_ENTITY % (self._plan_entity_type, int( self._plan_id)), association_request_json) if flag: if response.json() and 'securityAssociations' in response.json(): association_response = response.json( )['securityAssociations'][0]['securityAssociations']['associations'] else: raise SDKException('Plan', '102', 'Failed to get existing security associations') else: response_string = self._commcell_object._update_response_(response.text) raise SDKException('Response', '101', response_string) external_user = False if '\\' in user_or_group_name: external_user = True if is_user: user_obj = self._commcell_object.users.get(user_or_group_name) user_id = user_obj.user_id request_json['securityAssociations']['associations'][0]['userOrGroup'][0]['userId'] = int(user_id) request_json['securityAssociations']['associations'][0]['userOrGroup'][0]['_type_'] = 13 request_json['securityAssociations']['associations'][0]['userOrGroup'][0]['userName'] = user_or_group_name elif external_user: request_json['securityAssociations']['associations'][0]['userOrGroup'][0]['groupId'] = 0 request_json['securityAssociations']['associations'][0]['userOrGroup'][0]['_type_'] = 62 request_json['securityAssociations']['associations'][0]['userOrGroup'][0][ 'externalGroupName'] = user_or_group_name else: grp_obj = self._commcell_object.user_groups.get(user_or_group_name) grp_id = grp_obj.user_group_id request_json['securityAssociations']['associations'][0]['userOrGroup'][0]['userGroupId'] = int(grp_id) request_json['securityAssociations']['associations'][0]['userOrGroup'][0]['_type_'] = 15 request_json['securityAssociations']['associations'][0]['userOrGroup'][0][ 'userGroupName'] = user_or_group_name request_json['entityAssociated']['entity'][0]['entityId'] = int(self._plan_id) request_json['securityAssociations']['associationsOperationType'] = ops_type role_obj = self._commcell_object.roles.get(role_name) request_json['securityAssociations']['associations'][0]['properties']['role']['roleId'] = role_obj.role_id request_json['securityAssociations']['associations'][0]['properties']['role']['roleName'] = role_obj.role_name # Associate existing associations to the request if ops_type == 1 and len(self.security_associations) > 1: request_json['securityAssociations']['associations'].extend(association_response) flag, response = self._cvpysdk_object.make_request( 'POST', self._API_SECURITY, request_json ) if flag: if response.json() and 'response' in response.json(): response_json = response.json()['response'][0] error_code = response_json['errorCode'] if error_code != 0: error_message = response_json['errorString'] raise SDKException( 'Plan', '102', error_message) self.refresh() else: raise SDKException('Plan', '105') else: response_string = self._commcell_object._update_response_(response.text) raise SDKException('Response', '101', response_string) def schedule(self, schedule_name, pattern_json, ops_type=2): """Creates or modifies the schedule associated with plan Args: schedule_name (str) -- Schedule name pattern_json (dict) -- Schedule pattern dict (Refer to Create_schedule_pattern in schedule.py) ops_type (int) -- Operation type Default : 2 (Add) Supported : 2 (Add/Modify) Raises: SDKException: if input is not valid if failed to create/modify schedule if plan is not of type Data classification plan """ if not isinstance(schedule_name, str) or not isinstance(pattern_json, dict): raise SDKException('Plan', '101') if self.plan_type not in [PlanTypes.DC.value]: raise SDKException('Plan', '102', "Add/Modify Schedule is supported only for DC Plan via CvpySDK") if ops_type not in [2]: raise SDKException('Plan', '102', "Schedule operation type provided is not supported") request_json = copy.deepcopy(PlanConstants.PLAN_SCHEDULE_REQUEST_JSON[self.plan_type]) request_json['summary']['plan']['planId'] = int(self.plan_id) request_json['schedule']['associations'][0]['entityId'] = int(self.plan_id) request_json['schedule']['task']['taskName'] = f"Cvpysdk created Schedule policy for plan - {self.plan_name}" request_json['schedule']['subTasks'][0]['subTask'][ 'subTaskName'] = schedule_name request_json['schedule']['subTasks'][0]['pattern'] = pattern_json request_json['schedule']['subTasks'][0]['options']['adminOpts']['contentIndexingOption']['operationType'] = ops_type if self._dc_plan_props['targetApps'][0] == TargetApps.FS.value: request_json['schedule']['subTasks'][0]['subTask']['operationType'] = 5022 self._update_plan_props(request_json) def edit_plan(self, **kwargs): """Edit plan options Args: **kwargs for Data Classification Plan index_content (bool) -- Speifies whether to index content or not to index server content_analyzer (list) -- list of Content analyzer client name entity_list (list) -- list of entities which needs to be extracted classifier_list (list) -- list of classifier which needs to be classified enable_ocr (bool) -- specifies whether OCR is enabled or not ocr_language (int) -- Language to be used when doing OCR Default : English (Value-1) Supported Languages: ENGLISH = 1, HEBREW = 2, SPANISH = 3, FRENCH = 4, ITALIAN = 5, DANISH = 6 include_docs (str) -- Include documents type separated by comma exclude_path (list) -- List of paths which needs to be excluded min_doc_size (int) -- Minimum document size in MB max_doc_size (int) -- Maximum document size in MB """ if self.plan_type != PlanTypes.DC.value: raise SDKException('Plan', '102', "Function Not supported for this plan type") extraction_policy_list = [] request_json = None if self.plan_type == PlanTypes.DC.value: request_json = copy.deepcopy(PlanConstants.PLAN_UPDATE_REQUEST_JSON[self.plan_type]) request_json['summary']['plan']['planId'] = int(self.plan_id) if TargetApps.SDG.value in self.content_indexing_props['targetApps']: request_json['eePolicyInfo']['eePolicy']['detail']['eePolicy'] = self.content_indexing_props['eePolicy'] request_json['ciPolicyInfo']['ciPolicy']['detail']['ciPolicy'] = self.content_indexing_props['ciPolicy'] activate_obj = self._commcell_object.activate if 'content_analyzer' in kwargs: ca_list = [] for ca in kwargs.get('content_analyzer', []): ca_client_id = self._commcell_object.content_analyzers.get(ca).client_id ca_list.append({ 'clientId': ca_client_id }) request_json['eDiscoveryInfo'] = { 'contentAnalyzerClient': ca_list} if 'entity_list' in kwargs or 'classifier_list' in kwargs: entity_mgr_obj = activate_obj.entity_manager() # classifier is also an activate entity with type alone different so # append this to entity list itself entity_list = [] for entity in kwargs.get('entity_list', []): entity_list.append(entity) for entity in kwargs.get('classifier_list', []): entity_list.append(entity) for entity in entity_list: entity_obj = entity_mgr_obj.get(entity) extraction_policy_list.append(entity_obj.container_details) request_json['eePolicyInfo']['eePolicy']['detail']['eePolicy']['extractionPolicy']['extractionPolicyList'] = extraction_policy_list if 'enable_ocr' in kwargs: request_json['ciPolicyInfo']['ciPolicy']['detail']['ciPolicy']['enableImageExtraction'] = kwargs.get( 'enable_ocr', False) request_json['ciPolicyInfo']['ciPolicy']['detail']['ciPolicy']['ocrLanguages'] = [ kwargs.get('ocr_language', 1)] if 'include_docs' in kwargs: request_json['ciPolicyInfo']['ciPolicy']['detail']['ciPolicy']['filters']['fileFilters']['includeDocTypes'] = kwargs.get( 'include_docs', PlanConstants.DEFAULT_INCLUDE_DOC_TYPES) if 'min_doc_size' in kwargs: request_json['ciPolicyInfo']['ciPolicy']['detail']['ciPolicy']['filters']['fileFilters']['minDocSize'] = kwargs.get( 'min_doc_size', PlanConstants.DEFAULT_MIN_DOC_SIZE) if 'max_doc_size' in kwargs: request_json['ciPolicyInfo']['ciPolicy']['detail']['ciPolicy']['filters']['fileFilters'][ 'maxDocSize'] = kwargs.get('max_doc_size', PlanConstants.DEFAULT_MAX_DOC_SIZE) if 'exclude_path' in kwargs: request_json['ciPolicyInfo']['ciPolicy']['detail']['ciPolicy']['filters']['fileFilters'][ 'excludePaths'] = kwargs.get('exclude_path', PlanConstants.DEFAULT_EXCLUDE_LIST) if 'index_content' in kwargs: request_json['ciPolicyInfo']['ciPolicy']['detail']['ciPolicy']['opType'] = kwargs.get( 'index_content', PlanConstants.INDEXING_METADATA_AND_CONTENT) elif TargetApps.FSO.value in self.content_indexing_props['targetApps']: # currently we dont have any thing to update in DC plan for FSO app so throw exception raise SDKException('Plan', '102', 'No attributes to Edit for DC Plan with TargetApps as : FSO') self._update_plan_props(request_json) def policy_subclient_ids(self): """Returns Policy subclient IDs of the plan Returns: dict : OS and its associated subclient ID example: { 'Windows' : windows_subclient_policy_subclient_id, 'Linux' : linux_subclient_policy_subclient_id, 'Mac' : mac_subclient_policy_subclient_id } """ result = dict() for backupset_id in self.subclient_policy: url = self._commcell_object._services['ADD_SUBCLIENT'] + '?clientId=2&applicationId=1030&backupsetid=' + str(backupset_id) flag, response = self._commcell_object._cvpysdk_object.make_request('GET', url) if flag: if response.json() and 'subClientProperties' in response.json(): subclient_id = response.json()['subClientProperties'][0]['subClientEntity']['subclientId'] backupset_name = response.json()['subClientProperties'][0]['subClientEntity']['backupsetName'] os = backupset_name.split()[-3] result[os] = subclient_id else: raise SDKException('Plan', 102, 'Failed to get subclient Ids.') else: raise SDKException('Plan', 102, response.text) return result def update_content_policy(self, content): """ Args: content (dict) : dictionary with backup content details. example: content = { "windowsIncludedPaths": ["Desktop"], "windowsExcludedPaths": ["Music"], "windowsFilterToExcludePaths": ["Videos"], "unixIncludedPaths": ["Desktop"], "unixExcludedPaths": ["Music"], "unixFilterToExcludePaths": ["Videos"], "macIncludedPaths": ["Desktop"], "macExcludedPaths": ["Music"], "macFilterToExcludePaths": ["Videos"], "backupSystemState": True, "useVSSForSystemState": True, "backupSystemStateOnlyWithFullBackup": False } For unix and mac, replace key name with respective os name, **IncludedPaths, **ExcludedPaths, **FilterToExcludePaths """ request_json = { 'backupContent' : content } request_url = self._commcell_object._services['V4_SERVER_PLAN'] % self.plan_id flag, response = self._commcell_object._cvpysdk_object.make_request('PUT', request_url, request_json) if flag: if response.json(): if response.json()['errorCode']: raise SDKException('Plan', 102, response.json()['errorMessage']) else: raise SDKException('Plan', 102, 'Failed to update backup content') else: raise SDKException('Plan', 102, response.text) def update_backup_content(self, content, request_type = 'OVERWRITE'): """ Args: content (dict) : dictionary with backup content details. example: content = { 'Windows' : { 'Content' : ['\\%Pictures%', '\\%Desktop%'], 'Exclude' : ['\\%Documents%'], 'Backup System State' : True }, 'Linux' : { 'Content' : ['/%Pictures%'], 'Exclude' : ['/%Documents%'] }, 'Mac' : { 'Content' : ['/%Pictures%'], 'Exclude' : ['/%Documents%'] } } request_type (str) : Supported values 'OVERWRITE' (default), 'UPDATE', 'DELETE'. For plans created from SP32, Please use below format of content example: content = { "windowsIncludedPaths": ["Desktop"], "windowsExcludedPaths": ["Music"], "windowsFilterToExcludePaths": ["Videos"], "backupSystemState": True, "useVSSForSystemState": True, "backupSystemStateOnlyWithFullBackup": False } For unix and mac, replace key name with respective os name, **IncludedPaths, **ExcludedPaths, **FilterToExcludePaths """ update_request_type = { "OVERWRITE": 1, "UPDATE": 2, "DELETE": 3 } subclients = self.policy_subclient_ids() if not subclients: self.update_content_policy(content) return for os, value in content.items(): request_json = { "subClientProperties": { "fsExcludeFilterOperationType": update_request_type.get(request_type, 1), "fsContentOperationType" : update_request_type.get(request_type, 1) } } request_url = self._commcell_object._services['SUBCLIENT'] % subclients[os] contents = list() for key, val in value.items(): if key.lower() == 'content': for path in val: contents.append({"path" : path}) if key.lower() == 'exclude': for path in val: contents.append({"excludePath" : path}) if os == 'Windows' and key == 'Backup System State': request_json['subClientProperties']['fsSubClientProp'] = {'backupSystemState' : val} if contents: request_json['subClientProperties']['content'] = contents flag, response = self._commcell_object._cvpysdk_object.make_request('POST', request_url, request_json) if flag: if response.json() and 'response' in response.json(): errorCode = response.json()['response'][0].get('errorCode') if errorCode: raise SDKException('Plan', 102, 'Failed to Change Content of Plan.') else: raise SDKException('Plan', 102, 'Failed to get subclient Ids.') else: raise SDKException('Plan', 102, response.text) @property def applicable_solutions(self): """Method to read applicable solutions""" return self._applicable_solutions @applicable_solutions.setter def applicable_solutions(self, solutions: list = list()): """Method to update applicable solutions of plan Args: solutions (list) : List of Applicable Solutions example: ["File Servers", "Databases"] : FS and DB will be set as a applicable solutions [] : Passing empty list will reset applicable solutions to ALL """ request_url = self._commcell_object._services['APPLICABLE_SOLNS_ENABLE' if solutions else 'APPLICABLE_SOLNS_DISABLE'] % self.plan_id if solutions: supported_solutions = self._commcell_object.plans.get_supported_solutions() request_json = {"solutions": [{"id": supported_solutions[soln_name], "name": soln_name} for soln_name in solutions]} else: request_json = None flag, response = self._commcell_object._cvpysdk_object.make_request('PUT', request_url, request_json) if not flag: raise SDKException('Response', '101', self._update_response_(response.text)) if not response.json() or response.json()['errorCode']: raise SDKException('Plan', 102, 'Failed to update Applicable Solutions for Plan') self.refresh()
Instance variables
var addons
-
Treats the plan addons as read-only attribute
Expand source code Browse git
@property def addons(self): """Treats the plan addons as read-only attribute""" for addon in self._plan_properties.get('summary', {}).get('addons', []): self._addons.append( addon ) return self._addons
var applicable_solutions
-
Method to read applicable solutions
Expand source code Browse git
@property def applicable_solutions(self): """Method to read applicable solutions""" return self._applicable_solutions
var associated_entities
-
getter for the backup entities associated with the plan
Expand source code Browse git
@property def associated_entities(self): """getter for the backup entities associated with the plan""" return self._associated_entities
var content_indexing_props
-
returns the DC plan related CI properties from Plan
Expand source code Browse git
@property def content_indexing_props(self): """returns the DC plan related CI properties from Plan""" return self._dc_plan_props
var full_operation_window
-
Treats the plan full backup operation window as a read-only attribute
Expand source code Browse git
@property def full_operation_window(self): """Treats the plan full backup operation window as a read-only attribute""" return self._full_operation_window
var operation_window
-
Treats the plan incremental operation window as a read-only attribute
Expand source code Browse git
@property def operation_window(self): """Treats the plan incremental operation window as a read-only attribute""" return self._operation_window
var override_entities
-
Treats the plan override_entities as a read-only attribute.
Expand source code Browse git
@property def override_entities(self): """Treats the plan override_entities as a read-only attribute.""" return self._override_entities
var parent_plan
-
getter for the parent plan of a derived plan
Expand source code Browse git
@property def parent_plan(self): """getter for the parent plan of a derived plan""" return self._commcell_object.plans.get(self._parent_plan_name)
var plan_id
-
Treats the plan id as a read-only attribute.
Expand source code Browse git
@property def plan_id(self): """Treats the plan id as a read-only attribute.""" return self._plan_id
var plan_name
-
Treats the plan name as a read-only attribute.
Expand source code Browse git
@property def plan_name(self): """Treats the plan name as a read-only attribute.""" return self._plan_name
var plan_type
-
Treats the plan type as a read-only attribute.
Expand source code Browse git
@property def plan_type(self): """Treats the plan type as a read-only attribute.""" return self._plan_type
var properties
-
Returns the configured properties for the Plan
Expand source code Browse git
@property def properties(self): """Returns the configured properties for the Plan""" return self._plan_properties
var region_id
-
Returns the Backup destination region id
Expand source code Browse git
@property def region_id(self): """Returns the Backup destination region id""" return self._region_id
var schedule_policies
-
Treats the plan schedule policies as read-only attribute
Expand source code Browse git
@property def schedule_policies(self): """Treats the plan schedule policies as read-only attribute""" return self._child_policies['schedulePolicy']
var security_associations
-
getter for the plan's security associations
Eg
{ 'sample_user_group_name': 'role_name' }
Expand source code Browse git
@property def security_associations(self): """getter for the plan's security associations Eg: { 'sample_user_group_name': 'role_name' } """ return self._security_associations
var sla_in_minutes
-
Treats the plan SLA/RPO as a read-only attribute.
Expand source code Browse git
@property def sla_in_minutes(self): """Treats the plan SLA/RPO as a read-only attribute.""" return self._sla_in_minutes
var storage_copies
-
Treats the plan storage policy as a read-only attribute
Expand source code Browse git
@property def storage_copies(self): """Treats the plan storage policy as a read-only attribute""" return self._storage_copies
var storage_policy
-
Treats the plan storage policy as a read-only attribute
Expand source code Browse git
@property def storage_policy(self): """Treats the plan storage policy as a read-only attribute""" return self._child_policies['storagePolicy']
var subclient_policy
-
Treats the plan subclient policy as a read-only attribute
Expand source code Browse git
@property def subclient_policy(self): """Treats the plan subclient policy as a read-only attribute""" return self._child_policies['subclientPolicyIds']
var subtype
-
Treats the plan subtype as a read-only attribute.
Expand source code Browse git
@property def subtype(self): """Treats the plan subtype as a read-only attribute.""" return self._subtype
Methods
def add_storage_copy(self, copy_name, storage_pool, retention=30, extended_retention=None)
-
Add a storage copy as backup destination to this plan
Args
copy_name (str) - name of the copy that is being added
storage_pool (str) - name of the storage pool for the copy to be added
retention (int) - retention period in days for the copy
extended_retention (tuple) - extended retention rules of a copy Example: [1, True, "EXTENDED_ALLFULL", 0, 0]
Returns
dict - dictionary of all copies of this plan
Expand source code Browse git
def add_storage_copy(self, copy_name, storage_pool, retention=30, extended_retention=None): """Add a storage copy as backup destination to this plan Args: copy_name (str) - name of the copy that is being added storage_pool (str) - name of the storage pool for the copy to be added retention (int) - retention period in days for the copy extended_retention (tuple) - extended retention rules of a copy Example: [1, True, "EXTENDED_ALLFULL", 0, 0] Returns: dict - dictionary of all copies of this plan """ if isinstance(copy_name, str) and isinstance(storage_pool, str): if not self.storage_policy.has_copy(copy_name): self.storage_policy.create_secondary_copy( copy_name, global_policy=storage_pool ) self.storage_policy.get_copy(copy_name).copy_retention = (retention, 0, 0) if extended_retention: self.storage_policy.get_copy( copy_name).extended_retention_rules = extended_retention self.refresh() return self.storage_copies else: err_msg = f'Storage Policy copy "{copy_name}" already exists.' raise SDKException('Storage', '102', err_msg) else: raise SDKException( 'Plan', '102', 'Copy name and storage pool name must be a string.' )
def associate_user(self, userlist)
-
associates the users to the plan. # TODO: Need to handle user groups.
Arguments
userlist(list) - list of users to be associated to the plans.
Raises: SDKException: if response is empty
if response is not success
Expand source code Browse git
def associate_user(self, userlist): """associates the users to the plan. # TODO: Need to handle user groups. Arguments: userlist(list) - list of users to be associated to the plans. Raises: SDKException: if response is empty if response is not success """ users_list = [] for user in userlist: if self._commcell_object.users.has_user(user): temp = self._commcell_object.users.get(user) temp_dict = { 'sendInvite': True, 'user': { 'userName': temp.user_name, 'userId': int(temp.user_id) } } users_list.append(temp_dict) request_json = { "userOperationType": 1, "users": users_list } flag, response = self._cvpysdk_object.make_request( 'PUT', self._ADD_USERS_TO_PLAN, request_json ) if flag: if response.json() and 'errors' in response.json(): for error in response.json()["errors"]: error_code = error["status"]["errorCode"] if error_code == 0: pass else: o_str = 'Failed to add users with error code: "{0}"' raise SDKException('Plan', '102', o_str.format(error_code)) else: raise SDKException('Response', '102') else: response_string = self._update_response_(response.text) raise SDKException('Response', '101', response_string)
def derive_and_add(self, plan_name, storage_pool_name=None, sla_in_minutes=None, override_entities=None)
-
Derives the base plan based on the the inheritance properties to created a derived plan
Args: plan_name (str) -- name of the new plan to add storage_pool_name (str) -- name of the storage pool to be used for the plan default: None : when the name is left to default, it inherits the base plan storage pool if overriding is optional/not allowed sla_in_minutes (int) -- Backup SLA in hours default: None : when the SLA is left to default, it inherits the base plan SLA if overriding is optional/not allowed override_entities (dict) -- Specify the entities with respective overriding. default: None { 'privateEntities': [1, 4], 'enforcedEntities': [256, 512, 1024] } - where, privateEntities are set when respective entity overriding is must enforcedEntities are set when respective entity overriding is not allowed left blank if overriding is optional - entity IDs, 1 - Storage 4 - SLA/Schedules 256 - Windows content 512 - Unix content 1024 - Mac content
Returns
object - instance of the Plan class created by this method
Raises
SDKException: if plan name is in incorrect format
if plan already exists if neccessary arguments are not passed if inheritance rules are not followed
Expand source code Browse git
def derive_and_add(self, plan_name, storage_pool_name=None, sla_in_minutes=None, override_entities=None): """Derives the base plan based on the the inheritance properties to created a derived plan Args: plan_name (str) -- name of the new plan to add storage_pool_name (str) -- name of the storage pool to be used for the plan default: None : when the name is left to default, it inherits the base plan storage pool if overriding is optional/not allowed sla_in_minutes (int) -- Backup SLA in hours default: None : when the SLA is left to default, it inherits the base plan SLA if overriding is optional/not allowed override_entities (dict) -- Specify the entities with respective overriding. default: None { 'privateEntities': [1, 4], 'enforcedEntities': [256, 512, 1024] } - where, privateEntities are set when respective entity overriding is must enforcedEntities are set when respective entity overriding is not allowed left blank if overriding is optional - entity IDs, 1 - Storage 4 - SLA/Schedules 256 - Windows content 512 - Unix content 1024 - Mac content Returns: object - instance of the Plan class created by this method Raises: SDKException: if plan name is in incorrect format if plan already exists if neccessary arguments are not passed if inheritance rules are not followed """ if not isinstance(plan_name, str): raise SDKException('Plan', '101', 'Plan name must be string value') else: if self._commcell_object.plans.has_plan(plan_name): raise SDKException( 'Plan', '102', 'Plan "{0}" already exists'.format( plan_name) ) if self._override_entities is not None: request_json = self._commcell_object.plans._get_plan_template( str(self._subtype), "MSP") request_json['plan']['summary']['description'] = "Created from CvPySDK." request_json['plan']['summary']['plan']['planName'] = plan_name request_json['plan']['summary']['parent'] = { 'planId': int(self._plan_id) } is_dedupe = True if storage_pool_name is not None: storage_pool_obj = self._commcell_object.storage_pools.get( storage_pool_name ) if 'dedupDBDetailsList' \ not in storage_pool_obj._storage_pool_properties['storagePoolDetails']: is_dedupe = False storage_pool_id = int(storage_pool_obj.storage_pool_id) if is_dedupe: request_json['plan']['storage']['copy'][0]['dedupeFlags'][ 'useGlobalDedupStore'] = 1 else: del request_json['plan']['storage']['copy'][0]['storagePolicyFlags'] del request_json['plan']['storage']['copy'][0]['dedupeFlags'][ 'enableDeduplication'] del request_json['plan']['storage']['copy'][0]['dedupeFlags'][ 'enableClientSideDedup'] del request_json['plan']['storage']['copy'][0]['DDBPartitionInfo'] request_json['plan']['storage']['copy'][0]['extendedFlags'] = { 'useGlobalStoragePolicy': 1 } else: storage_pool_id = None if 1 in self._override_entities['enforcedEntities']: if storage_pool_id is None: request_json['plan']['storage'] = { "storagePolicyId": self.storage_policy.storage_policy_id } snap_copy_id = self.storage_policy.storage_policy_id else: raise SDKException( 'Plan', '102', 'Storage is enforced by base plan, cannot be overridden') elif 1 in self._override_entities['privateEntities']: if storage_pool_id is not None: request_json['plan']['storage']['copy'][0]['useGlobalPolicy'] = { "storagePolicyId": storage_pool_id } snap_copy_id = storage_pool_id else: raise SDKException('Plan', '102', 'Storage must be input') else: if storage_pool_id is not None: request_json['plan']['storage']['copy'][0]['useGlobalPolicy'] = { "storagePolicyId": storage_pool_id } snap_copy_id = storage_pool_id else: request_json['plan']['storage'] = { "storagePolicyId": self.storage_policy.storage_policy_id } snap_copy_id = self.storage_policy.storage_policy_id if 4 in self._override_entities['enforcedEntities']: if sla_in_minutes is None: request_json['plan']['summary']['slaInMinutes'] = self._sla_in_minutes else: raise SDKException( 'Plan', '102', 'SLA is enforced by base plan, cannot be overridden') elif 4 in self._override_entities['privateEntities']: if sla_in_minutes is not None: request_json['plan']['summary']['slaInMinutes'] = sla_in_minutes else: raise SDKException('Plan', '102', 'SLA must be input') else: if sla_in_minutes is not None: request_json['plan']['summary']['slaInMinutes'] = sla_in_minutes else: request_json['plan']['summary']['slaInMinutes'] = self._sla_in_minutes if isinstance(override_entities, dict): request_json['plan']['summary']['restrictions'] = 0 request_json['plan']['inheritance'] = { 'isSealed': False } for entity in self._override_entities['enforcedEntities']: from functools import reduce if override_entities and entity in reduce( lambda i, j: i + j, override_entities.values()): raise SDKException( 'Plan', '102', 'Override not allowed') if 'enforcedEntities' in override_entities: request_json['plan']['inheritance']['enforcedEntities'] = ( override_entities['enforcedEntities'] ) if 'privateEntities' in override_entities: request_json['plan']['inheritance']['privateEntities'] = ( override_entities['privateEntities'] ) else: request_json['plan']['summary']['restrictions'] = 1 request_json['plan']['inheritance'] = { 'isSealed': True } if sla_in_minutes is not None: request_json['plan']['definesSchedule'] = { 'definesEntity': True } else: request_json['plan']['definesSchedule'] = { 'definesEntity': False } if isinstance(self._override_entities, dict): if (4 not in self._override_entities['enforcedEntities'] + self._override_entities['privateEntities']): request_json['plan']['definesSchedule']['overrideEntity'] = 0 elif 4 in self._override_entities['enforcedEntities']: request_json['plan']['definesSchedule']['overrideEntity'] = 2 elif 4 in self._override_entities['privateEntities']: request_json['plan']['definesSchedule']['overrideEntity'] = 1 if storage_pool_id is not None: request_json['plan']['definesStorage'] = { 'definesEntity': True } else: request_json['plan']['definesStorage'] = { 'definesEntity': False } if isinstance(self._override_entities, dict): if (1 not in self._override_entities['enforcedEntities'] + self._override_entities['privateEntities']): request_json['plan']['definesStorage']['overrideEntity'] = 0 elif 1 in self._override_entities['enforcedEntities']: request_json['plan']['definesStorage']['overrideEntity'] = 2 elif 1 in self._override_entities['privateEntities']: request_json['plan']['definesStorage']['overrideEntity'] = 1 if self._subtype != 33554437: temp_defines_key = { 'definesEntity': False } if isinstance(self._override_entities, dict): if (not all(entity in self._override_entities['enforcedEntities'] + self._override_entities['privateEntities'] for entity in [256, 512, 1024])): temp_defines_key['overrideEntity'] = 0 elif all(entity in self._override_entities['enforcedEntities'] for entity in [256, 512, 1024]): temp_defines_key['overrideEntity'] = 2 elif all(entity in self._override_entities['privateEntities'] for entity in [256, 512, 1024]): temp_defines_key['overrideEntity'] = 1 request_json['plan']['laptop']['content']['definesSubclientLin'] = temp_defines_key request_json['plan']['laptop']['content']['definesSubclientMac'] = temp_defines_key request_json['plan']['laptop']['content']['definesSubclientWin'] = temp_defines_key if self._subtype == 33554437 and 'snap' in self.addons and 'copy' \ in request_json['plan']['storage']: request_json['plan']['storage']['copy'][1]['useGlobalPolicy'] = { 'storagePolicyId': snap_copy_id } request_json['plan']['storage']['copy'][1]['extendedFlags'] = { 'useGlobalStoragePolicy': 1 } add_plan_service = self._commcell_object.plans._PLANS headers = self._commcell_object._headers.copy() headers['LookupNames'] = 'False' flag, response = self._cvpysdk_object.make_request( 'POST', add_plan_service, request_json, headers=headers ) if flag: if response.json(): response_value = response.json() error_message = None error_code = None if 'errors' in response_value: error_code = response_value['errors'][0]['status']['errorCode'] error_message = response_value['errors'][0]['status']['errorMessage'] # error_codes 0 - OK, 1 - plan without storage, 84 - restricted plan if error_code not in [0, 1, 84]: o_str = 'Failed to create new Plan\nError: "{0}"'.format( error_message ) raise SDKException('Plan', '102', o_str) if 'plan' in response_value: plan_name = response_value['plan']['summary']['plan']['planName'] # initialize the plans again # so that the plans object has all the plans self._commcell_object.plans.refresh() return self._commcell_object.plans.get(plan_name) else: o_str = ('Failed to create new plan due to error code: "{0}"\n' 'Please check the documentation for ' 'more details on the error').format(error_code) raise SDKException('Plan', '102', o_str) else: raise SDKException('Response', 102) else: response_string = self._update_response_(response.text) raise SDKException('Response', '101', response_string) else: raise SDKException('Plan', '102', 'Inheritance disabled for plan')
def disable_full_schedule(self)
-
Disable the full backup schedule of the plan
Expand source code Browse git
def disable_full_schedule(self): """Disable the full backup schedule of the plan""" try: self.schedule_policies['data'].delete_schedule(schedule_id=list(filter( lambda st: st['subTask']['flags'] == 4194304, self.schedule_policies['data']._subtasks ))[0]['subTask']['subTaskId']) except IndexError: raise IndexError('Full backup schedule not enabled')
def edit_association(self, entities, new_plan=None)
-
Reassociates or dissociates the entities from this plan
Args
entities (list) – list containing entity objects whose plan association must be edited Eg: [ { "clientName": "client", "subclientName": "subclient", "backupsetName": "backupset", "appName": "app" } ]
new_plan (str) – new plan to which the associated entities must be reassociated with
Raises
SDKException if plan not found
Expand source code Browse git
def edit_association(self, entities, new_plan=None): """Reassociates or dissociates the entities from this plan Args: entities (list) -- list containing entity objects whose plan association must be edited Eg: [ { "clientName": "client", "subclientName": "subclient", "backupsetName": "backupset", "appName": "app" } ] new_plan (str) -- new plan to which the associated entities must be reassociated with Raises: SDKException if plan not found """ req_json = { 'plan': { 'planName': self.plan_name }, 'entities': entities } if new_plan is not None: if self._commcell_object.plans.has_plan(new_plan): req_json.update({ 'planOperationType': 'OVERWRITE', 'newPlan': { 'planName': new_plan } }) else: SDKException('Plan', '102', 'No plan exists with name: {0}'.format( new_plan) ) else: req_json.update({ 'planOperationType': 'DELETE' }) req_url = self._services['ASSOCIATED_ENTITIES'] % (self._plan_id) flag, response = self._cvpysdk_object.make_request( 'PUT', req_url, req_json ) if flag: if 'response' in response.json(): error_code = str(response.json()["response"][0]["errorCode"]) if error_code == "0": self.refresh() return else: error_message = str(response.json()["errorMessage"]) o_str = 'Failed to edit plan associated entities\nError: "{0}"' raise SDKException('Plan', '102', o_str.format(error_message)) else: response_string = self._commcell_object._update_response_(response.text) raise SDKException('Response', '101', response_string)
def edit_plan(self, **kwargs)
-
Edit plan options
Args
**kwargs for Data Classification Plan
index_content (bool) – Speifies whether to index content or not to index server
content_analyzer (list) – list of Content analyzer client name
entity_list (list) – list of entities which needs to be extracted
classifier_list (list) – list of classifier which needs to be classified
enable_ocr (bool) – specifies whether OCR is enabled or not
ocr_language (int) – Language to be used when doing OCR Default : English (Value-1)
Supported Languages:
ENGLISH = 1, HEBREW = 2, SPANISH = 3, FRENCH = 4, ITALIAN = 5, DANISH = 6
include_docs (str) – Include documents type separated by comma
exclude_path (list) – List of paths which needs to be excluded
min_doc_size (int) – Minimum document size in MB
max_doc_size (int) – Maximum document size in MB
Expand source code Browse git
def edit_plan(self, **kwargs): """Edit plan options Args: **kwargs for Data Classification Plan index_content (bool) -- Speifies whether to index content or not to index server content_analyzer (list) -- list of Content analyzer client name entity_list (list) -- list of entities which needs to be extracted classifier_list (list) -- list of classifier which needs to be classified enable_ocr (bool) -- specifies whether OCR is enabled or not ocr_language (int) -- Language to be used when doing OCR Default : English (Value-1) Supported Languages: ENGLISH = 1, HEBREW = 2, SPANISH = 3, FRENCH = 4, ITALIAN = 5, DANISH = 6 include_docs (str) -- Include documents type separated by comma exclude_path (list) -- List of paths which needs to be excluded min_doc_size (int) -- Minimum document size in MB max_doc_size (int) -- Maximum document size in MB """ if self.plan_type != PlanTypes.DC.value: raise SDKException('Plan', '102', "Function Not supported for this plan type") extraction_policy_list = [] request_json = None if self.plan_type == PlanTypes.DC.value: request_json = copy.deepcopy(PlanConstants.PLAN_UPDATE_REQUEST_JSON[self.plan_type]) request_json['summary']['plan']['planId'] = int(self.plan_id) if TargetApps.SDG.value in self.content_indexing_props['targetApps']: request_json['eePolicyInfo']['eePolicy']['detail']['eePolicy'] = self.content_indexing_props['eePolicy'] request_json['ciPolicyInfo']['ciPolicy']['detail']['ciPolicy'] = self.content_indexing_props['ciPolicy'] activate_obj = self._commcell_object.activate if 'content_analyzer' in kwargs: ca_list = [] for ca in kwargs.get('content_analyzer', []): ca_client_id = self._commcell_object.content_analyzers.get(ca).client_id ca_list.append({ 'clientId': ca_client_id }) request_json['eDiscoveryInfo'] = { 'contentAnalyzerClient': ca_list} if 'entity_list' in kwargs or 'classifier_list' in kwargs: entity_mgr_obj = activate_obj.entity_manager() # classifier is also an activate entity with type alone different so # append this to entity list itself entity_list = [] for entity in kwargs.get('entity_list', []): entity_list.append(entity) for entity in kwargs.get('classifier_list', []): entity_list.append(entity) for entity in entity_list: entity_obj = entity_mgr_obj.get(entity) extraction_policy_list.append(entity_obj.container_details) request_json['eePolicyInfo']['eePolicy']['detail']['eePolicy']['extractionPolicy']['extractionPolicyList'] = extraction_policy_list if 'enable_ocr' in kwargs: request_json['ciPolicyInfo']['ciPolicy']['detail']['ciPolicy']['enableImageExtraction'] = kwargs.get( 'enable_ocr', False) request_json['ciPolicyInfo']['ciPolicy']['detail']['ciPolicy']['ocrLanguages'] = [ kwargs.get('ocr_language', 1)] if 'include_docs' in kwargs: request_json['ciPolicyInfo']['ciPolicy']['detail']['ciPolicy']['filters']['fileFilters']['includeDocTypes'] = kwargs.get( 'include_docs', PlanConstants.DEFAULT_INCLUDE_DOC_TYPES) if 'min_doc_size' in kwargs: request_json['ciPolicyInfo']['ciPolicy']['detail']['ciPolicy']['filters']['fileFilters']['minDocSize'] = kwargs.get( 'min_doc_size', PlanConstants.DEFAULT_MIN_DOC_SIZE) if 'max_doc_size' in kwargs: request_json['ciPolicyInfo']['ciPolicy']['detail']['ciPolicy']['filters']['fileFilters'][ 'maxDocSize'] = kwargs.get('max_doc_size', PlanConstants.DEFAULT_MAX_DOC_SIZE) if 'exclude_path' in kwargs: request_json['ciPolicyInfo']['ciPolicy']['detail']['ciPolicy']['filters']['fileFilters'][ 'excludePaths'] = kwargs.get('exclude_path', PlanConstants.DEFAULT_EXCLUDE_LIST) if 'index_content' in kwargs: request_json['ciPolicyInfo']['ciPolicy']['detail']['ciPolicy']['opType'] = kwargs.get( 'index_content', PlanConstants.INDEXING_METADATA_AND_CONTENT) elif TargetApps.FSO.value in self.content_indexing_props['targetApps']: # currently we dont have any thing to update in DC plan for FSO app so throw exception raise SDKException('Plan', '102', 'No attributes to Edit for DC Plan with TargetApps as : FSO') self._update_plan_props(request_json)
def modify_schedule(self, schedule_json, is_full_schedule=False)
-
Modifies the incremental RPO schedule pattern of the plan with the given schedule json
Args: schedule_json (dict) – { pattern : {}, – Please refer SchedulePattern.create_schedule in schedules.py for the types of pattern to be sent
eg: { "freq_type": 'daily', "active_start_time": time_in_%H/%S (str), "repeat_days": days_to_repeat (int) } options: {} -- Please refer ScheduleOptions.py classes for respective schedule options eg: { "maxNumberOfStreams": 0, "useMaximumStreams": True, "useScallableResourceManagement": True, "totalJobsToProcess": 1000, "allCopies": True, "mediaAgent": { "mediaAgentName": "<ANY MEDIAAGENT>" } } }
is_full_schedule (bool) – Pass True if he schedule to be modified is the full backup schedule
Expand source code Browse git
def modify_schedule(self, schedule_json, is_full_schedule=False): """Modifies the incremental RPO schedule pattern of the plan with the given schedule json Args: schedule_json (dict) -- { pattern : {}, -- Please refer SchedulePattern.create_schedule in schedules.py for the types of pattern to be sent eg: { "freq_type": 'daily', "active_start_time": time_in_%H/%S (str), "repeat_days": days_to_repeat (int) } options: {} -- Please refer ScheduleOptions.py classes for respective schedule options eg: { "maxNumberOfStreams": 0, "useMaximumStreams": True, "useScallableResourceManagement": True, "totalJobsToProcess": 1000, "allCopies": True, "mediaAgent": { "mediaAgentName": "<ANY MEDIAAGENT>" } } } is_full_schedule (bool) -- Pass True if he schedule to be modified is the full backup schedule """ if is_full_schedule: try: schedule_id = list(filter( lambda st: st['subTask']['flags'] == 4194304, self.schedule_policies['data']._subtasks ))[0]['subTask']['subTaskId'] except IndexError: raise IndexError('Full backup schedule not enabled') else: schedule_id = list(filter( lambda st: st['subTask']['flags'] == 65536, self.schedule_policies['data']._subtasks ))[0]['subTask']['subTaskId'] self.schedule_policies['data'].modify_schedule( schedule_json, schedule_id=schedule_id ) self.refresh()
def policy_subclient_ids(self)
-
Returns Policy subclient IDs of the plan
Returns
dict
- OS and its associated subclient ID
example: { 'Windows' : windows_subclient_policy_subclient_id, 'Linux' : linux_subclient_policy_subclient_id, 'Mac' : mac_subclient_policy_subclient_id }
Expand source code Browse git
def policy_subclient_ids(self): """Returns Policy subclient IDs of the plan Returns: dict : OS and its associated subclient ID example: { 'Windows' : windows_subclient_policy_subclient_id, 'Linux' : linux_subclient_policy_subclient_id, 'Mac' : mac_subclient_policy_subclient_id } """ result = dict() for backupset_id in self.subclient_policy: url = self._commcell_object._services['ADD_SUBCLIENT'] + '?clientId=2&applicationId=1030&backupsetid=' + str(backupset_id) flag, response = self._commcell_object._cvpysdk_object.make_request('GET', url) if flag: if response.json() and 'subClientProperties' in response.json(): subclient_id = response.json()['subClientProperties'][0]['subClientEntity']['subclientId'] backupset_name = response.json()['subClientProperties'][0]['subClientEntity']['backupsetName'] os = backupset_name.split()[-3] result[os] = subclient_id else: raise SDKException('Plan', 102, 'Failed to get subclient Ids.') else: raise SDKException('Plan', 102, response.text) return result
def refresh(self)
-
Refresh the properties of the Plan.
Expand source code Browse git
def refresh(self): """Refresh the properties of the Plan.""" self._properties = self._get_plan_properties()
def schedule(self, schedule_name, pattern_json, ops_type=2)
-
Creates or modifies the schedule associated with plan
Args
schedule_name (str) – Schedule name
pattern_json (dict) – Schedule pattern dict (Refer to Create_schedule_pattern in schedule.py)
ops_type (int) – Operation type
Default : 2 (Add) Supported : 2 (Add/Modify)
Raises
SDKException:
if input is not valid if failed to create/modify schedule if plan is not of type Data classification plan
Expand source code Browse git
def schedule(self, schedule_name, pattern_json, ops_type=2): """Creates or modifies the schedule associated with plan Args: schedule_name (str) -- Schedule name pattern_json (dict) -- Schedule pattern dict (Refer to Create_schedule_pattern in schedule.py) ops_type (int) -- Operation type Default : 2 (Add) Supported : 2 (Add/Modify) Raises: SDKException: if input is not valid if failed to create/modify schedule if plan is not of type Data classification plan """ if not isinstance(schedule_name, str) or not isinstance(pattern_json, dict): raise SDKException('Plan', '101') if self.plan_type not in [PlanTypes.DC.value]: raise SDKException('Plan', '102', "Add/Modify Schedule is supported only for DC Plan via CvpySDK") if ops_type not in [2]: raise SDKException('Plan', '102', "Schedule operation type provided is not supported") request_json = copy.deepcopy(PlanConstants.PLAN_SCHEDULE_REQUEST_JSON[self.plan_type]) request_json['summary']['plan']['planId'] = int(self.plan_id) request_json['schedule']['associations'][0]['entityId'] = int(self.plan_id) request_json['schedule']['task']['taskName'] = f"Cvpysdk created Schedule policy for plan - {self.plan_name}" request_json['schedule']['subTasks'][0]['subTask'][ 'subTaskName'] = schedule_name request_json['schedule']['subTasks'][0]['pattern'] = pattern_json request_json['schedule']['subTasks'][0]['options']['adminOpts']['contentIndexingOption']['operationType'] = ops_type if self._dc_plan_props['targetApps'][0] == TargetApps.FS.value: request_json['schedule']['subTasks'][0]['subTask']['operationType'] = 5022 self._update_plan_props(request_json)
-
Shares plan with given user or group by associating with given role
Args
user_or_group_name (str) – User or Group name to which we are sharing
role_name (str) – Role name which needs to associated with
ops_type (int) – Operation type
Default : 1 (Add) Supported : 1 (Add) 3 (Delete)
Returns
None
Raises
SDKException:
if input is not valid if failed to do sharing if user/group/role not exists on commcell if failed to get exisitng association details
Expand source code Browse git
def share(self, user_or_group_name, role_name, is_user=True, ops_type=1): """Shares plan with given user or group by associating with given role Args: user_or_group_name (str) -- User or Group name to which we are sharing role_name (str) -- Role name which needs to associated with ops_type (int) -- Operation type Default : 1 (Add) Supported : 1 (Add) 3 (Delete) Returns: None Raises: SDKException: if input is not valid if failed to do sharing if user/group/role not exists on commcell if failed to get exisitng association details """ if not isinstance(user_or_group_name, str) or not isinstance(role_name, str): raise SDKException('Plan', '101') if ops_type not in [1, 3]: raise SDKException('Plan', '102', "Sharing operation type provided is not supported") if is_user: if not self._commcell_object.users.has_user(user_or_group_name): raise SDKException('Plan', '102', "User doesn't exists in the commcell") if not self._commcell_object.roles.has_role(role_name=role_name): raise SDKException('Plan', '102', "Role doesn't exists in the commcell") request_json = copy.deepcopy(PlanConstants.PLAN_SHARE_REQUEST_JSON) association_response = None if ops_type == 1 and len(self.security_associations) > 1: association_request_json = copy.deepcopy(PlanConstants.PLAN_SHARE_REQUEST_JSON) del association_request_json['securityAssociations'] association_request_json['entityAssociated']['entity'][0]['entityId'] = int(self._plan_id) flag, response = self._cvpysdk_object.make_request( 'GET', self._API_SECURITY_ENTITY % (self._plan_entity_type, int( self._plan_id)), association_request_json) if flag: if response.json() and 'securityAssociations' in response.json(): association_response = response.json( )['securityAssociations'][0]['securityAssociations']['associations'] else: raise SDKException('Plan', '102', 'Failed to get existing security associations') else: response_string = self._commcell_object._update_response_(response.text) raise SDKException('Response', '101', response_string) external_user = False if '\\' in user_or_group_name: external_user = True if is_user: user_obj = self._commcell_object.users.get(user_or_group_name) user_id = user_obj.user_id request_json['securityAssociations']['associations'][0]['userOrGroup'][0]['userId'] = int(user_id) request_json['securityAssociations']['associations'][0]['userOrGroup'][0]['_type_'] = 13 request_json['securityAssociations']['associations'][0]['userOrGroup'][0]['userName'] = user_or_group_name elif external_user: request_json['securityAssociations']['associations'][0]['userOrGroup'][0]['groupId'] = 0 request_json['securityAssociations']['associations'][0]['userOrGroup'][0]['_type_'] = 62 request_json['securityAssociations']['associations'][0]['userOrGroup'][0][ 'externalGroupName'] = user_or_group_name else: grp_obj = self._commcell_object.user_groups.get(user_or_group_name) grp_id = grp_obj.user_group_id request_json['securityAssociations']['associations'][0]['userOrGroup'][0]['userGroupId'] = int(grp_id) request_json['securityAssociations']['associations'][0]['userOrGroup'][0]['_type_'] = 15 request_json['securityAssociations']['associations'][0]['userOrGroup'][0][ 'userGroupName'] = user_or_group_name request_json['entityAssociated']['entity'][0]['entityId'] = int(self._plan_id) request_json['securityAssociations']['associationsOperationType'] = ops_type role_obj = self._commcell_object.roles.get(role_name) request_json['securityAssociations']['associations'][0]['properties']['role']['roleId'] = role_obj.role_id request_json['securityAssociations']['associations'][0]['properties']['role']['roleName'] = role_obj.role_name # Associate existing associations to the request if ops_type == 1 and len(self.security_associations) > 1: request_json['securityAssociations']['associations'].extend(association_response) flag, response = self._cvpysdk_object.make_request( 'POST', self._API_SECURITY, request_json ) if flag: if response.json() and 'response' in response.json(): response_json = response.json()['response'][0] error_code = response_json['errorCode'] if error_code != 0: error_message = response_json['errorString'] raise SDKException( 'Plan', '102', error_message) self.refresh() else: raise SDKException('Plan', '105') else: response_string = self._commcell_object._update_response_(response.text) raise SDKException('Response', '101', response_string)
def update_backup_content(self, content, request_type='OVERWRITE')
-
Args
content (dict) : dictionary with backup content details.
example
- content = { 'Windows' : { 'Content' : ['\%Pictures%', '\%Desktop%'], 'Exclude' : ['\%Documents%'], 'Backup System State' : True }, 'Linux' : { 'Content' : ['/%Pictures%'], 'Exclude' : ['/%Documents%'] }, 'Mac' : { 'Content' : ['/%Pictures%'], 'Exclude' : ['/%Documents%'] } }
request_type (str) : Supported values 'OVERWRITE' (default), 'UPDATE', 'DELETE'.
For plans created from SP32, Please use below format of content example: content = { "windowsIncludedPaths": ["Desktop"], "windowsExcludedPaths": ["Music"], "windowsFilterToExcludePaths": ["Videos"], "backupSystemState": True, "useVSSForSystemState": True, "backupSystemStateOnlyWithFullBackup": False }
For unix and mac, replace key name with respective os name, IncludedPaths, ExcludedPaths, **FilterToExcludePaths
Expand source code Browse git
def update_backup_content(self, content, request_type = 'OVERWRITE'): """ Args: content (dict) : dictionary with backup content details. example: content = { 'Windows' : { 'Content' : ['\\%Pictures%', '\\%Desktop%'], 'Exclude' : ['\\%Documents%'], 'Backup System State' : True }, 'Linux' : { 'Content' : ['/%Pictures%'], 'Exclude' : ['/%Documents%'] }, 'Mac' : { 'Content' : ['/%Pictures%'], 'Exclude' : ['/%Documents%'] } } request_type (str) : Supported values 'OVERWRITE' (default), 'UPDATE', 'DELETE'. For plans created from SP32, Please use below format of content example: content = { "windowsIncludedPaths": ["Desktop"], "windowsExcludedPaths": ["Music"], "windowsFilterToExcludePaths": ["Videos"], "backupSystemState": True, "useVSSForSystemState": True, "backupSystemStateOnlyWithFullBackup": False } For unix and mac, replace key name with respective os name, **IncludedPaths, **ExcludedPaths, **FilterToExcludePaths """ update_request_type = { "OVERWRITE": 1, "UPDATE": 2, "DELETE": 3 } subclients = self.policy_subclient_ids() if not subclients: self.update_content_policy(content) return for os, value in content.items(): request_json = { "subClientProperties": { "fsExcludeFilterOperationType": update_request_type.get(request_type, 1), "fsContentOperationType" : update_request_type.get(request_type, 1) } } request_url = self._commcell_object._services['SUBCLIENT'] % subclients[os] contents = list() for key, val in value.items(): if key.lower() == 'content': for path in val: contents.append({"path" : path}) if key.lower() == 'exclude': for path in val: contents.append({"excludePath" : path}) if os == 'Windows' and key == 'Backup System State': request_json['subClientProperties']['fsSubClientProp'] = {'backupSystemState' : val} if contents: request_json['subClientProperties']['content'] = contents flag, response = self._commcell_object._cvpysdk_object.make_request('POST', request_url, request_json) if flag: if response.json() and 'response' in response.json(): errorCode = response.json()['response'][0].get('errorCode') if errorCode: raise SDKException('Plan', 102, 'Failed to Change Content of Plan.') else: raise SDKException('Plan', 102, 'Failed to get subclient Ids.') else: raise SDKException('Plan', 102, response.text)
def update_content_policy(self, content)
-
Args
content (dict) : dictionary with backup content details.
example: content = { "windowsIncludedPaths": ["Desktop"], "windowsExcludedPaths": ["Music"], "windowsFilterToExcludePaths": ["Videos"], "unixIncludedPaths": ["Desktop"], "unixExcludedPaths": ["Music"], "unixFilterToExcludePaths": ["Videos"], "macIncludedPaths": ["Desktop"], "macExcludedPaths": ["Music"], "macFilterToExcludePaths": ["Videos"], "backupSystemState": True, "useVSSForSystemState": True, "backupSystemStateOnlyWithFullBackup": False }
For unix and mac, replace key name with respective os name, IncludedPaths, ExcludedPaths, **FilterToExcludePaths
Expand source code Browse git
def update_content_policy(self, content): """ Args: content (dict) : dictionary with backup content details. example: content = { "windowsIncludedPaths": ["Desktop"], "windowsExcludedPaths": ["Music"], "windowsFilterToExcludePaths": ["Videos"], "unixIncludedPaths": ["Desktop"], "unixExcludedPaths": ["Music"], "unixFilterToExcludePaths": ["Videos"], "macIncludedPaths": ["Desktop"], "macExcludedPaths": ["Music"], "macFilterToExcludePaths": ["Videos"], "backupSystemState": True, "useVSSForSystemState": True, "backupSystemStateOnlyWithFullBackup": False } For unix and mac, replace key name with respective os name, **IncludedPaths, **ExcludedPaths, **FilterToExcludePaths """ request_json = { 'backupContent' : content } request_url = self._commcell_object._services['V4_SERVER_PLAN'] % self.plan_id flag, response = self._commcell_object._cvpysdk_object.make_request('PUT', request_url, request_json) if flag: if response.json(): if response.json()['errorCode']: raise SDKException('Plan', 102, response.json()['errorMessage']) else: raise SDKException('Plan', 102, 'Failed to update backup content') else: raise SDKException('Plan', 102, response.text)
def update_security_associations(self, associations_list, is_user=True, request_type=None, external_group=False)
-
Adds the security association on the plan object
Args
associations_list (list) – list of users to be associated Example: associations_list = [ { 'user_name': user1, 'role_name': role1 }, { 'user_name': user2, 'role_name': role2 } ]
is_user (bool) – True or False. set is_user = False, If associations_list made up of user groups request_type (str) – eg : 'OVERWRITE' or 'UPDATE' or 'DELETE', Default will be OVERWRITE operation external_group (bool) – True or False, set external_group = True. If Security associations is being done on External User Groups
Raises
SDKException: if association is not of List type
Expand source code Browse git
def update_security_associations(self, associations_list, is_user = True, request_type = None, external_group = False): """ Adds the security association on the plan object Args: associations_list (list) -- list of users to be associated Example: associations_list = [ { 'user_name': user1, 'role_name': role1 }, { 'user_name': user2, 'role_name': role2 } ] is_user (bool) -- True or False. set is_user = False, If associations_list made up of user groups request_type (str) -- eg : 'OVERWRITE' or 'UPDATE' or 'DELETE', Default will be OVERWRITE operation external_group (bool) -- True or False, set external_group = True. If Security associations is being done on External User Groups Raises: SDKException: if association is not of List type """ if not isinstance(associations_list, list): raise SDKException('Plan', '102') SecurityAssociation(self._commcell_object, self)._add_security_association(associations_list, is_user, request_type, external_group)
class PlanTypes (value, names=None, *, module=None, qualname=None, type=None, start=1)
-
Class Enum to represent different plan types
Expand source code Browse git
class PlanTypes(Enum): """Class Enum to represent different plan types""" Any = 0 DLO = 1 MSP = 2 FS = 3 SNAP = 4 VSA = 5 EXCHANGE = 6 DC = 7 EDISCOVERY = 8 ARCHIVER = 9
Ancestors
- enum.Enum
Class variables
var ARCHIVER
var Any
var DC
var DLO
var EDISCOVERY
var EXCHANGE
var FS
var MSP
var SNAP
var VSA
class Plans (commcell_object)
-
Class for representing all the plans in the commcell.
Initialize object of Plans class.
Args
commcell_object (object) – instance of the Commcell class
Returns
object - instance of Plans class
Expand source code Browse git
class Plans(object): """Class for representing all the plans in the commcell.""" def __init__(self, commcell_object): """Initialize object of Plans class. Args: commcell_object (object) -- instance of the Commcell class Returns: object - instance of Plans class """ self._commcell_object = commcell_object self._cvpysdk_object = commcell_object._cvpysdk_object self._services = commcell_object._services self._update_response_ = commcell_object._update_response_ self._PLANS = self._services['PLANS'] self._plans = None self.refresh() def __str__(self): """Representation string consisting of all plans of the Commcell. Returns: str - string of all the plans for a commcell """ representation_string = "{:^5}\t{:^50}\n\n".format('S. No.', 'Plan') for index, plan in enumerate(self._plans): sub_str = '{:^5}\t{:30}\n'.format(index + 1, plan) representation_string += sub_str return representation_string.strip() def __repr__(self): """Representation string for the instance of the Plans class.""" return "Plans class instance for Commcell: '{0}'".format( self._commcell_object.commserv_name ) def __len__(self): """Returns the number of the plans added to the Commcell.""" return len(self.all_plans) def __getitem__(self, value): """Returns the name of the plan for the given plan ID or the details of the plan for given plan Name. Args: value (str / int) -- Name or ID of the plan Returns: str - name of the plan, if the plan id was given dict - dict of details of the plan, if plan name was given Raises: IndexError: no plan exists with the given Name / Id """ value = str(value) if value in self.all_plans: return self.all_plans[value] else: try: return list(filter(lambda x: x[1]['id'] == value, self.all_plans.items()))[0][0] except IndexError: raise IndexError('No plan exists with the given Name / Id') def _get_plans(self): """Gets all the plans associated with the commcell Returns: dict - consists of all plans in the commcell { "plan1_name": plan1_id, "plan2_name": plan2_id } Raises: SDKException: if response is empty if response is not success """ flag, response = self._cvpysdk_object.make_request('GET', self._PLANS) if flag: plans = {} if response.json() and 'plans' in response.json(): response_value = response.json()['plans'] for temp in response_value: temp_name = temp['plan']['planName'].lower() temp_id = str(temp['plan']['planId']).lower() plans[temp_name] = temp_id return plans else: response_string = self._update_response_(response.text) raise SDKException('Response', '101', response_string) def _get_plan_template(self, plan_sub_type, plan_type="MSP"): """Gets the Plan subtype's JSON template. Args: plan_sub_type (str) -- Sub-type of plan to add "Server" - Server Plans "FSServer" - File System Plans "Laptop" - Laptop Plans plan_type (str) -- Type of plan to add default: "MSP" Returns: str - JSON string of the Plan's template Raises: SDKException: if type or subtype of the plan does not exist if there is a failure in getting the template """ if not (isinstance(plan_sub_type, str) and isinstance(plan_type, str)): raise SDKException('Plan', '101') else: template_url = self._services['GET_PLAN_TEMPLATE'] % (plan_type, plan_sub_type) flag, response = self._cvpysdk_object.make_request('GET', template_url) if flag: if response.json() and 'plan' in response.json(): return response.json() else: raise SDKException('Plan', '102', 'Failed to get Plan template') else: response_string = self._update_response_(response.text) raise SDKException('Response', '101', response_string) @property def all_plans(self): """Returns the dictionary consisting of all the plans added to the Commcell. dict - consists of all the plans configured on the commcell { "plan1_name": plan1_id, "plan2_name": plan2_id } """ return self._plans def filter_plans(self, plan_type, company_name = None): """ Returns the dictionary consisting of specified type and company plans. Args: plan_type (str) -- Type of plan ['DLO', 'Server', 'Laptop', 'Database', 'FSServer', 'FSIBMiVTL', 'Snap', 'VSAServer', 'VSAReplication', 'ExchangeUser', 'ExchangeJournal', 'Office365', 'Dynamics365', 'DataClassification', 'Archiver'] company_name (str) -- To filter plans based on company. For Commcell, company_name = "". Default will return all plans Returns: dict - consists of all the plans with specified types configured on the commcell { "plan1_name": plan1_id, "plan2_name": plan2_id } Raises: SDKException: if input data type is not valid if invalid plan type is passed as parameter if failed to get the response """ plan_type_subtype = { "dlo" : ("1", "16777223"), "server" : ("2", "33554437"), "laptop" : ("2", "33554439"), "database" : ("2", "33579013"), "fsserver" : ("3", "50331655"), "fsibmivtl" : ("3", "50331653"), "snap" : ("4", "67108869"), "vsaserver" : ("5", "83886085"), "vsareplication" : ("5", "83918853"), "exchangeuser" : ("6", "100859907"), "exchangejournal" : ("6", "100794372"), "office365" : ("6", "100859937"), "dynamics365" : ("6", "100794391"), "dataclassification" : ("7", "117506053"), "archiver" : ("9", "150994951") } if not isinstance(plan_type, str): raise SDKException('Plan', '101') elif plan_type.lower() not in plan_type_subtype: raise SDKException('Plan', '102', 'Invalid Plan Type Passed as Parameter') else: template_url = self._services['GET_PLANS'] % plan_type_subtype[plan_type.lower()] flag, response = self._cvpysdk_object.make_request('GET', template_url) if flag: result = dict() if 'plans' in response.json(): for plan in response.json()['plans']: if company_name is None: result[plan['plan']['planName']] = plan['plan']['planId'] else: if plan['plan']['entityInfo']['companyName'].lower() == company_name.lower(): result[plan['plan']['planName']] = plan['plan']['planId'] return result else: response_string = self._update_response_(response.text) raise SDKException('Response', '101', response_string) def has_plan(self, plan_name): """Checks if a plan exists in the commcell with the input plan name. Args: plan_name (str) -- name of the plan Returns: bool - boolean output whether the plan exists in the commcell or not Raises: SDKException: if type of the plan name argument is not string """ if not isinstance(plan_name, str): raise SDKException('Plan', '101') return self._plans and plan_name.lower() in self._plans def get(self, plan_name): """Returns a plan object of the specified plan name. Args: plan_name (str) -- name of the plan Returns: object - instance of the Plan class for the the given plan name Raises: SDKException: if type of the plan name argument is not string if no plan exists with the given name """ if not isinstance(plan_name, str): raise SDKException('Plan', '101') else: plan_name = plan_name.lower() if self.has_plan(plan_name): return Plan( self._commcell_object, plan_name, self._plans[plan_name] ) raise SDKException( 'Plan', '102', 'No plan exists with name: {0}'.format( plan_name) ) def delete(self, plan_name): """Deletes the plan from the commcell. Args: plan_name (str) -- name of the plan to remove from the commcell Raises: SDKException: if type of the plan name argument is not string if failed to delete plan if response is empty if response is not success if no plan exists with the given name """ if not isinstance(plan_name, str): raise SDKException('Plan', '101') else: plan_name = plan_name.lower() if self.has_plan(plan_name): plan_id = self._plans[plan_name] delete_plan = self._services['DELETE_PLAN'] % (plan_id) flag, response = self._cvpysdk_object.make_request('DELETE', delete_plan) error_code = 0 if flag: if 'error' in response.json(): if isinstance(response.json()['error'], list): error_code = response.json()['error'][0]['status']['errorCode'] else: error_code = response.json()['errorCode'] if error_code != 0: o_str = 'Failed to delete plan' if isinstance(response.json()['error'], list): error_message = response.json()['error'][0]['status']['errorMessage'] else: error_message = response.json()['errorMessage'] o_str += '\nError: "{0}"'.format(error_message) raise SDKException('Plan', '102', o_str) else: # initialize the plan again # so the plan object has all the plan self.refresh() else: response_string = self._update_response_(response.text) raise SDKException('Response', '101', response_string) else: raise SDKException( 'Plan', '102', 'No plan exists with name: {0}'.format(plan_name) ) def add(self, plan_name, plan_sub_type, storage_pool_name=None, sla_in_minutes=1440, override_entities=None): """Adds a new Plan to the CommCell. Args: plan_name (str) -- name of the new plan to add plan_sub_type (str) -- Type of plan to add "Server" - Server Plans "FSServer" - File System Plans "Laptop" - Laptop Plans "ExchangeUser" - Exchange Mailbox Plan storage_pool_name (str) -- name of the storage pool to be used for the plan sla_in_minutes (int) -- Backup SLA in hours default: 1440 override_entities (dict) -- Specify the entities with respective inheritance values. default: None { 'privateEntities': [1, 4], 'enforcedEntities': [256, 512, 1024] } - where, privateEntities are set when respective entity overriding is required enforcedEntities are set when respective entity overriding is not allowed left blank if overriding is optional - entity IDs, 1 - Storage 4 - RPO/Schedules 256 - Windows content 512 - Unix content 1024 - Mac content Returns: object - instance of the Plan class created by this method Raises: SDKException: if input parameters are incorrect if Plan already exists """ if not (isinstance(plan_name, str) and isinstance(plan_sub_type, str)): raise SDKException('Plan', '101') else: if self.has_plan(plan_name): raise SDKException( 'Plan', '102', 'Plan "{0}" already exists'.format(plan_name) ) if not plan_sub_type == 'ExchangeUser': storage_pool_obj = self._commcell_object.storage_pools.get( storage_pool_name) is_dedupe = True if 'dedupDBDetailsList' \ not in storage_pool_obj._storage_pool_properties['storagePoolDetails']: is_dedupe = False request_json = self._get_plan_template(plan_sub_type, "MSP") request_json['plan']['summary']['rpoInMinutes'] = sla_in_minutes request_json['plan']['summary']['description'] = "Created from CvPySDK." request_json['plan']['summary']['plan']['planName'] = plan_name template_schedules = [schedule['subTask']['subTaskName'] for schedule in request_json['plan']['schedule']['subTasks']] if 'Synthetic Fulls' in template_schedules: synth_full_index = template_schedules.index('Synthetic Fulls') request_json['plan']['schedule']['subTasks'][synth_full_index]['options']['commonOpts'][ 'automaticSchedulePattern'].update({ 'minBackupInterval': 0, 'maxBackupIntervalMinutes': 0, 'minSyncInterval': 0, 'minSyncIntervalMinutes': 0 }) request_json['plan']['schedule']['subTasks'][synth_full_index]['options']['commonOpts'][ 'automaticSchedulePattern']['ignoreOpWindowPastMaxInterval'] = True del request_json['plan']['schedule']['task']['taskName'] if plan_sub_type != 'ExchangeUser': request_json['plan']['storage']['copy'][0]['useGlobalPolicy'] = { "storagePolicyId": int(storage_pool_obj.storage_pool_id) } if is_dedupe: request_json['plan']['storage']['copy'][0]['dedupeFlags'][ 'useGlobalDedupStore'] = 1 else: del request_json['plan']['storage']['copy'][0]['storagePolicyFlags'] del request_json['plan']['storage']['copy'][0]['dedupeFlags'][ 'enableDeduplication'] del request_json['plan']['storage']['copy'][0]['dedupeFlags'][ 'enableClientSideDedup'] del request_json['plan']['storage']['copy'][0]['DDBPartitionInfo'] request_json['plan']['storage']['copy'][0]['extendedFlags'] = { 'useGlobalStoragePolicy': 1 } # Configurations for database and snap addons if plan_sub_type == "Server" and 'database' in request_json['plan']: request_json['plan']['database']['storageLog']['copy'][0]['dedupeFlags'][ 'useGlobalDedupStore'] = 1 request_json['plan']['database']['storageLog']['copy'][0].pop( 'DDBPartitionInfo', None ) request_json['plan']['database']['storageLog']['copy'][0]['dedupeFlags'][ 'useGlobalPolicy'] = { "storagePolicyId": int(storage_pool_obj.storage_pool_id) } request_json['plan']['storage']['copy'][1]['extendedFlags'] = { 'useGlobalStoragePolicy': 1 } request_json['plan']['storage']['copy'][1]['useGlobalPolicy'] = { "storagePolicyId": int(storage_pool_obj.storage_pool_id) } # Enable full backup schedule for subtask in request_json['plan']['schedule']['subTasks']: if 'flags' in subtask['subTask'] and subtask['subTask']['flags'] == 65536: import copy full_schedule = copy.deepcopy(subtask) del copy full_schedule['subTask'].update({ 'subTaskName': 'Full backup schedule', 'flags': 4194304 }) full_schedule['pattern'].update({ 'freq_type': 4, 'freq_interval': 1, 'name': 'Full backup schedule', 'active_end_time': 0 }) full_schedule['options']['backupOpts']['backupLevel'] = 'FULL' request_json['plan']['schedule']['subTasks'].append(full_schedule) break if isinstance(override_entities, dict): request_json['plan']['summary']['restrictions'] = 0 request_json['plan']['inheritance'] = { 'isSealed': False } if 'enforcedEntities' in override_entities: request_json['plan']['inheritance']['enforcedEntities'] = override_entities[ 'enforcedEntities'] if 'privateEntities' in override_entities: request_json['plan']['inheritance']['privateEntities'] = override_entities[ 'privateEntities'] else: request_json['plan']['summary']['restrictions'] = 1 request_json['plan']['inheritance'] = { 'isSealed': True } headers = self._commcell_object._headers.copy() headers['LookupNames'] = 'False' flag, response = self._cvpysdk_object.make_request( 'POST', self._PLANS, request_json, headers=headers ) if flag: if response.json(): response_value = response.json() error_message = None error_code = None if 'errors' in response_value: error_code = response_value['errors'][0]['status']['errorCode'] error_message = response_value['errors'][0]['status']['errorMessage'] if error_code > 1: o_str = 'Failed to create new Plan\nError: "{0}"'.format( error_message ) raise SDKException('Plan', '102', o_str) if 'plan' in response_value: plan_name = response_value['plan']['summary']['plan']['planName'] # initialize the plans again # so that the plans object has all the plans self.refresh() # with plan delete storage policy associated might be deleted # initialize storage policy again self._commcell_object.storage_policies.refresh() return self.get(plan_name) else: o_str = ('Failed to create new plan due to error code: "{0}"\n' 'Please check the documentation for ' 'more details on the error').format(error_code) raise SDKException('Plan', '102', o_str) else: raise SDKException('Response', 102) else: response_string = self._update_response_(response.text) raise SDKException('Response', '101', response_string) def get_eligible_plans(self, entities): """Returns dict of plans that are eligible for the specified entities Args: entities (dict) - dictionary containing entities as keys and their respective IDs as values { 'clientId': id, 'appId': id, 'backupsetId': id } Returns: dict - dict of eligible plans Raises: SDKException: if there is an error in the response """ query = '' for i in entities: query += '{0}={1}&'.format(i, entities[i]) requset_url = self._services['ELIGIBLE_PLANS'] % query[0:-1] flag, response = self._cvpysdk_object.make_request('GET', requset_url) del query if flag: plans = {} if response.json() and 'plans' in response.json(): response_value = response.json()['plans'] for temp in response_value: temp_name = temp['plan']['planName'].lower() temp_id = str(temp['plan']['planId']).lower() plans[temp_name] = temp_id return plans else: response_string = self._update_response_(response.text) raise SDKException('Response', '101', response_string) def get_supported_solutions(self): """Method to get supported solutions for plans""" flag, response = self._cvpysdk_object.make_request( 'GET', self._services['PLAN_SUPPORTED_SOLUTIONS'] ) if not flag: raise SDKException('Response', '101', self._update_response_(response.text)) if response.json() and 'id' in response.json(): return {solution['name']: solution['id'] for solution in response.json()['id']} else: raise SDKException('Response', '102') def refresh(self): """Refresh the plans associated with the Commcell.""" self._plans = self._get_plans() def add_data_classification_plan(self, plan_name, index_server, target_app=TargetApps.FSO, **kwargs): """Adds data classification plan to the commcell Args: plan_name (str) -- Name of plan index_server (str) -- Index server name target_app (enum) -- Target app for this plan cvpysdk.activateapps.constants.TargetApps **kwargs index_content (bool) -- Speifies whether to index content or not to index server content_analyzer (list) -- list of Content analyzer client name entity_list (list) -- list of entities which needs to be extracted classifier_list (list) -- list of classifier which needs to be classified enable_ocr (bool) -- specifies whether OCR is enabled or not ocr_language (int) -- Language to be used when doing OCR Default : English (Value-1) Supported Languages: ENGLISH = 1, HEBREW = 2, SPANISH = 3, FRENCH = 4, ITALIAN = 5, DANISH = 6 include_docs (str) -- Include documents type separated by comma exclude_path (list) -- List of paths which needs to be excluded min_doc_size (int) -- Minimum document size in MB max_doc_size (int) -- Maximum document size in MB Returns: object - Plan object Raises: SDKException: if input is not valid if failed to create plan if failed to find entities/classifier details """ extraction_policy_list = [] if not (isinstance(plan_name, str) and isinstance(index_server, str)): raise SDKException('Plan', '101') request_json = self._get_plan_template("DataClassification", "MSP") request_json['plan']['summary']['description'] = "DC Plan Created from CvPySDK." request_json['plan']['summary']['plan']['planName'] = plan_name request_json['plan']['options'] = { "enableThreatAnalysis": False, "targetApps": [ target_app.value ] } index_server_client_id = self._commcell_object.index_servers.get(index_server).index_server_client_id request_json['plan']['eDiscoveryInfo']['analyticsIndexServer'] = { 'clientId': index_server_client_id } if target_app.value == TargetApps.FSO.value: del request_json['plan']['ciPolicy']['detail']['ciPolicy']['filters'] request_json['plan']['ciPolicy']['detail']['ciPolicy']['opType'] = PlanConstants.INDEXING_ONLY_METADATA elif target_app.value == TargetApps.SDG.value: if 'content_analyzer' not in kwargs: raise SDKException('Plan', '103') ca_list = [] for ca in kwargs.get('content_analyzer', []): ca_client_id = self._commcell_object.content_analyzers.get(ca).client_id ca_list.append({ 'clientId': ca_client_id }) request_json['plan']['eDiscoveryInfo']['contentAnalyzerClient'] = ca_list if 'entity_list' not in kwargs and 'classifier_list' not in kwargs: raise SDKException('Plan', '104') activate_obj = self._commcell_object.activate if 'entity_list' in kwargs or 'classifier_list' in kwargs: entity_mgr_obj = activate_obj.entity_manager() # classifier is also an activate entity with type alone different so append this to entity list itself entity_list = [] for entity in kwargs.get('entity_list', []): entity_list.append(entity) for entity in kwargs.get('classifier_list', []): entity_list.append(entity) for entity in entity_list: entity_obj = entity_mgr_obj.get(entity) extraction_policy_list.append(entity_obj.container_details) request_json['plan']['eePolicy']['policyType'] = 3 request_json['plan']['eePolicy']['flags'] = 8 request_json['plan']['eePolicy']['detail'] = { "eePolicy": { "copyPrecedence": 0, "extractionPolicyType": 6, # container entities "extractionPolicy": { "extractionPolicyList": extraction_policy_list } } } if 'index_content' in kwargs: request_json['plan']['ciPolicy']['detail']['ciPolicy']['opType'] = kwargs.get( 'index_content', PlanConstants.INDEXING_METADATA_AND_CONTENT) if 'enable_ocr' in kwargs: request_json['plan']['ciPolicy']['detail']['ciPolicy']['enableImageExtraction'] = kwargs.get( 'enable_ocr', False) request_json['plan']['ciPolicy']['detail']['ciPolicy']['ocrLanguages'] = [kwargs.get('ocr_language', 1)] if 'include_docs' in kwargs: request_json['plan']['ciPolicy']['detail']['ciPolicy']['filters']['fileFilters']['includeDocTypes'] = kwargs.get( 'include_docs', PlanConstants.DEFAULT_INCLUDE_DOC_TYPES) if 'min_doc_size' in kwargs: request_json['plan']['ciPolicy']['detail']['ciPolicy']['filters']['fileFilters']['minDocSize'] = kwargs.get( 'min_doc_size', PlanConstants.DEFAULT_MIN_DOC_SIZE) if 'max_doc_size' in kwargs: request_json['plan']['ciPolicy']['detail']['ciPolicy']['filters']['fileFilters'][ 'maxDocSize'] = kwargs.get('max_doc_size', PlanConstants.DEFAULT_MAX_DOC_SIZE) if 'exclude_path' in kwargs: request_json['plan']['ciPolicy']['detail']['ciPolicy']['filters']['fileFilters'][ 'excludePaths'] = kwargs.get('exclude_path', PlanConstants.DEFAULT_EXCLUDE_LIST) headers = self._commcell_object._headers.copy() headers['LookupNames'] = 'False' flag, response = self._cvpysdk_object.make_request( 'POST', self._PLANS, request_json, headers=headers ) if flag: if response.json(): response_value = response.json() error_message = None error_code = None if 'errors' in response_value: error_code = response_value['errors'][0]['status']['errorCode'] error_message = response_value['errors'][0]['status']['errorMessage'] if error_code > 1: o_str = 'Failed to create new Plan\nError: "{0}"'.format( error_message ) raise SDKException('Plan', '102', o_str) if 'plan' in response_value: plan_name = response_value['plan']['summary']['plan']['planName'] # initialize the plans again self.refresh() return self.get(plan_name) else: o_str = ('Failed to create new plan due to error code: "{0}"\n' 'Please check the documentation for ' 'more details on the error').format(error_code) raise SDKException('Plan', '102', o_str) else: raise SDKException('Response', 102) else: response_string = self._update_response_(response.text) raise SDKException('Response', '101', response_string)
Instance variables
var all_plans
-
Returns the dictionary consisting of all the plans added to the Commcell.
dict - consists of all the plans configured on the commcell
{ "plan1_name": plan1_id, "plan2_name": plan2_id }
Expand source code Browse git
@property def all_plans(self): """Returns the dictionary consisting of all the plans added to the Commcell. dict - consists of all the plans configured on the commcell { "plan1_name": plan1_id, "plan2_name": plan2_id } """ return self._plans
Methods
def add(self, plan_name, plan_sub_type, storage_pool_name=None, sla_in_minutes=1440, override_entities=None)
-
Adds a new Plan to the CommCell.
Args
plan_name (str) – name of the new plan to add
plan_sub_type (str) – Type of plan to add
"Server" - Server Plans "FSServer" - File System Plans "Laptop" - Laptop Plans "ExchangeUser" - Exchange Mailbox Plan
storage_pool_name (str) – name of the storage pool to be used for the plan
sla_in_minutes (int) – Backup SLA in hours
default: 1440
override_entities (dict) – Specify the entities with respective inheritance values.
default: None { 'privateEntities': [1, 4], 'enforcedEntities': [256, 512, 1024] } - where, privateEntities are set when respective entity overriding is required enforcedEntities are set when respective entity overriding is not allowed left blank if overriding is optional - entity IDs, 1 - Storage 4 - RPO/Schedules 256 - Windows content 512 - Unix content 1024 - Mac content
Returns
object - instance of the Plan class created by this method
Raises
SDKException: if input parameters are incorrect
if Plan already exists
Expand source code Browse git
def add(self, plan_name, plan_sub_type, storage_pool_name=None, sla_in_minutes=1440, override_entities=None): """Adds a new Plan to the CommCell. Args: plan_name (str) -- name of the new plan to add plan_sub_type (str) -- Type of plan to add "Server" - Server Plans "FSServer" - File System Plans "Laptop" - Laptop Plans "ExchangeUser" - Exchange Mailbox Plan storage_pool_name (str) -- name of the storage pool to be used for the plan sla_in_minutes (int) -- Backup SLA in hours default: 1440 override_entities (dict) -- Specify the entities with respective inheritance values. default: None { 'privateEntities': [1, 4], 'enforcedEntities': [256, 512, 1024] } - where, privateEntities are set when respective entity overriding is required enforcedEntities are set when respective entity overriding is not allowed left blank if overriding is optional - entity IDs, 1 - Storage 4 - RPO/Schedules 256 - Windows content 512 - Unix content 1024 - Mac content Returns: object - instance of the Plan class created by this method Raises: SDKException: if input parameters are incorrect if Plan already exists """ if not (isinstance(plan_name, str) and isinstance(plan_sub_type, str)): raise SDKException('Plan', '101') else: if self.has_plan(plan_name): raise SDKException( 'Plan', '102', 'Plan "{0}" already exists'.format(plan_name) ) if not plan_sub_type == 'ExchangeUser': storage_pool_obj = self._commcell_object.storage_pools.get( storage_pool_name) is_dedupe = True if 'dedupDBDetailsList' \ not in storage_pool_obj._storage_pool_properties['storagePoolDetails']: is_dedupe = False request_json = self._get_plan_template(plan_sub_type, "MSP") request_json['plan']['summary']['rpoInMinutes'] = sla_in_minutes request_json['plan']['summary']['description'] = "Created from CvPySDK." request_json['plan']['summary']['plan']['planName'] = plan_name template_schedules = [schedule['subTask']['subTaskName'] for schedule in request_json['plan']['schedule']['subTasks']] if 'Synthetic Fulls' in template_schedules: synth_full_index = template_schedules.index('Synthetic Fulls') request_json['plan']['schedule']['subTasks'][synth_full_index]['options']['commonOpts'][ 'automaticSchedulePattern'].update({ 'minBackupInterval': 0, 'maxBackupIntervalMinutes': 0, 'minSyncInterval': 0, 'minSyncIntervalMinutes': 0 }) request_json['plan']['schedule']['subTasks'][synth_full_index]['options']['commonOpts'][ 'automaticSchedulePattern']['ignoreOpWindowPastMaxInterval'] = True del request_json['plan']['schedule']['task']['taskName'] if plan_sub_type != 'ExchangeUser': request_json['plan']['storage']['copy'][0]['useGlobalPolicy'] = { "storagePolicyId": int(storage_pool_obj.storage_pool_id) } if is_dedupe: request_json['plan']['storage']['copy'][0]['dedupeFlags'][ 'useGlobalDedupStore'] = 1 else: del request_json['plan']['storage']['copy'][0]['storagePolicyFlags'] del request_json['plan']['storage']['copy'][0]['dedupeFlags'][ 'enableDeduplication'] del request_json['plan']['storage']['copy'][0]['dedupeFlags'][ 'enableClientSideDedup'] del request_json['plan']['storage']['copy'][0]['DDBPartitionInfo'] request_json['plan']['storage']['copy'][0]['extendedFlags'] = { 'useGlobalStoragePolicy': 1 } # Configurations for database and snap addons if plan_sub_type == "Server" and 'database' in request_json['plan']: request_json['plan']['database']['storageLog']['copy'][0]['dedupeFlags'][ 'useGlobalDedupStore'] = 1 request_json['plan']['database']['storageLog']['copy'][0].pop( 'DDBPartitionInfo', None ) request_json['plan']['database']['storageLog']['copy'][0]['dedupeFlags'][ 'useGlobalPolicy'] = { "storagePolicyId": int(storage_pool_obj.storage_pool_id) } request_json['plan']['storage']['copy'][1]['extendedFlags'] = { 'useGlobalStoragePolicy': 1 } request_json['plan']['storage']['copy'][1]['useGlobalPolicy'] = { "storagePolicyId": int(storage_pool_obj.storage_pool_id) } # Enable full backup schedule for subtask in request_json['plan']['schedule']['subTasks']: if 'flags' in subtask['subTask'] and subtask['subTask']['flags'] == 65536: import copy full_schedule = copy.deepcopy(subtask) del copy full_schedule['subTask'].update({ 'subTaskName': 'Full backup schedule', 'flags': 4194304 }) full_schedule['pattern'].update({ 'freq_type': 4, 'freq_interval': 1, 'name': 'Full backup schedule', 'active_end_time': 0 }) full_schedule['options']['backupOpts']['backupLevel'] = 'FULL' request_json['plan']['schedule']['subTasks'].append(full_schedule) break if isinstance(override_entities, dict): request_json['plan']['summary']['restrictions'] = 0 request_json['plan']['inheritance'] = { 'isSealed': False } if 'enforcedEntities' in override_entities: request_json['plan']['inheritance']['enforcedEntities'] = override_entities[ 'enforcedEntities'] if 'privateEntities' in override_entities: request_json['plan']['inheritance']['privateEntities'] = override_entities[ 'privateEntities'] else: request_json['plan']['summary']['restrictions'] = 1 request_json['plan']['inheritance'] = { 'isSealed': True } headers = self._commcell_object._headers.copy() headers['LookupNames'] = 'False' flag, response = self._cvpysdk_object.make_request( 'POST', self._PLANS, request_json, headers=headers ) if flag: if response.json(): response_value = response.json() error_message = None error_code = None if 'errors' in response_value: error_code = response_value['errors'][0]['status']['errorCode'] error_message = response_value['errors'][0]['status']['errorMessage'] if error_code > 1: o_str = 'Failed to create new Plan\nError: "{0}"'.format( error_message ) raise SDKException('Plan', '102', o_str) if 'plan' in response_value: plan_name = response_value['plan']['summary']['plan']['planName'] # initialize the plans again # so that the plans object has all the plans self.refresh() # with plan delete storage policy associated might be deleted # initialize storage policy again self._commcell_object.storage_policies.refresh() return self.get(plan_name) else: o_str = ('Failed to create new plan due to error code: "{0}"\n' 'Please check the documentation for ' 'more details on the error').format(error_code) raise SDKException('Plan', '102', o_str) else: raise SDKException('Response', 102) else: response_string = self._update_response_(response.text) raise SDKException('Response', '101', response_string)
def add_data_classification_plan(self, plan_name, index_server, target_app=TargetApps.FSO, **kwargs)
-
Adds data classification plan to the commcell
Args
plan_name (str) – Name of plan
index_server (str) – Index server name
target_app (enum) – Target app for this plan cvpysdk.activateapps.constants.TargetApps
**kwargs
index_content (bool) -- Speifies whether to index content or not to index server content_analyzer (list) -- list of Content analyzer client name entity_list (list) -- list of entities which needs to be extracted classifier_list (list) -- list of classifier which needs to be classified enable_ocr (bool) -- specifies whether OCR is enabled or not ocr_language (int) -- Language to be used when doing OCR Default : English (Value-1) Supported Languages: ENGLISH = 1, HEBREW = 2, SPANISH = 3, FRENCH = 4, ITALIAN = 5, DANISH = 6 include_docs (str) -- Include documents type separated by comma exclude_path (list) -- List of paths which needs to be excluded min_doc_size (int) -- Minimum document size in MB max_doc_size (int) -- Maximum document size in MB
Returns
object - Plan object
Raises
SDKException:
if input is not valid if failed to create plan if failed to find entities/classifier details
Expand source code Browse git
def add_data_classification_plan(self, plan_name, index_server, target_app=TargetApps.FSO, **kwargs): """Adds data classification plan to the commcell Args: plan_name (str) -- Name of plan index_server (str) -- Index server name target_app (enum) -- Target app for this plan cvpysdk.activateapps.constants.TargetApps **kwargs index_content (bool) -- Speifies whether to index content or not to index server content_analyzer (list) -- list of Content analyzer client name entity_list (list) -- list of entities which needs to be extracted classifier_list (list) -- list of classifier which needs to be classified enable_ocr (bool) -- specifies whether OCR is enabled or not ocr_language (int) -- Language to be used when doing OCR Default : English (Value-1) Supported Languages: ENGLISH = 1, HEBREW = 2, SPANISH = 3, FRENCH = 4, ITALIAN = 5, DANISH = 6 include_docs (str) -- Include documents type separated by comma exclude_path (list) -- List of paths which needs to be excluded min_doc_size (int) -- Minimum document size in MB max_doc_size (int) -- Maximum document size in MB Returns: object - Plan object Raises: SDKException: if input is not valid if failed to create plan if failed to find entities/classifier details """ extraction_policy_list = [] if not (isinstance(plan_name, str) and isinstance(index_server, str)): raise SDKException('Plan', '101') request_json = self._get_plan_template("DataClassification", "MSP") request_json['plan']['summary']['description'] = "DC Plan Created from CvPySDK." request_json['plan']['summary']['plan']['planName'] = plan_name request_json['plan']['options'] = { "enableThreatAnalysis": False, "targetApps": [ target_app.value ] } index_server_client_id = self._commcell_object.index_servers.get(index_server).index_server_client_id request_json['plan']['eDiscoveryInfo']['analyticsIndexServer'] = { 'clientId': index_server_client_id } if target_app.value == TargetApps.FSO.value: del request_json['plan']['ciPolicy']['detail']['ciPolicy']['filters'] request_json['plan']['ciPolicy']['detail']['ciPolicy']['opType'] = PlanConstants.INDEXING_ONLY_METADATA elif target_app.value == TargetApps.SDG.value: if 'content_analyzer' not in kwargs: raise SDKException('Plan', '103') ca_list = [] for ca in kwargs.get('content_analyzer', []): ca_client_id = self._commcell_object.content_analyzers.get(ca).client_id ca_list.append({ 'clientId': ca_client_id }) request_json['plan']['eDiscoveryInfo']['contentAnalyzerClient'] = ca_list if 'entity_list' not in kwargs and 'classifier_list' not in kwargs: raise SDKException('Plan', '104') activate_obj = self._commcell_object.activate if 'entity_list' in kwargs or 'classifier_list' in kwargs: entity_mgr_obj = activate_obj.entity_manager() # classifier is also an activate entity with type alone different so append this to entity list itself entity_list = [] for entity in kwargs.get('entity_list', []): entity_list.append(entity) for entity in kwargs.get('classifier_list', []): entity_list.append(entity) for entity in entity_list: entity_obj = entity_mgr_obj.get(entity) extraction_policy_list.append(entity_obj.container_details) request_json['plan']['eePolicy']['policyType'] = 3 request_json['plan']['eePolicy']['flags'] = 8 request_json['plan']['eePolicy']['detail'] = { "eePolicy": { "copyPrecedence": 0, "extractionPolicyType": 6, # container entities "extractionPolicy": { "extractionPolicyList": extraction_policy_list } } } if 'index_content' in kwargs: request_json['plan']['ciPolicy']['detail']['ciPolicy']['opType'] = kwargs.get( 'index_content', PlanConstants.INDEXING_METADATA_AND_CONTENT) if 'enable_ocr' in kwargs: request_json['plan']['ciPolicy']['detail']['ciPolicy']['enableImageExtraction'] = kwargs.get( 'enable_ocr', False) request_json['plan']['ciPolicy']['detail']['ciPolicy']['ocrLanguages'] = [kwargs.get('ocr_language', 1)] if 'include_docs' in kwargs: request_json['plan']['ciPolicy']['detail']['ciPolicy']['filters']['fileFilters']['includeDocTypes'] = kwargs.get( 'include_docs', PlanConstants.DEFAULT_INCLUDE_DOC_TYPES) if 'min_doc_size' in kwargs: request_json['plan']['ciPolicy']['detail']['ciPolicy']['filters']['fileFilters']['minDocSize'] = kwargs.get( 'min_doc_size', PlanConstants.DEFAULT_MIN_DOC_SIZE) if 'max_doc_size' in kwargs: request_json['plan']['ciPolicy']['detail']['ciPolicy']['filters']['fileFilters'][ 'maxDocSize'] = kwargs.get('max_doc_size', PlanConstants.DEFAULT_MAX_DOC_SIZE) if 'exclude_path' in kwargs: request_json['plan']['ciPolicy']['detail']['ciPolicy']['filters']['fileFilters'][ 'excludePaths'] = kwargs.get('exclude_path', PlanConstants.DEFAULT_EXCLUDE_LIST) headers = self._commcell_object._headers.copy() headers['LookupNames'] = 'False' flag, response = self._cvpysdk_object.make_request( 'POST', self._PLANS, request_json, headers=headers ) if flag: if response.json(): response_value = response.json() error_message = None error_code = None if 'errors' in response_value: error_code = response_value['errors'][0]['status']['errorCode'] error_message = response_value['errors'][0]['status']['errorMessage'] if error_code > 1: o_str = 'Failed to create new Plan\nError: "{0}"'.format( error_message ) raise SDKException('Plan', '102', o_str) if 'plan' in response_value: plan_name = response_value['plan']['summary']['plan']['planName'] # initialize the plans again self.refresh() return self.get(plan_name) else: o_str = ('Failed to create new plan due to error code: "{0}"\n' 'Please check the documentation for ' 'more details on the error').format(error_code) raise SDKException('Plan', '102', o_str) else: raise SDKException('Response', 102) else: response_string = self._update_response_(response.text) raise SDKException('Response', '101', response_string)
def delete(self, plan_name)
-
Deletes the plan from the commcell.
Args
plan_name (str) – name of the plan to remove from the commcell
Raises
SDKException: if type of the plan name argument is not string
if failed to delete plan if response is empty if response is not success if no plan exists with the given name
Expand source code Browse git
def delete(self, plan_name): """Deletes the plan from the commcell. Args: plan_name (str) -- name of the plan to remove from the commcell Raises: SDKException: if type of the plan name argument is not string if failed to delete plan if response is empty if response is not success if no plan exists with the given name """ if not isinstance(plan_name, str): raise SDKException('Plan', '101') else: plan_name = plan_name.lower() if self.has_plan(plan_name): plan_id = self._plans[plan_name] delete_plan = self._services['DELETE_PLAN'] % (plan_id) flag, response = self._cvpysdk_object.make_request('DELETE', delete_plan) error_code = 0 if flag: if 'error' in response.json(): if isinstance(response.json()['error'], list): error_code = response.json()['error'][0]['status']['errorCode'] else: error_code = response.json()['errorCode'] if error_code != 0: o_str = 'Failed to delete plan' if isinstance(response.json()['error'], list): error_message = response.json()['error'][0]['status']['errorMessage'] else: error_message = response.json()['errorMessage'] o_str += '\nError: "{0}"'.format(error_message) raise SDKException('Plan', '102', o_str) else: # initialize the plan again # so the plan object has all the plan self.refresh() else: response_string = self._update_response_(response.text) raise SDKException('Response', '101', response_string) else: raise SDKException( 'Plan', '102', 'No plan exists with name: {0}'.format(plan_name) )
def filter_plans(self, plan_type, company_name=None)
-
Returns the dictionary consisting of specified type and company plans.
Args
plan_type (str) – Type of plan ['DLO', 'Server', 'Laptop', 'Database', 'FSServer', 'FSIBMiVTL', 'Snap', 'VSAServer', 'VSAReplication', 'ExchangeUser', 'ExchangeJournal', 'Office365', 'Dynamics365', 'DataClassification', 'Archiver']
company_name (str) – To filter plans based on company. For Commcell, company_name = "". Default will return all plans
Returns
dict - consists of all the plans with specified types configured on the commcell
{ "plan1_name": plan1_id, "plan2_name": plan2_id }
Raises
SDKException: if input data type is not valid
if invalid plan type is passed as parameter if failed to get the response
Expand source code Browse git
def filter_plans(self, plan_type, company_name = None): """ Returns the dictionary consisting of specified type and company plans. Args: plan_type (str) -- Type of plan ['DLO', 'Server', 'Laptop', 'Database', 'FSServer', 'FSIBMiVTL', 'Snap', 'VSAServer', 'VSAReplication', 'ExchangeUser', 'ExchangeJournal', 'Office365', 'Dynamics365', 'DataClassification', 'Archiver'] company_name (str) -- To filter plans based on company. For Commcell, company_name = "". Default will return all plans Returns: dict - consists of all the plans with specified types configured on the commcell { "plan1_name": plan1_id, "plan2_name": plan2_id } Raises: SDKException: if input data type is not valid if invalid plan type is passed as parameter if failed to get the response """ plan_type_subtype = { "dlo" : ("1", "16777223"), "server" : ("2", "33554437"), "laptop" : ("2", "33554439"), "database" : ("2", "33579013"), "fsserver" : ("3", "50331655"), "fsibmivtl" : ("3", "50331653"), "snap" : ("4", "67108869"), "vsaserver" : ("5", "83886085"), "vsareplication" : ("5", "83918853"), "exchangeuser" : ("6", "100859907"), "exchangejournal" : ("6", "100794372"), "office365" : ("6", "100859937"), "dynamics365" : ("6", "100794391"), "dataclassification" : ("7", "117506053"), "archiver" : ("9", "150994951") } if not isinstance(plan_type, str): raise SDKException('Plan', '101') elif plan_type.lower() not in plan_type_subtype: raise SDKException('Plan', '102', 'Invalid Plan Type Passed as Parameter') else: template_url = self._services['GET_PLANS'] % plan_type_subtype[plan_type.lower()] flag, response = self._cvpysdk_object.make_request('GET', template_url) if flag: result = dict() if 'plans' in response.json(): for plan in response.json()['plans']: if company_name is None: result[plan['plan']['planName']] = plan['plan']['planId'] else: if plan['plan']['entityInfo']['companyName'].lower() == company_name.lower(): result[plan['plan']['planName']] = plan['plan']['planId'] return result else: response_string = self._update_response_(response.text) raise SDKException('Response', '101', response_string)
def get(self, plan_name)
-
Returns a plan object of the specified plan name.
Args
plan_name (str) – name of the plan
Returns
object - instance of the Plan class for the the given plan name
Raises
SDKException: if type of the plan name argument is not string
if no plan exists with the given name
Expand source code Browse git
def get(self, plan_name): """Returns a plan object of the specified plan name. Args: plan_name (str) -- name of the plan Returns: object - instance of the Plan class for the the given plan name Raises: SDKException: if type of the plan name argument is not string if no plan exists with the given name """ if not isinstance(plan_name, str): raise SDKException('Plan', '101') else: plan_name = plan_name.lower() if self.has_plan(plan_name): return Plan( self._commcell_object, plan_name, self._plans[plan_name] ) raise SDKException( 'Plan', '102', 'No plan exists with name: {0}'.format( plan_name) )
def get_eligible_plans(self, entities)
-
Returns dict of plans that are eligible for the specified entities
Args
entities (dict) - dictionary containing entities as keys and their respective IDs as values { 'clientId': id, 'appId': id, 'backupsetId': id }
Returns
dict - dict of eligible plans
Raises
SDKException: if there is an error in the response
Expand source code Browse git
def get_eligible_plans(self, entities): """Returns dict of plans that are eligible for the specified entities Args: entities (dict) - dictionary containing entities as keys and their respective IDs as values { 'clientId': id, 'appId': id, 'backupsetId': id } Returns: dict - dict of eligible plans Raises: SDKException: if there is an error in the response """ query = '' for i in entities: query += '{0}={1}&'.format(i, entities[i]) requset_url = self._services['ELIGIBLE_PLANS'] % query[0:-1] flag, response = self._cvpysdk_object.make_request('GET', requset_url) del query if flag: plans = {} if response.json() and 'plans' in response.json(): response_value = response.json()['plans'] for temp in response_value: temp_name = temp['plan']['planName'].lower() temp_id = str(temp['plan']['planId']).lower() plans[temp_name] = temp_id return plans else: response_string = self._update_response_(response.text) raise SDKException('Response', '101', response_string)
def get_supported_solutions(self)
-
Method to get supported solutions for plans
Expand source code Browse git
def get_supported_solutions(self): """Method to get supported solutions for plans""" flag, response = self._cvpysdk_object.make_request( 'GET', self._services['PLAN_SUPPORTED_SOLUTIONS'] ) if not flag: raise SDKException('Response', '101', self._update_response_(response.text)) if response.json() and 'id' in response.json(): return {solution['name']: solution['id'] for solution in response.json()['id']} else: raise SDKException('Response', '102')
def has_plan(self, plan_name)
-
Checks if a plan exists in the commcell with the input plan name.
Args
plan_name (str) – name of the plan
Returns
bool - boolean output whether the plan exists in the commcell or not
Raises
SDKException: if type of the plan name argument is not string
Expand source code Browse git
def has_plan(self, plan_name): """Checks if a plan exists in the commcell with the input plan name. Args: plan_name (str) -- name of the plan Returns: bool - boolean output whether the plan exists in the commcell or not Raises: SDKException: if type of the plan name argument is not string """ if not isinstance(plan_name, str): raise SDKException('Plan', '101') return self._plans and plan_name.lower() in self._plans
def refresh(self)
-
Refresh the plans associated with the Commcell.
Expand source code Browse git
def refresh(self): """Refresh the plans associated with the Commcell.""" self._plans = self._get_plans()