Module cvpysdk.subclient

Main file for performing subclient operations.

Subclients and Subclient are 2 classes defined in this file.

Subclients: Class for representing all the subclients associated with a backupset / instance

Subclient: Base class consisting of all the common properties and operations for a Subclient

Subclients:

__init__(class_object)      --  initialise object of subclients object associated with
the specified backup set/instance.

__str__()                   --  returns all the subclients associated with the backupset

__repr__()                  --  returns the string for the instance of the Subclients class

__len__()                   --  returns the number of subclients associated with the Agent
for the selected Client

__getitem__()               --  returns the name of the subclient for the given subclient Id
or the details for the given subclient name

_get_subclients()           --  gets all the subclients associated with the backupset specified

_process_add_request()      --  to post the add client request

default_subclient()         --  returns the name of the default subclient

all_subclients()            --  returns dict of all the subclients on commcell

has_subclient()             --  checks if a subclient exists with the given name or not

add()                       --  adds a new subclient to the backupset

add_oracle_logical_dump_subclient()  --  add subclient for oracle logical dump

add_postgresql_subclient()  --  Adds a new postgresql subclient to the backupset.

 add_mysql_subclient()  --  Adds a new mysql subclient to the instance.

add_virtual_server_subclient()  -- adds a new virtual server subclient to the backupset

add_onedrive_subclient()   --  adds a new onedrive subclient to the instance

get(subclient_name)         --  returns the subclient object of the input subclient name

delete(subclient_name)      --  deletes the subclient (subclient name) from the backupset

refresh()                   --  refresh the subclients associated with the Backupset / Instance

Subclient:

__init__()                  --  initialise instance of the Subclient class,
associated to the specified backupset

__getattr__()               --  provides access to restore helper methods

__repr__()                  --  return the subclient name, the instance is associated with

_get_subclient_id()         --  method to get subclient id, if not specified in __init__ method

_get_subclient_properties() --  get the properties of this subclient

_set_subclient_properties() --  sets the properties of this sub client .

_process_backup_request()   --  runs the backup request provided, and processes the response

_browse_and_find_json()     --  returns the appropriate JSON request to pass for either
Browse operation or Find operation

_process_browse_response()  --  processes response received for both Browse and Find request

_common_backup_options()    --  Generates the advanced job options dict

_json_task()                --  setter for task property

_json_restore_subtask()     --  setter for sub task property

_association_json()         --  setter for association property

update_properties()         --  To update the subclient properties

description()               --  update the description of the subclient

content()                   --  update the content of the subclient

enable_backup()             --  enables the backup for the subclient

enable_trueup()             --  enables true up option for the subclient

enable_trueup_days()        --  enables true up option and sets days for backup

enable_backup_at_time()     --  enables backup for the subclient at the input time specified

disable_backup()             --  disables the backup for the subclient

set_proxy_for_snap()        --  method to set Use proxy option for intellisnap subclient

unset_proxy_for_snap()      --  method to unset Use proxy option for intellisnap subclient

backup()                    --  run a backup job for the subclient

browse()                    --  gets the content of the backup for this subclient
at the path specified

browse_in_time()            --  gets the content of the backup for this subclient
at the input path in the time range specified

find()                      --  searches a given file/folder name in the subclient content

list_media()                --  List media required to browse and restore backed up data from the backupset

restore_in_place()          --  Restores the files/folders specified in the
input paths list to the same location

restore_out_of_place()      --  Restores the files/folders specified in the input paths list
to the input client, at the specified destionation location

set_backup_nodes()          -- Set Backup Nodes for NFS Share Pseudo client's subclient.

find_latest_job()           --  Finds the latest job for the subclient
which includes current running job also.

refresh()                   --  refresh the properties of the subclient

Subclient Instance Attributes:

**properties**                      --  returns the properties of the subclient

**name**                            --  returns the name of the subclient

**display_name**                    --  returns the display name of the subclient

**description**                     --  returns the description of the subclient

**snapshot_engine_name**            --  returns snapshot engine name associated
with the subclient

**is_default_subclient**            --  returns True if the subclient is default
subclient else returns False

**is_blocklevel_backup_enabled**    --  returns True if block level backup is enabled
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 subclient operations.

Subclients and Subclient are 2 classes defined in this file.

Subclients: Class for representing all the subclients associated with a backupset / instance

Subclient: Base class consisting of all the common properties and operations for a Subclient


Subclients:
===========
    __init__(class_object)      --  initialise object of subclients object associated with
    the specified backup set/instance.

    __str__()                   --  returns all the subclients associated with the backupset

    __repr__()                  --  returns the string for the instance of the Subclients class

    __len__()                   --  returns the number of subclients associated with the Agent
    for the selected Client

    __getitem__()               --  returns the name of the subclient for the given subclient Id
    or the details for the given subclient name

    _get_subclients()           --  gets all the subclients associated with the backupset specified

    _process_add_request()      --  to post the add client request

    default_subclient()         --  returns the name of the default subclient

    all_subclients()            --  returns dict of all the subclients on commcell

    has_subclient()             --  checks if a subclient exists with the given name or not

    add()                       --  adds a new subclient to the backupset

    add_oracle_logical_dump_subclient()  --  add subclient for oracle logical dump

    add_postgresql_subclient()  --  Adds a new postgresql subclient to the backupset.

     add_mysql_subclient()  --  Adds a new mysql subclient to the instance.

    add_virtual_server_subclient()  -- adds a new virtual server subclient to the backupset

    add_onedrive_subclient()   --  adds a new onedrive subclient to the instance

    get(subclient_name)         --  returns the subclient object of the input subclient name

    delete(subclient_name)      --  deletes the subclient (subclient name) from the backupset

    refresh()                   --  refresh the subclients associated with the Backupset / Instance


Subclient:
==========
    __init__()                  --  initialise instance of the Subclient class,
    associated to the specified backupset

    __getattr__()               --  provides access to restore helper methods

    __repr__()                  --  return the subclient name, the instance is associated with

    _get_subclient_id()         --  method to get subclient id, if not specified in __init__ method

    _get_subclient_properties() --  get the properties of this subclient

    _set_subclient_properties() --  sets the properties of this sub client .

    _process_backup_request()   --  runs the backup request provided, and processes the response

    _browse_and_find_json()     --  returns the appropriate JSON request to pass for either
    Browse operation or Find operation

    _process_browse_response()  --  processes response received for both Browse and Find request

    _common_backup_options()    --  Generates the advanced job options dict

    _json_task()                --  setter for task property

    _json_restore_subtask()     --  setter for sub task property

    _association_json()         --  setter for association property

    update_properties()         --  To update the subclient properties

    description()               --  update the description of the subclient

    content()                   --  update the content of the subclient

    enable_backup()             --  enables the backup for the subclient

    enable_trueup()             --  enables true up option for the subclient

    enable_trueup_days()        --  enables true up option and sets days for backup

    enable_backup_at_time()     --  enables backup for the subclient at the input time specified

    disable_backup()             --  disables the backup for the subclient

    set_proxy_for_snap()        --  method to set Use proxy option for intellisnap subclient

    unset_proxy_for_snap()      --  method to unset Use proxy option for intellisnap subclient

    backup()                    --  run a backup job for the subclient

    browse()                    --  gets the content of the backup for this subclient
    at the path specified

    browse_in_time()            --  gets the content of the backup for this subclient
    at the input path in the time range specified

    find()                      --  searches a given file/folder name in the subclient content

    list_media()                --  List media required to browse and restore backed up data from the backupset

    restore_in_place()          --  Restores the files/folders specified in the
    input paths list to the same location

    restore_out_of_place()      --  Restores the files/folders specified in the input paths list
    to the input client, at the specified destionation location

    set_backup_nodes()          -- Set Backup Nodes for NFS Share Pseudo client's subclient.

    find_latest_job()           --  Finds the latest job for the subclient
    which includes current running job also.

    refresh()                   --  refresh the properties of the subclient


Subclient Instance Attributes:
==============================

    **properties**                      --  returns the properties of the subclient

    **name**                            --  returns the name of the subclient

    **display_name**                    --  returns the display name of the subclient

    **description**                     --  returns the description of the subclient

    **snapshot_engine_name**            --  returns snapshot engine name associated
    with the subclient

    **is_default_subclient**            --  returns True if the subclient is default
    subclient else returns False

    **is_blocklevel_backup_enabled**    --  returns True if block level backup is enabled

"""

from __future__ import absolute_import
from __future__ import unicode_literals

import math
import time
import copy
from base64 import b64encode
from .job import Job
from .job import JobController
from .schedules import Schedules
from .exception import SDKException
from .schedules import SchedulePattern


class Subclients(object):
    """Class for getting all the subclients associated with a client."""

    def __init__(self, class_object):
        """Initialize the Subclients object for the given backupset.

            Args:
                class_object    (object)    --  instance of the Agent / Instance / Backupset class

            Returns:
                object  -   instance of the Subclients class

            Raises:
                SDKException:
                    if class object is not an instance of Agent / Instance / Backupset

        """
        from .agent import Agent
        from .instance import Instance
        from .backupset import Backupset

        self._agent_object = None
        self._instance_object = None
        self._backupset_object = None
        self._url_param = ''

        if isinstance(class_object, Agent):
            self._agent_object = class_object
            self._url_param += self._agent_object.agent_id

        elif isinstance(class_object, Instance):
            self._instance_object = class_object
            self._agent_object = self._instance_object._agent_object
            self._url_param += '{0}&instanceId={1}'.format(
                self._agent_object.agent_id, self._instance_object.instance_id
            )

        elif isinstance(class_object, Backupset):
            self._backupset_object = class_object
            self._instance_object = class_object._instance_object
            self._agent_object = self._instance_object._agent_object
            self._url_param += self._agent_object.agent_id
            self._url_param += '&backupsetId={0}'.format(
                self._backupset_object.backupset_id
            )
        else:
            raise SDKException('Subclient', '115')

        self._client_object = self._agent_object._client_object
        self._commcell_object = self._agent_object._commcell_object

        self._cvpysdk_object = self._commcell_object._cvpysdk_object
        self._services = self._commcell_object._services
        self._update_response_ = self._commcell_object._update_response_

        self._SUBCLIENTS = self._services['GET_ALL_SUBCLIENTS'] % (
            self._client_object.client_id, self._url_param
        )

        self._ADD_SUBCLIENT = self._services['ADD_SUBCLIENT']

        self._default_subclient = None

        # sql server subclient type dict
        self._sqlsubclient_type_dict = {
            'DATABASE': 1,
            'FILE_FILEGROUP': 2,
        }

        # this will work only for `Exchange Database` Agent, as only an object of
        # ExchangeDatabaseAgent class has these attributes
        if self._instance_object is None and hasattr(
                self._agent_object, '_instance_object'):
            self._instance_object = self._agent_object._instance_object

        if self._backupset_object is None and hasattr(
                self._agent_object, '_backupset_object'):
            self._backupset_object = self._agent_object._backupset_object

        self.refresh()

    def __str__(self):
        """Representation string consisting of all subclients of the backupset.

            Returns:
                str - string of all the subclients of th backupset of an agent of a client
        """
        representation_string = '{:^5}\t{:^20}\t{:^20}\t{:^20}\t{:^20}\t{:^20}\n\n'.format(
            'S. No.', 'Subclient', 'Backupset', 'Instance', 'Agent', 'Client'
        )

        for index, subclient in enumerate(self._subclients):
            sub_str = '{:^5}\t{:20}\t{:20}\t{:20}\t{:20}\t{:20}\n'.format(
                index + 1,
                subclient.split('\\')[-1],
                self._subclients[subclient]['backupset'],
                self._instance_object.instance_name,
                self._agent_object.agent_name,
                self._client_object.client_name
            )
            representation_string += sub_str

        return representation_string.strip()

    def __repr__(self):
        """Representation string for the instance of the Subclients class."""
        if self._backupset_object is not None:
            o_str = (
                'Subclients class instance for Backupset: "{0}", '
                'of Instance: "{1}", for Agent: "{2}"'
            ).format(
                self._backupset_object.backupset_name,
                self._instance_object.instance_name,
                self._agent_object.agent_name
            )
        elif self._instance_object is not None:
            o_str = 'Subclients class instance for Instance: "{0}", of Agent: "{1}"'.format(
                self._instance_object.instance_name,
                self._agent_object.agent_name
            )
        else:
            o_str = 'Subclients class instance for Agent: "{0}"'.format(
                self._agent_object.agent_name
            )

        return o_str

    def __len__(self):
        """Returns the number of the subclients associated to the Agent for the selected Client."""
        return len(self.all_subclients)

    def __getitem__(self, value):
        """Returns the name of the subclient for the given subclient ID or
            the details of the subclient for given subclient Name.

            Args:
                value   (str / int)     --  Name or ID of the subclient

            Returns:
                str     -   name of the subclient, if the subclient id was given

                dict    -   dict of details of the subclient, if subclient name was given

            Raises:
                IndexError:
                    no subclient exists with the given Name / Id

        """
        value = str(value)

        if value in self.all_subclients:
            return self.all_subclients[value]
        else:
            try:
                return list(
                    filter(lambda x: x[1]['id'] == value, self.all_subclients.items())
                )[0][0]
            except IndexError:
                raise IndexError('No subclient exists with the given Name / Id')

    def _get_subclients(self):
        """Gets all the subclients associated to the client specified by the backupset object.

            Returns:
                dict - consists of all subclients in the backupset
                    {
                         "subclient1_name": {
                             "id": subclient1_id,
                             "backupset": backupset
                         },
                         "subclient2_name": {
                             "id": subclient2_id,
                             "backupset": backupset
                         }
                    }

            Raises:
                SDKException:
                    if response is empty

                    if response is not success
        """
        flag, response = self._cvpysdk_object.make_request(
            'GET', self._SUBCLIENTS)

        if flag:
            if response.json() and 'subClientProperties' in response.json():
                return_dict = {}

                for dictionary in response.json()['subClientProperties']:
                    # store the agent, instance, and backupset name for the current subclient
                    # the API call returns the subclients for all Agents, so we need to filter
                    # them out based on the Agent / Instance / Backupset that had been selected
                    # by the user earlier
                    agent = dictionary['subClientEntity']['appName'].lower()
                    instance = dictionary['subClientEntity']['instanceName'].lower(
                    )
                    backupset = dictionary['subClientEntity']['backupsetName'].lower(
                    )

                    # filter subclients for all entities: Agent, Instance, and Backupset
                    # as the instance of the Backupset class was passed for Subclients instance
                    # creation
                    if self._backupset_object is not None:
                        if (self._backupset_object.backupset_name in backupset and
                                self._instance_object.instance_name in instance and
                                self._agent_object.agent_name in agent):
                            temp_name = dictionary['subClientEntity']['subclientName'].lower(
                            )
                            temp_id = str(
                                dictionary['subClientEntity']['subclientId']).lower()

                            return_dict[temp_name] = {
                                "id": temp_id,
                                "backupset": backupset
                            }

                            if dictionary['commonProperties'].get(
                                    'isDefaultSubclient'):
                                self._default_subclient = temp_name

                    elif self._instance_object is not None:
                        if (self._instance_object.instance_name in instance and
                                self._agent_object.agent_name in agent):
                            temp_name = dictionary['subClientEntity']['subclientName'].lower(
                            )
                            temp_id = str(
                                dictionary['subClientEntity']['subclientId']).lower()

                            if len(
                                    self._instance_object.backupsets.all_backupsets) > 1:
                                temp_name = "{0}\\{1}".format(
                                    backupset, temp_name)

                            return_dict[temp_name] = {
                                "id": temp_id,
                                "backupset": backupset
                            }

                            if dictionary['commonProperties'].get(
                                    'isDefaultSubclient'):
                                self._default_subclient = temp_name

                    elif self._agent_object is not None:
                        if self._agent_object.agent_name in agent:
                            temp_name = dictionary['subClientEntity']['subclientName'].lower(
                            )
                            temp_id = str(
                                dictionary['subClientEntity']['subclientId']).lower()

                            if len(self._agent_object.instances.all_instances) > 1:
                                if len(
                                        self._instance_object.backupsets.all_backupsets) > 1:
                                    temp_name = "{0}\\{1}\\{2}".format(
                                        instance, backupset, temp_name
                                    )
                                else:
                                    temp_name = "{0}\\{1}".format(
                                        instance, temp_name)
                            else:
                                if len(
                                        self._instance_object.backupsets.all_backupsets) > 1:
                                    temp_name = "{0}\\{1}".format(
                                        backupset, temp_name)

                            return_dict[temp_name] = {
                                "id": temp_id,
                                "backupset": backupset
                            }

                            if dictionary['commonProperties'].get(
                                    'isDefaultSubclient'):
                                self._default_subclient = temp_name

                return return_dict
            else:
                raise SDKException('Response', '102')
        else:
            raise SDKException(
                'Response',
                '101',
                self._update_response_(
                    response.text))

    @property
    def all_subclients(self):
        """Returns dict of all the subclients configured on this backupset

            Retruns:
                dict    -   consists of all subclients in the backupset

                    {
                        "subclient1_name": {
                            "id": subclient1_id,

                            "backupset": backupset
                        },
                        "subclient2_name": {
                            "id": subclient2_id,

                            "backupset": backupset
                        }
                    }

        """
        return self._subclients

    def has_subclient(self, subclient_name):
        """Checks if a subclient exists in the commcell with the input subclient name.

            Args:
                subclient_name (str)  --  name of the subclient

            Returns:
                bool - boolean output whether the subclient exists in the backupset or not

            Raises:
                SDKException:
                    if type of the subclient name argument is not string
        """
        if not isinstance(subclient_name, str):
            raise SDKException('Subclient', '101')

        return self._subclients and subclient_name.lower() in self._subclients

    def _process_add_request(self, request_json):
        """To post the add subclient request

        Args:
            request_json    (dict)  -- Request json to be passed as the payload

        Returns:
            object  -   instance of the Subclient class

        """
        flag, response = self._cvpysdk_object.make_request(
            'POST', self._ADD_SUBCLIENT, request_json
        )

        if flag:
            if response.json() and 'response' in response.json():
                error_code = response.json()['response']['errorCode']

                if error_code != 0:
                    error_string = response.json()['response']['errorString']
                    raise SDKException(
                        'Subclient',
                        '102',
                        'Failed to create subclient\nError: "{0}"'.format(error_string)
                    )
                else:
                    # initialize the subclients again so the subclient object has all the subclients
                    self.refresh()

                    subclient_name = request_json['subClientProperties']['subClientEntity']['subclientName']

                    return self.get(subclient_name)

            else:
                raise SDKException('Response', '102')
        else:
            raise SDKException('Response', '101', self._update_response_(response.text))

    def add(self, subclient_name, storage_policy=None,
            subclient_type=None, description='', advanced_options=None,
            pre_scan_cmd=None):
        """Adds a new subclient to the backupset.

            Args:
                subclient_name      (str)   --  name of the new subclient to add

                storage_policy      (str)   --  name of the storage policy to be associated
                with the subclient

                    default: None

                subclient_type      (str)   --  type of subclient for sql server

                    default: None

                    Valid Values are:

                        - DATABASE

                        - FILE_FILEGROUP


                description         (str)   --  description for the subclient (optional)

                    default: ''

                advanced_options    (dict)  --  dict of additional options needed to create
                                                subclient with additional properties
                                                default : None
                    Example:
                        {
                            ondemand_subclient : True
                        }

                pre_scan_cmd        (str)   --  path to the batch file/shell script file to run
                                                before each backup of the subclient

            Returns:
                object  -   instance of the Subclient class

            Raises:
                SDKException:
                    if subclient name argument is not of type string

                    if storage policy argument is not of type string

                    if description argument is not of type string

                    if failed to create subclient

                    if response is empty

                    if response is not success

                    if subclient already exists with the given name

        """
        if not (isinstance(subclient_name, str) and
                isinstance(description, str)):
            raise SDKException('Subclient', '101')

        if self.has_subclient(subclient_name):
            raise SDKException(
                'Subclient', '102', 'Subclient "{0}" already exists.'.format(
                    subclient_name)
            )

        if self._backupset_object is None:
            if self._instance_object.backupsets.has_backupset(
                    'defaultBackupSet'):
                self._backupset_object = self._instance_object.backupsets.get(
                    'defaultBackupSet')
            else:
                self._backupset_object = self._instance_object.backupsets.get(
                    sorted(self._instance_object.backupsets.all_backupsets)[0]
                )

        if storage_policy and not self._commcell_object.storage_policies.has_policy(
                storage_policy):
            raise SDKException(
                'Subclient',
                '102',
                'Storage Policy: "{0}" does not exist in the Commcell'.format(
                    storage_policy)
            )

        if advanced_options:
            if advanced_options.get("ondemand_subclient", False):
                ondemand_value = advanced_options.get("ondemand_subclient")
            else:
                ondemand_value = False
        else:
            ondemand_value = False

        request_json = {
            "subClientProperties": {
                "contentOperationType": 2,
                "subClientEntity": {
                    "clientName": self._client_object.client_name,
                    "appName": self._agent_object.agent_name,
                    "instanceName": self._instance_object.instance_name,
                    "backupsetName": self._backupset_object.backupset_name,
                    "subclientName": subclient_name
                },
                "commonProperties": {
                    "description": description,
                    "enableBackup": True,
                    "onDemandSubClient": ondemand_value,
                    "storageDevice": {
                        "dataBackupStoragePolicy": {
                            "storagePolicyName": storage_policy
                        }
                    },
                }
            }
        }

        if storage_policy is None:
            del request_json["subClientProperties"]["commonProperties"]["storageDevice"]

        if pre_scan_cmd is not None:
            request_json["subClientProperties"]["commonProperties"]["prepostProcess"] = {
                "runAs": 1,
                "preScanCommand": pre_scan_cmd
            }

        if self._agent_object.agent_name == 'sql server':
            request_json['subClientProperties']['mssqlSubClientProp'] = {
                'sqlSubclientType': self._sqlsubclient_type_dict[subclient_type]
            }

        return self._process_add_request(request_json)

    def add_oracle_logical_dump_subclient(
            self,
            subclient_name,
            storage_policy,
            dump_dir,
            user_name,
            domain_name,
            password,
            full_mode,
            schema_value=None):
        """
        Method to add subclient for oracle logical dump.
        This method add two type of subclient full mode
        and schema mode. For full mode full_mode should be
        true and schema_value should be none and for schema
        mode full_mode should be false and schema_value should
        be list of values.Rest of thing should be same for both.
        Args:
              subclient_name     (Str)  --  subclient name for logical dump

              storage_policy     (Str)  --  Storage policy for subclient

              dump_dir            (Str)  --  dump directory for subclient

              user_name           (Str)  --  username for oracle database

              domain_name         (Str)  --  domainname for oracle database

              password           (Str)  --  password for oracle database
                                            (should be in encrypted and decrypted form)

              full_mode           (bool) --  if ture then subclient for full mode otherwise schema mode

              schema_value        (list) --  schema value for schema mode subclient

                   default: None
        Return:
                object  -   instance of the Subclient class

            Raises:
                SDKException:
                    if subclient name argument is not of type string

                    if storage policy argument is not of type string

                    if subclient name already present

                    if storage policy does not exist

        """
        if not (isinstance(subclient_name, str) and
                isinstance(storage_policy, str) and
                isinstance(dump_dir, str) and
                isinstance(user_name, str) and
                isinstance(domain_name, str) and
                isinstance(password, str) and
                isinstance(full_mode, bool)):
            raise SDKException('Subclient', '101')
        if (full_mode == False and not
        isinstance(schema_value, list)):
            raise SDKException('Subclient', '101')

        if self.has_subclient(subclient_name):
            raise SDKException(
                'Subclient', '102', 'Subclient "{0}" already exists.'.format(
                    subclient_name)
            )

        if self._backupset_object is None:
            if self._instance_object.backupsets.has_backupset(
                    'defaultBackupSet'):
                self._backupset_object = self._instance_object.backupsets.get(
                    'defaultBackupSet')
            else:
                self._backupset_object = self._instance_object.backupsets.get(
                    sorted(self._instance_object.backupsets.all_backupsets)[0]
                )

        if not self._commcell_object.storage_policies.has_policy(
                storage_policy):
            raise SDKException(
                'Subclient',
                '102',
                'Storage Policy: "{0}" does not exist in the Commcell'.format(
                    storage_policy)
            )

        request_json = {
            "subClientProperties": {
                "subClientEntity": {
                    "clientName": self._client_object.client_name,
                    "instanceName": self._instance_object.instance_name,
                    "appName": self._agent_object.agent_name,
                    "backupsetName": self._backupset_object.backupset_name,
                    "subclientName": subclient_name
                },
                "oracleSubclientProp": {
                    "data": False,
                    "archiveDelete": False,
                    "useSQLConntect": False,
                    "dbSubclientType": 2,
                    "mergeIncImageCopies": False,
                    "selectiveOnlineFull": False,
                    "protectBackupRecoveryArea": False,
                    "selectArchiveLogDestForBackup": False,
                    "backupSPFile": False,
                    "backupControlFile": False,
                    "backupArchiveLog": False,
                    "validate": False,
                },
                "commonProperties": {
                    "snapCopyInfo": {
                        "useSeparateProxyForSnapToTape": False,
                        "checkProxyForSQLIntegrity": False,
                        "snapToTapeProxyToUseSource": False,
                        "isSnapBackupEnabled": False,
                        "IsOracleSposDriverEnabled": False,
                        "isRMANEnableForTapeMovement": False
                    },
                    "dbDumpConfig": {
                        "fullMode": True,
                        "database": "",
                        "dumpDir": dump_dir,
                        "parallelism": 2,
                        "overrideInstanceUser": True,
                        "sqlConnect": {
                            "password": b64encode(password.encode()).decode(),
                            "domainName": domain_name,
                            "userName": user_name
                        }
                    },
                    "storageDevice": {
                        "dataBackupStoragePolicy": {
                            "storagePolicyName": storage_policy
                        },
                        "deDuplicationOptions": {
                            "enableDeduplication": True
                        }
                    }
                }
            }
        }

        if (full_mode == False):
            request_json["subClientProperties"]["commonProperties"]["dbDumpConfig"]["fullMode"] = False
            request_json["subClientProperties"]["commonProperties"]["dbDumpConfig"]["schema"] = schema_value

        return self._process_add_request(request_json)

    def add_postgresql_subclient(
            self, subclient_name, storage_policy,
            contents, no_of_streams=1, collect_object_list=False):
        """Adds a new postgresql subclient to the backupset.

            Args:
                subclient_name          (str)   --  name of the new subclient to add

                storage_policy          (str)   --  name of the storage policy to be associated
                with the subclient

                contents                (list)  --  database list to be added as subclient content


                no_of_streams           (int)   --  No of backup streams to be used

                    default: 1

                collect_object_list     (bool)  --  Boolean flag to determine if collect object
                list needs to be enabled for subclient or not

                    default: False

            Returns:
                object  -   instance of the Subclient class

            Raises:
                SDKException:
                    if subclient name argument is not of type string

                    if storage policy argument is not of type string

                    if conetnts argument is not of type list

                    if contents is empty list

                    if failed to create subclient

                    if response is empty

                    if response is not success

                    if subclient already exists with the given name

        """
        if not (isinstance(subclient_name, str) and
                isinstance(storage_policy, str) and
                isinstance(contents, list)):
            raise SDKException('Subclient', '101')

        if self.has_subclient(subclient_name):
            raise SDKException(
                'Subclient', '102', 'Subclient "{0}" already exists.'.format(
                    subclient_name)
            )

        if not self._commcell_object.storage_policies.has_policy(
                storage_policy):
            raise SDKException(
                'Subclient',
                '102',
                'Storage Policy: "{0}" does not exist in the Commcell'.format(
                    storage_policy)
            )

        if not contents:
            raise SDKException(
                'Subclient',
                '102',
                'Content list cannot be empty'
            )

        content_list = []
        for content in contents:
            content_list.append({"postgreSQLContent": {"databaseName": content}})

        request_json = {
            "subClientProperties": {
                "contentOperationType": 2,
                "subClientEntity": {
                    "clientName": self._client_object.client_name,
                    "appName": self._agent_object.agent_name,
                    "instanceName": self._instance_object.instance_name,
                    "backupsetName": self._backupset_object.backupset_name,
                    "subclientName": subclient_name
                },
                "commonProperties": {
                    "storageDevice": {
                        "dataBackupStoragePolicy": {
                            "storagePolicyName": storage_policy
                        }
                    },
                },
                "postgreSQLSubclientProp": {
                    "numberOfBackupStreams": no_of_streams,
                    "collectObjectListDuringBackup": collect_object_list
                },
                "content": content_list
            }
        }

        return self._process_add_request(request_json)

    def add_mysql_subclient(
            self,
            subclient_name,
            storage_policy,
            contents,
            **kwargs
    ):
        """Adds a new mysql subclient to the instance.

            Args:
                subclient_name          (str)   --  name of the new subclient to add

                storage_policy          (str)   --  name of the storage policy to be associated
                with the subclient

                contents                (list)  --  database list to be added as subclient content

                kwargs      (dict)  -- dict of keyword arguments as follows

                    no_of_backup_streams    (int)   --  No of backup streams to be used
                    default: 1

                    no_of_log_backup_streams    (int)   -- No of Transaction log backup streams
                    default: 1

                    full_instance_xtrabackup    (bool)  -- True if XtraBackup is selected for subclient
                    default: False

            Returns:
                object  -   instance of the Subclient class

            Raises:
                SDKException:
                    if subclient name argument is not of type string

                    if storage policy argument is not of type string

                    if conetnts argument is not of type list

                    if contents is empty list

                    if failed to create subclient

                    if response is empty

                    if response is not success

                    if subclient already exists with the given name

        """
        if not (isinstance(subclient_name, str) and
                isinstance(storage_policy, str) and
                isinstance(contents, list)):
            raise SDKException('Subclient', '101')

        if self.has_subclient(subclient_name):
            raise SDKException(
                'Subclient', '102', 'Subclient "{0}" already exists.'.format(
                    subclient_name)
            )

        if not self._commcell_object.storage_policies.has_policy(
                storage_policy):
            raise SDKException(
                'Subclient',
                '102',
                'Storage Policy: "{0}" does not exist in the Commcell'.format(
                    storage_policy)
            )

        if not contents:
            raise SDKException(
                'Subclient',
                '102',
                'Content list cannot be empty'
            )

        content_list = []
        for content in contents:
            content_list.append({"mySQLContent": {"databaseName": content}})

        request_json = {
            "subClientProperties": {
                "contentOperationType": 2,
                "subClientEntity": {
                    "clientName": self._client_object.client_name,
                    "appName": self._agent_object.agent_name,
                    "instanceName": self._instance_object.instance_name,
                    "backupsetName": "defaultDummyBackupSet",
                    "subclientName": subclient_name
                },
                "commonProperties": {
                    "storageDevice": {
                        "dataBackupStoragePolicy": {
                            "storagePolicyName": storage_policy
                        }
                    },
                },
                "mySqlSubclientProp": {
                    "numberOfBackupStreams": kwargs.get('no_of_backup_streams', 1),
                    "numberOfTransactionLogStreams": kwargs.get('no_of_log_backup_streams', 1),
                    "fullInstanceXtraBackup": kwargs.get('full_instance_xtrabackup', False)
                },
                "content": content_list
            }
        }

        return self._process_add_request(request_json)

    def add_virtual_server_subclient(
            self,
            subclient_name,
            subclient_content,
            **kwargs
    ):
        """Adds a new virtual server subclient to the backupset.

            Args:
                subclient_name      (str)   --  Name of the subclient to be created

                subclient_content   (list)  --  Content to be added to the subclient

                    Example 1:
                        [{
                            'equal_value': True,
                            'allOrAnyChildren': True,
                            'id': '',
                            'path': '',
                            'display_name': 'sample1',
                            'type': VSAObjects.VMName
                        }]
                    Example 2:
                         [{
                        'allOrAnyChildren': False,
                        'content': [{
                            'equal_value': True,
                            'allOrAnyChildren': True,
                            'display_name': 'sample1',
                            'type': VSAObjects.VMName
                        }, {
                            'equal_value': True,
                            'allOrAnyChildren': True,
                            'display_name': 'sample2',
                            'type': VSAObjects.VMName
                        }
                        ]
                        }, {
                        'allOrAnyChildren': True,
                        'content': [{
                            'equal_value': True,
                            'allOrAnyChildren': True,
                            'display_name': 'sample3',
                            'type': VSAObjects.RESOURCE_POOL
                        }, {
                            'equal_value': True,
                            'allOrAnyChildren': True,
                            'id': 'sample4',
                            'display_name': 'sample4',
                            'type': VSAObjects.SERVER
                            }
                            ]
                        }
                        ]
                        **Note** Use VSAObjects Enum present in constants.py to pass value to type

                kwargs      (dict)  -- dict of keyword arguments as follows

                    plan_name           (str)   --  Plan to be associated with the subclient

                    storage_policy      (str)   --  Storage policy to be associated with the subclient

                    description         (str)   --  Description for the subclient

                        default: ''

            Returns:
                object  -   instance of the Subclient class

            Raises:
                SDKException:
                    if subclient name argument is not of type string

                    if storage policy argument is not of type string

                    if description argument is not of type string

                    if failed to create subclient

                    if response is empty

                    if response is not success

                    if subclient already exists with the given name

        """
        if not (isinstance(subclient_name, str) and
                isinstance(subclient_content, list)):
            raise SDKException('Subclient', '101')

        if self.has_subclient(subclient_name):
            raise SDKException(
                'Subclient', '102', 'Subclient "{0}" already exists.'.format(
                    subclient_name)
            )

        if self._backupset_object is None:
            if self._instance_object.backupsets.has_backupset(
                    'defaultBackupSet'):
                self._backupset_object = self._instance_object.backupsets.get(
                    'defaultBackupSet')
            else:
                self._backupset_object = self._instance_object.backupsets.get(
                    sorted(self._instance_object.backupsets.all_backupsets)[0]
                )

        content = []

        def set_content(item_content):
            """
            create content dictionary
            Args:
                item_content            (dict):     Dict of content details

                Example:
                    {
                        'equal_value': True,
                        'allOrAnyChildren': True,
                        'display_name': 'sample1',
                        'type':  < VSAObjects.VMName: 10 >
                    }

            Returns:

            """
            return {
                "equalsOrNotEquals": item_content.get('equal_value', True),
                "name": item_content.get('id', ''),
                "displayName": item_content.get('display_name', ''),
                "path": item_content.get('path', ''),
                "allOrAnyChildren": item.get('allOrAnyChildren', True),
                "type": item_content['type'] if isinstance(item_content['type'], int) else item_content['type'].value
            }

        for item in subclient_content:
            _temp_list = []
            _temp_dict = {}
            allOrAnyChildren = item.get('allOrAnyChildren', None)
            if 'content' in item:
                nested_content = item['content']
                for each_condition in nested_content:
                    temp_dict = set_content(each_condition)
                    _temp_list.append(temp_dict)
                _temp_dict['allOrAnyChildren'] = allOrAnyChildren
                _temp_dict['children'] = _temp_list
                content.append(_temp_dict)
            else:
                temp_dict = set_content(item)
                content.append(temp_dict)

        request_json = {
            "subClientProperties": {
                "vmContentOperationType": 2,
                "vmContent": {
                    "children": content
                },
                "subClientEntity": {
                    "clientName": self._client_object.client_name,
                    "appName": self._agent_object.agent_name,
                    "instanceName": self._instance_object.instance_name,
                    "backupsetName": self._backupset_object.backupset_name,
                    "subclientName": subclient_name
                },
                "commonProperties": {
                    "description": kwargs.get('description'),
                    "enableBackup": True
                }
            }
        }

        if kwargs.get("customSnapshotResourceGroup"):
            request_json["subClientProperties"]["vsaSubclientProp"] = \
                {"customSnapshotResourceGroup": kwargs.get("customSnapshotResourceGroup")}

        if kwargs.get('plan_name'):
            if not self._commcell_object.plans.has_plan(kwargs['plan_name']):
                raise SDKException(
                    'Subclient',
                    '102',
                    'Plan: "{0}" does not exist in the Commcell'.format(kwargs['plan_name'])
                )
            request_json['subClientProperties']['planEntity'] = {
                "planName": kwargs['plan_name']
            }

        elif kwargs.get('storage_policy'):
            if not self._commcell_object.storage_policies.has_policy(kwargs.get('storage_policy')):
                raise SDKException(
                    'Subclient',
                    '102',
                    'Storage Policy: "{0}" does not exist in the Commcell'.format(kwargs.get('storage_policy'))
                )
            request_json['subClientProperties']['commonProperties']['storageDevice'] = {
                "dataBackupStoragePolicy": {
                    "storagePolicyName": kwargs.get('storage_policy')
                }
            }
        else:
            raise SDKException('Subclient', '102', 'Either Plan or Storage policy should be given as input')

        return self._process_add_request(request_json)

    def add_onedrive_subclient(self,
                               subclient_name,
                               server_plan):

        """Adds a new subclient to the backupset.

            Args:
                subclient_name     (str)   --  name of the new subclient to add

                server_plan     (str)   --  name of the server plan to be associated
                                                with the subclient

            Returns:
                object  -   instance of the Subclient class

            Raises:
                SDKException:
                    if subclient name argument is not of type string

                    if server plan argument is not of type string

                    if description argument is not of type string

                    if failed to create subclient

                    if response is empty

                    if response is not success

                    if subclient already exists with the given name

                    if server plan  donot exists with the given name

                """

        if not (isinstance(subclient_name, str) and
                isinstance(server_plan, str)):
            raise SDKException('Subclient', '101')

        if self.has_subclient(subclient_name):
            raise SDKException(
                'Subclient', '102', 'Subclient "{0}" already exists.'.format(
                    subclient_name)
            )

        if self._backupset_object is None:
            if self._instance_object.backupsets.has_backupset(
                    self._instance_object.backupsets.default_backup_set):
                self._backupset_object = self._instance_object.backupsets.get(
                    self._instance_object.backupsets.default_backup_set)
            else:
                self._backupset_object = self._instance_object.backupsets.get(
                    sorted(self._instance_object.backupsets.all_backupsets)[0]
                )

        if self._commcell_object.plans.has_plan(server_plan):
            server_plan_object = self._commcell_object.plans.get(server_plan)
            server_plan_id = int(server_plan_object.plan_id)
        else:
            raise SDKException('Plan', '102', 'Provide Valid Plan Name')

        request_json = {
            "subClientProperties": {
                "subClientEntity": {
                    "clientName": self._client_object.client_name,
                    "instanceName": self._instance_object.instance_name,
                    "backupsetId": int(self._backupset_object.backupset_id),
                    "instanceId": int(self._instance_object.instance_id),
                    "clientId": int(self._client_object.client_id),
                    "appName": self._agent_object.agent_name,
                    "applicationId": 134,
                    "subclientName": subclient_name
                },
                "planEntity": {
                    "planId": server_plan_id
                },
                "cloudAppsSubClientProp": {
                    "instanceType": 7,
                    "oneDriveSubclient": {
                        "enableOneNote": False,
                        "isEnterprise": True
                    }
                },
                "cloudconnectorSubclientProp": {
                    "isAutoDiscoveryEnabled": False
                },
                "commonProperties": {
                    "enableBackup": True
                }
            }
        }

        return self._process_add_request(request_json)

    def get(self, subclient_name):
        """Returns a subclient object of the specified backupset name.

            Args:
                subclient_name (str)  --  name of the subclient

            Returns:
                object - instance of the Subclient class for the given subclient name

            Raises:
                SDKException:
                    if type of the subclient name argument is not string

                    if no subclient exists with the given name
        """
        if not isinstance(subclient_name, str):
            raise SDKException('Subclient', '101')
        else:
            subclient_name = subclient_name.lower()

            if self.has_subclient(subclient_name):

                if self._backupset_object is None:
                    self._backupset_object = self._instance_object.backupsets.get(
                        self._subclients[subclient_name]['backupset']
                    )
                return Subclient(
                    self._backupset_object, subclient_name, self._subclients[subclient_name]['id']
                )

            raise SDKException(
                'Subclient', '102', 'No subclient exists with name: {0}'.format(
                    subclient_name)
            )

    def delete(self, subclient_name):
        """Deletes the subclient specified by the subclient_name from the backupset.

            Args:
                subclient_name (str)  --  name of the subclient to remove from the backupset

            Raises:
                SDKException:
                    if type of the subclient name argument is not string

                    if failed to delete subclient

                    if response is empty

                    if response is not success

                    if no subclient exists with the given name
        """
        if not isinstance(subclient_name, str):
            raise SDKException('Subclient', '101')
        else:
            subclient_name = subclient_name.lower()

        if self.has_subclient(subclient_name):
            delete_subclient_service = self._services['SUBCLIENT'] % (
                self._subclients[subclient_name]['id']
            )

            flag, response = self._cvpysdk_object.make_request(
                'DELETE', delete_subclient_service)

            if flag:
                if response.json():
                    if 'response' in response.json():
                        response_value = response.json()['response'][0]
                        error_code = str(response_value['errorCode'])
                        error_message = None

                        if 'errorString' in response_value:
                            error_message = response_value['errorString']

                        if error_message:
                            o_str = 'Failed to delete subclient\nError: "{0}"'
                            raise SDKException(
                                'Subclient', '102', o_str.format(error_message))
                        else:
                            if error_code == '0':
                                # initialize the subclients again
                                # so the subclient object has all the
                                # subclients
                                self.refresh()
                            else:
                                o_str = ('Failed to delete subclient with Error Code: "{0}"\n'
                                         'Please check the documentation for '
                                         'more details on the error')
                                raise SDKException(
                                    'Subclient', '102', o_str.format(error_code))
                else:
                    raise SDKException('Response', '102')
            else:
                raise SDKException(
                    'Response', '101', self._update_response_(
                        response.text))
        else:
            raise SDKException(
                'Subclient', '102', 'No subclient exists with name: {0}'.format(
                    subclient_name)
            )

    def refresh(self):
        """Refresh the subclients associated with the Backupset / Instance."""
        self._subclients = self._get_subclients()

    @property
    def default_subclient(self):
        """Returns the name of the default subclient for the selected Agent and Backupset."""
        return self._default_subclient


class Subclient(object):
    """Base class consisting of all the common properties and operations for a Subclient"""

    def __new__(cls, backupset_object, subclient_name, subclient_id=None):
        """Class composition for CV subclients"""
        from .subclients.fssubclient import FileSystemSubclient
        from .subclients.bigdataappssubclient import BigDataAppsSubclient
        from .subclients.vssubclient import VirtualServerSubclient
        from .subclients.casubclient import CloudAppsSubclient
        from .subclients.sqlsubclient import SQLServerSubclient
        from .subclients.nassubclient import NASSubclient
        from .subclients.hanasubclient import SAPHANASubclient
        from .subclients.oraclesubclient import OracleSubclient
        from .subclients.lotusnotes.lndbsubclient import LNDbSubclient
        from .subclients.lotusnotes.lndocsubclient import LNDocSubclient
        from .subclients.lotusnotes.lndmsubclient import LNDmSubclient
        from .subclients.sybasesubclient import SybaseSubclient
        from .subclients.saporaclesubclient import SAPOracleSubclient
        from .subclients.exchsubclient import ExchangeSubclient
        from .subclients.mysqlsubclient import MYSQLSubclient
        from .subclients.exchange.exchange_database_subclient import ExchangeDatabaseSubclient
        from .subclients.postgressubclient import PostgresSubclient
        from .subclients.informixsubclient import InformixSubclient
        from .subclients.adsubclient import ADSubclient
        from .subclients.sharepointsubclient import SharepointSubclient
        from .subclients.sharepointsubclient import SharepointV1Subclient
        from .subclients.vminstancesubclient import VMInstanceSubclient
        from .subclients.db2subclient import DB2Subclient
        from .subclients.casesubclient import CaseSubclient
        from .subclients.aadsubclient import AzureAdSubclient

        # add the agent name to this dict, and its class as the value
        # the appropriate class object will be initialized based on the agent
        _subclients_dict = {
            'big data apps': BigDataAppsSubclient,
            'file system': FileSystemSubclient,
            'virtual server': [VirtualServerSubclient, VMInstanceSubclient],
            'cloud apps': CloudAppsSubclient,
            'sql server': SQLServerSubclient,
            'nas': NASSubclient,  # SP11 or lower CS honors NAS as the Agent Name
            'ndmp': NASSubclient,  # SP12 and above honors NDMP as the Agent Name
            'sap hana': SAPHANASubclient,
            'oracle': OracleSubclient,
            'oracle rac': OracleSubclient,
            'notes database': LNDbSubclient,
            'notes document': LNDocSubclient,
            'domino mailbox archiver': LNDmSubclient,
            'sybase': SybaseSubclient,
            'sap for oracle': SAPOracleSubclient,
            "exchange mailbox": [ExchangeSubclient, CaseSubclient],
            'mysql': MYSQLSubclient,
            'exchange database': ExchangeDatabaseSubclient,
            'postgresql': PostgresSubclient,
            'db2': DB2Subclient,
            'informix': InformixSubclient,
            'active directory': ADSubclient,
            'sharepoint server': [SharepointV1Subclient, SharepointSubclient],
            "azure ad": AzureAdSubclient
        }

        agent_object = backupset_object._agent_object
        instance_object = backupset_object._instance_object
        client_object = agent_object._client_object

        agent_name = agent_object.agent_name.lower()

        if isinstance(_subclients_dict.get(agent_name), list):
            if instance_object.instance_name == "vminstance":
                _class = _subclients_dict[agent_name][-1]
            elif client_object.client_type and int(client_object.client_type) == 36:
                # client type 36 is case manager client
                _class = _subclients_dict[agent_name][-1]
            elif int(agent_object.agent_id) == 78 and client_object.client_type:
                # agent id 78 is sharepoint client
                _class = _subclients_dict[agent_name][-1]
            else:
                _class = _subclients_dict[agent_name][0]
        else:
            _class = _subclients_dict.get(agent_name, cls)

        if _class.__new__ == cls.__new__:
            return object.__new__(_class)
        return _class.__new__(_class, backupset_object, subclient_name, subclient_id)

    def __init__(self, backupset_object, subclient_name, subclient_id=None):
        """Initialise the Subclient object.

            Args:
                backupset_object (object)  --  instance of the Backupset class

                subclient_name   (str)     --  name of the subclient

                subclient_id     (str)     --  id of the subclient
                    default: None

            Returns:
                object - instance of the Subclient class
        """
        self._backupset_object = backupset_object
        self._subclient_name = subclient_name.split('\\')[-1].lower()

        self._commcell_object = self._backupset_object._commcell_object

        self._cvpysdk_object = self._commcell_object._cvpysdk_object
        self._services = self._commcell_object._services
        self._update_response_ = self._commcell_object._update_response_

        self._instance_object = self._backupset_object._instance_object
        self._agent_object = self._backupset_object._agent_object
        self._client_object = self._agent_object._client_object

        self._restore_methods = [
            '_process_restore_response',
            '_filter_paths',
            '_restore_json',
            '_impersonation_json',
            '_restore_browse_option_json',
            '_restore_common_options_json',
            '_restore_destination_json',
            '_restore_fileoption_json',
            '_json_restore_subtask'
        ]

        self._restore_options_json = [
            '_impersonation_json_',
            '_browse_restore_json',
            '_destination_restore_json',
            '_commonoption_restore_json',
            '_fileoption_restore_json',

        ]

        self._backupcopy_interfaces = {
            'FILESYSTEM': 1,
            'RMAN': 2,
            'VOLUME': 3
        }

        if subclient_id:
            self._subclient_id = str(subclient_id)
        else:
            self._subclient_id = self._get_subclient_id()

        self._SUBCLIENT = self._services['SUBCLIENT'] % (self.subclient_id)

        self._BROWSE = self._services['BROWSE']

        self._RESTORE = self._services['RESTORE']

        self._subclient_properties = {}
        self._content = []

        self.schedules = None
        self.refresh()

    def __getattr__(self, attribute):
        """Returns the persistent attributes"""
        if attribute in self._restore_methods:
            return getattr(self._backupset_object, attribute)
        if attribute in self._restore_options_json:
            return getattr(self._backupset_object, attribute)

        return super(Subclient, self).__getattribute__(attribute)

    def __repr__(self):
        """String representation of the instance of this class."""
        representation_string = 'Subclient class instance for Subclient: "{0}" of Backupset: "{1}"'
        return representation_string.format(
            self.subclient_name, self._backupset_object.backupset_name
        )

    def _get_subclient_id(self):
        """Gets the subclient id associated to the specified backupset name and client name.

            Returns:
                str - id associated with this subclient
        """
        subclients = Subclients(self._backupset_object)
        return subclients.get(self.subclient_name).subclient_id

    def _get_subclient_properties(self):
        """Gets the subclient properties of this subclient.

            Raises:
                SDKException:
                    if response is empty

                    if response is not success
        """

        flag, response = self._cvpysdk_object.make_request(
            'GET', self._SUBCLIENT)

        if flag:
            if response.json() and 'subClientProperties' in response.json():
                self._subclient_properties = response.json()[
                    'subClientProperties'][0]

                if 'commonProperties' in self._subclient_properties:
                    self._commonProperties = self._subclient_properties['commonProperties']

                if 'subClientEntity' in self._subclient_properties:
                    self._subClientEntity = self._subclient_properties['subClientEntity']

                if 'proxyClient' in self._subclient_properties:
                    self._proxyClient = self._subclient_properties['proxyClient']

                if 'planEntity' in self._subclient_properties:
                    self._planEntity = self._subclient_properties['planEntity']

            else:
                raise SDKException('Response', '102')
        else:
            raise SDKException(
                'Response',
                '101',
                self._update_response_(
                    response.text))

    def _set_subclient_properties(self, attr_name, value):
        """sets the properties of this sub client.value is updated to instance once when post call
            succeeds

            Args:
                attr_name (str) --  Name of the attribute. This should be an instance variable.
                value (str)     --  Value of the attribute. This should be an instance variable.

            Raises:
                SDKException:
                    if failed to update number properties for subclient


        """
        try:
            backup = eval('self.%s' % attr_name)  # Take backup of old value
        except (AttributeError, KeyError):
            backup = None

        exec("self.%s = %s" % (attr_name, 'value'))  # set new value

        request_json = self._get_subclient_properties_json()

        flag, response = self._cvpysdk_object.make_request('POST', self._SUBCLIENT, request_json)

        output = self._process_update_response(flag, response)

        if output[0]:
            return
        else:
            o_str = 'Failed to update properties of subclient\nError: "{0}"'

            # Restore original value from backup on failure
            exec("self.%s = %s" % (attr_name, backup))

            raise SDKException('Subclient', '102', o_str.format(output[2]))

    @staticmethod
    def _convert_size(input_size):
        """Converts the given float size to appropriate size in B / KB / MB / GB, etc.

            Args:
                size (float)  --  float value to convert

            Returns:
                str - size converted to the specific type (B, KB, MB, GB, etc.)
        """
        if input_size == 0:
            return '0B'

        size_name = ("B", "KB", "MB", "GB", "TB", "PB", "EB", "ZB", "YB")
        i = int(math.floor(math.log(input_size, 1024)))
        power = math.pow(1024, i)
        size = round(input_size / power, 2)
        return '%s %s' % (size, size_name[i])

    def _process_update_response(self, flag, response):
        """Updates the subclient properties with the request provided.

            Args:
                update_request  (str)  --  update request specifying the details to update

            Returns:
                (bool, str, str):
                    bool -  flag specifies whether success / failure

                    str  -  error code received in the response

                    str  -  error message received

            Raises:
                SDKException:
                    if failed to update properties

                    if response is empty

                    if response is not success
        """
        if flag:
            if response.json():
                if "response" in response.json():
                    error_code = str(
                        response.json()["response"][0]["errorCode"])

                    if error_code == "0":
                        return (True, "0", "")
                    else:
                        error_message = ""

                        if "errorString" in response.json()["response"][0]:
                            error_message = response.json(
                            )["response"][0]["errorString"]

                        if error_message:
                            return (False, error_code, error_message)
                        else:
                            return (False, error_code, "")
                elif "errorCode" in response.json():
                    error_code = str(response.json()['errorCode'])
                    error_message = response.json()['errorMessage']

                    if error_code == "0":
                        return (True, "0", "")

                    if error_message:
                        return (False, error_code, error_message)
                    else:
                        return (False, error_code, "")
                else:
                    raise SDKException('Response', '102')
            else:
                raise SDKException('Response', '102')
        else:
            raise SDKException(
                'Response',
                '101',
                self._update_response_(
                    response.text))

    def _process_backup_response(self, flag, response):
        """Runs the Backup for a subclient with the request provided and returns the Job object.

            Args:
                update_request  (str)  --  update request specifying the details to update

            Returns:
                object - instance of the Job class for this backup job if its an immediate Job
                        instance of the Schedule class for the backup job if its a scheduled Job


            Raises:
                SDKException:
                    if job initialization failed

                    if response is empty

                    if response is not success
        """
        if flag:
            if response.json():
                if "jobIds" in response.json():
                    if len(response.json()['jobIds']) == 1:
                        return Job(self._commcell_object,
                                   response.json()['jobIds'][0])
                    else:
                        joblist = []
                        for jobids in response.json()['jobIds']:
                            joblist.append(Job(self._commcell_object, jobids))
                        return joblist
                elif "taskId" in response.json():
                    return Schedules(self._commcell_object).get(task_id=response.json()['taskId'])
                elif "errorCode" in response.json():
                    o_str = 'Initializing backup failed\nError: "{0}"'.format(
                        response.json()['errorMessage']
                    )
                    raise SDKException('Subclient', '102', o_str)
            else:
                raise SDKException('Response', '102')
        else:
            raise SDKException(
                'Response',
                '101',
                self._update_response_(
                    response.text))

    def _backup_json(self,
                     backup_level,
                     incremental_backup,
                     incremental_level,
                     advanced_options=None,
                     schedule_pattern=None,
                     common_backup_options=None):
        """Returns the JSON request to pass to the API as per the options selected by the user.

            Args:
                backup_level        (str)   --  level of backup the user wish to run

                    Full / Incremental / Differential / Synthetic_full

                incremental_backup  (bool)  --  run incremental backup

                    only applicable in case of Synthetic_full backup

                incremental_level   (str)   --  run incremental backup before/after synthetic full

                    BEFORE_SYNTH / AFTER_SYNTH

                    only applicable in case of Synthetic_full backup

                advanced_options   (dict)  --  advanced backup options to be included while
                making the request

                    default: None

                common_backup_options   (dict)  --  advanced job options to be included while
                                                    making the request.

                    default: None

            Returns:
                dict    -   JSON request to pass to the API

        """

        request_json = {
            "taskInfo": {
                "associations": [self._subClientEntity],
                "task": self._json_task,
                "subTasks": [
                    {
                        "subTaskOperation": 1,
                        "subTask": self._json_backup_subtasks,
                        "options": {
                            "backupOpts": {
                                "backupLevel": backup_level,
                                "incLevel": incremental_level,
                                "runIncrementalBackup": incremental_backup
                            }
                        }
                    }
                ]
            }
        }

        advanced_options_dict = {}

        if advanced_options:
            advanced_options_dict = self._advanced_backup_options(
                advanced_options)

        if advanced_options_dict:
            request_json["taskInfo"]["subTasks"][0]["options"]["backupOpts"].update(
                advanced_options_dict
            )

        advance_job_option_dict = {}

        if common_backup_options:
            advance_job_option_dict = self._common_backup_options(
                common_backup_options)

        if advance_job_option_dict:
            request_json["taskInfo"]["subTasks"][0]["options"]["commonOpts"] = advance_job_option_dict

        if schedule_pattern:
            request_json = SchedulePattern().create_schedule(request_json, schedule_pattern)

        return request_json

    def _common_backup_options(self, options):
        """
         Generates the advanced job options dict

            Args:
                options     (dict)      --    advanced job options that are to be included
                                                    in the request

            Returns:
                (dict)  -           generated advanced job options dict
        """
        return options

    def _advanced_backup_options(self, options):
        """Generates the advanced backup options dict

            Args:
                options     (dict)  --  advanced backup options that are to be included
                                            in the request
c
            Returns:
                (dict)  -   generated advanced options dict
        """
        return options

    def update_properties(self, properties_dict):
        """Updates the subclient properties

            Args:
                properties_dict (dict)  --  subclient property dict which is to be updated

            Returns:
                None

            Raises:
                SDKException:
                    if failed to add

                    if response is empty

                    if response code is not as expected

        **Note** self.properties can be used to get a deep copy of all the properties, modify the properties which you
        need to change and use the update_properties method to set the properties

        """
        request_json = {
            "subClientProperties": {}
        }

        request_json['subClientProperties'].update(properties_dict)

        # check if subclient name is updated in the request
        # if subclient name is updated set the newName field in the request
        if properties_dict.get('subClientEntity', {}).get('subclientName') and properties_dict.get(
                'subClientEntity', {}).get('subclientName') != self._subClientEntity.get('subclientName'):
            request_json['newName'] = properties_dict.get('subClientEntity', {}).get('subclientName')
        flag, response = self._cvpysdk_object.make_request('POST', self._SUBCLIENT, request_json)
        status, _, error_string = self._process_update_response(flag, response)
        self.refresh()

        if not status:
            raise SDKException('Subclient', '102', 'Failed to update subclient properties\nError: "{}"'.format(
                error_string))

    @property
    def properties(self):
        """Returns the subclient properties"""
        return copy.deepcopy(self._subclient_properties)

    @property
    def name(self):
        """Returns the Subclient display name"""
        return self._subclient_properties['subClientEntity']['subclientName']

    @property
    def display_name(self):
        """Returns the Subclient display name"""
        return self.name

    @property
    def subclient_guid(self):
        """Returns the SubclientGUID"""
        return self._subclient_properties.get('subClientEntity', {}).get('subclientGUID')

    @display_name.setter
    def display_name(self, display_name):
        """Sets the display name for the subclient
        Args:
            display_name    (str)   -- Display name for the subclient

        """
        update_properties = self.properties
        update_properties['subClientEntity']['subclientName'] = display_name
        self.update_properties(update_properties)

    @name.setter
    def name(self, name):
        """Sets the name for the subclient
        Args:
            name    (str)   -- name for the subclient
        """
        self.display_name = name

    @property
    def _json_task(self):
        """getter for the task information in JSON"""

        _taks_option_json = {
            "initiatedFrom": 2,
            "taskType": 1,
            "policyType": 0,
            "taskFlags": {
                "disabled": False
            }
        }

        return _taks_option_json

    @property
    def _json_backup_subtasks(self):
        """getter for the subtask in restore JSON . It is read only attribute"""

        _backup_subtask = {
            "subTaskType": 2,
            "operationType": 2
        }

        return _backup_subtask

    @property
    def subclient_id(self):
        """Treats the subclient id as a read-only attribute."""
        return self._subclient_id

    @property
    def subclient_name(self):
        """Treats the subclient name as a read-only attribute."""
        return self._subclient_name

    @property
    def last_backup_time(self):
        """Treats the last backup time as a read-only attribute."""
        if 'lastBackupTime' in self._commonProperties:
            if self._commonProperties['lastBackupTime'] != 0:
                _last_backup_time = time.ctime(
                    self._commonProperties['lastBackupTime']
                )
                return _last_backup_time
        return 0

    @property
    def next_backup_time(self):
        """Treats the next backup time as a read-only attribute."""
        if 'nextBackupTime' in self._commonProperties:
            if self._commonProperties['nextBackupTime'] != 0:
                _next_backup = time.ctime(
                    self._commonProperties['nextBackupTime']
                )
                return _next_backup

    @property
    def is_backup_enabled(self):
        """Treats the is backup enabled as a read-only attribute."""
        if 'enableBackup' in self._commonProperties:
            return self._commonProperties['enableBackup']

    @property
    def is_intelli_snap_enabled(self):
        """Treats the is intelli snap enabled as a read-only attribute."""
        if 'snapCopyInfo' in self._commonProperties:
            snap_copy_info = self._commonProperties.get('snapCopyInfo')
            return snap_copy_info.get('isSnapBackupEnabled')

    @property
    def is_blocklevel_backup_enabled(self):
        """returns True if block level backup is enabled else returns false"""
        return bool(self._subclient_properties.get(
            'postgreSQLSubclientProp', {}).get('isUseBlockLevelBackup', False))

    @property
    def snapshot_engine_name(self):
        """returns snapshot engine name associated with the subclient"""
        if self.is_intelli_snap_enabled:
            if 'snapCopyInfo' in self._commonProperties:
                snap_copy_info = self._commonProperties.get('snapCopyInfo', "")
                if 'snapToTapeSelectedEngine' in snap_copy_info:
                    if 'snapShotEngineName' in snap_copy_info.get('snapToTapeSelectedEngine', ""):
                        return snap_copy_info['snapToTapeSelectedEngine'].get(
                            'snapShotEngineName', "")
        raise SDKException(
            'Subclient',
            '102',
            'Cannot fetch snap engine name.')

    @property
    def is_trueup_enabled(self):
        """Treats the True up enabled as a property of the Subclient class."""
        if 'isTrueUpOptionEnabled' in self._commonProperties:
            return self._commonProperties['isTrueUpOptionEnabled']

    @property
    def is_on_demand_subclient(self):
        """Treats the on demand subclient as a read-only attribute."""
        return self._backupset_object.is_on_demand_backupset

    @property
    def description(self):
        """Treats the subclient description as a property of the Subclient class."""
        if 'description' in self._commonProperties:
            return self._commonProperties['description']

    @property
    def storage_policy(self):
        """Treats the subclient storage policy as a read-only attribute."""
        storage_device = self._commonProperties['storageDevice']
        if 'dataBackupStoragePolicy' in storage_device:
            data_backup_storage_policy = storage_device['dataBackupStoragePolicy']
            if 'storagePolicyName' in data_backup_storage_policy:
                return data_backup_storage_policy['storagePolicyName']

    @property
    def storage_ma(self):
        """Treats the subclient storage ma as a read-only attribute."""
        storage_device = self._commonProperties['storageDevice']
        if 'performanceMode' in storage_device:
            data_backup_storage_device = storage_device['performanceMode']
            data_storage_details = data_backup_storage_device["perfCRCDetails"][0]
            if 'perfMa' in data_storage_details:
                return data_storage_details['perfMa']

    @property
    def storage_ma_id(self):
        """Treats the subclient storage ma id as a read-only attribute."""
        storage_device = self._commonProperties['storageDevice']
        if 'performanceMode' in storage_device:
            data_backup_storage_device = storage_device['performanceMode']
            data_storage_details = data_backup_storage_device["perfCRCDetails"][0]
            if 'perfMaId' in data_storage_details:
                return data_storage_details['perfMaId']

    @property
    def data_readers(self):
        """Treats the data readers as a read-only attribute."""
        if 'numberOfBackupStreams' in self._commonProperties:
            return int(
                self._commonProperties['numberOfBackupStreams']
            )

    @data_readers.setter
    def data_readers(self, value):
        """Sets the count of data readers for the subclient as the value provided as input.

            Raises:
                SDKException:
                    if failed to update number of data readers for subclient

                    if the type of value input is not int
        """
        if isinstance(value, int):
            self._set_subclient_properties(
                "_commonProperties['numberOfBackupStreams']", value)
        else:
            raise SDKException(
                'Subclient', '102', 'Subclient data readers should be an int value'
            )

    @property
    def allow_multiple_readers(self):
        """Treats the allow multiple readers as a read-only attribute."""
        if 'allowMultipleDataReaders' in self._commonProperties:
            return bool(
                self._commonProperties['allowMultipleDataReaders']
            )

    @allow_multiple_readers.setter
    def allow_multiple_readers(self, value):
        """To enable or disable allow multiple readers property
        for the subclient based on the value provided as input.

            Raises:
                SDKException:
                    if failed to update allow multiple readers for subclient

                    if the type of value input is not bool
        """
        # Has to be initialized for new subclient as attribute is not present
        # default value is False
        if 'allowMultipleDataReaders' not in self._commonProperties:
            self._commonProperties['allowMultipleDataReaders'] = False

        if isinstance(value, bool):
            self._set_subclient_properties(
                "_commonProperties['allowMultipleDataReaders']",
                value)
        else:
            raise SDKException(
                'Subclient', '102',
                'Subclient allow multple readers should be a bool value'
            )

    @property
    def read_buffer_size(self):
        """Treats the read buffer size as a read-only attribute."""
        if 'readBuffersize' in self._commonProperties:
            return int(
                self._commonProperties['readBuffersize']
            )

    @property
    def is_default_subclient(self):
        """Returns True if the subclient is default
        subclient else returns False"""
        return self._commonProperties.get('isDefaultSubclient')

    @read_buffer_size.setter
    def read_buffer_size(self, value):
        """Sets the read buffer size for the subclient
        as the value provided as input.
            (value in KB)

            Raises:
                SDKException:
                    if failed to update read buffer size for subclient

                    if the type of value input is not int
        """
        # Has to be initialized for new subclient as attribute is not present
        # default value is 0
        if 'readBuffersize' not in self._commonProperties:
            self._commonProperties['readBuffersize'] = 0

        if isinstance(value, int):
            self._set_subclient_properties(
                "_commonProperties['readBuffersize']",
                value)
        else:
            raise SDKException(
                'Subclient', '102',
                'Subclient read buffer size should be an int value'
            )

    @description.setter
    def description(self, value):
        """Sets the description of the subclient as the value provided as input.

            Raises:
                SDKException:
                    if failed to update description of subclient

                    if the type of value input is not string
        """
        if isinstance(value, str):
            self._set_subclient_properties(
                "_commonProperties['description']", value)
        else:
            raise SDKException(
                'Subclient', '102', 'Subclient description should be a string value'
            )

    @storage_policy.setter
    def storage_policy(self, value):
        """Sets the storage policy of subclient as the value provided as input.

            Args:
                value   (str)   -- Storage policy name to be assigned to subclient

            Raises:
                SDKException:
                    if storage policy name is not in string format

                    if failed to update storage policy name

        """
        if isinstance(value, str):
            value = value.lower()

            if not self._commcell_object.storage_policies.has_policy(value):
                raise SDKException(
                    'Subclient',
                    '102',
                    'Storage Policy: "{0}" does not exist in the Commcell'.format(value)
                )

            self._set_subclient_properties(
                "_commonProperties['storageDevice']['dataBackupStoragePolicy']",
                {
                    "storagePolicyName": value,
                    "storagePolicyId": int(
                        self._commcell_object.storage_policies.all_storage_policies[value]
                    )
                }
            )
        else:
            raise SDKException('Subclient', '101')

    def enable_backup(self):
        """Enables Backup for the subclient.

            Raises:
                SDKException:
                    if failed to enable backup of subclient
        """
        self._set_subclient_properties("_commonProperties['enableBackup']", True)

    def enable_trueup(self):
        """Setter for the TrueUp Option for a Subclient"""
        if 'isTrueUpOptionEnabled' in self._commonProperties:
            self._set_subclient_properties("_commonProperties['isTrueUpOptionEnabled']", True)

    def enable_trueup_days(self, days=30):
        """Setter for the TrueUp Option with reconcile after x days"""
        self.enable_trueup()
        self._set_subclient_properties("_commonProperties['runTrueUpJobAfterDays']", days)

    def enable_backup_at_time(self, enable_time):
        """Disables Backup if not already disabled, and enables at the time specified.

            Args:
                enable_time (str)  --  UTC time to enable the backup at, in 24 Hour format
                    format: YYYY-MM-DD HH:mm:ss

                **Note** In case of linux CommServer provide time in GMT timezone

            Raises:
                SDKException:
                    if time value entered is less than the current time

                    if time value entered is not of correct format

                    if failed to enable backup

                    if response is empty

                    if response is not success
        """
        try:
            time_tuple = time.strptime(enable_time, "%Y-%m-%d %H:%M:%S")
            if time.mktime(time_tuple) < time.time():
                raise SDKException('Subclient', '108')
        except ValueError:
            raise SDKException('Subclient', '109')

        enable_backup_at_time = {
            "TimeZoneName": self._commcell_object.default_timezone,
            "timeValue": enable_time
        }

        self._set_subclient_properties(
            "_commonProperties['enableBackupAtDateTime']", enable_backup_at_time
        )

    def disable_backup(self):
        """Disables Backup for the subclient.

            Raises:
                SDKException:
                    if failed to disable backup of subclient
        """
        self._set_subclient_properties(
            "_commonProperties['enableBackup']", False)

    def exclude_from_sla(self):
        """Exclude subclient from SLA.

            Raises:
                SDKException:
                    if failed to exclude the subclient from SLA
        """
        self._set_subclient_properties(
            "_commonProperties['excludeFromSLA']", True)

    def enable_intelli_snap(self, snap_engine_name, proxy_options=None):
        """Enables Intelli Snap for the subclient.

            Args:
                snap_engine_name    (str)   --  Snap Engine Name

            Raises:
                SDKException:
                    if failed to enable intelli snap for subclient
        """
        if not isinstance(snap_engine_name, str):
            raise SDKException("Subclient", "101")

        properties_dict = {
            "isSnapBackupEnabled": True,
            "snapToTapeSelectedEngine": {
                "snapShotEngineName": snap_engine_name
            }
        }

        if proxy_options is not None:
            if "snap_proxy" in proxy_options:
                properties_dict["snapToTapeProxyToUse"] = {
                    "clientName": proxy_options["snap_proxy"]
                }

            if "backupcopy_proxy" in proxy_options:
                properties_dict["useSeparateProxyForSnapToTape"] = True
                properties_dict["separateProxyForSnapToTape"] = {
                    "clientName": proxy_options["backupcopy_proxy"]
                }

            if "use_source_if_proxy_unreachable" in proxy_options:
                properties_dict["snapToTapeProxyToUseSource"] = True

        self._set_subclient_properties(
            "_commonProperties['snapCopyInfo']", properties_dict)

    def disable_intelli_snap(self):
        """Disables Intelli Snap for the subclient.

            Raises:
                SDKException:
                    if failed to disable intelli snap for subclient
        """
        self._set_subclient_properties(
            "_commonProperties['snapCopyInfo']['isSnapBackupEnabled']", False
        )

    def set_proxy_for_snap(self, proxy_name):
        """ method to set Use proxy option for intellisnap subclient 

        Args:
            proxy_name(str) -- Name of the proxy to be used

        """
        if not isinstance(proxy_name, str):
            raise SDKException("Subclient", "101")

        properties_dict = {
            "clientName": proxy_name
        }

        update_properties = self.properties
        update_properties['commonProperties']['snapCopyInfo']['snapToTapeProxyToUse'] = properties_dict
        self.update_properties(update_properties)

    def unset_proxy_for_snap(self):
        """ method to unset Use proxy option for intellisnap subclient """

        properties_dict = {
            "clientId": 0
        }
        update_properties = self.properties
        update_properties['commonProperties']['snapCopyInfo']['snapToTapeProxyToUse'] = properties_dict
        self.update_properties(update_properties)

    def backup(self,
               backup_level="Incremental",
               incremental_backup=False,
               incremental_level='BEFORE_SYNTH',
               collect_metadata=False):
        """Runs a backup job for the subclient of the level specified.

            Args:
                backup_level        (str)   --  level of backup the user wish to run
                        Full / Incremental / Differential / Synthetic_full
                    default: Incremental

                incremental_backup  (bool)  --  run incremental backup
                        only applicable in case of Synthetic_full backup
                    default: False

                incremental_level   (str)   --  run incremental backup before/after synthetic full
                        BEFORE_SYNTH / AFTER_SYNTH

                        only applicable in case of Synthetic_full backup
                    default: BEFORE_SYNTH

            Returns:
                object - instance of the Job class for this backup job

            Raises:
                SDKException:
                    if backup level specified is not correct

                    if response is empty

                    if response is not success
        """
        backup_level = backup_level.lower()

        if backup_level not in ['full', 'incremental', 'transaction_log',
                                'differential', 'synthetic_full']:
            raise SDKException('Subclient', '103')

        backup_request = backup_level

        if backup_level == 'synthetic_full':
            if incremental_backup:
                backup_request += '&runIncrementalBackup=True'
                backup_request += '&incrementalLevel=%s' % (
                    incremental_level.lower())
            else:
                backup_request += '&runIncrementalBackup=False'

        backup_request += '&collectMetaInfo=%s' % collect_metadata

        backup_service = self._services['SUBCLIENT_BACKUP'] % (
            self.subclient_id, backup_request)

        flag, response = self._cvpysdk_object.make_request(
            'POST', backup_service)

        return self._process_backup_response(flag, response)

    def get_ma_associated_storagepolicy(self):
        """
        Get Media agents associated with storage policy
        
        Raise Exception:
                if unable to get MA names
        """
        storage = self._subclient_properties['commonProperties']['storageDevice']
        if 'performanceMode' in storage:
            data_backup_storage_device = storage['performanceMode']["perfCRCDetails"]
            malist = []
            for each_ma in data_backup_storage_device:
                malist.append(each_ma['perfMa'])
            return malist

    def browse(self, *args, **kwargs):
        """Browses the content of the Subclient.

            Args:
                Dictionary of browse options:
                    Example:

                        browse({
                            'path': 'c:\\\\hello',

                            'show_deleted': True,

                            'from_time': '2014-04-20 12:00:00',

                            'to_time': '2016-04-21 12:00:00'
                        })

            Kwargs:
                Keyword argument of browse options:
                    Example:

                        browse(
                            path='c:\\hello',

                            show_deleted=True,

                            from_time='2014-04-20 12:00:00',

                            to_time='2016-04-21 12:00:00'
                        )

            Returns:
                (list, dict)
                    list    -   List of only the file, folder paths from the browse response

                    dict    -   Dictionary of all the paths with additional metadata retrieved
                    from browse operation


            Refer `default_browse_options`_ for all the supported options.

            .. _default_browse_options: https://github.com/CommvaultEngg/cvpysdk/blob/master/cvpysdk/backupset.py#L565

        """
        if args and isinstance(args[0], dict):
            options = args[0]
        else:
            options = kwargs

        options['_subclient_id'] = self._subclient_id

        return self._backupset_object.browse(options)

    def find(self, *args, **kwargs):
        """Searches a file/folder in the backed up content of the subclient,
            and returns all the files matching the filters given.

            Args:
                Dictionary of browse options:
                    Example:

                        find({
                            'file_name': '*.txt',

                            'show_deleted': True,

                            'from_time': '2014-04-20 12:00:00',

                            'to_time': '2016-04-31 12:00:00'
                        })

            Kwargs:
                Keyword argument of browse options:
                    Example:

                        find(
                            file_name='*.txt',

                            show_deleted=True,

                            'from_time': '2014-04-20 12:00:00',

                            to_time='2016-04-31 12:00:00'
                        )

            Returns:
                (list, dict)
                    list    -   List of only the file, folder paths from the browse response

                    dict    -   Dictionary of all the paths with additional metadata retrieved
                    from browse operation


            Refer `default_browse_options`_ for all the supported options.

            Additional options supported:
                file_name       (str)   --  Find files with name

                file_size_gt    (int)   --  Find files with size greater than size

                file_size_lt    (int)   --  Find files with size lesser than size

                file_size_et    (int)   --  Find files with size equal to size

            .. _default_browse_options: https://github.com/CommvaultEngg/cvpysdk/blob/master/cvpysdk/backupset.py#L565

        """
        if args and isinstance(args[0], dict):
            options = args[0]
        else:
            options = kwargs

        options['_subclient_id'] = self._subclient_id

        return self._backupset_object.find(options)

    def list_media(self, *args, **kwargs):
        """List media required to browse and restore backed up data from the subclient

            Args:
                Dictionary of options:
                    Example:

                        list_media({
                            'path': 'c:\\hello',
                            'show_deleted': True,
                            'from_time': '2020-04-20 12:00:00',
                            'to_time': '2021-04-19 12:00:00'
                        })

            Kwargs:
                Keyword argument of options:
                    Example:

                        list_media(
                            path='c:\\hello',
                            show_deleted=True,
                            from_time='2020-04-20 12:00:00',
                            to_time='2021-04-19 12:00:00'
                        )

            Note:
                Refer `_default_browse_options` in backupset.py for all the supported options.

            Returns:
                (List, Dict) -
                    List - List of all the media required for the given options

                    Dict - Total size of the media

            Raises:
                SDKException:
                    if failed to list media for content

                    if response is not success

        """

        if args and isinstance(args[0], dict):
            options = args[0]
        else:
            options = kwargs

        options['_subclient_id'] = self._subclient_id

        return self._backupset_object.list_media(options)

    def restore_in_place(
            self,
            paths,
            overwrite=True,
            restore_data_and_acl=True,
            copy_precedence=None,
            from_time=None,
            to_time=None,
            fs_options=None,
            schedule_pattern=None,
            proxy_client=None,
            advanced_options=None):

        """Restores the files/folders specified in the input paths list to the same location.

            Args:
                paths                   (list)  --  list of full paths of files/folders to restore

                overwrite               (bool)  --  unconditional overwrite files during restore
                    default: True

                restore_data_and_acl    (bool)  --  restore data and ACL files
                    default: True

                copy_precedence         (int)   --  copy precedence value of storage policy copy
                    default: None

                from_time           (str)       --  time to restore the contents after
                        format: YYYY-MM-DD HH:MM:SS

                    default: None

                to_time           (str)         --  time to restore the contents before
                        format: YYYY-MM-DD HH:MM:SS

                    default: None

                fs_options      (dict)          -- dictionary that includes all advanced options
                    options:
                        all_versions        : if set to True restores all the versions of the
                                                specified file
                        versions            : list of version numbers to be backed up
                        validate_only       : To validate data backed up for restore


                schedule_pattern (dict) -- scheduling options to be included for the task

                        Please refer schedules.schedulePattern.createSchedule()
                                                                    doc for the types of Jsons

                schedule_pattern (dict) -- scheduling options to be included for the task

                        Please refer schedules.schedulePattern.createSchedule()
                                                                    doc for the types of Jsons

                proxy_client    (str)          -- Proxy client used during FS under NAS operations

                advanced_options    (dict)  -- Advanced restore options

                    Options:

                        job_description (str)   --  Restore job description

                        timezone        (str)   --  Timezone to be used for restore

                            **Note** make use of TIMEZONES dict in constants.py to pass timezone

            Returns:
                object - instance of the Job class for this restore job if its an immediate Job
                         instance of the Schedule class for this restore job if its a scheduled Job

            Raises:
                SDKException:
                    if paths is not a list

                    if failed to initialize job

                    if response is empty

                    if response is not success
        """
        self._instance_object._restore_association = self._subClientEntity

        return self._instance_object._restore_in_place(
            paths=paths,
            overwrite=overwrite,
            restore_data_and_acl=restore_data_and_acl,
            copy_precedence=copy_precedence,
            from_time=from_time,
            to_time=to_time,
            fs_options=fs_options,
            schedule_pattern=schedule_pattern,
            proxy_client=proxy_client,
            advanced_options=advanced_options
        )

    def restore_out_of_place(
            self,
            client,
            destination_path,
            paths,
            overwrite=True,
            restore_data_and_acl=True,
            copy_precedence=None,
            from_time=None,
            to_time=None,
            fs_options=None,
            schedule_pattern=None,
            proxy_client=None,
            advanced_options=None):
        """Restores the files/folders specified in the input paths list to the input client,
            at the specified destionation location.

            Args:
                client                (str/object) --  either the name of the client or
                                                           the instance of the Client

                destination_path      (str)        --  full path of the restore location on client

                paths                 (list)       --  list of full paths of
                                                           files/folders to restore

                overwrite             (bool)       --  unconditional overwrite files during restore
                    default: True

                restore_data_and_acl  (bool)       --  restore data and ACL files
                    default: True

                copy_precedence         (int)   --  copy precedence value of storage policy copy
                    default: None

                from_time           (str)       --  time to restore the contents after
                        format: YYYY-MM-DD HH:MM:SS

                    default: None

                to_time           (str)         --  time to restore the contents before
                        format: YYYY-MM-DD HH:MM:SS

                    default: None

                fs_options      (dict)          -- dictionary that includes all advanced options
                    options:
                        preserve_level      : preserve level option to set in restore
                        proxy_client        : proxy that needed to be used for restore
                        impersonate_user    : Impersonate user options for restore
                        impersonate_password: Impersonate password option for restore
                                                in base64 encoded form
                        all_versions        : if set to True restores all the versions of the
                                                specified file
                        versions            : list of version numbers to be backed up
                        media_agent         : Media Agent need to be used for Browse and restore
                        validate_only       : To validate data backed up for restore


                schedule_pattern (dict) -- scheduling options to be included for the task

                        Please refer schedules.schedulePattern.createSchedule()
                                                                    doc for the types of Jsons

                schedule_pattern (dict) -- scheduling options to be included for the task

                        Please refer schedules.schedulePattern.createSchedule()
                                                                    doc for the types of Jsons

                proxy_client    (str)          -- Proxy client used during FS under NAS operations

                advanced_options    (dict)  -- Advanced restore options

                    Options:

                        job_description (str)   --  Restore job description

                        timezone        (str)   --  Timezone to be used for restore

                            **Note** make use of TIMEZONES dict in constants.py to pass timezone

            Returns:
                object - instance of the Job class for this restore job if its an immediate Job
                         instance of the Schedule class for this restore job if its a scheduled Job

            Raises:
                SDKException:
                    if client is not a string or Client instance

                    if destination_path is not a string

                    if paths is not a list

                    if failed to initialize job

                    if response is empty

                    if response is not success
        """
        self._instance_object._restore_association = self._subClientEntity

        if fs_options and 'proxy_client' in fs_options:
            proxy_client = fs_options['proxy_client']

        return self._instance_object._restore_out_of_place(
            client=client,
            destination_path=destination_path,
            paths=paths,
            overwrite=overwrite,
            restore_data_and_acl=restore_data_and_acl,
            copy_precedence=copy_precedence,
            from_time=from_time,
            to_time=to_time,
            fs_options=fs_options,
            schedule_pattern=schedule_pattern,
            proxy_client=proxy_client,
            advanced_options=advanced_options
        )

    def set_backup_nodes(self, data_access_nodes):
        """Sets the the backup nodes for NFS share subclient.

            Args:
                data_access_nodes   (list)  --  the list of data access nodes to be set
                as backup nodes for NFS share subclient

            Returns:
                None    -   if the operation is successful

            Raises:
                SDKException:
                    if unable to update the backup nodes for the subclient

        """
        data_access_nodes_json = []
        for access_node in data_access_nodes:
            data_access_nodes_json.append({"clientName": access_node})

        request_json = {
            "subClientProperties": {
                "fsSubClientProp": {
                    "backupConfiguration": {
                        "backupDataAccessNodes": data_access_nodes_json
                    }
                }
            }
        }

        flag, response = self._cvpysdk_object.make_request('POST', self._SUBCLIENT, request_json)

        output = self._process_update_response(flag, response)

        if output[0]:
            return
        else:
            o_str = 'Failed to update properties of subclient\nError: "{0}"'
            raise SDKException('Subclient', '102', o_str.format(output[2]))

    def find_latest_job(
            self,
            include_active=True,
            include_finished=True,
            lookup_time=1,
            job_filter='Backup,SYNTHFULL'):
        """Finds the latest job for the subclient
            which includes current running job also.

            Args:
                include_active    (bool)    -- to indicate if
                                                active jobs should be included
                    default: True

                include_finished  (bool)    -- to indicate if finished jobs
                                                should be included
                    default: True

                lookup_time       (int)     -- get jobs executed
                                                within the number of hours
                    default: 1 Hour

                job_filter        (str)     -- to specify type of job
                    default: 'Backup,SYNTHFULL'

                    for multiple filters,
                    give the values **comma(,)** separated

                    List of Possible Values:

                            Backup

                            Restore

                            AUXCOPY

                            WORKFLOW

                            etc..

                    http://documentation.commvault.com/commvault/v11/article?p=features/rest_api/operations/get_job.htm
                        to get the complete list of filters available

            Returns:
                object  -   instance of the Job class for the latest job

            Raises:
                SDKException:
                    if any error occurred while finding the latest job.

        """
        job_controller = JobController(self._commcell_object)
        entity_dict = {
            "subclientId": int(self.subclient_id)
        }
        if include_active and include_finished:
            client_jobs = job_controller.all_jobs(
                client_name=self._client_object.client_name,
                lookup_time=lookup_time,
                job_filter=job_filter,
                entity=entity_dict
            )
        elif include_active:
            client_jobs = job_controller.active_jobs(
                client_name=self._client_object.client_name,
                lookup_time=lookup_time,
                job_filter=job_filter,
                entity=entity_dict
            )
        elif include_finished:
            client_jobs = job_controller.finished_jobs(
                client_name=self._client_object.client_name,
                lookup_time=lookup_time,
                job_filter=job_filter,
                entity=entity_dict
            )
        else:
            raise SDKException(
                'Subclient',
                '102',
                "Either active or finished job must be included"
            )

        latest_jobid = 0
        for job in client_jobs:
            if client_jobs[job]['subclient_id'] == int(self._subclient_id):
                if int(job) > latest_jobid:
                    latest_jobid = int(job)

        if latest_jobid == 0:
            raise SDKException('Subclient', '102', "No jobs found")

        return Job(self._commcell_object, latest_jobid)

    def refresh(self):
        """Refresh the properties of the Subclient."""
        self._get_subclient_properties()
        self.schedules = Schedules(self)

    @property
    def software_compression(self):
        """Returns the value of Software Compression settings on the Subclient."""
        mapping = {
            0: 'ON_CLIENT',
            1: 'ON_MEDIAAGENT',
            2: 'USE_STORAGE_POLICY_SETTINGS',
            4: 'OFF'
        }

        try:
            return mapping[self._commonProperties['storageDevice']['softwareCompression']]
        except KeyError:
            return self._commonProperties['storageDevice']['softwareCompression']

    @software_compression.setter
    def software_compression(self, value):
        """Sets the software compression of the subclient as the value provided as input.

            Args:
                value   (str)   --  software compression setting

                Valid values are:

                -   ON_CLIENT
                -   ON_MEDIAAGENT
                -   USE_STORAGE_POLICY_SETTINGS
                -   OFF

            Raises:
                SDKException:
                    if failed to update software compression of subclient

                    if the type of value input is not string

        """
        if isinstance(value, str):
            self._set_subclient_properties(
                "_commonProperties['storageDevice']['softwareCompression']", value
            )
        else:
            raise SDKException('Subclient', '101')

    @property
    def network_agent(self):
        """Returns the value of network agents setting on the Subclient."""
        return self._commonProperties['storageDevice']['networkAgents']

    @network_agent.setter
    def network_agent(self, value):
        """Sets the network agents of the subclient as the value provided as input.

            Args:
                value   (int)   --  network agents value

            Raises:
                SDKException:
                    if failed to update network agents of subclient

                    if the type of value input is not integer

        """

        if isinstance(value, int):
            self._set_subclient_properties("_commonProperties['storageDevice']['networkAgents']", value)
        else:
            raise SDKException('Subclient', '101')

    @property
    def encryption_flag(self):
        """Returns the value of encryption flag settings on the Subclient."""
        mapping = {
            0: 'ENC_NONE',
            1: 'ENC_MEDIA_ONLY',
            2: 'ENC_NETWORK_AND_MEDIA',
            3: 'ENC_NETWORK_ONLY'
        }

        try:
            return mapping[self._commonProperties['encryptionFlag']]
        except KeyError:
            return self._commonProperties['encryptionFlag']

    @encryption_flag.setter
    def encryption_flag(self, value):
        """Sets the encryption Flag of the subclient as the value provided as input.

            Args:
                value   (str)   --  encryption flag value

                Valid values are:

                -   ENC_NONE
                -   ENC_MEDIA_ONLY
                -   ENC_NETWORK_AND_MEDIA
                -   ENC_NETWORK_ONLY

            Raises:
                SDKException:
                    if failed to update encryption Flag of subclient

                    if the type of value input is not string

        """

        if isinstance(value, str):
            self._set_subclient_properties("_commonProperties['encryptionFlag']", value)
        else:
            raise SDKException('Subclient', '101')

    @property
    def deduplication_options(self):
        """Returns the value of deduplication options settings on the Subclient."""
        mapping_dedupe = {
            0: False,
            1: True,
        }
        mapping_signature = {
            1: "ON_CLIENT",
            2: "ON_MEDIA_AGENT"
        }

        dedupe_options = self._commonProperties['storageDevice']['deDuplicationOptions']

        if "enableDeduplication" in dedupe_options:
            if dedupe_options['enableDeduplication'] == 0:
                return mapping_dedupe[dedupe_options['enableDeduplication']]
            else:
                if 'generateSignature' in dedupe_options:
                    try:
                        return mapping_signature[dedupe_options['generateSignature']]
                    except KeyError:
                        return dedupe_options['generateSignature']

    @deduplication_options.setter
    def deduplication_options(self, enable_dedupe):
        """Enables / Disables the deduplication options of the Subclient.

            Args:
                enable_dedupe   (tuple)     --  to enable or disable deduplication

                    tuple:
                        **bool**    -   boolean flag to specify whether to
                        enable / disable deduplication

                        **str**     -   where to generate the signature at

                            Valid Values are:

                            -   ON_CLIENT
                            -   ON_MEDIA_AGENT


                    e.g.:

                        >>> subclient.deduplication_options = (False, None)

                        >>> subclient.deduplication_options = (True, "ON_CLIENT")

                        >>> subclient.deduplication_options = (True, "ON_MEDIA_AGENT")

            Raises:
                SDKException:
                    if failed to update deDuplication Options of subclient

                    if the type of value input is not correct

        """
        if enable_dedupe[0] is True:
            if enable_dedupe[1] is not None:
                self._set_subclient_properties(
                    "_commonProperties['storageDevice']['deDuplicationOptions']",
                    {
                        "enableDeduplication": enable_dedupe[0],
                        "generateSignature": enable_dedupe[1]
                    }
                )
            else:
                raise SDKException('Subclient', '102', "Input data is not correct")
        else:
            self._set_subclient_properties(
                "_commonProperties['storageDevice']['deDuplicationOptions']",
                {
                    "enableDeduplication": enable_dedupe[0],
                }
            )

    @property
    def plan(self):
        """Returns the name of the plan associated with the subclient.
           Returns None if no plan is associated
        """

        if 'planEntity' in self._subclient_properties:
            planEntity = self._subclient_properties['planEntity']

            if bool(planEntity) and 'planName' in planEntity:
                return planEntity['planName']
            else:
                return None
        else:
            raise SDKException('Subclient', '112')

    @plan.setter
    def plan(self, value):
        """Associates a plan to the subclient.

            Args:
                value   (object)    --  the Plan object which is to be associated
                                        with the subclient

                value   (str)       --  name of the plan to be associated

                value   (None)      --  set value to None to remove plan associations

            Raises:
                SDKException:
                    if the type of input is incorrect

                    if the plan association is unsuccessful
        """
        from .plan import Plan
        if isinstance(value, Plan):
            if self._commcell_object.plans.has_plan(value.plan_name):
                self.update_properties({
                    'planEntity': {
                        'planName': value.plan_name
                    },
                    "useContentFromPlan": True
                })
            else:
                raise SDKException('Subclient', '102', 'Plan does not exist')
        elif isinstance(value, str):
            if self._commcell_object.plans.has_plan(value):
                self.update_properties({
                    'planEntity': {
                        'planName': value
                    },
                    "useContentFromPlan": True
                })
            else:
                raise SDKException('Subclient', '102', 'Plan does not exist')
        elif value is None:
            self.update_properties({
                'removePlanAssociation': True
            })
        else:
            raise SDKException('Subclient', '101')

Classes

class Subclient (backupset_object, subclient_name, subclient_id=None)

Base class consisting of all the common properties and operations for a Subclient

Initialise the Subclient object.

Args

backupset_object (object) – instance of the Backupset class

subclient_name (str) – name of the subclient

subclient_id (str) – id of the subclient default: None

Returns

object - instance of the Subclient class

Expand source code Browse git
class Subclient(object):
    """Base class consisting of all the common properties and operations for a Subclient"""

    def __new__(cls, backupset_object, subclient_name, subclient_id=None):
        """Class composition for CV subclients"""
        from .subclients.fssubclient import FileSystemSubclient
        from .subclients.bigdataappssubclient import BigDataAppsSubclient
        from .subclients.vssubclient import VirtualServerSubclient
        from .subclients.casubclient import CloudAppsSubclient
        from .subclients.sqlsubclient import SQLServerSubclient
        from .subclients.nassubclient import NASSubclient
        from .subclients.hanasubclient import SAPHANASubclient
        from .subclients.oraclesubclient import OracleSubclient
        from .subclients.lotusnotes.lndbsubclient import LNDbSubclient
        from .subclients.lotusnotes.lndocsubclient import LNDocSubclient
        from .subclients.lotusnotes.lndmsubclient import LNDmSubclient
        from .subclients.sybasesubclient import SybaseSubclient
        from .subclients.saporaclesubclient import SAPOracleSubclient
        from .subclients.exchsubclient import ExchangeSubclient
        from .subclients.mysqlsubclient import MYSQLSubclient
        from .subclients.exchange.exchange_database_subclient import ExchangeDatabaseSubclient
        from .subclients.postgressubclient import PostgresSubclient
        from .subclients.informixsubclient import InformixSubclient
        from .subclients.adsubclient import ADSubclient
        from .subclients.sharepointsubclient import SharepointSubclient
        from .subclients.sharepointsubclient import SharepointV1Subclient
        from .subclients.vminstancesubclient import VMInstanceSubclient
        from .subclients.db2subclient import DB2Subclient
        from .subclients.casesubclient import CaseSubclient
        from .subclients.aadsubclient import AzureAdSubclient

        # add the agent name to this dict, and its class as the value
        # the appropriate class object will be initialized based on the agent
        _subclients_dict = {
            'big data apps': BigDataAppsSubclient,
            'file system': FileSystemSubclient,
            'virtual server': [VirtualServerSubclient, VMInstanceSubclient],
            'cloud apps': CloudAppsSubclient,
            'sql server': SQLServerSubclient,
            'nas': NASSubclient,  # SP11 or lower CS honors NAS as the Agent Name
            'ndmp': NASSubclient,  # SP12 and above honors NDMP as the Agent Name
            'sap hana': SAPHANASubclient,
            'oracle': OracleSubclient,
            'oracle rac': OracleSubclient,
            'notes database': LNDbSubclient,
            'notes document': LNDocSubclient,
            'domino mailbox archiver': LNDmSubclient,
            'sybase': SybaseSubclient,
            'sap for oracle': SAPOracleSubclient,
            "exchange mailbox": [ExchangeSubclient, CaseSubclient],
            'mysql': MYSQLSubclient,
            'exchange database': ExchangeDatabaseSubclient,
            'postgresql': PostgresSubclient,
            'db2': DB2Subclient,
            'informix': InformixSubclient,
            'active directory': ADSubclient,
            'sharepoint server': [SharepointV1Subclient, SharepointSubclient],
            "azure ad": AzureAdSubclient
        }

        agent_object = backupset_object._agent_object
        instance_object = backupset_object._instance_object
        client_object = agent_object._client_object

        agent_name = agent_object.agent_name.lower()

        if isinstance(_subclients_dict.get(agent_name), list):
            if instance_object.instance_name == "vminstance":
                _class = _subclients_dict[agent_name][-1]
            elif client_object.client_type and int(client_object.client_type) == 36:
                # client type 36 is case manager client
                _class = _subclients_dict[agent_name][-1]
            elif int(agent_object.agent_id) == 78 and client_object.client_type:
                # agent id 78 is sharepoint client
                _class = _subclients_dict[agent_name][-1]
            else:
                _class = _subclients_dict[agent_name][0]
        else:
            _class = _subclients_dict.get(agent_name, cls)

        if _class.__new__ == cls.__new__:
            return object.__new__(_class)
        return _class.__new__(_class, backupset_object, subclient_name, subclient_id)

    def __init__(self, backupset_object, subclient_name, subclient_id=None):
        """Initialise the Subclient object.

            Args:
                backupset_object (object)  --  instance of the Backupset class

                subclient_name   (str)     --  name of the subclient

                subclient_id     (str)     --  id of the subclient
                    default: None

            Returns:
                object - instance of the Subclient class
        """
        self._backupset_object = backupset_object
        self._subclient_name = subclient_name.split('\\')[-1].lower()

        self._commcell_object = self._backupset_object._commcell_object

        self._cvpysdk_object = self._commcell_object._cvpysdk_object
        self._services = self._commcell_object._services
        self._update_response_ = self._commcell_object._update_response_

        self._instance_object = self._backupset_object._instance_object
        self._agent_object = self._backupset_object._agent_object
        self._client_object = self._agent_object._client_object

        self._restore_methods = [
            '_process_restore_response',
            '_filter_paths',
            '_restore_json',
            '_impersonation_json',
            '_restore_browse_option_json',
            '_restore_common_options_json',
            '_restore_destination_json',
            '_restore_fileoption_json',
            '_json_restore_subtask'
        ]

        self._restore_options_json = [
            '_impersonation_json_',
            '_browse_restore_json',
            '_destination_restore_json',
            '_commonoption_restore_json',
            '_fileoption_restore_json',

        ]

        self._backupcopy_interfaces = {
            'FILESYSTEM': 1,
            'RMAN': 2,
            'VOLUME': 3
        }

        if subclient_id:
            self._subclient_id = str(subclient_id)
        else:
            self._subclient_id = self._get_subclient_id()

        self._SUBCLIENT = self._services['SUBCLIENT'] % (self.subclient_id)

        self._BROWSE = self._services['BROWSE']

        self._RESTORE = self._services['RESTORE']

        self._subclient_properties = {}
        self._content = []

        self.schedules = None
        self.refresh()

    def __getattr__(self, attribute):
        """Returns the persistent attributes"""
        if attribute in self._restore_methods:
            return getattr(self._backupset_object, attribute)
        if attribute in self._restore_options_json:
            return getattr(self._backupset_object, attribute)

        return super(Subclient, self).__getattribute__(attribute)

    def __repr__(self):
        """String representation of the instance of this class."""
        representation_string = 'Subclient class instance for Subclient: "{0}" of Backupset: "{1}"'
        return representation_string.format(
            self.subclient_name, self._backupset_object.backupset_name
        )

    def _get_subclient_id(self):
        """Gets the subclient id associated to the specified backupset name and client name.

            Returns:
                str - id associated with this subclient
        """
        subclients = Subclients(self._backupset_object)
        return subclients.get(self.subclient_name).subclient_id

    def _get_subclient_properties(self):
        """Gets the subclient properties of this subclient.

            Raises:
                SDKException:
                    if response is empty

                    if response is not success
        """

        flag, response = self._cvpysdk_object.make_request(
            'GET', self._SUBCLIENT)

        if flag:
            if response.json() and 'subClientProperties' in response.json():
                self._subclient_properties = response.json()[
                    'subClientProperties'][0]

                if 'commonProperties' in self._subclient_properties:
                    self._commonProperties = self._subclient_properties['commonProperties']

                if 'subClientEntity' in self._subclient_properties:
                    self._subClientEntity = self._subclient_properties['subClientEntity']

                if 'proxyClient' in self._subclient_properties:
                    self._proxyClient = self._subclient_properties['proxyClient']

                if 'planEntity' in self._subclient_properties:
                    self._planEntity = self._subclient_properties['planEntity']

            else:
                raise SDKException('Response', '102')
        else:
            raise SDKException(
                'Response',
                '101',
                self._update_response_(
                    response.text))

    def _set_subclient_properties(self, attr_name, value):
        """sets the properties of this sub client.value is updated to instance once when post call
            succeeds

            Args:
                attr_name (str) --  Name of the attribute. This should be an instance variable.
                value (str)     --  Value of the attribute. This should be an instance variable.

            Raises:
                SDKException:
                    if failed to update number properties for subclient


        """
        try:
            backup = eval('self.%s' % attr_name)  # Take backup of old value
        except (AttributeError, KeyError):
            backup = None

        exec("self.%s = %s" % (attr_name, 'value'))  # set new value

        request_json = self._get_subclient_properties_json()

        flag, response = self._cvpysdk_object.make_request('POST', self._SUBCLIENT, request_json)

        output = self._process_update_response(flag, response)

        if output[0]:
            return
        else:
            o_str = 'Failed to update properties of subclient\nError: "{0}"'

            # Restore original value from backup on failure
            exec("self.%s = %s" % (attr_name, backup))

            raise SDKException('Subclient', '102', o_str.format(output[2]))

    @staticmethod
    def _convert_size(input_size):
        """Converts the given float size to appropriate size in B / KB / MB / GB, etc.

            Args:
                size (float)  --  float value to convert

            Returns:
                str - size converted to the specific type (B, KB, MB, GB, etc.)
        """
        if input_size == 0:
            return '0B'

        size_name = ("B", "KB", "MB", "GB", "TB", "PB", "EB", "ZB", "YB")
        i = int(math.floor(math.log(input_size, 1024)))
        power = math.pow(1024, i)
        size = round(input_size / power, 2)
        return '%s %s' % (size, size_name[i])

    def _process_update_response(self, flag, response):
        """Updates the subclient properties with the request provided.

            Args:
                update_request  (str)  --  update request specifying the details to update

            Returns:
                (bool, str, str):
                    bool -  flag specifies whether success / failure

                    str  -  error code received in the response

                    str  -  error message received

            Raises:
                SDKException:
                    if failed to update properties

                    if response is empty

                    if response is not success
        """
        if flag:
            if response.json():
                if "response" in response.json():
                    error_code = str(
                        response.json()["response"][0]["errorCode"])

                    if error_code == "0":
                        return (True, "0", "")
                    else:
                        error_message = ""

                        if "errorString" in response.json()["response"][0]:
                            error_message = response.json(
                            )["response"][0]["errorString"]

                        if error_message:
                            return (False, error_code, error_message)
                        else:
                            return (False, error_code, "")
                elif "errorCode" in response.json():
                    error_code = str(response.json()['errorCode'])
                    error_message = response.json()['errorMessage']

                    if error_code == "0":
                        return (True, "0", "")

                    if error_message:
                        return (False, error_code, error_message)
                    else:
                        return (False, error_code, "")
                else:
                    raise SDKException('Response', '102')
            else:
                raise SDKException('Response', '102')
        else:
            raise SDKException(
                'Response',
                '101',
                self._update_response_(
                    response.text))

    def _process_backup_response(self, flag, response):
        """Runs the Backup for a subclient with the request provided and returns the Job object.

            Args:
                update_request  (str)  --  update request specifying the details to update

            Returns:
                object - instance of the Job class for this backup job if its an immediate Job
                        instance of the Schedule class for the backup job if its a scheduled Job


            Raises:
                SDKException:
                    if job initialization failed

                    if response is empty

                    if response is not success
        """
        if flag:
            if response.json():
                if "jobIds" in response.json():
                    if len(response.json()['jobIds']) == 1:
                        return Job(self._commcell_object,
                                   response.json()['jobIds'][0])
                    else:
                        joblist = []
                        for jobids in response.json()['jobIds']:
                            joblist.append(Job(self._commcell_object, jobids))
                        return joblist
                elif "taskId" in response.json():
                    return Schedules(self._commcell_object).get(task_id=response.json()['taskId'])
                elif "errorCode" in response.json():
                    o_str = 'Initializing backup failed\nError: "{0}"'.format(
                        response.json()['errorMessage']
                    )
                    raise SDKException('Subclient', '102', o_str)
            else:
                raise SDKException('Response', '102')
        else:
            raise SDKException(
                'Response',
                '101',
                self._update_response_(
                    response.text))

    def _backup_json(self,
                     backup_level,
                     incremental_backup,
                     incremental_level,
                     advanced_options=None,
                     schedule_pattern=None,
                     common_backup_options=None):
        """Returns the JSON request to pass to the API as per the options selected by the user.

            Args:
                backup_level        (str)   --  level of backup the user wish to run

                    Full / Incremental / Differential / Synthetic_full

                incremental_backup  (bool)  --  run incremental backup

                    only applicable in case of Synthetic_full backup

                incremental_level   (str)   --  run incremental backup before/after synthetic full

                    BEFORE_SYNTH / AFTER_SYNTH

                    only applicable in case of Synthetic_full backup

                advanced_options   (dict)  --  advanced backup options to be included while
                making the request

                    default: None

                common_backup_options   (dict)  --  advanced job options to be included while
                                                    making the request.

                    default: None

            Returns:
                dict    -   JSON request to pass to the API

        """

        request_json = {
            "taskInfo": {
                "associations": [self._subClientEntity],
                "task": self._json_task,
                "subTasks": [
                    {
                        "subTaskOperation": 1,
                        "subTask": self._json_backup_subtasks,
                        "options": {
                            "backupOpts": {
                                "backupLevel": backup_level,
                                "incLevel": incremental_level,
                                "runIncrementalBackup": incremental_backup
                            }
                        }
                    }
                ]
            }
        }

        advanced_options_dict = {}

        if advanced_options:
            advanced_options_dict = self._advanced_backup_options(
                advanced_options)

        if advanced_options_dict:
            request_json["taskInfo"]["subTasks"][0]["options"]["backupOpts"].update(
                advanced_options_dict
            )

        advance_job_option_dict = {}

        if common_backup_options:
            advance_job_option_dict = self._common_backup_options(
                common_backup_options)

        if advance_job_option_dict:
            request_json["taskInfo"]["subTasks"][0]["options"]["commonOpts"] = advance_job_option_dict

        if schedule_pattern:
            request_json = SchedulePattern().create_schedule(request_json, schedule_pattern)

        return request_json

    def _common_backup_options(self, options):
        """
         Generates the advanced job options dict

            Args:
                options     (dict)      --    advanced job options that are to be included
                                                    in the request

            Returns:
                (dict)  -           generated advanced job options dict
        """
        return options

    def _advanced_backup_options(self, options):
        """Generates the advanced backup options dict

            Args:
                options     (dict)  --  advanced backup options that are to be included
                                            in the request
c
            Returns:
                (dict)  -   generated advanced options dict
        """
        return options

    def update_properties(self, properties_dict):
        """Updates the subclient properties

            Args:
                properties_dict (dict)  --  subclient property dict which is to be updated

            Returns:
                None

            Raises:
                SDKException:
                    if failed to add

                    if response is empty

                    if response code is not as expected

        **Note** self.properties can be used to get a deep copy of all the properties, modify the properties which you
        need to change and use the update_properties method to set the properties

        """
        request_json = {
            "subClientProperties": {}
        }

        request_json['subClientProperties'].update(properties_dict)

        # check if subclient name is updated in the request
        # if subclient name is updated set the newName field in the request
        if properties_dict.get('subClientEntity', {}).get('subclientName') and properties_dict.get(
                'subClientEntity', {}).get('subclientName') != self._subClientEntity.get('subclientName'):
            request_json['newName'] = properties_dict.get('subClientEntity', {}).get('subclientName')
        flag, response = self._cvpysdk_object.make_request('POST', self._SUBCLIENT, request_json)
        status, _, error_string = self._process_update_response(flag, response)
        self.refresh()

        if not status:
            raise SDKException('Subclient', '102', 'Failed to update subclient properties\nError: "{}"'.format(
                error_string))

    @property
    def properties(self):
        """Returns the subclient properties"""
        return copy.deepcopy(self._subclient_properties)

    @property
    def name(self):
        """Returns the Subclient display name"""
        return self._subclient_properties['subClientEntity']['subclientName']

    @property
    def display_name(self):
        """Returns the Subclient display name"""
        return self.name

    @property
    def subclient_guid(self):
        """Returns the SubclientGUID"""
        return self._subclient_properties.get('subClientEntity', {}).get('subclientGUID')

    @display_name.setter
    def display_name(self, display_name):
        """Sets the display name for the subclient
        Args:
            display_name    (str)   -- Display name for the subclient

        """
        update_properties = self.properties
        update_properties['subClientEntity']['subclientName'] = display_name
        self.update_properties(update_properties)

    @name.setter
    def name(self, name):
        """Sets the name for the subclient
        Args:
            name    (str)   -- name for the subclient
        """
        self.display_name = name

    @property
    def _json_task(self):
        """getter for the task information in JSON"""

        _taks_option_json = {
            "initiatedFrom": 2,
            "taskType": 1,
            "policyType": 0,
            "taskFlags": {
                "disabled": False
            }
        }

        return _taks_option_json

    @property
    def _json_backup_subtasks(self):
        """getter for the subtask in restore JSON . It is read only attribute"""

        _backup_subtask = {
            "subTaskType": 2,
            "operationType": 2
        }

        return _backup_subtask

    @property
    def subclient_id(self):
        """Treats the subclient id as a read-only attribute."""
        return self._subclient_id

    @property
    def subclient_name(self):
        """Treats the subclient name as a read-only attribute."""
        return self._subclient_name

    @property
    def last_backup_time(self):
        """Treats the last backup time as a read-only attribute."""
        if 'lastBackupTime' in self._commonProperties:
            if self._commonProperties['lastBackupTime'] != 0:
                _last_backup_time = time.ctime(
                    self._commonProperties['lastBackupTime']
                )
                return _last_backup_time
        return 0

    @property
    def next_backup_time(self):
        """Treats the next backup time as a read-only attribute."""
        if 'nextBackupTime' in self._commonProperties:
            if self._commonProperties['nextBackupTime'] != 0:
                _next_backup = time.ctime(
                    self._commonProperties['nextBackupTime']
                )
                return _next_backup

    @property
    def is_backup_enabled(self):
        """Treats the is backup enabled as a read-only attribute."""
        if 'enableBackup' in self._commonProperties:
            return self._commonProperties['enableBackup']

    @property
    def is_intelli_snap_enabled(self):
        """Treats the is intelli snap enabled as a read-only attribute."""
        if 'snapCopyInfo' in self._commonProperties:
            snap_copy_info = self._commonProperties.get('snapCopyInfo')
            return snap_copy_info.get('isSnapBackupEnabled')

    @property
    def is_blocklevel_backup_enabled(self):
        """returns True if block level backup is enabled else returns false"""
        return bool(self._subclient_properties.get(
            'postgreSQLSubclientProp', {}).get('isUseBlockLevelBackup', False))

    @property
    def snapshot_engine_name(self):
        """returns snapshot engine name associated with the subclient"""
        if self.is_intelli_snap_enabled:
            if 'snapCopyInfo' in self._commonProperties:
                snap_copy_info = self._commonProperties.get('snapCopyInfo', "")
                if 'snapToTapeSelectedEngine' in snap_copy_info:
                    if 'snapShotEngineName' in snap_copy_info.get('snapToTapeSelectedEngine', ""):
                        return snap_copy_info['snapToTapeSelectedEngine'].get(
                            'snapShotEngineName', "")
        raise SDKException(
            'Subclient',
            '102',
            'Cannot fetch snap engine name.')

    @property
    def is_trueup_enabled(self):
        """Treats the True up enabled as a property of the Subclient class."""
        if 'isTrueUpOptionEnabled' in self._commonProperties:
            return self._commonProperties['isTrueUpOptionEnabled']

    @property
    def is_on_demand_subclient(self):
        """Treats the on demand subclient as a read-only attribute."""
        return self._backupset_object.is_on_demand_backupset

    @property
    def description(self):
        """Treats the subclient description as a property of the Subclient class."""
        if 'description' in self._commonProperties:
            return self._commonProperties['description']

    @property
    def storage_policy(self):
        """Treats the subclient storage policy as a read-only attribute."""
        storage_device = self._commonProperties['storageDevice']
        if 'dataBackupStoragePolicy' in storage_device:
            data_backup_storage_policy = storage_device['dataBackupStoragePolicy']
            if 'storagePolicyName' in data_backup_storage_policy:
                return data_backup_storage_policy['storagePolicyName']

    @property
    def storage_ma(self):
        """Treats the subclient storage ma as a read-only attribute."""
        storage_device = self._commonProperties['storageDevice']
        if 'performanceMode' in storage_device:
            data_backup_storage_device = storage_device['performanceMode']
            data_storage_details = data_backup_storage_device["perfCRCDetails"][0]
            if 'perfMa' in data_storage_details:
                return data_storage_details['perfMa']

    @property
    def storage_ma_id(self):
        """Treats the subclient storage ma id as a read-only attribute."""
        storage_device = self._commonProperties['storageDevice']
        if 'performanceMode' in storage_device:
            data_backup_storage_device = storage_device['performanceMode']
            data_storage_details = data_backup_storage_device["perfCRCDetails"][0]
            if 'perfMaId' in data_storage_details:
                return data_storage_details['perfMaId']

    @property
    def data_readers(self):
        """Treats the data readers as a read-only attribute."""
        if 'numberOfBackupStreams' in self._commonProperties:
            return int(
                self._commonProperties['numberOfBackupStreams']
            )

    @data_readers.setter
    def data_readers(self, value):
        """Sets the count of data readers for the subclient as the value provided as input.

            Raises:
                SDKException:
                    if failed to update number of data readers for subclient

                    if the type of value input is not int
        """
        if isinstance(value, int):
            self._set_subclient_properties(
                "_commonProperties['numberOfBackupStreams']", value)
        else:
            raise SDKException(
                'Subclient', '102', 'Subclient data readers should be an int value'
            )

    @property
    def allow_multiple_readers(self):
        """Treats the allow multiple readers as a read-only attribute."""
        if 'allowMultipleDataReaders' in self._commonProperties:
            return bool(
                self._commonProperties['allowMultipleDataReaders']
            )

    @allow_multiple_readers.setter
    def allow_multiple_readers(self, value):
        """To enable or disable allow multiple readers property
        for the subclient based on the value provided as input.

            Raises:
                SDKException:
                    if failed to update allow multiple readers for subclient

                    if the type of value input is not bool
        """
        # Has to be initialized for new subclient as attribute is not present
        # default value is False
        if 'allowMultipleDataReaders' not in self._commonProperties:
            self._commonProperties['allowMultipleDataReaders'] = False

        if isinstance(value, bool):
            self._set_subclient_properties(
                "_commonProperties['allowMultipleDataReaders']",
                value)
        else:
            raise SDKException(
                'Subclient', '102',
                'Subclient allow multple readers should be a bool value'
            )

    @property
    def read_buffer_size(self):
        """Treats the read buffer size as a read-only attribute."""
        if 'readBuffersize' in self._commonProperties:
            return int(
                self._commonProperties['readBuffersize']
            )

    @property
    def is_default_subclient(self):
        """Returns True if the subclient is default
        subclient else returns False"""
        return self._commonProperties.get('isDefaultSubclient')

    @read_buffer_size.setter
    def read_buffer_size(self, value):
        """Sets the read buffer size for the subclient
        as the value provided as input.
            (value in KB)

            Raises:
                SDKException:
                    if failed to update read buffer size for subclient

                    if the type of value input is not int
        """
        # Has to be initialized for new subclient as attribute is not present
        # default value is 0
        if 'readBuffersize' not in self._commonProperties:
            self._commonProperties['readBuffersize'] = 0

        if isinstance(value, int):
            self._set_subclient_properties(
                "_commonProperties['readBuffersize']",
                value)
        else:
            raise SDKException(
                'Subclient', '102',
                'Subclient read buffer size should be an int value'
            )

    @description.setter
    def description(self, value):
        """Sets the description of the subclient as the value provided as input.

            Raises:
                SDKException:
                    if failed to update description of subclient

                    if the type of value input is not string
        """
        if isinstance(value, str):
            self._set_subclient_properties(
                "_commonProperties['description']", value)
        else:
            raise SDKException(
                'Subclient', '102', 'Subclient description should be a string value'
            )

    @storage_policy.setter
    def storage_policy(self, value):
        """Sets the storage policy of subclient as the value provided as input.

            Args:
                value   (str)   -- Storage policy name to be assigned to subclient

            Raises:
                SDKException:
                    if storage policy name is not in string format

                    if failed to update storage policy name

        """
        if isinstance(value, str):
            value = value.lower()

            if not self._commcell_object.storage_policies.has_policy(value):
                raise SDKException(
                    'Subclient',
                    '102',
                    'Storage Policy: "{0}" does not exist in the Commcell'.format(value)
                )

            self._set_subclient_properties(
                "_commonProperties['storageDevice']['dataBackupStoragePolicy']",
                {
                    "storagePolicyName": value,
                    "storagePolicyId": int(
                        self._commcell_object.storage_policies.all_storage_policies[value]
                    )
                }
            )
        else:
            raise SDKException('Subclient', '101')

    def enable_backup(self):
        """Enables Backup for the subclient.

            Raises:
                SDKException:
                    if failed to enable backup of subclient
        """
        self._set_subclient_properties("_commonProperties['enableBackup']", True)

    def enable_trueup(self):
        """Setter for the TrueUp Option for a Subclient"""
        if 'isTrueUpOptionEnabled' in self._commonProperties:
            self._set_subclient_properties("_commonProperties['isTrueUpOptionEnabled']", True)

    def enable_trueup_days(self, days=30):
        """Setter for the TrueUp Option with reconcile after x days"""
        self.enable_trueup()
        self._set_subclient_properties("_commonProperties['runTrueUpJobAfterDays']", days)

    def enable_backup_at_time(self, enable_time):
        """Disables Backup if not already disabled, and enables at the time specified.

            Args:
                enable_time (str)  --  UTC time to enable the backup at, in 24 Hour format
                    format: YYYY-MM-DD HH:mm:ss

                **Note** In case of linux CommServer provide time in GMT timezone

            Raises:
                SDKException:
                    if time value entered is less than the current time

                    if time value entered is not of correct format

                    if failed to enable backup

                    if response is empty

                    if response is not success
        """
        try:
            time_tuple = time.strptime(enable_time, "%Y-%m-%d %H:%M:%S")
            if time.mktime(time_tuple) < time.time():
                raise SDKException('Subclient', '108')
        except ValueError:
            raise SDKException('Subclient', '109')

        enable_backup_at_time = {
            "TimeZoneName": self._commcell_object.default_timezone,
            "timeValue": enable_time
        }

        self._set_subclient_properties(
            "_commonProperties['enableBackupAtDateTime']", enable_backup_at_time
        )

    def disable_backup(self):
        """Disables Backup for the subclient.

            Raises:
                SDKException:
                    if failed to disable backup of subclient
        """
        self._set_subclient_properties(
            "_commonProperties['enableBackup']", False)

    def exclude_from_sla(self):
        """Exclude subclient from SLA.

            Raises:
                SDKException:
                    if failed to exclude the subclient from SLA
        """
        self._set_subclient_properties(
            "_commonProperties['excludeFromSLA']", True)

    def enable_intelli_snap(self, snap_engine_name, proxy_options=None):
        """Enables Intelli Snap for the subclient.

            Args:
                snap_engine_name    (str)   --  Snap Engine Name

            Raises:
                SDKException:
                    if failed to enable intelli snap for subclient
        """
        if not isinstance(snap_engine_name, str):
            raise SDKException("Subclient", "101")

        properties_dict = {
            "isSnapBackupEnabled": True,
            "snapToTapeSelectedEngine": {
                "snapShotEngineName": snap_engine_name
            }
        }

        if proxy_options is not None:
            if "snap_proxy" in proxy_options:
                properties_dict["snapToTapeProxyToUse"] = {
                    "clientName": proxy_options["snap_proxy"]
                }

            if "backupcopy_proxy" in proxy_options:
                properties_dict["useSeparateProxyForSnapToTape"] = True
                properties_dict["separateProxyForSnapToTape"] = {
                    "clientName": proxy_options["backupcopy_proxy"]
                }

            if "use_source_if_proxy_unreachable" in proxy_options:
                properties_dict["snapToTapeProxyToUseSource"] = True

        self._set_subclient_properties(
            "_commonProperties['snapCopyInfo']", properties_dict)

    def disable_intelli_snap(self):
        """Disables Intelli Snap for the subclient.

            Raises:
                SDKException:
                    if failed to disable intelli snap for subclient
        """
        self._set_subclient_properties(
            "_commonProperties['snapCopyInfo']['isSnapBackupEnabled']", False
        )

    def set_proxy_for_snap(self, proxy_name):
        """ method to set Use proxy option for intellisnap subclient 

        Args:
            proxy_name(str) -- Name of the proxy to be used

        """
        if not isinstance(proxy_name, str):
            raise SDKException("Subclient", "101")

        properties_dict = {
            "clientName": proxy_name
        }

        update_properties = self.properties
        update_properties['commonProperties']['snapCopyInfo']['snapToTapeProxyToUse'] = properties_dict
        self.update_properties(update_properties)

    def unset_proxy_for_snap(self):
        """ method to unset Use proxy option for intellisnap subclient """

        properties_dict = {
            "clientId": 0
        }
        update_properties = self.properties
        update_properties['commonProperties']['snapCopyInfo']['snapToTapeProxyToUse'] = properties_dict
        self.update_properties(update_properties)

    def backup(self,
               backup_level="Incremental",
               incremental_backup=False,
               incremental_level='BEFORE_SYNTH',
               collect_metadata=False):
        """Runs a backup job for the subclient of the level specified.

            Args:
                backup_level        (str)   --  level of backup the user wish to run
                        Full / Incremental / Differential / Synthetic_full
                    default: Incremental

                incremental_backup  (bool)  --  run incremental backup
                        only applicable in case of Synthetic_full backup
                    default: False

                incremental_level   (str)   --  run incremental backup before/after synthetic full
                        BEFORE_SYNTH / AFTER_SYNTH

                        only applicable in case of Synthetic_full backup
                    default: BEFORE_SYNTH

            Returns:
                object - instance of the Job class for this backup job

            Raises:
                SDKException:
                    if backup level specified is not correct

                    if response is empty

                    if response is not success
        """
        backup_level = backup_level.lower()

        if backup_level not in ['full', 'incremental', 'transaction_log',
                                'differential', 'synthetic_full']:
            raise SDKException('Subclient', '103')

        backup_request = backup_level

        if backup_level == 'synthetic_full':
            if incremental_backup:
                backup_request += '&runIncrementalBackup=True'
                backup_request += '&incrementalLevel=%s' % (
                    incremental_level.lower())
            else:
                backup_request += '&runIncrementalBackup=False'

        backup_request += '&collectMetaInfo=%s' % collect_metadata

        backup_service = self._services['SUBCLIENT_BACKUP'] % (
            self.subclient_id, backup_request)

        flag, response = self._cvpysdk_object.make_request(
            'POST', backup_service)

        return self._process_backup_response(flag, response)

    def get_ma_associated_storagepolicy(self):
        """
        Get Media agents associated with storage policy
        
        Raise Exception:
                if unable to get MA names
        """
        storage = self._subclient_properties['commonProperties']['storageDevice']
        if 'performanceMode' in storage:
            data_backup_storage_device = storage['performanceMode']["perfCRCDetails"]
            malist = []
            for each_ma in data_backup_storage_device:
                malist.append(each_ma['perfMa'])
            return malist

    def browse(self, *args, **kwargs):
        """Browses the content of the Subclient.

            Args:
                Dictionary of browse options:
                    Example:

                        browse({
                            'path': 'c:\\\\hello',

                            'show_deleted': True,

                            'from_time': '2014-04-20 12:00:00',

                            'to_time': '2016-04-21 12:00:00'
                        })

            Kwargs:
                Keyword argument of browse options:
                    Example:

                        browse(
                            path='c:\\hello',

                            show_deleted=True,

                            from_time='2014-04-20 12:00:00',

                            to_time='2016-04-21 12:00:00'
                        )

            Returns:
                (list, dict)
                    list    -   List of only the file, folder paths from the browse response

                    dict    -   Dictionary of all the paths with additional metadata retrieved
                    from browse operation


            Refer `default_browse_options`_ for all the supported options.

            .. _default_browse_options: https://github.com/CommvaultEngg/cvpysdk/blob/master/cvpysdk/backupset.py#L565

        """
        if args and isinstance(args[0], dict):
            options = args[0]
        else:
            options = kwargs

        options['_subclient_id'] = self._subclient_id

        return self._backupset_object.browse(options)

    def find(self, *args, **kwargs):
        """Searches a file/folder in the backed up content of the subclient,
            and returns all the files matching the filters given.

            Args:
                Dictionary of browse options:
                    Example:

                        find({
                            'file_name': '*.txt',

                            'show_deleted': True,

                            'from_time': '2014-04-20 12:00:00',

                            'to_time': '2016-04-31 12:00:00'
                        })

            Kwargs:
                Keyword argument of browse options:
                    Example:

                        find(
                            file_name='*.txt',

                            show_deleted=True,

                            'from_time': '2014-04-20 12:00:00',

                            to_time='2016-04-31 12:00:00'
                        )

            Returns:
                (list, dict)
                    list    -   List of only the file, folder paths from the browse response

                    dict    -   Dictionary of all the paths with additional metadata retrieved
                    from browse operation


            Refer `default_browse_options`_ for all the supported options.

            Additional options supported:
                file_name       (str)   --  Find files with name

                file_size_gt    (int)   --  Find files with size greater than size

                file_size_lt    (int)   --  Find files with size lesser than size

                file_size_et    (int)   --  Find files with size equal to size

            .. _default_browse_options: https://github.com/CommvaultEngg/cvpysdk/blob/master/cvpysdk/backupset.py#L565

        """
        if args and isinstance(args[0], dict):
            options = args[0]
        else:
            options = kwargs

        options['_subclient_id'] = self._subclient_id

        return self._backupset_object.find(options)

    def list_media(self, *args, **kwargs):
        """List media required to browse and restore backed up data from the subclient

            Args:
                Dictionary of options:
                    Example:

                        list_media({
                            'path': 'c:\\hello',
                            'show_deleted': True,
                            'from_time': '2020-04-20 12:00:00',
                            'to_time': '2021-04-19 12:00:00'
                        })

            Kwargs:
                Keyword argument of options:
                    Example:

                        list_media(
                            path='c:\\hello',
                            show_deleted=True,
                            from_time='2020-04-20 12:00:00',
                            to_time='2021-04-19 12:00:00'
                        )

            Note:
                Refer `_default_browse_options` in backupset.py for all the supported options.

            Returns:
                (List, Dict) -
                    List - List of all the media required for the given options

                    Dict - Total size of the media

            Raises:
                SDKException:
                    if failed to list media for content

                    if response is not success

        """

        if args and isinstance(args[0], dict):
            options = args[0]
        else:
            options = kwargs

        options['_subclient_id'] = self._subclient_id

        return self._backupset_object.list_media(options)

    def restore_in_place(
            self,
            paths,
            overwrite=True,
            restore_data_and_acl=True,
            copy_precedence=None,
            from_time=None,
            to_time=None,
            fs_options=None,
            schedule_pattern=None,
            proxy_client=None,
            advanced_options=None):

        """Restores the files/folders specified in the input paths list to the same location.

            Args:
                paths                   (list)  --  list of full paths of files/folders to restore

                overwrite               (bool)  --  unconditional overwrite files during restore
                    default: True

                restore_data_and_acl    (bool)  --  restore data and ACL files
                    default: True

                copy_precedence         (int)   --  copy precedence value of storage policy copy
                    default: None

                from_time           (str)       --  time to restore the contents after
                        format: YYYY-MM-DD HH:MM:SS

                    default: None

                to_time           (str)         --  time to restore the contents before
                        format: YYYY-MM-DD HH:MM:SS

                    default: None

                fs_options      (dict)          -- dictionary that includes all advanced options
                    options:
                        all_versions        : if set to True restores all the versions of the
                                                specified file
                        versions            : list of version numbers to be backed up
                        validate_only       : To validate data backed up for restore


                schedule_pattern (dict) -- scheduling options to be included for the task

                        Please refer schedules.schedulePattern.createSchedule()
                                                                    doc for the types of Jsons

                schedule_pattern (dict) -- scheduling options to be included for the task

                        Please refer schedules.schedulePattern.createSchedule()
                                                                    doc for the types of Jsons

                proxy_client    (str)          -- Proxy client used during FS under NAS operations

                advanced_options    (dict)  -- Advanced restore options

                    Options:

                        job_description (str)   --  Restore job description

                        timezone        (str)   --  Timezone to be used for restore

                            **Note** make use of TIMEZONES dict in constants.py to pass timezone

            Returns:
                object - instance of the Job class for this restore job if its an immediate Job
                         instance of the Schedule class for this restore job if its a scheduled Job

            Raises:
                SDKException:
                    if paths is not a list

                    if failed to initialize job

                    if response is empty

                    if response is not success
        """
        self._instance_object._restore_association = self._subClientEntity

        return self._instance_object._restore_in_place(
            paths=paths,
            overwrite=overwrite,
            restore_data_and_acl=restore_data_and_acl,
            copy_precedence=copy_precedence,
            from_time=from_time,
            to_time=to_time,
            fs_options=fs_options,
            schedule_pattern=schedule_pattern,
            proxy_client=proxy_client,
            advanced_options=advanced_options
        )

    def restore_out_of_place(
            self,
            client,
            destination_path,
            paths,
            overwrite=True,
            restore_data_and_acl=True,
            copy_precedence=None,
            from_time=None,
            to_time=None,
            fs_options=None,
            schedule_pattern=None,
            proxy_client=None,
            advanced_options=None):
        """Restores the files/folders specified in the input paths list to the input client,
            at the specified destionation location.

            Args:
                client                (str/object) --  either the name of the client or
                                                           the instance of the Client

                destination_path      (str)        --  full path of the restore location on client

                paths                 (list)       --  list of full paths of
                                                           files/folders to restore

                overwrite             (bool)       --  unconditional overwrite files during restore
                    default: True

                restore_data_and_acl  (bool)       --  restore data and ACL files
                    default: True

                copy_precedence         (int)   --  copy precedence value of storage policy copy
                    default: None

                from_time           (str)       --  time to restore the contents after
                        format: YYYY-MM-DD HH:MM:SS

                    default: None

                to_time           (str)         --  time to restore the contents before
                        format: YYYY-MM-DD HH:MM:SS

                    default: None

                fs_options      (dict)          -- dictionary that includes all advanced options
                    options:
                        preserve_level      : preserve level option to set in restore
                        proxy_client        : proxy that needed to be used for restore
                        impersonate_user    : Impersonate user options for restore
                        impersonate_password: Impersonate password option for restore
                                                in base64 encoded form
                        all_versions        : if set to True restores all the versions of the
                                                specified file
                        versions            : list of version numbers to be backed up
                        media_agent         : Media Agent need to be used for Browse and restore
                        validate_only       : To validate data backed up for restore


                schedule_pattern (dict) -- scheduling options to be included for the task

                        Please refer schedules.schedulePattern.createSchedule()
                                                                    doc for the types of Jsons

                schedule_pattern (dict) -- scheduling options to be included for the task

                        Please refer schedules.schedulePattern.createSchedule()
                                                                    doc for the types of Jsons

                proxy_client    (str)          -- Proxy client used during FS under NAS operations

                advanced_options    (dict)  -- Advanced restore options

                    Options:

                        job_description (str)   --  Restore job description

                        timezone        (str)   --  Timezone to be used for restore

                            **Note** make use of TIMEZONES dict in constants.py to pass timezone

            Returns:
                object - instance of the Job class for this restore job if its an immediate Job
                         instance of the Schedule class for this restore job if its a scheduled Job

            Raises:
                SDKException:
                    if client is not a string or Client instance

                    if destination_path is not a string

                    if paths is not a list

                    if failed to initialize job

                    if response is empty

                    if response is not success
        """
        self._instance_object._restore_association = self._subClientEntity

        if fs_options and 'proxy_client' in fs_options:
            proxy_client = fs_options['proxy_client']

        return self._instance_object._restore_out_of_place(
            client=client,
            destination_path=destination_path,
            paths=paths,
            overwrite=overwrite,
            restore_data_and_acl=restore_data_and_acl,
            copy_precedence=copy_precedence,
            from_time=from_time,
            to_time=to_time,
            fs_options=fs_options,
            schedule_pattern=schedule_pattern,
            proxy_client=proxy_client,
            advanced_options=advanced_options
        )

    def set_backup_nodes(self, data_access_nodes):
        """Sets the the backup nodes for NFS share subclient.

            Args:
                data_access_nodes   (list)  --  the list of data access nodes to be set
                as backup nodes for NFS share subclient

            Returns:
                None    -   if the operation is successful

            Raises:
                SDKException:
                    if unable to update the backup nodes for the subclient

        """
        data_access_nodes_json = []
        for access_node in data_access_nodes:
            data_access_nodes_json.append({"clientName": access_node})

        request_json = {
            "subClientProperties": {
                "fsSubClientProp": {
                    "backupConfiguration": {
                        "backupDataAccessNodes": data_access_nodes_json
                    }
                }
            }
        }

        flag, response = self._cvpysdk_object.make_request('POST', self._SUBCLIENT, request_json)

        output = self._process_update_response(flag, response)

        if output[0]:
            return
        else:
            o_str = 'Failed to update properties of subclient\nError: "{0}"'
            raise SDKException('Subclient', '102', o_str.format(output[2]))

    def find_latest_job(
            self,
            include_active=True,
            include_finished=True,
            lookup_time=1,
            job_filter='Backup,SYNTHFULL'):
        """Finds the latest job for the subclient
            which includes current running job also.

            Args:
                include_active    (bool)    -- to indicate if
                                                active jobs should be included
                    default: True

                include_finished  (bool)    -- to indicate if finished jobs
                                                should be included
                    default: True

                lookup_time       (int)     -- get jobs executed
                                                within the number of hours
                    default: 1 Hour

                job_filter        (str)     -- to specify type of job
                    default: 'Backup,SYNTHFULL'

                    for multiple filters,
                    give the values **comma(,)** separated

                    List of Possible Values:

                            Backup

                            Restore

                            AUXCOPY

                            WORKFLOW

                            etc..

                    http://documentation.commvault.com/commvault/v11/article?p=features/rest_api/operations/get_job.htm
                        to get the complete list of filters available

            Returns:
                object  -   instance of the Job class for the latest job

            Raises:
                SDKException:
                    if any error occurred while finding the latest job.

        """
        job_controller = JobController(self._commcell_object)
        entity_dict = {
            "subclientId": int(self.subclient_id)
        }
        if include_active and include_finished:
            client_jobs = job_controller.all_jobs(
                client_name=self._client_object.client_name,
                lookup_time=lookup_time,
                job_filter=job_filter,
                entity=entity_dict
            )
        elif include_active:
            client_jobs = job_controller.active_jobs(
                client_name=self._client_object.client_name,
                lookup_time=lookup_time,
                job_filter=job_filter,
                entity=entity_dict
            )
        elif include_finished:
            client_jobs = job_controller.finished_jobs(
                client_name=self._client_object.client_name,
                lookup_time=lookup_time,
                job_filter=job_filter,
                entity=entity_dict
            )
        else:
            raise SDKException(
                'Subclient',
                '102',
                "Either active or finished job must be included"
            )

        latest_jobid = 0
        for job in client_jobs:
            if client_jobs[job]['subclient_id'] == int(self._subclient_id):
                if int(job) > latest_jobid:
                    latest_jobid = int(job)

        if latest_jobid == 0:
            raise SDKException('Subclient', '102', "No jobs found")

        return Job(self._commcell_object, latest_jobid)

    def refresh(self):
        """Refresh the properties of the Subclient."""
        self._get_subclient_properties()
        self.schedules = Schedules(self)

    @property
    def software_compression(self):
        """Returns the value of Software Compression settings on the Subclient."""
        mapping = {
            0: 'ON_CLIENT',
            1: 'ON_MEDIAAGENT',
            2: 'USE_STORAGE_POLICY_SETTINGS',
            4: 'OFF'
        }

        try:
            return mapping[self._commonProperties['storageDevice']['softwareCompression']]
        except KeyError:
            return self._commonProperties['storageDevice']['softwareCompression']

    @software_compression.setter
    def software_compression(self, value):
        """Sets the software compression of the subclient as the value provided as input.

            Args:
                value   (str)   --  software compression setting

                Valid values are:

                -   ON_CLIENT
                -   ON_MEDIAAGENT
                -   USE_STORAGE_POLICY_SETTINGS
                -   OFF

            Raises:
                SDKException:
                    if failed to update software compression of subclient

                    if the type of value input is not string

        """
        if isinstance(value, str):
            self._set_subclient_properties(
                "_commonProperties['storageDevice']['softwareCompression']", value
            )
        else:
            raise SDKException('Subclient', '101')

    @property
    def network_agent(self):
        """Returns the value of network agents setting on the Subclient."""
        return self._commonProperties['storageDevice']['networkAgents']

    @network_agent.setter
    def network_agent(self, value):
        """Sets the network agents of the subclient as the value provided as input.

            Args:
                value   (int)   --  network agents value

            Raises:
                SDKException:
                    if failed to update network agents of subclient

                    if the type of value input is not integer

        """

        if isinstance(value, int):
            self._set_subclient_properties("_commonProperties['storageDevice']['networkAgents']", value)
        else:
            raise SDKException('Subclient', '101')

    @property
    def encryption_flag(self):
        """Returns the value of encryption flag settings on the Subclient."""
        mapping = {
            0: 'ENC_NONE',
            1: 'ENC_MEDIA_ONLY',
            2: 'ENC_NETWORK_AND_MEDIA',
            3: 'ENC_NETWORK_ONLY'
        }

        try:
            return mapping[self._commonProperties['encryptionFlag']]
        except KeyError:
            return self._commonProperties['encryptionFlag']

    @encryption_flag.setter
    def encryption_flag(self, value):
        """Sets the encryption Flag of the subclient as the value provided as input.

            Args:
                value   (str)   --  encryption flag value

                Valid values are:

                -   ENC_NONE
                -   ENC_MEDIA_ONLY
                -   ENC_NETWORK_AND_MEDIA
                -   ENC_NETWORK_ONLY

            Raises:
                SDKException:
                    if failed to update encryption Flag of subclient

                    if the type of value input is not string

        """

        if isinstance(value, str):
            self._set_subclient_properties("_commonProperties['encryptionFlag']", value)
        else:
            raise SDKException('Subclient', '101')

    @property
    def deduplication_options(self):
        """Returns the value of deduplication options settings on the Subclient."""
        mapping_dedupe = {
            0: False,
            1: True,
        }
        mapping_signature = {
            1: "ON_CLIENT",
            2: "ON_MEDIA_AGENT"
        }

        dedupe_options = self._commonProperties['storageDevice']['deDuplicationOptions']

        if "enableDeduplication" in dedupe_options:
            if dedupe_options['enableDeduplication'] == 0:
                return mapping_dedupe[dedupe_options['enableDeduplication']]
            else:
                if 'generateSignature' in dedupe_options:
                    try:
                        return mapping_signature[dedupe_options['generateSignature']]
                    except KeyError:
                        return dedupe_options['generateSignature']

    @deduplication_options.setter
    def deduplication_options(self, enable_dedupe):
        """Enables / Disables the deduplication options of the Subclient.

            Args:
                enable_dedupe   (tuple)     --  to enable or disable deduplication

                    tuple:
                        **bool**    -   boolean flag to specify whether to
                        enable / disable deduplication

                        **str**     -   where to generate the signature at

                            Valid Values are:

                            -   ON_CLIENT
                            -   ON_MEDIA_AGENT


                    e.g.:

                        >>> subclient.deduplication_options = (False, None)

                        >>> subclient.deduplication_options = (True, "ON_CLIENT")

                        >>> subclient.deduplication_options = (True, "ON_MEDIA_AGENT")

            Raises:
                SDKException:
                    if failed to update deDuplication Options of subclient

                    if the type of value input is not correct

        """
        if enable_dedupe[0] is True:
            if enable_dedupe[1] is not None:
                self._set_subclient_properties(
                    "_commonProperties['storageDevice']['deDuplicationOptions']",
                    {
                        "enableDeduplication": enable_dedupe[0],
                        "generateSignature": enable_dedupe[1]
                    }
                )
            else:
                raise SDKException('Subclient', '102', "Input data is not correct")
        else:
            self._set_subclient_properties(
                "_commonProperties['storageDevice']['deDuplicationOptions']",
                {
                    "enableDeduplication": enable_dedupe[0],
                }
            )

    @property
    def plan(self):
        """Returns the name of the plan associated with the subclient.
           Returns None if no plan is associated
        """

        if 'planEntity' in self._subclient_properties:
            planEntity = self._subclient_properties['planEntity']

            if bool(planEntity) and 'planName' in planEntity:
                return planEntity['planName']
            else:
                return None
        else:
            raise SDKException('Subclient', '112')

    @plan.setter
    def plan(self, value):
        """Associates a plan to the subclient.

            Args:
                value   (object)    --  the Plan object which is to be associated
                                        with the subclient

                value   (str)       --  name of the plan to be associated

                value   (None)      --  set value to None to remove plan associations

            Raises:
                SDKException:
                    if the type of input is incorrect

                    if the plan association is unsuccessful
        """
        from .plan import Plan
        if isinstance(value, Plan):
            if self._commcell_object.plans.has_plan(value.plan_name):
                self.update_properties({
                    'planEntity': {
                        'planName': value.plan_name
                    },
                    "useContentFromPlan": True
                })
            else:
                raise SDKException('Subclient', '102', 'Plan does not exist')
        elif isinstance(value, str):
            if self._commcell_object.plans.has_plan(value):
                self.update_properties({
                    'planEntity': {
                        'planName': value
                    },
                    "useContentFromPlan": True
                })
            else:
                raise SDKException('Subclient', '102', 'Plan does not exist')
        elif value is None:
            self.update_properties({
                'removePlanAssociation': True
            })
        else:
            raise SDKException('Subclient', '101')

Subclasses

Instance variables

var allow_multiple_readers

Treats the allow multiple readers as a read-only attribute.

Expand source code Browse git
@property
def allow_multiple_readers(self):
    """Treats the allow multiple readers as a read-only attribute."""
    if 'allowMultipleDataReaders' in self._commonProperties:
        return bool(
            self._commonProperties['allowMultipleDataReaders']
        )
var data_readers

Treats the data readers as a read-only attribute.

Expand source code Browse git
@property
def data_readers(self):
    """Treats the data readers as a read-only attribute."""
    if 'numberOfBackupStreams' in self._commonProperties:
        return int(
            self._commonProperties['numberOfBackupStreams']
        )
var deduplication_options

Returns the value of deduplication options settings on the Subclient.

Expand source code Browse git
@property
def deduplication_options(self):
    """Returns the value of deduplication options settings on the Subclient."""
    mapping_dedupe = {
        0: False,
        1: True,
    }
    mapping_signature = {
        1: "ON_CLIENT",
        2: "ON_MEDIA_AGENT"
    }

    dedupe_options = self._commonProperties['storageDevice']['deDuplicationOptions']

    if "enableDeduplication" in dedupe_options:
        if dedupe_options['enableDeduplication'] == 0:
            return mapping_dedupe[dedupe_options['enableDeduplication']]
        else:
            if 'generateSignature' in dedupe_options:
                try:
                    return mapping_signature[dedupe_options['generateSignature']]
                except KeyError:
                    return dedupe_options['generateSignature']
var description

Treats the subclient description as a property of the Subclient class.

Expand source code Browse git
@property
def description(self):
    """Treats the subclient description as a property of the Subclient class."""
    if 'description' in self._commonProperties:
        return self._commonProperties['description']
var display_name

Returns the Subclient display name

Expand source code Browse git
@property
def display_name(self):
    """Returns the Subclient display name"""
    return self.name
var encryption_flag

Returns the value of encryption flag settings on the Subclient.

Expand source code Browse git
@property
def encryption_flag(self):
    """Returns the value of encryption flag settings on the Subclient."""
    mapping = {
        0: 'ENC_NONE',
        1: 'ENC_MEDIA_ONLY',
        2: 'ENC_NETWORK_AND_MEDIA',
        3: 'ENC_NETWORK_ONLY'
    }

    try:
        return mapping[self._commonProperties['encryptionFlag']]
    except KeyError:
        return self._commonProperties['encryptionFlag']
var is_backup_enabled

Treats the is backup enabled as a read-only attribute.

Expand source code Browse git
@property
def is_backup_enabled(self):
    """Treats the is backup enabled as a read-only attribute."""
    if 'enableBackup' in self._commonProperties:
        return self._commonProperties['enableBackup']
var is_blocklevel_backup_enabled

returns True if block level backup is enabled else returns false

Expand source code Browse git
@property
def is_blocklevel_backup_enabled(self):
    """returns True if block level backup is enabled else returns false"""
    return bool(self._subclient_properties.get(
        'postgreSQLSubclientProp', {}).get('isUseBlockLevelBackup', False))
var is_default_subclient

Returns True if the subclient is default subclient else returns False

Expand source code Browse git
@property
def is_default_subclient(self):
    """Returns True if the subclient is default
    subclient else returns False"""
    return self._commonProperties.get('isDefaultSubclient')
var is_intelli_snap_enabled

Treats the is intelli snap enabled as a read-only attribute.

Expand source code Browse git
@property
def is_intelli_snap_enabled(self):
    """Treats the is intelli snap enabled as a read-only attribute."""
    if 'snapCopyInfo' in self._commonProperties:
        snap_copy_info = self._commonProperties.get('snapCopyInfo')
        return snap_copy_info.get('isSnapBackupEnabled')
var is_on_demand_subclient

Treats the on demand subclient as a read-only attribute.

Expand source code Browse git
@property
def is_on_demand_subclient(self):
    """Treats the on demand subclient as a read-only attribute."""
    return self._backupset_object.is_on_demand_backupset
var is_trueup_enabled

Treats the True up enabled as a property of the Subclient class.

Expand source code Browse git
@property
def is_trueup_enabled(self):
    """Treats the True up enabled as a property of the Subclient class."""
    if 'isTrueUpOptionEnabled' in self._commonProperties:
        return self._commonProperties['isTrueUpOptionEnabled']
var last_backup_time

Treats the last backup time as a read-only attribute.

Expand source code Browse git
@property
def last_backup_time(self):
    """Treats the last backup time as a read-only attribute."""
    if 'lastBackupTime' in self._commonProperties:
        if self._commonProperties['lastBackupTime'] != 0:
            _last_backup_time = time.ctime(
                self._commonProperties['lastBackupTime']
            )
            return _last_backup_time
    return 0
var name

Returns the Subclient display name

Expand source code Browse git
@property
def name(self):
    """Returns the Subclient display name"""
    return self._subclient_properties['subClientEntity']['subclientName']
var network_agent

Returns the value of network agents setting on the Subclient.

Expand source code Browse git
@property
def network_agent(self):
    """Returns the value of network agents setting on the Subclient."""
    return self._commonProperties['storageDevice']['networkAgents']
var next_backup_time

Treats the next backup time as a read-only attribute.

Expand source code Browse git
@property
def next_backup_time(self):
    """Treats the next backup time as a read-only attribute."""
    if 'nextBackupTime' in self._commonProperties:
        if self._commonProperties['nextBackupTime'] != 0:
            _next_backup = time.ctime(
                self._commonProperties['nextBackupTime']
            )
            return _next_backup
var plan

Returns the name of the plan associated with the subclient. Returns None if no plan is associated

Expand source code Browse git
@property
def plan(self):
    """Returns the name of the plan associated with the subclient.
       Returns None if no plan is associated
    """

    if 'planEntity' in self._subclient_properties:
        planEntity = self._subclient_properties['planEntity']

        if bool(planEntity) and 'planName' in planEntity:
            return planEntity['planName']
        else:
            return None
    else:
        raise SDKException('Subclient', '112')
var properties

Returns the subclient properties

Expand source code Browse git
@property
def properties(self):
    """Returns the subclient properties"""
    return copy.deepcopy(self._subclient_properties)
var read_buffer_size

Treats the read buffer size as a read-only attribute.

Expand source code Browse git
@property
def read_buffer_size(self):
    """Treats the read buffer size as a read-only attribute."""
    if 'readBuffersize' in self._commonProperties:
        return int(
            self._commonProperties['readBuffersize']
        )
var snapshot_engine_name

returns snapshot engine name associated with the subclient

Expand source code Browse git
@property
def snapshot_engine_name(self):
    """returns snapshot engine name associated with the subclient"""
    if self.is_intelli_snap_enabled:
        if 'snapCopyInfo' in self._commonProperties:
            snap_copy_info = self._commonProperties.get('snapCopyInfo', "")
            if 'snapToTapeSelectedEngine' in snap_copy_info:
                if 'snapShotEngineName' in snap_copy_info.get('snapToTapeSelectedEngine', ""):
                    return snap_copy_info['snapToTapeSelectedEngine'].get(
                        'snapShotEngineName', "")
    raise SDKException(
        'Subclient',
        '102',
        'Cannot fetch snap engine name.')
var software_compression

Returns the value of Software Compression settings on the Subclient.

Expand source code Browse git
@property
def software_compression(self):
    """Returns the value of Software Compression settings on the Subclient."""
    mapping = {
        0: 'ON_CLIENT',
        1: 'ON_MEDIAAGENT',
        2: 'USE_STORAGE_POLICY_SETTINGS',
        4: 'OFF'
    }

    try:
        return mapping[self._commonProperties['storageDevice']['softwareCompression']]
    except KeyError:
        return self._commonProperties['storageDevice']['softwareCompression']
var storage_ma

Treats the subclient storage ma as a read-only attribute.

Expand source code Browse git
@property
def storage_ma(self):
    """Treats the subclient storage ma as a read-only attribute."""
    storage_device = self._commonProperties['storageDevice']
    if 'performanceMode' in storage_device:
        data_backup_storage_device = storage_device['performanceMode']
        data_storage_details = data_backup_storage_device["perfCRCDetails"][0]
        if 'perfMa' in data_storage_details:
            return data_storage_details['perfMa']
var storage_ma_id

Treats the subclient storage ma id as a read-only attribute.

Expand source code Browse git
@property
def storage_ma_id(self):
    """Treats the subclient storage ma id as a read-only attribute."""
    storage_device = self._commonProperties['storageDevice']
    if 'performanceMode' in storage_device:
        data_backup_storage_device = storage_device['performanceMode']
        data_storage_details = data_backup_storage_device["perfCRCDetails"][0]
        if 'perfMaId' in data_storage_details:
            return data_storage_details['perfMaId']
var storage_policy

Treats the subclient storage policy as a read-only attribute.

Expand source code Browse git
@property
def storage_policy(self):
    """Treats the subclient storage policy as a read-only attribute."""
    storage_device = self._commonProperties['storageDevice']
    if 'dataBackupStoragePolicy' in storage_device:
        data_backup_storage_policy = storage_device['dataBackupStoragePolicy']
        if 'storagePolicyName' in data_backup_storage_policy:
            return data_backup_storage_policy['storagePolicyName']
var subclient_guid

Returns the SubclientGUID

Expand source code Browse git
@property
def subclient_guid(self):
    """Returns the SubclientGUID"""
    return self._subclient_properties.get('subClientEntity', {}).get('subclientGUID')
var subclient_id

Treats the subclient id as a read-only attribute.

Expand source code Browse git
@property
def subclient_id(self):
    """Treats the subclient id as a read-only attribute."""
    return self._subclient_id
var subclient_name

Treats the subclient name as a read-only attribute.

Expand source code Browse git
@property
def subclient_name(self):
    """Treats the subclient name as a read-only attribute."""
    return self._subclient_name

Methods

def backup(self, backup_level='Incremental', incremental_backup=False, incremental_level='BEFORE_SYNTH', collect_metadata=False)

Runs a backup job for the subclient of the level specified.

Args

backup_level (str) – level of backup the user wish to run Full / Incremental / Differential / Synthetic_full default: Incremental

incremental_backup (bool) – run incremental backup only applicable in case of Synthetic_full backup default: False

incremental_level (str) – run incremental backup before/after synthetic full BEFORE_SYNTH / AFTER_SYNTH

    only applicable in case of Synthetic_full backup
default: BEFORE_SYNTH

Returns

object - instance of the Job class for this backup job

Raises

SDKException: if backup level specified is not correct

if response is empty

if response is not success
Expand source code Browse git
def backup(self,
           backup_level="Incremental",
           incremental_backup=False,
           incremental_level='BEFORE_SYNTH',
           collect_metadata=False):
    """Runs a backup job for the subclient of the level specified.

        Args:
            backup_level        (str)   --  level of backup the user wish to run
                    Full / Incremental / Differential / Synthetic_full
                default: Incremental

            incremental_backup  (bool)  --  run incremental backup
                    only applicable in case of Synthetic_full backup
                default: False

            incremental_level   (str)   --  run incremental backup before/after synthetic full
                    BEFORE_SYNTH / AFTER_SYNTH

                    only applicable in case of Synthetic_full backup
                default: BEFORE_SYNTH

        Returns:
            object - instance of the Job class for this backup job

        Raises:
            SDKException:
                if backup level specified is not correct

                if response is empty

                if response is not success
    """
    backup_level = backup_level.lower()

    if backup_level not in ['full', 'incremental', 'transaction_log',
                            'differential', 'synthetic_full']:
        raise SDKException('Subclient', '103')

    backup_request = backup_level

    if backup_level == 'synthetic_full':
        if incremental_backup:
            backup_request += '&runIncrementalBackup=True'
            backup_request += '&incrementalLevel=%s' % (
                incremental_level.lower())
        else:
            backup_request += '&runIncrementalBackup=False'

    backup_request += '&collectMetaInfo=%s' % collect_metadata

    backup_service = self._services['SUBCLIENT_BACKUP'] % (
        self.subclient_id, backup_request)

    flag, response = self._cvpysdk_object.make_request(
        'POST', backup_service)

    return self._process_backup_response(flag, response)
def browse(self, *args, **kwargs)

Browses the content of the Subclient.

Args

Dictionary of browse options: Example:

    browse({
        'path': 'c:\\hello',

        'show_deleted': True,

        'from_time': '2014-04-20 12:00:00',

        'to_time': '2016-04-21 12:00:00'
    })

Kwargs

Keyword argument of browse options: Example:

    browse(
        path='c:\hello',

        show_deleted=True,

        from_time='2014-04-20 12:00:00',

        to_time='2016-04-21 12:00:00'
    )

Returns

(list, dict) list - List of only the file, folder paths from the browse response

dict    -   Dictionary of all the paths with additional metadata retrieved
from browse operation

Refer default_browse_options_ for all the supported options.

.. _default_browse_options: https://github.com/CommvaultEngg/cvpysdk/blob/master/cvpysdk/backupset.py#L565

Expand source code Browse git
def browse(self, *args, **kwargs):
    """Browses the content of the Subclient.

        Args:
            Dictionary of browse options:
                Example:

                    browse({
                        'path': 'c:\\\\hello',

                        'show_deleted': True,

                        'from_time': '2014-04-20 12:00:00',

                        'to_time': '2016-04-21 12:00:00'
                    })

        Kwargs:
            Keyword argument of browse options:
                Example:

                    browse(
                        path='c:\\hello',

                        show_deleted=True,

                        from_time='2014-04-20 12:00:00',

                        to_time='2016-04-21 12:00:00'
                    )

        Returns:
            (list, dict)
                list    -   List of only the file, folder paths from the browse response

                dict    -   Dictionary of all the paths with additional metadata retrieved
                from browse operation


        Refer `default_browse_options`_ for all the supported options.

        .. _default_browse_options: https://github.com/CommvaultEngg/cvpysdk/blob/master/cvpysdk/backupset.py#L565

    """
    if args and isinstance(args[0], dict):
        options = args[0]
    else:
        options = kwargs

    options['_subclient_id'] = self._subclient_id

    return self._backupset_object.browse(options)
def disable_backup(self)

Disables Backup for the subclient.

Raises

SDKException: if failed to disable backup of subclient

Expand source code Browse git
def disable_backup(self):
    """Disables Backup for the subclient.

        Raises:
            SDKException:
                if failed to disable backup of subclient
    """
    self._set_subclient_properties(
        "_commonProperties['enableBackup']", False)
def disable_intelli_snap(self)

Disables Intelli Snap for the subclient.

Raises

SDKException: if failed to disable intelli snap for subclient

Expand source code Browse git
def disable_intelli_snap(self):
    """Disables Intelli Snap for the subclient.

        Raises:
            SDKException:
                if failed to disable intelli snap for subclient
    """
    self._set_subclient_properties(
        "_commonProperties['snapCopyInfo']['isSnapBackupEnabled']", False
    )
def enable_backup(self)

Enables Backup for the subclient.

Raises

SDKException: if failed to enable backup of subclient

Expand source code Browse git
def enable_backup(self):
    """Enables Backup for the subclient.

        Raises:
            SDKException:
                if failed to enable backup of subclient
    """
    self._set_subclient_properties("_commonProperties['enableBackup']", True)
def enable_backup_at_time(self, enable_time)

Disables Backup if not already disabled, and enables at the time specified.

Args

enable_time (str) – UTC time to enable the backup at, in 24 Hour format format: YYYY-MM-DD HH:mm:ss

Note In case of linux CommServer provide time in GMT timezone

Raises

SDKException: if time value entered is less than the current time

if time value entered is not of correct format

if failed to enable backup

if response is empty

if response is not success
Expand source code Browse git
def enable_backup_at_time(self, enable_time):
    """Disables Backup if not already disabled, and enables at the time specified.

        Args:
            enable_time (str)  --  UTC time to enable the backup at, in 24 Hour format
                format: YYYY-MM-DD HH:mm:ss

            **Note** In case of linux CommServer provide time in GMT timezone

        Raises:
            SDKException:
                if time value entered is less than the current time

                if time value entered is not of correct format

                if failed to enable backup

                if response is empty

                if response is not success
    """
    try:
        time_tuple = time.strptime(enable_time, "%Y-%m-%d %H:%M:%S")
        if time.mktime(time_tuple) < time.time():
            raise SDKException('Subclient', '108')
    except ValueError:
        raise SDKException('Subclient', '109')

    enable_backup_at_time = {
        "TimeZoneName": self._commcell_object.default_timezone,
        "timeValue": enable_time
    }

    self._set_subclient_properties(
        "_commonProperties['enableBackupAtDateTime']", enable_backup_at_time
    )
def enable_intelli_snap(self, snap_engine_name, proxy_options=None)

Enables Intelli Snap for the subclient.

Args

snap_engine_name (str) – Snap Engine Name

Raises

SDKException: if failed to enable intelli snap for subclient

Expand source code Browse git
def enable_intelli_snap(self, snap_engine_name, proxy_options=None):
    """Enables Intelli Snap for the subclient.

        Args:
            snap_engine_name    (str)   --  Snap Engine Name

        Raises:
            SDKException:
                if failed to enable intelli snap for subclient
    """
    if not isinstance(snap_engine_name, str):
        raise SDKException("Subclient", "101")

    properties_dict = {
        "isSnapBackupEnabled": True,
        "snapToTapeSelectedEngine": {
            "snapShotEngineName": snap_engine_name
        }
    }

    if proxy_options is not None:
        if "snap_proxy" in proxy_options:
            properties_dict["snapToTapeProxyToUse"] = {
                "clientName": proxy_options["snap_proxy"]
            }

        if "backupcopy_proxy" in proxy_options:
            properties_dict["useSeparateProxyForSnapToTape"] = True
            properties_dict["separateProxyForSnapToTape"] = {
                "clientName": proxy_options["backupcopy_proxy"]
            }

        if "use_source_if_proxy_unreachable" in proxy_options:
            properties_dict["snapToTapeProxyToUseSource"] = True

    self._set_subclient_properties(
        "_commonProperties['snapCopyInfo']", properties_dict)
def enable_trueup(self)

Setter for the TrueUp Option for a Subclient

Expand source code Browse git
def enable_trueup(self):
    """Setter for the TrueUp Option for a Subclient"""
    if 'isTrueUpOptionEnabled' in self._commonProperties:
        self._set_subclient_properties("_commonProperties['isTrueUpOptionEnabled']", True)
def enable_trueup_days(self, days=30)

Setter for the TrueUp Option with reconcile after x days

Expand source code Browse git
def enable_trueup_days(self, days=30):
    """Setter for the TrueUp Option with reconcile after x days"""
    self.enable_trueup()
    self._set_subclient_properties("_commonProperties['runTrueUpJobAfterDays']", days)
def exclude_from_sla(self)

Exclude subclient from SLA.

Raises

SDKException: if failed to exclude the subclient from SLA

Expand source code Browse git
def exclude_from_sla(self):
    """Exclude subclient from SLA.

        Raises:
            SDKException:
                if failed to exclude the subclient from SLA
    """
    self._set_subclient_properties(
        "_commonProperties['excludeFromSLA']", True)
def find(self, *args, **kwargs)

Searches a file/folder in the backed up content of the subclient, and returns all the files matching the filters given.

Args

Dictionary of browse options: Example:

    find({
        'file_name': '*.txt',

        'show_deleted': True,

        'from_time': '2014-04-20 12:00:00',

        'to_time': '2016-04-31 12:00:00'
    })

Kwargs

Keyword argument of browse options: Example:

    find(
        file_name='*.txt',

        show_deleted=True,

        'from_time': '2014-04-20 12:00:00',

        to_time='2016-04-31 12:00:00'
    )

Returns

(list, dict) list - List of only the file, folder paths from the browse response

dict    -   Dictionary of all the paths with additional metadata retrieved
from browse operation

Refer default_browse_options_ for all the supported options.

Additional options supported: file_name (str) – Find files with name

file_size_gt    (int)   --  Find files with size greater than size

file_size_lt    (int)   --  Find files with size lesser than size

file_size_et    (int)   --  Find files with size equal to size

.. _default_browse_options: https://github.com/CommvaultEngg/cvpysdk/blob/master/cvpysdk/backupset.py#L565

Expand source code Browse git
def find(self, *args, **kwargs):
    """Searches a file/folder in the backed up content of the subclient,
        and returns all the files matching the filters given.

        Args:
            Dictionary of browse options:
                Example:

                    find({
                        'file_name': '*.txt',

                        'show_deleted': True,

                        'from_time': '2014-04-20 12:00:00',

                        'to_time': '2016-04-31 12:00:00'
                    })

        Kwargs:
            Keyword argument of browse options:
                Example:

                    find(
                        file_name='*.txt',

                        show_deleted=True,

                        'from_time': '2014-04-20 12:00:00',

                        to_time='2016-04-31 12:00:00'
                    )

        Returns:
            (list, dict)
                list    -   List of only the file, folder paths from the browse response

                dict    -   Dictionary of all the paths with additional metadata retrieved
                from browse operation


        Refer `default_browse_options`_ for all the supported options.

        Additional options supported:
            file_name       (str)   --  Find files with name

            file_size_gt    (int)   --  Find files with size greater than size

            file_size_lt    (int)   --  Find files with size lesser than size

            file_size_et    (int)   --  Find files with size equal to size

        .. _default_browse_options: https://github.com/CommvaultEngg/cvpysdk/blob/master/cvpysdk/backupset.py#L565

    """
    if args and isinstance(args[0], dict):
        options = args[0]
    else:
        options = kwargs

    options['_subclient_id'] = self._subclient_id

    return self._backupset_object.find(options)
def find_latest_job(self, include_active=True, include_finished=True, lookup_time=1, job_filter='Backup,SYNTHFULL')

Finds the latest job for the subclient which includes current running job also.

Args

include_active (bool) – to indicate if active jobs should be included default: True

include_finished (bool) – to indicate if finished jobs should be included default: True

lookup_time (int) – get jobs executed within the number of hours default: 1 Hour

job_filter (str) – to specify type of job default: 'Backup,SYNTHFULL'

for multiple filters,
give the values **comma(,)** separated

List of Possible Values:

        Backup

        Restore

        AUXCOPY

        WORKFLOW

        etc..

<http://documentation.commvault.com/commvault/v11/article?p=features/rest_api/operations/get_job.htm>
    to get the complete list of filters available

Returns

object - instance of the Job class for the latest job

Raises

SDKException: if any error occurred while finding the latest job.

Expand source code Browse git
def find_latest_job(
        self,
        include_active=True,
        include_finished=True,
        lookup_time=1,
        job_filter='Backup,SYNTHFULL'):
    """Finds the latest job for the subclient
        which includes current running job also.

        Args:
            include_active    (bool)    -- to indicate if
                                            active jobs should be included
                default: True

            include_finished  (bool)    -- to indicate if finished jobs
                                            should be included
                default: True

            lookup_time       (int)     -- get jobs executed
                                            within the number of hours
                default: 1 Hour

            job_filter        (str)     -- to specify type of job
                default: 'Backup,SYNTHFULL'

                for multiple filters,
                give the values **comma(,)** separated

                List of Possible Values:

                        Backup

                        Restore

                        AUXCOPY

                        WORKFLOW

                        etc..

                http://documentation.commvault.com/commvault/v11/article?p=features/rest_api/operations/get_job.htm
                    to get the complete list of filters available

        Returns:
            object  -   instance of the Job class for the latest job

        Raises:
            SDKException:
                if any error occurred while finding the latest job.

    """
    job_controller = JobController(self._commcell_object)
    entity_dict = {
        "subclientId": int(self.subclient_id)
    }
    if include_active and include_finished:
        client_jobs = job_controller.all_jobs(
            client_name=self._client_object.client_name,
            lookup_time=lookup_time,
            job_filter=job_filter,
            entity=entity_dict
        )
    elif include_active:
        client_jobs = job_controller.active_jobs(
            client_name=self._client_object.client_name,
            lookup_time=lookup_time,
            job_filter=job_filter,
            entity=entity_dict
        )
    elif include_finished:
        client_jobs = job_controller.finished_jobs(
            client_name=self._client_object.client_name,
            lookup_time=lookup_time,
            job_filter=job_filter,
            entity=entity_dict
        )
    else:
        raise SDKException(
            'Subclient',
            '102',
            "Either active or finished job must be included"
        )

    latest_jobid = 0
    for job in client_jobs:
        if client_jobs[job]['subclient_id'] == int(self._subclient_id):
            if int(job) > latest_jobid:
                latest_jobid = int(job)

    if latest_jobid == 0:
        raise SDKException('Subclient', '102', "No jobs found")

    return Job(self._commcell_object, latest_jobid)
def get_ma_associated_storagepolicy(self)

Get Media agents associated with storage policy

Raise Exception: if unable to get MA names

Expand source code Browse git
def get_ma_associated_storagepolicy(self):
    """
    Get Media agents associated with storage policy
    
    Raise Exception:
            if unable to get MA names
    """
    storage = self._subclient_properties['commonProperties']['storageDevice']
    if 'performanceMode' in storage:
        data_backup_storage_device = storage['performanceMode']["perfCRCDetails"]
        malist = []
        for each_ma in data_backup_storage_device:
            malist.append(each_ma['perfMa'])
        return malist
def list_media(self, *args, **kwargs)

List media required to browse and restore backed up data from the subclient

Args

Dictionary of options: Example:

    list_media({
        'path': 'c:\hello',
        'show_deleted': True,
        'from_time': '2020-04-20 12:00:00',
        'to_time': '2021-04-19 12:00:00'
    })

Kwargs

Keyword argument of options: Example:

    list_media(
        path='c:\hello',
        show_deleted=True,
        from_time='2020-04-20 12:00:00',
        to_time='2021-04-19 12:00:00'
    )

Note

Refer _default_browse_options in backupset.py for all the supported options.

Returns

(List, Dict) - List - List of all the media required for the given options

Dict - Total size of the media

Raises

SDKException: if failed to list media for content

if response is not success
Expand source code Browse git
def list_media(self, *args, **kwargs):
    """List media required to browse and restore backed up data from the subclient

        Args:
            Dictionary of options:
                Example:

                    list_media({
                        'path': 'c:\\hello',
                        'show_deleted': True,
                        'from_time': '2020-04-20 12:00:00',
                        'to_time': '2021-04-19 12:00:00'
                    })

        Kwargs:
            Keyword argument of options:
                Example:

                    list_media(
                        path='c:\\hello',
                        show_deleted=True,
                        from_time='2020-04-20 12:00:00',
                        to_time='2021-04-19 12:00:00'
                    )

        Note:
            Refer `_default_browse_options` in backupset.py for all the supported options.

        Returns:
            (List, Dict) -
                List - List of all the media required for the given options

                Dict - Total size of the media

        Raises:
            SDKException:
                if failed to list media for content

                if response is not success

    """

    if args and isinstance(args[0], dict):
        options = args[0]
    else:
        options = kwargs

    options['_subclient_id'] = self._subclient_id

    return self._backupset_object.list_media(options)
def refresh(self)

Refresh the properties of the Subclient.

Expand source code Browse git
def refresh(self):
    """Refresh the properties of the Subclient."""
    self._get_subclient_properties()
    self.schedules = Schedules(self)
def restore_in_place(self, paths, overwrite=True, restore_data_and_acl=True, copy_precedence=None, from_time=None, to_time=None, fs_options=None, schedule_pattern=None, proxy_client=None, advanced_options=None)

Restores the files/folders specified in the input paths list to the same location.

Args

paths (list) – list of full paths of files/folders to restore

overwrite (bool) – unconditional overwrite files during restore default: True

restore_data_and_acl (bool) – restore data and ACL files default: True

copy_precedence (int) – copy precedence value of storage policy copy default: None

from_time (str) – time to restore the contents after format: YYYY-MM-DD HH:MM:SS

default: None

to_time (str) – time to restore the contents before format: YYYY-MM-DD HH:MM:SS

default: None

fs_options (dict) – dictionary that includes all advanced options options: all_versions : if set to True restores all the versions of the specified file versions : list of version numbers to be backed up validate_only : To validate data backed up for restore

schedule_pattern (dict) – scheduling options to be included for the task

    Please refer schedules.schedulePattern.createSchedule()
                                                doc for the types of Jsons

schedule_pattern (dict) – scheduling options to be included for the task

    Please refer schedules.schedulePattern.createSchedule()
                                                doc for the types of Jsons

proxy_client (str) – Proxy client used during FS under NAS operations

advanced_options (dict) – Advanced restore options

Options:

    job_description (str)   --  Restore job description

    timezone        (str)   --  Timezone to be used for restore

        **Note** make use of TIMEZONES dict in constants.py to pass timezone

Returns

object - instance of the Job class for this restore job if its an immediate Job instance of the Schedule class for this restore job if its a scheduled Job

Raises

SDKException: if paths is not a list

if failed to initialize job

if response is empty

if response is not success
Expand source code Browse git
def restore_in_place(
        self,
        paths,
        overwrite=True,
        restore_data_and_acl=True,
        copy_precedence=None,
        from_time=None,
        to_time=None,
        fs_options=None,
        schedule_pattern=None,
        proxy_client=None,
        advanced_options=None):

    """Restores the files/folders specified in the input paths list to the same location.

        Args:
            paths                   (list)  --  list of full paths of files/folders to restore

            overwrite               (bool)  --  unconditional overwrite files during restore
                default: True

            restore_data_and_acl    (bool)  --  restore data and ACL files
                default: True

            copy_precedence         (int)   --  copy precedence value of storage policy copy
                default: None

            from_time           (str)       --  time to restore the contents after
                    format: YYYY-MM-DD HH:MM:SS

                default: None

            to_time           (str)         --  time to restore the contents before
                    format: YYYY-MM-DD HH:MM:SS

                default: None

            fs_options      (dict)          -- dictionary that includes all advanced options
                options:
                    all_versions        : if set to True restores all the versions of the
                                            specified file
                    versions            : list of version numbers to be backed up
                    validate_only       : To validate data backed up for restore


            schedule_pattern (dict) -- scheduling options to be included for the task

                    Please refer schedules.schedulePattern.createSchedule()
                                                                doc for the types of Jsons

            schedule_pattern (dict) -- scheduling options to be included for the task

                    Please refer schedules.schedulePattern.createSchedule()
                                                                doc for the types of Jsons

            proxy_client    (str)          -- Proxy client used during FS under NAS operations

            advanced_options    (dict)  -- Advanced restore options

                Options:

                    job_description (str)   --  Restore job description

                    timezone        (str)   --  Timezone to be used for restore

                        **Note** make use of TIMEZONES dict in constants.py to pass timezone

        Returns:
            object - instance of the Job class for this restore job if its an immediate Job
                     instance of the Schedule class for this restore job if its a scheduled Job

        Raises:
            SDKException:
                if paths is not a list

                if failed to initialize job

                if response is empty

                if response is not success
    """
    self._instance_object._restore_association = self._subClientEntity

    return self._instance_object._restore_in_place(
        paths=paths,
        overwrite=overwrite,
        restore_data_and_acl=restore_data_and_acl,
        copy_precedence=copy_precedence,
        from_time=from_time,
        to_time=to_time,
        fs_options=fs_options,
        schedule_pattern=schedule_pattern,
        proxy_client=proxy_client,
        advanced_options=advanced_options
    )
def restore_out_of_place(self, client, destination_path, paths, overwrite=True, restore_data_and_acl=True, copy_precedence=None, from_time=None, to_time=None, fs_options=None, schedule_pattern=None, proxy_client=None, advanced_options=None)

Restores the files/folders specified in the input paths list to the input client, at the specified destionation location.

Args

client (str/object) – either the name of the client or the instance of the Client

destination_path (str) – full path of the restore location on client

paths (list) – list of full paths of files/folders to restore

overwrite (bool) – unconditional overwrite files during restore default: True

restore_data_and_acl (bool) – restore data and ACL files default: True

copy_precedence (int) – copy precedence value of storage policy copy default: None

from_time (str) – time to restore the contents after format: YYYY-MM-DD HH:MM:SS

default: None

to_time (str) – time to restore the contents before format: YYYY-MM-DD HH:MM:SS

default: None

fs_options (dict) – dictionary that includes all advanced options options: preserve_level : preserve level option to set in restore proxy_client : proxy that needed to be used for restore impersonate_user : Impersonate user options for restore impersonate_password: Impersonate password option for restore in base64 encoded form all_versions : if set to True restores all the versions of the specified file versions : list of version numbers to be backed up media_agent : Media Agent need to be used for Browse and restore validate_only : To validate data backed up for restore

schedule_pattern (dict) – scheduling options to be included for the task

    Please refer schedules.schedulePattern.createSchedule()
                                                doc for the types of Jsons

schedule_pattern (dict) – scheduling options to be included for the task

    Please refer schedules.schedulePattern.createSchedule()
                                                doc for the types of Jsons

proxy_client (str) – Proxy client used during FS under NAS operations

advanced_options (dict) – Advanced restore options

Options:

    job_description (str)   --  Restore job description

    timezone        (str)   --  Timezone to be used for restore

        **Note** make use of TIMEZONES dict in constants.py to pass timezone

Returns

object - instance of the Job class for this restore job if its an immediate Job instance of the Schedule class for this restore job if its a scheduled Job

Raises

SDKException: if client is not a string or Client instance

if destination_path is not a string

if paths is not a list

if failed to initialize job

if response is empty

if response is not success
Expand source code Browse git
def restore_out_of_place(
        self,
        client,
        destination_path,
        paths,
        overwrite=True,
        restore_data_and_acl=True,
        copy_precedence=None,
        from_time=None,
        to_time=None,
        fs_options=None,
        schedule_pattern=None,
        proxy_client=None,
        advanced_options=None):
    """Restores the files/folders specified in the input paths list to the input client,
        at the specified destionation location.

        Args:
            client                (str/object) --  either the name of the client or
                                                       the instance of the Client

            destination_path      (str)        --  full path of the restore location on client

            paths                 (list)       --  list of full paths of
                                                       files/folders to restore

            overwrite             (bool)       --  unconditional overwrite files during restore
                default: True

            restore_data_and_acl  (bool)       --  restore data and ACL files
                default: True

            copy_precedence         (int)   --  copy precedence value of storage policy copy
                default: None

            from_time           (str)       --  time to restore the contents after
                    format: YYYY-MM-DD HH:MM:SS

                default: None

            to_time           (str)         --  time to restore the contents before
                    format: YYYY-MM-DD HH:MM:SS

                default: None

            fs_options      (dict)          -- dictionary that includes all advanced options
                options:
                    preserve_level      : preserve level option to set in restore
                    proxy_client        : proxy that needed to be used for restore
                    impersonate_user    : Impersonate user options for restore
                    impersonate_password: Impersonate password option for restore
                                            in base64 encoded form
                    all_versions        : if set to True restores all the versions of the
                                            specified file
                    versions            : list of version numbers to be backed up
                    media_agent         : Media Agent need to be used for Browse and restore
                    validate_only       : To validate data backed up for restore


            schedule_pattern (dict) -- scheduling options to be included for the task

                    Please refer schedules.schedulePattern.createSchedule()
                                                                doc for the types of Jsons

            schedule_pattern (dict) -- scheduling options to be included for the task

                    Please refer schedules.schedulePattern.createSchedule()
                                                                doc for the types of Jsons

            proxy_client    (str)          -- Proxy client used during FS under NAS operations

            advanced_options    (dict)  -- Advanced restore options

                Options:

                    job_description (str)   --  Restore job description

                    timezone        (str)   --  Timezone to be used for restore

                        **Note** make use of TIMEZONES dict in constants.py to pass timezone

        Returns:
            object - instance of the Job class for this restore job if its an immediate Job
                     instance of the Schedule class for this restore job if its a scheduled Job

        Raises:
            SDKException:
                if client is not a string or Client instance

                if destination_path is not a string

                if paths is not a list

                if failed to initialize job

                if response is empty

                if response is not success
    """
    self._instance_object._restore_association = self._subClientEntity

    if fs_options and 'proxy_client' in fs_options:
        proxy_client = fs_options['proxy_client']

    return self._instance_object._restore_out_of_place(
        client=client,
        destination_path=destination_path,
        paths=paths,
        overwrite=overwrite,
        restore_data_and_acl=restore_data_and_acl,
        copy_precedence=copy_precedence,
        from_time=from_time,
        to_time=to_time,
        fs_options=fs_options,
        schedule_pattern=schedule_pattern,
        proxy_client=proxy_client,
        advanced_options=advanced_options
    )
def set_backup_nodes(self, data_access_nodes)

Sets the the backup nodes for NFS share subclient.

Args

data_access_nodes (list) – the list of data access nodes to be set as backup nodes for NFS share subclient

Returns

None - if the operation is successful

Raises

SDKException: if unable to update the backup nodes for the subclient

Expand source code Browse git
def set_backup_nodes(self, data_access_nodes):
    """Sets the the backup nodes for NFS share subclient.

        Args:
            data_access_nodes   (list)  --  the list of data access nodes to be set
            as backup nodes for NFS share subclient

        Returns:
            None    -   if the operation is successful

        Raises:
            SDKException:
                if unable to update the backup nodes for the subclient

    """
    data_access_nodes_json = []
    for access_node in data_access_nodes:
        data_access_nodes_json.append({"clientName": access_node})

    request_json = {
        "subClientProperties": {
            "fsSubClientProp": {
                "backupConfiguration": {
                    "backupDataAccessNodes": data_access_nodes_json
                }
            }
        }
    }

    flag, response = self._cvpysdk_object.make_request('POST', self._SUBCLIENT, request_json)

    output = self._process_update_response(flag, response)

    if output[0]:
        return
    else:
        o_str = 'Failed to update properties of subclient\nError: "{0}"'
        raise SDKException('Subclient', '102', o_str.format(output[2]))
def set_proxy_for_snap(self, proxy_name)

method to set Use proxy option for intellisnap subclient

Args

proxy_name(str) – Name of the proxy to be used

Expand source code Browse git
def set_proxy_for_snap(self, proxy_name):
    """ method to set Use proxy option for intellisnap subclient 

    Args:
        proxy_name(str) -- Name of the proxy to be used

    """
    if not isinstance(proxy_name, str):
        raise SDKException("Subclient", "101")

    properties_dict = {
        "clientName": proxy_name
    }

    update_properties = self.properties
    update_properties['commonProperties']['snapCopyInfo']['snapToTapeProxyToUse'] = properties_dict
    self.update_properties(update_properties)
def unset_proxy_for_snap(self)

method to unset Use proxy option for intellisnap subclient

Expand source code Browse git
def unset_proxy_for_snap(self):
    """ method to unset Use proxy option for intellisnap subclient """

    properties_dict = {
        "clientId": 0
    }
    update_properties = self.properties
    update_properties['commonProperties']['snapCopyInfo']['snapToTapeProxyToUse'] = properties_dict
    self.update_properties(update_properties)
def update_properties(self, properties_dict)

Updates the subclient properties

Args:
    properties_dict (dict)  --  subclient property dict which is to be updated

Returns:
    None

Raises:
    SDKException:
        if failed to add

        if response is empty

        if response code is not as expected

Note self.properties can be used to get a deep copy of all the properties, modify the properties which you need to change and use the update_properties method to set the properties

Expand source code Browse git
def update_properties(self, properties_dict):
    """Updates the subclient properties

        Args:
            properties_dict (dict)  --  subclient property dict which is to be updated

        Returns:
            None

        Raises:
            SDKException:
                if failed to add

                if response is empty

                if response code is not as expected

    **Note** self.properties can be used to get a deep copy of all the properties, modify the properties which you
    need to change and use the update_properties method to set the properties

    """
    request_json = {
        "subClientProperties": {}
    }

    request_json['subClientProperties'].update(properties_dict)

    # check if subclient name is updated in the request
    # if subclient name is updated set the newName field in the request
    if properties_dict.get('subClientEntity', {}).get('subclientName') and properties_dict.get(
            'subClientEntity', {}).get('subclientName') != self._subClientEntity.get('subclientName'):
        request_json['newName'] = properties_dict.get('subClientEntity', {}).get('subclientName')
    flag, response = self._cvpysdk_object.make_request('POST', self._SUBCLIENT, request_json)
    status, _, error_string = self._process_update_response(flag, response)
    self.refresh()

    if not status:
        raise SDKException('Subclient', '102', 'Failed to update subclient properties\nError: "{}"'.format(
            error_string))
class Subclients (class_object)

Class for getting all the subclients associated with a client.

Initialize the Subclients object for the given backupset.

Args

class_object (object) – instance of the Agent / Instance / Backupset class

Returns

object - instance of the Subclients class

Raises

SDKException: if class object is not an instance of Agent / Instance / Backupset

Expand source code Browse git
class Subclients(object):
    """Class for getting all the subclients associated with a client."""

    def __init__(self, class_object):
        """Initialize the Subclients object for the given backupset.

            Args:
                class_object    (object)    --  instance of the Agent / Instance / Backupset class

            Returns:
                object  -   instance of the Subclients class

            Raises:
                SDKException:
                    if class object is not an instance of Agent / Instance / Backupset

        """
        from .agent import Agent
        from .instance import Instance
        from .backupset import Backupset

        self._agent_object = None
        self._instance_object = None
        self._backupset_object = None
        self._url_param = ''

        if isinstance(class_object, Agent):
            self._agent_object = class_object
            self._url_param += self._agent_object.agent_id

        elif isinstance(class_object, Instance):
            self._instance_object = class_object
            self._agent_object = self._instance_object._agent_object
            self._url_param += '{0}&instanceId={1}'.format(
                self._agent_object.agent_id, self._instance_object.instance_id
            )

        elif isinstance(class_object, Backupset):
            self._backupset_object = class_object
            self._instance_object = class_object._instance_object
            self._agent_object = self._instance_object._agent_object
            self._url_param += self._agent_object.agent_id
            self._url_param += '&backupsetId={0}'.format(
                self._backupset_object.backupset_id
            )
        else:
            raise SDKException('Subclient', '115')

        self._client_object = self._agent_object._client_object
        self._commcell_object = self._agent_object._commcell_object

        self._cvpysdk_object = self._commcell_object._cvpysdk_object
        self._services = self._commcell_object._services
        self._update_response_ = self._commcell_object._update_response_

        self._SUBCLIENTS = self._services['GET_ALL_SUBCLIENTS'] % (
            self._client_object.client_id, self._url_param
        )

        self._ADD_SUBCLIENT = self._services['ADD_SUBCLIENT']

        self._default_subclient = None

        # sql server subclient type dict
        self._sqlsubclient_type_dict = {
            'DATABASE': 1,
            'FILE_FILEGROUP': 2,
        }

        # this will work only for `Exchange Database` Agent, as only an object of
        # ExchangeDatabaseAgent class has these attributes
        if self._instance_object is None and hasattr(
                self._agent_object, '_instance_object'):
            self._instance_object = self._agent_object._instance_object

        if self._backupset_object is None and hasattr(
                self._agent_object, '_backupset_object'):
            self._backupset_object = self._agent_object._backupset_object

        self.refresh()

    def __str__(self):
        """Representation string consisting of all subclients of the backupset.

            Returns:
                str - string of all the subclients of th backupset of an agent of a client
        """
        representation_string = '{:^5}\t{:^20}\t{:^20}\t{:^20}\t{:^20}\t{:^20}\n\n'.format(
            'S. No.', 'Subclient', 'Backupset', 'Instance', 'Agent', 'Client'
        )

        for index, subclient in enumerate(self._subclients):
            sub_str = '{:^5}\t{:20}\t{:20}\t{:20}\t{:20}\t{:20}\n'.format(
                index + 1,
                subclient.split('\\')[-1],
                self._subclients[subclient]['backupset'],
                self._instance_object.instance_name,
                self._agent_object.agent_name,
                self._client_object.client_name
            )
            representation_string += sub_str

        return representation_string.strip()

    def __repr__(self):
        """Representation string for the instance of the Subclients class."""
        if self._backupset_object is not None:
            o_str = (
                'Subclients class instance for Backupset: "{0}", '
                'of Instance: "{1}", for Agent: "{2}"'
            ).format(
                self._backupset_object.backupset_name,
                self._instance_object.instance_name,
                self._agent_object.agent_name
            )
        elif self._instance_object is not None:
            o_str = 'Subclients class instance for Instance: "{0}", of Agent: "{1}"'.format(
                self._instance_object.instance_name,
                self._agent_object.agent_name
            )
        else:
            o_str = 'Subclients class instance for Agent: "{0}"'.format(
                self._agent_object.agent_name
            )

        return o_str

    def __len__(self):
        """Returns the number of the subclients associated to the Agent for the selected Client."""
        return len(self.all_subclients)

    def __getitem__(self, value):
        """Returns the name of the subclient for the given subclient ID or
            the details of the subclient for given subclient Name.

            Args:
                value   (str / int)     --  Name or ID of the subclient

            Returns:
                str     -   name of the subclient, if the subclient id was given

                dict    -   dict of details of the subclient, if subclient name was given

            Raises:
                IndexError:
                    no subclient exists with the given Name / Id

        """
        value = str(value)

        if value in self.all_subclients:
            return self.all_subclients[value]
        else:
            try:
                return list(
                    filter(lambda x: x[1]['id'] == value, self.all_subclients.items())
                )[0][0]
            except IndexError:
                raise IndexError('No subclient exists with the given Name / Id')

    def _get_subclients(self):
        """Gets all the subclients associated to the client specified by the backupset object.

            Returns:
                dict - consists of all subclients in the backupset
                    {
                         "subclient1_name": {
                             "id": subclient1_id,
                             "backupset": backupset
                         },
                         "subclient2_name": {
                             "id": subclient2_id,
                             "backupset": backupset
                         }
                    }

            Raises:
                SDKException:
                    if response is empty

                    if response is not success
        """
        flag, response = self._cvpysdk_object.make_request(
            'GET', self._SUBCLIENTS)

        if flag:
            if response.json() and 'subClientProperties' in response.json():
                return_dict = {}

                for dictionary in response.json()['subClientProperties']:
                    # store the agent, instance, and backupset name for the current subclient
                    # the API call returns the subclients for all Agents, so we need to filter
                    # them out based on the Agent / Instance / Backupset that had been selected
                    # by the user earlier
                    agent = dictionary['subClientEntity']['appName'].lower()
                    instance = dictionary['subClientEntity']['instanceName'].lower(
                    )
                    backupset = dictionary['subClientEntity']['backupsetName'].lower(
                    )

                    # filter subclients for all entities: Agent, Instance, and Backupset
                    # as the instance of the Backupset class was passed for Subclients instance
                    # creation
                    if self._backupset_object is not None:
                        if (self._backupset_object.backupset_name in backupset and
                                self._instance_object.instance_name in instance and
                                self._agent_object.agent_name in agent):
                            temp_name = dictionary['subClientEntity']['subclientName'].lower(
                            )
                            temp_id = str(
                                dictionary['subClientEntity']['subclientId']).lower()

                            return_dict[temp_name] = {
                                "id": temp_id,
                                "backupset": backupset
                            }

                            if dictionary['commonProperties'].get(
                                    'isDefaultSubclient'):
                                self._default_subclient = temp_name

                    elif self._instance_object is not None:
                        if (self._instance_object.instance_name in instance and
                                self._agent_object.agent_name in agent):
                            temp_name = dictionary['subClientEntity']['subclientName'].lower(
                            )
                            temp_id = str(
                                dictionary['subClientEntity']['subclientId']).lower()

                            if len(
                                    self._instance_object.backupsets.all_backupsets) > 1:
                                temp_name = "{0}\\{1}".format(
                                    backupset, temp_name)

                            return_dict[temp_name] = {
                                "id": temp_id,
                                "backupset": backupset
                            }

                            if dictionary['commonProperties'].get(
                                    'isDefaultSubclient'):
                                self._default_subclient = temp_name

                    elif self._agent_object is not None:
                        if self._agent_object.agent_name in agent:
                            temp_name = dictionary['subClientEntity']['subclientName'].lower(
                            )
                            temp_id = str(
                                dictionary['subClientEntity']['subclientId']).lower()

                            if len(self._agent_object.instances.all_instances) > 1:
                                if len(
                                        self._instance_object.backupsets.all_backupsets) > 1:
                                    temp_name = "{0}\\{1}\\{2}".format(
                                        instance, backupset, temp_name
                                    )
                                else:
                                    temp_name = "{0}\\{1}".format(
                                        instance, temp_name)
                            else:
                                if len(
                                        self._instance_object.backupsets.all_backupsets) > 1:
                                    temp_name = "{0}\\{1}".format(
                                        backupset, temp_name)

                            return_dict[temp_name] = {
                                "id": temp_id,
                                "backupset": backupset
                            }

                            if dictionary['commonProperties'].get(
                                    'isDefaultSubclient'):
                                self._default_subclient = temp_name

                return return_dict
            else:
                raise SDKException('Response', '102')
        else:
            raise SDKException(
                'Response',
                '101',
                self._update_response_(
                    response.text))

    @property
    def all_subclients(self):
        """Returns dict of all the subclients configured on this backupset

            Retruns:
                dict    -   consists of all subclients in the backupset

                    {
                        "subclient1_name": {
                            "id": subclient1_id,

                            "backupset": backupset
                        },
                        "subclient2_name": {
                            "id": subclient2_id,

                            "backupset": backupset
                        }
                    }

        """
        return self._subclients

    def has_subclient(self, subclient_name):
        """Checks if a subclient exists in the commcell with the input subclient name.

            Args:
                subclient_name (str)  --  name of the subclient

            Returns:
                bool - boolean output whether the subclient exists in the backupset or not

            Raises:
                SDKException:
                    if type of the subclient name argument is not string
        """
        if not isinstance(subclient_name, str):
            raise SDKException('Subclient', '101')

        return self._subclients and subclient_name.lower() in self._subclients

    def _process_add_request(self, request_json):
        """To post the add subclient request

        Args:
            request_json    (dict)  -- Request json to be passed as the payload

        Returns:
            object  -   instance of the Subclient class

        """
        flag, response = self._cvpysdk_object.make_request(
            'POST', self._ADD_SUBCLIENT, request_json
        )

        if flag:
            if response.json() and 'response' in response.json():
                error_code = response.json()['response']['errorCode']

                if error_code != 0:
                    error_string = response.json()['response']['errorString']
                    raise SDKException(
                        'Subclient',
                        '102',
                        'Failed to create subclient\nError: "{0}"'.format(error_string)
                    )
                else:
                    # initialize the subclients again so the subclient object has all the subclients
                    self.refresh()

                    subclient_name = request_json['subClientProperties']['subClientEntity']['subclientName']

                    return self.get(subclient_name)

            else:
                raise SDKException('Response', '102')
        else:
            raise SDKException('Response', '101', self._update_response_(response.text))

    def add(self, subclient_name, storage_policy=None,
            subclient_type=None, description='', advanced_options=None,
            pre_scan_cmd=None):
        """Adds a new subclient to the backupset.

            Args:
                subclient_name      (str)   --  name of the new subclient to add

                storage_policy      (str)   --  name of the storage policy to be associated
                with the subclient

                    default: None

                subclient_type      (str)   --  type of subclient for sql server

                    default: None

                    Valid Values are:

                        - DATABASE

                        - FILE_FILEGROUP


                description         (str)   --  description for the subclient (optional)

                    default: ''

                advanced_options    (dict)  --  dict of additional options needed to create
                                                subclient with additional properties
                                                default : None
                    Example:
                        {
                            ondemand_subclient : True
                        }

                pre_scan_cmd        (str)   --  path to the batch file/shell script file to run
                                                before each backup of the subclient

            Returns:
                object  -   instance of the Subclient class

            Raises:
                SDKException:
                    if subclient name argument is not of type string

                    if storage policy argument is not of type string

                    if description argument is not of type string

                    if failed to create subclient

                    if response is empty

                    if response is not success

                    if subclient already exists with the given name

        """
        if not (isinstance(subclient_name, str) and
                isinstance(description, str)):
            raise SDKException('Subclient', '101')

        if self.has_subclient(subclient_name):
            raise SDKException(
                'Subclient', '102', 'Subclient "{0}" already exists.'.format(
                    subclient_name)
            )

        if self._backupset_object is None:
            if self._instance_object.backupsets.has_backupset(
                    'defaultBackupSet'):
                self._backupset_object = self._instance_object.backupsets.get(
                    'defaultBackupSet')
            else:
                self._backupset_object = self._instance_object.backupsets.get(
                    sorted(self._instance_object.backupsets.all_backupsets)[0]
                )

        if storage_policy and not self._commcell_object.storage_policies.has_policy(
                storage_policy):
            raise SDKException(
                'Subclient',
                '102',
                'Storage Policy: "{0}" does not exist in the Commcell'.format(
                    storage_policy)
            )

        if advanced_options:
            if advanced_options.get("ondemand_subclient", False):
                ondemand_value = advanced_options.get("ondemand_subclient")
            else:
                ondemand_value = False
        else:
            ondemand_value = False

        request_json = {
            "subClientProperties": {
                "contentOperationType": 2,
                "subClientEntity": {
                    "clientName": self._client_object.client_name,
                    "appName": self._agent_object.agent_name,
                    "instanceName": self._instance_object.instance_name,
                    "backupsetName": self._backupset_object.backupset_name,
                    "subclientName": subclient_name
                },
                "commonProperties": {
                    "description": description,
                    "enableBackup": True,
                    "onDemandSubClient": ondemand_value,
                    "storageDevice": {
                        "dataBackupStoragePolicy": {
                            "storagePolicyName": storage_policy
                        }
                    },
                }
            }
        }

        if storage_policy is None:
            del request_json["subClientProperties"]["commonProperties"]["storageDevice"]

        if pre_scan_cmd is not None:
            request_json["subClientProperties"]["commonProperties"]["prepostProcess"] = {
                "runAs": 1,
                "preScanCommand": pre_scan_cmd
            }

        if self._agent_object.agent_name == 'sql server':
            request_json['subClientProperties']['mssqlSubClientProp'] = {
                'sqlSubclientType': self._sqlsubclient_type_dict[subclient_type]
            }

        return self._process_add_request(request_json)

    def add_oracle_logical_dump_subclient(
            self,
            subclient_name,
            storage_policy,
            dump_dir,
            user_name,
            domain_name,
            password,
            full_mode,
            schema_value=None):
        """
        Method to add subclient for oracle logical dump.
        This method add two type of subclient full mode
        and schema mode. For full mode full_mode should be
        true and schema_value should be none and for schema
        mode full_mode should be false and schema_value should
        be list of values.Rest of thing should be same for both.
        Args:
              subclient_name     (Str)  --  subclient name for logical dump

              storage_policy     (Str)  --  Storage policy for subclient

              dump_dir            (Str)  --  dump directory for subclient

              user_name           (Str)  --  username for oracle database

              domain_name         (Str)  --  domainname for oracle database

              password           (Str)  --  password for oracle database
                                            (should be in encrypted and decrypted form)

              full_mode           (bool) --  if ture then subclient for full mode otherwise schema mode

              schema_value        (list) --  schema value for schema mode subclient

                   default: None
        Return:
                object  -   instance of the Subclient class

            Raises:
                SDKException:
                    if subclient name argument is not of type string

                    if storage policy argument is not of type string

                    if subclient name already present

                    if storage policy does not exist

        """
        if not (isinstance(subclient_name, str) and
                isinstance(storage_policy, str) and
                isinstance(dump_dir, str) and
                isinstance(user_name, str) and
                isinstance(domain_name, str) and
                isinstance(password, str) and
                isinstance(full_mode, bool)):
            raise SDKException('Subclient', '101')
        if (full_mode == False and not
        isinstance(schema_value, list)):
            raise SDKException('Subclient', '101')

        if self.has_subclient(subclient_name):
            raise SDKException(
                'Subclient', '102', 'Subclient "{0}" already exists.'.format(
                    subclient_name)
            )

        if self._backupset_object is None:
            if self._instance_object.backupsets.has_backupset(
                    'defaultBackupSet'):
                self._backupset_object = self._instance_object.backupsets.get(
                    'defaultBackupSet')
            else:
                self._backupset_object = self._instance_object.backupsets.get(
                    sorted(self._instance_object.backupsets.all_backupsets)[0]
                )

        if not self._commcell_object.storage_policies.has_policy(
                storage_policy):
            raise SDKException(
                'Subclient',
                '102',
                'Storage Policy: "{0}" does not exist in the Commcell'.format(
                    storage_policy)
            )

        request_json = {
            "subClientProperties": {
                "subClientEntity": {
                    "clientName": self._client_object.client_name,
                    "instanceName": self._instance_object.instance_name,
                    "appName": self._agent_object.agent_name,
                    "backupsetName": self._backupset_object.backupset_name,
                    "subclientName": subclient_name
                },
                "oracleSubclientProp": {
                    "data": False,
                    "archiveDelete": False,
                    "useSQLConntect": False,
                    "dbSubclientType": 2,
                    "mergeIncImageCopies": False,
                    "selectiveOnlineFull": False,
                    "protectBackupRecoveryArea": False,
                    "selectArchiveLogDestForBackup": False,
                    "backupSPFile": False,
                    "backupControlFile": False,
                    "backupArchiveLog": False,
                    "validate": False,
                },
                "commonProperties": {
                    "snapCopyInfo": {
                        "useSeparateProxyForSnapToTape": False,
                        "checkProxyForSQLIntegrity": False,
                        "snapToTapeProxyToUseSource": False,
                        "isSnapBackupEnabled": False,
                        "IsOracleSposDriverEnabled": False,
                        "isRMANEnableForTapeMovement": False
                    },
                    "dbDumpConfig": {
                        "fullMode": True,
                        "database": "",
                        "dumpDir": dump_dir,
                        "parallelism": 2,
                        "overrideInstanceUser": True,
                        "sqlConnect": {
                            "password": b64encode(password.encode()).decode(),
                            "domainName": domain_name,
                            "userName": user_name
                        }
                    },
                    "storageDevice": {
                        "dataBackupStoragePolicy": {
                            "storagePolicyName": storage_policy
                        },
                        "deDuplicationOptions": {
                            "enableDeduplication": True
                        }
                    }
                }
            }
        }

        if (full_mode == False):
            request_json["subClientProperties"]["commonProperties"]["dbDumpConfig"]["fullMode"] = False
            request_json["subClientProperties"]["commonProperties"]["dbDumpConfig"]["schema"] = schema_value

        return self._process_add_request(request_json)

    def add_postgresql_subclient(
            self, subclient_name, storage_policy,
            contents, no_of_streams=1, collect_object_list=False):
        """Adds a new postgresql subclient to the backupset.

            Args:
                subclient_name          (str)   --  name of the new subclient to add

                storage_policy          (str)   --  name of the storage policy to be associated
                with the subclient

                contents                (list)  --  database list to be added as subclient content


                no_of_streams           (int)   --  No of backup streams to be used

                    default: 1

                collect_object_list     (bool)  --  Boolean flag to determine if collect object
                list needs to be enabled for subclient or not

                    default: False

            Returns:
                object  -   instance of the Subclient class

            Raises:
                SDKException:
                    if subclient name argument is not of type string

                    if storage policy argument is not of type string

                    if conetnts argument is not of type list

                    if contents is empty list

                    if failed to create subclient

                    if response is empty

                    if response is not success

                    if subclient already exists with the given name

        """
        if not (isinstance(subclient_name, str) and
                isinstance(storage_policy, str) and
                isinstance(contents, list)):
            raise SDKException('Subclient', '101')

        if self.has_subclient(subclient_name):
            raise SDKException(
                'Subclient', '102', 'Subclient "{0}" already exists.'.format(
                    subclient_name)
            )

        if not self._commcell_object.storage_policies.has_policy(
                storage_policy):
            raise SDKException(
                'Subclient',
                '102',
                'Storage Policy: "{0}" does not exist in the Commcell'.format(
                    storage_policy)
            )

        if not contents:
            raise SDKException(
                'Subclient',
                '102',
                'Content list cannot be empty'
            )

        content_list = []
        for content in contents:
            content_list.append({"postgreSQLContent": {"databaseName": content}})

        request_json = {
            "subClientProperties": {
                "contentOperationType": 2,
                "subClientEntity": {
                    "clientName": self._client_object.client_name,
                    "appName": self._agent_object.agent_name,
                    "instanceName": self._instance_object.instance_name,
                    "backupsetName": self._backupset_object.backupset_name,
                    "subclientName": subclient_name
                },
                "commonProperties": {
                    "storageDevice": {
                        "dataBackupStoragePolicy": {
                            "storagePolicyName": storage_policy
                        }
                    },
                },
                "postgreSQLSubclientProp": {
                    "numberOfBackupStreams": no_of_streams,
                    "collectObjectListDuringBackup": collect_object_list
                },
                "content": content_list
            }
        }

        return self._process_add_request(request_json)

    def add_mysql_subclient(
            self,
            subclient_name,
            storage_policy,
            contents,
            **kwargs
    ):
        """Adds a new mysql subclient to the instance.

            Args:
                subclient_name          (str)   --  name of the new subclient to add

                storage_policy          (str)   --  name of the storage policy to be associated
                with the subclient

                contents                (list)  --  database list to be added as subclient content

                kwargs      (dict)  -- dict of keyword arguments as follows

                    no_of_backup_streams    (int)   --  No of backup streams to be used
                    default: 1

                    no_of_log_backup_streams    (int)   -- No of Transaction log backup streams
                    default: 1

                    full_instance_xtrabackup    (bool)  -- True if XtraBackup is selected for subclient
                    default: False

            Returns:
                object  -   instance of the Subclient class

            Raises:
                SDKException:
                    if subclient name argument is not of type string

                    if storage policy argument is not of type string

                    if conetnts argument is not of type list

                    if contents is empty list

                    if failed to create subclient

                    if response is empty

                    if response is not success

                    if subclient already exists with the given name

        """
        if not (isinstance(subclient_name, str) and
                isinstance(storage_policy, str) and
                isinstance(contents, list)):
            raise SDKException('Subclient', '101')

        if self.has_subclient(subclient_name):
            raise SDKException(
                'Subclient', '102', 'Subclient "{0}" already exists.'.format(
                    subclient_name)
            )

        if not self._commcell_object.storage_policies.has_policy(
                storage_policy):
            raise SDKException(
                'Subclient',
                '102',
                'Storage Policy: "{0}" does not exist in the Commcell'.format(
                    storage_policy)
            )

        if not contents:
            raise SDKException(
                'Subclient',
                '102',
                'Content list cannot be empty'
            )

        content_list = []
        for content in contents:
            content_list.append({"mySQLContent": {"databaseName": content}})

        request_json = {
            "subClientProperties": {
                "contentOperationType": 2,
                "subClientEntity": {
                    "clientName": self._client_object.client_name,
                    "appName": self._agent_object.agent_name,
                    "instanceName": self._instance_object.instance_name,
                    "backupsetName": "defaultDummyBackupSet",
                    "subclientName": subclient_name
                },
                "commonProperties": {
                    "storageDevice": {
                        "dataBackupStoragePolicy": {
                            "storagePolicyName": storage_policy
                        }
                    },
                },
                "mySqlSubclientProp": {
                    "numberOfBackupStreams": kwargs.get('no_of_backup_streams', 1),
                    "numberOfTransactionLogStreams": kwargs.get('no_of_log_backup_streams', 1),
                    "fullInstanceXtraBackup": kwargs.get('full_instance_xtrabackup', False)
                },
                "content": content_list
            }
        }

        return self._process_add_request(request_json)

    def add_virtual_server_subclient(
            self,
            subclient_name,
            subclient_content,
            **kwargs
    ):
        """Adds a new virtual server subclient to the backupset.

            Args:
                subclient_name      (str)   --  Name of the subclient to be created

                subclient_content   (list)  --  Content to be added to the subclient

                    Example 1:
                        [{
                            'equal_value': True,
                            'allOrAnyChildren': True,
                            'id': '',
                            'path': '',
                            'display_name': 'sample1',
                            'type': VSAObjects.VMName
                        }]
                    Example 2:
                         [{
                        'allOrAnyChildren': False,
                        'content': [{
                            'equal_value': True,
                            'allOrAnyChildren': True,
                            'display_name': 'sample1',
                            'type': VSAObjects.VMName
                        }, {
                            'equal_value': True,
                            'allOrAnyChildren': True,
                            'display_name': 'sample2',
                            'type': VSAObjects.VMName
                        }
                        ]
                        }, {
                        'allOrAnyChildren': True,
                        'content': [{
                            'equal_value': True,
                            'allOrAnyChildren': True,
                            'display_name': 'sample3',
                            'type': VSAObjects.RESOURCE_POOL
                        }, {
                            'equal_value': True,
                            'allOrAnyChildren': True,
                            'id': 'sample4',
                            'display_name': 'sample4',
                            'type': VSAObjects.SERVER
                            }
                            ]
                        }
                        ]
                        **Note** Use VSAObjects Enum present in constants.py to pass value to type

                kwargs      (dict)  -- dict of keyword arguments as follows

                    plan_name           (str)   --  Plan to be associated with the subclient

                    storage_policy      (str)   --  Storage policy to be associated with the subclient

                    description         (str)   --  Description for the subclient

                        default: ''

            Returns:
                object  -   instance of the Subclient class

            Raises:
                SDKException:
                    if subclient name argument is not of type string

                    if storage policy argument is not of type string

                    if description argument is not of type string

                    if failed to create subclient

                    if response is empty

                    if response is not success

                    if subclient already exists with the given name

        """
        if not (isinstance(subclient_name, str) and
                isinstance(subclient_content, list)):
            raise SDKException('Subclient', '101')

        if self.has_subclient(subclient_name):
            raise SDKException(
                'Subclient', '102', 'Subclient "{0}" already exists.'.format(
                    subclient_name)
            )

        if self._backupset_object is None:
            if self._instance_object.backupsets.has_backupset(
                    'defaultBackupSet'):
                self._backupset_object = self._instance_object.backupsets.get(
                    'defaultBackupSet')
            else:
                self._backupset_object = self._instance_object.backupsets.get(
                    sorted(self._instance_object.backupsets.all_backupsets)[0]
                )

        content = []

        def set_content(item_content):
            """
            create content dictionary
            Args:
                item_content            (dict):     Dict of content details

                Example:
                    {
                        'equal_value': True,
                        'allOrAnyChildren': True,
                        'display_name': 'sample1',
                        'type':  < VSAObjects.VMName: 10 >
                    }

            Returns:

            """
            return {
                "equalsOrNotEquals": item_content.get('equal_value', True),
                "name": item_content.get('id', ''),
                "displayName": item_content.get('display_name', ''),
                "path": item_content.get('path', ''),
                "allOrAnyChildren": item.get('allOrAnyChildren', True),
                "type": item_content['type'] if isinstance(item_content['type'], int) else item_content['type'].value
            }

        for item in subclient_content:
            _temp_list = []
            _temp_dict = {}
            allOrAnyChildren = item.get('allOrAnyChildren', None)
            if 'content' in item:
                nested_content = item['content']
                for each_condition in nested_content:
                    temp_dict = set_content(each_condition)
                    _temp_list.append(temp_dict)
                _temp_dict['allOrAnyChildren'] = allOrAnyChildren
                _temp_dict['children'] = _temp_list
                content.append(_temp_dict)
            else:
                temp_dict = set_content(item)
                content.append(temp_dict)

        request_json = {
            "subClientProperties": {
                "vmContentOperationType": 2,
                "vmContent": {
                    "children": content
                },
                "subClientEntity": {
                    "clientName": self._client_object.client_name,
                    "appName": self._agent_object.agent_name,
                    "instanceName": self._instance_object.instance_name,
                    "backupsetName": self._backupset_object.backupset_name,
                    "subclientName": subclient_name
                },
                "commonProperties": {
                    "description": kwargs.get('description'),
                    "enableBackup": True
                }
            }
        }

        if kwargs.get("customSnapshotResourceGroup"):
            request_json["subClientProperties"]["vsaSubclientProp"] = \
                {"customSnapshotResourceGroup": kwargs.get("customSnapshotResourceGroup")}

        if kwargs.get('plan_name'):
            if not self._commcell_object.plans.has_plan(kwargs['plan_name']):
                raise SDKException(
                    'Subclient',
                    '102',
                    'Plan: "{0}" does not exist in the Commcell'.format(kwargs['plan_name'])
                )
            request_json['subClientProperties']['planEntity'] = {
                "planName": kwargs['plan_name']
            }

        elif kwargs.get('storage_policy'):
            if not self._commcell_object.storage_policies.has_policy(kwargs.get('storage_policy')):
                raise SDKException(
                    'Subclient',
                    '102',
                    'Storage Policy: "{0}" does not exist in the Commcell'.format(kwargs.get('storage_policy'))
                )
            request_json['subClientProperties']['commonProperties']['storageDevice'] = {
                "dataBackupStoragePolicy": {
                    "storagePolicyName": kwargs.get('storage_policy')
                }
            }
        else:
            raise SDKException('Subclient', '102', 'Either Plan or Storage policy should be given as input')

        return self._process_add_request(request_json)

    def add_onedrive_subclient(self,
                               subclient_name,
                               server_plan):

        """Adds a new subclient to the backupset.

            Args:
                subclient_name     (str)   --  name of the new subclient to add

                server_plan     (str)   --  name of the server plan to be associated
                                                with the subclient

            Returns:
                object  -   instance of the Subclient class

            Raises:
                SDKException:
                    if subclient name argument is not of type string

                    if server plan argument is not of type string

                    if description argument is not of type string

                    if failed to create subclient

                    if response is empty

                    if response is not success

                    if subclient already exists with the given name

                    if server plan  donot exists with the given name

                """

        if not (isinstance(subclient_name, str) and
                isinstance(server_plan, str)):
            raise SDKException('Subclient', '101')

        if self.has_subclient(subclient_name):
            raise SDKException(
                'Subclient', '102', 'Subclient "{0}" already exists.'.format(
                    subclient_name)
            )

        if self._backupset_object is None:
            if self._instance_object.backupsets.has_backupset(
                    self._instance_object.backupsets.default_backup_set):
                self._backupset_object = self._instance_object.backupsets.get(
                    self._instance_object.backupsets.default_backup_set)
            else:
                self._backupset_object = self._instance_object.backupsets.get(
                    sorted(self._instance_object.backupsets.all_backupsets)[0]
                )

        if self._commcell_object.plans.has_plan(server_plan):
            server_plan_object = self._commcell_object.plans.get(server_plan)
            server_plan_id = int(server_plan_object.plan_id)
        else:
            raise SDKException('Plan', '102', 'Provide Valid Plan Name')

        request_json = {
            "subClientProperties": {
                "subClientEntity": {
                    "clientName": self._client_object.client_name,
                    "instanceName": self._instance_object.instance_name,
                    "backupsetId": int(self._backupset_object.backupset_id),
                    "instanceId": int(self._instance_object.instance_id),
                    "clientId": int(self._client_object.client_id),
                    "appName": self._agent_object.agent_name,
                    "applicationId": 134,
                    "subclientName": subclient_name
                },
                "planEntity": {
                    "planId": server_plan_id
                },
                "cloudAppsSubClientProp": {
                    "instanceType": 7,
                    "oneDriveSubclient": {
                        "enableOneNote": False,
                        "isEnterprise": True
                    }
                },
                "cloudconnectorSubclientProp": {
                    "isAutoDiscoveryEnabled": False
                },
                "commonProperties": {
                    "enableBackup": True
                }
            }
        }

        return self._process_add_request(request_json)

    def get(self, subclient_name):
        """Returns a subclient object of the specified backupset name.

            Args:
                subclient_name (str)  --  name of the subclient

            Returns:
                object - instance of the Subclient class for the given subclient name

            Raises:
                SDKException:
                    if type of the subclient name argument is not string

                    if no subclient exists with the given name
        """
        if not isinstance(subclient_name, str):
            raise SDKException('Subclient', '101')
        else:
            subclient_name = subclient_name.lower()

            if self.has_subclient(subclient_name):

                if self._backupset_object is None:
                    self._backupset_object = self._instance_object.backupsets.get(
                        self._subclients[subclient_name]['backupset']
                    )
                return Subclient(
                    self._backupset_object, subclient_name, self._subclients[subclient_name]['id']
                )

            raise SDKException(
                'Subclient', '102', 'No subclient exists with name: {0}'.format(
                    subclient_name)
            )

    def delete(self, subclient_name):
        """Deletes the subclient specified by the subclient_name from the backupset.

            Args:
                subclient_name (str)  --  name of the subclient to remove from the backupset

            Raises:
                SDKException:
                    if type of the subclient name argument is not string

                    if failed to delete subclient

                    if response is empty

                    if response is not success

                    if no subclient exists with the given name
        """
        if not isinstance(subclient_name, str):
            raise SDKException('Subclient', '101')
        else:
            subclient_name = subclient_name.lower()

        if self.has_subclient(subclient_name):
            delete_subclient_service = self._services['SUBCLIENT'] % (
                self._subclients[subclient_name]['id']
            )

            flag, response = self._cvpysdk_object.make_request(
                'DELETE', delete_subclient_service)

            if flag:
                if response.json():
                    if 'response' in response.json():
                        response_value = response.json()['response'][0]
                        error_code = str(response_value['errorCode'])
                        error_message = None

                        if 'errorString' in response_value:
                            error_message = response_value['errorString']

                        if error_message:
                            o_str = 'Failed to delete subclient\nError: "{0}"'
                            raise SDKException(
                                'Subclient', '102', o_str.format(error_message))
                        else:
                            if error_code == '0':
                                # initialize the subclients again
                                # so the subclient object has all the
                                # subclients
                                self.refresh()
                            else:
                                o_str = ('Failed to delete subclient with Error Code: "{0}"\n'
                                         'Please check the documentation for '
                                         'more details on the error')
                                raise SDKException(
                                    'Subclient', '102', o_str.format(error_code))
                else:
                    raise SDKException('Response', '102')
            else:
                raise SDKException(
                    'Response', '101', self._update_response_(
                        response.text))
        else:
            raise SDKException(
                'Subclient', '102', 'No subclient exists with name: {0}'.format(
                    subclient_name)
            )

    def refresh(self):
        """Refresh the subclients associated with the Backupset / Instance."""
        self._subclients = self._get_subclients()

    @property
    def default_subclient(self):
        """Returns the name of the default subclient for the selected Agent and Backupset."""
        return self._default_subclient

Subclasses

Instance variables

var all_subclients

Returns dict of all the subclients configured on this backupset

Retruns

dict - consists of all subclients in the backupset

{
    "subclient1_name": {
        "id": subclient1_id,

        "backupset": backupset
    },
    "subclient2_name": {
        "id": subclient2_id,

        "backupset": backupset
    }
}
Expand source code Browse git
@property
def all_subclients(self):
    """Returns dict of all the subclients configured on this backupset

        Retruns:
            dict    -   consists of all subclients in the backupset

                {
                    "subclient1_name": {
                        "id": subclient1_id,

                        "backupset": backupset
                    },
                    "subclient2_name": {
                        "id": subclient2_id,

                        "backupset": backupset
                    }
                }

    """
    return self._subclients
var default_subclient

Returns the name of the default subclient for the selected Agent and Backupset.

Expand source code Browse git
@property
def default_subclient(self):
    """Returns the name of the default subclient for the selected Agent and Backupset."""
    return self._default_subclient

Methods

def add(self, subclient_name, storage_policy=None, subclient_type=None, description='', advanced_options=None, pre_scan_cmd=None)

Adds a new subclient to the backupset.

Args

subclient_name (str) – name of the new subclient to add

storage_policy (str) – name of the storage policy to be associated with the subclient

default: None

subclient_type (str) – type of subclient for sql server

default: None

Valid Values are:

    - DATABASE

    - FILE_FILEGROUP

description (str) – description for the subclient (optional)

default: ''

advanced_options (dict) – dict of additional options needed to create subclient with additional properties default : None Example: { ondemand_subclient : True }

pre_scan_cmd (str) – path to the batch file/shell script file to run before each backup of the subclient

Returns

object - instance of the Subclient class

Raises

SDKException: if subclient name argument is not of type string

if storage policy argument is not of type string

if description argument is not of type string

if failed to create subclient

if response is empty

if response is not success

if subclient already exists with the given name
Expand source code Browse git
def add(self, subclient_name, storage_policy=None,
        subclient_type=None, description='', advanced_options=None,
        pre_scan_cmd=None):
    """Adds a new subclient to the backupset.

        Args:
            subclient_name      (str)   --  name of the new subclient to add

            storage_policy      (str)   --  name of the storage policy to be associated
            with the subclient

                default: None

            subclient_type      (str)   --  type of subclient for sql server

                default: None

                Valid Values are:

                    - DATABASE

                    - FILE_FILEGROUP


            description         (str)   --  description for the subclient (optional)

                default: ''

            advanced_options    (dict)  --  dict of additional options needed to create
                                            subclient with additional properties
                                            default : None
                Example:
                    {
                        ondemand_subclient : True
                    }

            pre_scan_cmd        (str)   --  path to the batch file/shell script file to run
                                            before each backup of the subclient

        Returns:
            object  -   instance of the Subclient class

        Raises:
            SDKException:
                if subclient name argument is not of type string

                if storage policy argument is not of type string

                if description argument is not of type string

                if failed to create subclient

                if response is empty

                if response is not success

                if subclient already exists with the given name

    """
    if not (isinstance(subclient_name, str) and
            isinstance(description, str)):
        raise SDKException('Subclient', '101')

    if self.has_subclient(subclient_name):
        raise SDKException(
            'Subclient', '102', 'Subclient "{0}" already exists.'.format(
                subclient_name)
        )

    if self._backupset_object is None:
        if self._instance_object.backupsets.has_backupset(
                'defaultBackupSet'):
            self._backupset_object = self._instance_object.backupsets.get(
                'defaultBackupSet')
        else:
            self._backupset_object = self._instance_object.backupsets.get(
                sorted(self._instance_object.backupsets.all_backupsets)[0]
            )

    if storage_policy and not self._commcell_object.storage_policies.has_policy(
            storage_policy):
        raise SDKException(
            'Subclient',
            '102',
            'Storage Policy: "{0}" does not exist in the Commcell'.format(
                storage_policy)
        )

    if advanced_options:
        if advanced_options.get("ondemand_subclient", False):
            ondemand_value = advanced_options.get("ondemand_subclient")
        else:
            ondemand_value = False
    else:
        ondemand_value = False

    request_json = {
        "subClientProperties": {
            "contentOperationType": 2,
            "subClientEntity": {
                "clientName": self._client_object.client_name,
                "appName": self._agent_object.agent_name,
                "instanceName": self._instance_object.instance_name,
                "backupsetName": self._backupset_object.backupset_name,
                "subclientName": subclient_name
            },
            "commonProperties": {
                "description": description,
                "enableBackup": True,
                "onDemandSubClient": ondemand_value,
                "storageDevice": {
                    "dataBackupStoragePolicy": {
                        "storagePolicyName": storage_policy
                    }
                },
            }
        }
    }

    if storage_policy is None:
        del request_json["subClientProperties"]["commonProperties"]["storageDevice"]

    if pre_scan_cmd is not None:
        request_json["subClientProperties"]["commonProperties"]["prepostProcess"] = {
            "runAs": 1,
            "preScanCommand": pre_scan_cmd
        }

    if self._agent_object.agent_name == 'sql server':
        request_json['subClientProperties']['mssqlSubClientProp'] = {
            'sqlSubclientType': self._sqlsubclient_type_dict[subclient_type]
        }

    return self._process_add_request(request_json)
def add_mysql_subclient(self, subclient_name, storage_policy, contents, **kwargs)

Adds a new mysql subclient to the instance.

Args

subclient_name (str) – name of the new subclient to add

storage_policy (str) – name of the storage policy to be associated with the subclient

contents (list) – database list to be added as subclient content

kwargs (dict) – dict of keyword arguments as follows

no_of_backup_streams    (int)   --  No of backup streams to be used
default: 1

no_of_log_backup_streams    (int)   -- No of Transaction log backup streams
default: 1

full_instance_xtrabackup    (bool)  -- True if XtraBackup is selected for subclient
default: False

Returns

object - instance of the Subclient class

Raises

SDKException: if subclient name argument is not of type string

if storage policy argument is not of type string

if conetnts argument is not of type list

if contents is empty list

if failed to create subclient

if response is empty

if response is not success

if subclient already exists with the given name
Expand source code Browse git
def add_mysql_subclient(
        self,
        subclient_name,
        storage_policy,
        contents,
        **kwargs
):
    """Adds a new mysql subclient to the instance.

        Args:
            subclient_name          (str)   --  name of the new subclient to add

            storage_policy          (str)   --  name of the storage policy to be associated
            with the subclient

            contents                (list)  --  database list to be added as subclient content

            kwargs      (dict)  -- dict of keyword arguments as follows

                no_of_backup_streams    (int)   --  No of backup streams to be used
                default: 1

                no_of_log_backup_streams    (int)   -- No of Transaction log backup streams
                default: 1

                full_instance_xtrabackup    (bool)  -- True if XtraBackup is selected for subclient
                default: False

        Returns:
            object  -   instance of the Subclient class

        Raises:
            SDKException:
                if subclient name argument is not of type string

                if storage policy argument is not of type string

                if conetnts argument is not of type list

                if contents is empty list

                if failed to create subclient

                if response is empty

                if response is not success

                if subclient already exists with the given name

    """
    if not (isinstance(subclient_name, str) and
            isinstance(storage_policy, str) and
            isinstance(contents, list)):
        raise SDKException('Subclient', '101')

    if self.has_subclient(subclient_name):
        raise SDKException(
            'Subclient', '102', 'Subclient "{0}" already exists.'.format(
                subclient_name)
        )

    if not self._commcell_object.storage_policies.has_policy(
            storage_policy):
        raise SDKException(
            'Subclient',
            '102',
            'Storage Policy: "{0}" does not exist in the Commcell'.format(
                storage_policy)
        )

    if not contents:
        raise SDKException(
            'Subclient',
            '102',
            'Content list cannot be empty'
        )

    content_list = []
    for content in contents:
        content_list.append({"mySQLContent": {"databaseName": content}})

    request_json = {
        "subClientProperties": {
            "contentOperationType": 2,
            "subClientEntity": {
                "clientName": self._client_object.client_name,
                "appName": self._agent_object.agent_name,
                "instanceName": self._instance_object.instance_name,
                "backupsetName": "defaultDummyBackupSet",
                "subclientName": subclient_name
            },
            "commonProperties": {
                "storageDevice": {
                    "dataBackupStoragePolicy": {
                        "storagePolicyName": storage_policy
                    }
                },
            },
            "mySqlSubclientProp": {
                "numberOfBackupStreams": kwargs.get('no_of_backup_streams', 1),
                "numberOfTransactionLogStreams": kwargs.get('no_of_log_backup_streams', 1),
                "fullInstanceXtraBackup": kwargs.get('full_instance_xtrabackup', False)
            },
            "content": content_list
        }
    }

    return self._process_add_request(request_json)
def add_onedrive_subclient(self, subclient_name, server_plan)

Adds a new subclient to the backupset.

Args

subclient_name (str) – name of the new subclient to add

server_plan (str) – name of the server plan to be associated with the subclient

Returns

object - instance of the Subclient class

Raises

SDKException: if subclient name argument is not of type string

if server plan argument is not of type string

if description argument is not of type string

if failed to create subclient

if response is empty

if response is not success

if subclient already exists with the given name

if server plan  donot exists with the given name
Expand source code Browse git
def add_onedrive_subclient(self,
                           subclient_name,
                           server_plan):

    """Adds a new subclient to the backupset.

        Args:
            subclient_name     (str)   --  name of the new subclient to add

            server_plan     (str)   --  name of the server plan to be associated
                                            with the subclient

        Returns:
            object  -   instance of the Subclient class

        Raises:
            SDKException:
                if subclient name argument is not of type string

                if server plan argument is not of type string

                if description argument is not of type string

                if failed to create subclient

                if response is empty

                if response is not success

                if subclient already exists with the given name

                if server plan  donot exists with the given name

            """

    if not (isinstance(subclient_name, str) and
            isinstance(server_plan, str)):
        raise SDKException('Subclient', '101')

    if self.has_subclient(subclient_name):
        raise SDKException(
            'Subclient', '102', 'Subclient "{0}" already exists.'.format(
                subclient_name)
        )

    if self._backupset_object is None:
        if self._instance_object.backupsets.has_backupset(
                self._instance_object.backupsets.default_backup_set):
            self._backupset_object = self._instance_object.backupsets.get(
                self._instance_object.backupsets.default_backup_set)
        else:
            self._backupset_object = self._instance_object.backupsets.get(
                sorted(self._instance_object.backupsets.all_backupsets)[0]
            )

    if self._commcell_object.plans.has_plan(server_plan):
        server_plan_object = self._commcell_object.plans.get(server_plan)
        server_plan_id = int(server_plan_object.plan_id)
    else:
        raise SDKException('Plan', '102', 'Provide Valid Plan Name')

    request_json = {
        "subClientProperties": {
            "subClientEntity": {
                "clientName": self._client_object.client_name,
                "instanceName": self._instance_object.instance_name,
                "backupsetId": int(self._backupset_object.backupset_id),
                "instanceId": int(self._instance_object.instance_id),
                "clientId": int(self._client_object.client_id),
                "appName": self._agent_object.agent_name,
                "applicationId": 134,
                "subclientName": subclient_name
            },
            "planEntity": {
                "planId": server_plan_id
            },
            "cloudAppsSubClientProp": {
                "instanceType": 7,
                "oneDriveSubclient": {
                    "enableOneNote": False,
                    "isEnterprise": True
                }
            },
            "cloudconnectorSubclientProp": {
                "isAutoDiscoveryEnabled": False
            },
            "commonProperties": {
                "enableBackup": True
            }
        }
    }

    return self._process_add_request(request_json)
def add_oracle_logical_dump_subclient(self, subclient_name, storage_policy, dump_dir, user_name, domain_name, password, full_mode, schema_value=None)

Method to add subclient for oracle logical dump. This method add two type of subclient full mode and schema mode. For full mode full_mode should be true and schema_value should be none and for schema mode full_mode should be false and schema_value should be list of values.Rest of thing should be same for both.

Args

subclient_name (Str) – subclient name for logical dump

storage_policy (Str) – Storage policy for subclient

dump_dir (Str) – dump directory for subclient

user_name (Str) – username for oracle database

domain_name (Str) – domainname for oracle database

password (Str) – password for oracle database (should be in encrypted and decrypted form)

full_mode (bool) – if ture then subclient for full mode otherwise schema mode

schema_value (list) – schema value for schema mode subclient

 default: None

Return

object  -   instance of the Subclient class

Raises: SDKException: if subclient name argument is not of type string

    if storage policy argument is not of type string

    if subclient name already present

    if storage policy does not exist
Expand source code Browse git
def add_oracle_logical_dump_subclient(
        self,
        subclient_name,
        storage_policy,
        dump_dir,
        user_name,
        domain_name,
        password,
        full_mode,
        schema_value=None):
    """
    Method to add subclient for oracle logical dump.
    This method add two type of subclient full mode
    and schema mode. For full mode full_mode should be
    true and schema_value should be none and for schema
    mode full_mode should be false and schema_value should
    be list of values.Rest of thing should be same for both.
    Args:
          subclient_name     (Str)  --  subclient name for logical dump

          storage_policy     (Str)  --  Storage policy for subclient

          dump_dir            (Str)  --  dump directory for subclient

          user_name           (Str)  --  username for oracle database

          domain_name         (Str)  --  domainname for oracle database

          password           (Str)  --  password for oracle database
                                        (should be in encrypted and decrypted form)

          full_mode           (bool) --  if ture then subclient for full mode otherwise schema mode

          schema_value        (list) --  schema value for schema mode subclient

               default: None
    Return:
            object  -   instance of the Subclient class

        Raises:
            SDKException:
                if subclient name argument is not of type string

                if storage policy argument is not of type string

                if subclient name already present

                if storage policy does not exist

    """
    if not (isinstance(subclient_name, str) and
            isinstance(storage_policy, str) and
            isinstance(dump_dir, str) and
            isinstance(user_name, str) and
            isinstance(domain_name, str) and
            isinstance(password, str) and
            isinstance(full_mode, bool)):
        raise SDKException('Subclient', '101')
    if (full_mode == False and not
    isinstance(schema_value, list)):
        raise SDKException('Subclient', '101')

    if self.has_subclient(subclient_name):
        raise SDKException(
            'Subclient', '102', 'Subclient "{0}" already exists.'.format(
                subclient_name)
        )

    if self._backupset_object is None:
        if self._instance_object.backupsets.has_backupset(
                'defaultBackupSet'):
            self._backupset_object = self._instance_object.backupsets.get(
                'defaultBackupSet')
        else:
            self._backupset_object = self._instance_object.backupsets.get(
                sorted(self._instance_object.backupsets.all_backupsets)[0]
            )

    if not self._commcell_object.storage_policies.has_policy(
            storage_policy):
        raise SDKException(
            'Subclient',
            '102',
            'Storage Policy: "{0}" does not exist in the Commcell'.format(
                storage_policy)
        )

    request_json = {
        "subClientProperties": {
            "subClientEntity": {
                "clientName": self._client_object.client_name,
                "instanceName": self._instance_object.instance_name,
                "appName": self._agent_object.agent_name,
                "backupsetName": self._backupset_object.backupset_name,
                "subclientName": subclient_name
            },
            "oracleSubclientProp": {
                "data": False,
                "archiveDelete": False,
                "useSQLConntect": False,
                "dbSubclientType": 2,
                "mergeIncImageCopies": False,
                "selectiveOnlineFull": False,
                "protectBackupRecoveryArea": False,
                "selectArchiveLogDestForBackup": False,
                "backupSPFile": False,
                "backupControlFile": False,
                "backupArchiveLog": False,
                "validate": False,
            },
            "commonProperties": {
                "snapCopyInfo": {
                    "useSeparateProxyForSnapToTape": False,
                    "checkProxyForSQLIntegrity": False,
                    "snapToTapeProxyToUseSource": False,
                    "isSnapBackupEnabled": False,
                    "IsOracleSposDriverEnabled": False,
                    "isRMANEnableForTapeMovement": False
                },
                "dbDumpConfig": {
                    "fullMode": True,
                    "database": "",
                    "dumpDir": dump_dir,
                    "parallelism": 2,
                    "overrideInstanceUser": True,
                    "sqlConnect": {
                        "password": b64encode(password.encode()).decode(),
                        "domainName": domain_name,
                        "userName": user_name
                    }
                },
                "storageDevice": {
                    "dataBackupStoragePolicy": {
                        "storagePolicyName": storage_policy
                    },
                    "deDuplicationOptions": {
                        "enableDeduplication": True
                    }
                }
            }
        }
    }

    if (full_mode == False):
        request_json["subClientProperties"]["commonProperties"]["dbDumpConfig"]["fullMode"] = False
        request_json["subClientProperties"]["commonProperties"]["dbDumpConfig"]["schema"] = schema_value

    return self._process_add_request(request_json)
def add_postgresql_subclient(self, subclient_name, storage_policy, contents, no_of_streams=1, collect_object_list=False)

Adds a new postgresql subclient to the backupset.

Args

subclient_name (str) – name of the new subclient to add

storage_policy (str) – name of the storage policy to be associated with the subclient

contents (list) – database list to be added as subclient content

no_of_streams (int) – No of backup streams to be used

default: 1

collect_object_list (bool) – Boolean flag to determine if collect object list needs to be enabled for subclient or not

default: False

Returns

object - instance of the Subclient class

Raises

SDKException: if subclient name argument is not of type string

if storage policy argument is not of type string

if conetnts argument is not of type list

if contents is empty list

if failed to create subclient

if response is empty

if response is not success

if subclient already exists with the given name
Expand source code Browse git
def add_postgresql_subclient(
        self, subclient_name, storage_policy,
        contents, no_of_streams=1, collect_object_list=False):
    """Adds a new postgresql subclient to the backupset.

        Args:
            subclient_name          (str)   --  name of the new subclient to add

            storage_policy          (str)   --  name of the storage policy to be associated
            with the subclient

            contents                (list)  --  database list to be added as subclient content


            no_of_streams           (int)   --  No of backup streams to be used

                default: 1

            collect_object_list     (bool)  --  Boolean flag to determine if collect object
            list needs to be enabled for subclient or not

                default: False

        Returns:
            object  -   instance of the Subclient class

        Raises:
            SDKException:
                if subclient name argument is not of type string

                if storage policy argument is not of type string

                if conetnts argument is not of type list

                if contents is empty list

                if failed to create subclient

                if response is empty

                if response is not success

                if subclient already exists with the given name

    """
    if not (isinstance(subclient_name, str) and
            isinstance(storage_policy, str) and
            isinstance(contents, list)):
        raise SDKException('Subclient', '101')

    if self.has_subclient(subclient_name):
        raise SDKException(
            'Subclient', '102', 'Subclient "{0}" already exists.'.format(
                subclient_name)
        )

    if not self._commcell_object.storage_policies.has_policy(
            storage_policy):
        raise SDKException(
            'Subclient',
            '102',
            'Storage Policy: "{0}" does not exist in the Commcell'.format(
                storage_policy)
        )

    if not contents:
        raise SDKException(
            'Subclient',
            '102',
            'Content list cannot be empty'
        )

    content_list = []
    for content in contents:
        content_list.append({"postgreSQLContent": {"databaseName": content}})

    request_json = {
        "subClientProperties": {
            "contentOperationType": 2,
            "subClientEntity": {
                "clientName": self._client_object.client_name,
                "appName": self._agent_object.agent_name,
                "instanceName": self._instance_object.instance_name,
                "backupsetName": self._backupset_object.backupset_name,
                "subclientName": subclient_name
            },
            "commonProperties": {
                "storageDevice": {
                    "dataBackupStoragePolicy": {
                        "storagePolicyName": storage_policy
                    }
                },
            },
            "postgreSQLSubclientProp": {
                "numberOfBackupStreams": no_of_streams,
                "collectObjectListDuringBackup": collect_object_list
            },
            "content": content_list
        }
    }

    return self._process_add_request(request_json)
def add_virtual_server_subclient(self, subclient_name, subclient_content, **kwargs)

Adds a new virtual server subclient to the backupset.

Args

subclient_name (str) – Name of the subclient to be created

subclient_content (list) – Content to be added to the subclient

Example 1:
    [{
        'equal_value': True,
        'allOrAnyChildren': True,
        'id': '',
        'path': '',
        'display_name': 'sample1',
        'type': VSAObjects.VMName
    }]
Example 2:
     [{
    'allOrAnyChildren': False,
    'content': [{
        'equal_value': True,
        'allOrAnyChildren': True,
        'display_name': 'sample1',
        'type': VSAObjects.VMName
    }, {
        'equal_value': True,
        'allOrAnyChildren': True,
        'display_name': 'sample2',
        'type': VSAObjects.VMName
    }
    ]
    }, {
    'allOrAnyChildren': True,
    'content': [{
        'equal_value': True,
        'allOrAnyChildren': True,
        'display_name': 'sample3',
        'type': VSAObjects.RESOURCE_POOL
    }, {
        'equal_value': True,
        'allOrAnyChildren': True,
        'id': 'sample4',
        'display_name': 'sample4',
        'type': VSAObjects.SERVER
        }
        ]
    }
    ]
    **Note** Use VSAObjects Enum present in constants.py to pass value to type

kwargs (dict) – dict of keyword arguments as follows

plan_name           (str)   --  Plan to be associated with the subclient

storage_policy      (str)   --  Storage policy to be associated with the subclient

description         (str)   --  Description for the subclient

    default: ''

Returns

object - instance of the Subclient class

Raises

SDKException: if subclient name argument is not of type string

if storage policy argument is not of type string

if description argument is not of type string

if failed to create subclient

if response is empty

if response is not success

if subclient already exists with the given name
Expand source code Browse git
def add_virtual_server_subclient(
        self,
        subclient_name,
        subclient_content,
        **kwargs
):
    """Adds a new virtual server subclient to the backupset.

        Args:
            subclient_name      (str)   --  Name of the subclient to be created

            subclient_content   (list)  --  Content to be added to the subclient

                Example 1:
                    [{
                        'equal_value': True,
                        'allOrAnyChildren': True,
                        'id': '',
                        'path': '',
                        'display_name': 'sample1',
                        'type': VSAObjects.VMName
                    }]
                Example 2:
                     [{
                    'allOrAnyChildren': False,
                    'content': [{
                        'equal_value': True,
                        'allOrAnyChildren': True,
                        'display_name': 'sample1',
                        'type': VSAObjects.VMName
                    }, {
                        'equal_value': True,
                        'allOrAnyChildren': True,
                        'display_name': 'sample2',
                        'type': VSAObjects.VMName
                    }
                    ]
                    }, {
                    'allOrAnyChildren': True,
                    'content': [{
                        'equal_value': True,
                        'allOrAnyChildren': True,
                        'display_name': 'sample3',
                        'type': VSAObjects.RESOURCE_POOL
                    }, {
                        'equal_value': True,
                        'allOrAnyChildren': True,
                        'id': 'sample4',
                        'display_name': 'sample4',
                        'type': VSAObjects.SERVER
                        }
                        ]
                    }
                    ]
                    **Note** Use VSAObjects Enum present in constants.py to pass value to type

            kwargs      (dict)  -- dict of keyword arguments as follows

                plan_name           (str)   --  Plan to be associated with the subclient

                storage_policy      (str)   --  Storage policy to be associated with the subclient

                description         (str)   --  Description for the subclient

                    default: ''

        Returns:
            object  -   instance of the Subclient class

        Raises:
            SDKException:
                if subclient name argument is not of type string

                if storage policy argument is not of type string

                if description argument is not of type string

                if failed to create subclient

                if response is empty

                if response is not success

                if subclient already exists with the given name

    """
    if not (isinstance(subclient_name, str) and
            isinstance(subclient_content, list)):
        raise SDKException('Subclient', '101')

    if self.has_subclient(subclient_name):
        raise SDKException(
            'Subclient', '102', 'Subclient "{0}" already exists.'.format(
                subclient_name)
        )

    if self._backupset_object is None:
        if self._instance_object.backupsets.has_backupset(
                'defaultBackupSet'):
            self._backupset_object = self._instance_object.backupsets.get(
                'defaultBackupSet')
        else:
            self._backupset_object = self._instance_object.backupsets.get(
                sorted(self._instance_object.backupsets.all_backupsets)[0]
            )

    content = []

    def set_content(item_content):
        """
        create content dictionary
        Args:
            item_content            (dict):     Dict of content details

            Example:
                {
                    'equal_value': True,
                    'allOrAnyChildren': True,
                    'display_name': 'sample1',
                    'type':  < VSAObjects.VMName: 10 >
                }

        Returns:

        """
        return {
            "equalsOrNotEquals": item_content.get('equal_value', True),
            "name": item_content.get('id', ''),
            "displayName": item_content.get('display_name', ''),
            "path": item_content.get('path', ''),
            "allOrAnyChildren": item.get('allOrAnyChildren', True),
            "type": item_content['type'] if isinstance(item_content['type'], int) else item_content['type'].value
        }

    for item in subclient_content:
        _temp_list = []
        _temp_dict = {}
        allOrAnyChildren = item.get('allOrAnyChildren', None)
        if 'content' in item:
            nested_content = item['content']
            for each_condition in nested_content:
                temp_dict = set_content(each_condition)
                _temp_list.append(temp_dict)
            _temp_dict['allOrAnyChildren'] = allOrAnyChildren
            _temp_dict['children'] = _temp_list
            content.append(_temp_dict)
        else:
            temp_dict = set_content(item)
            content.append(temp_dict)

    request_json = {
        "subClientProperties": {
            "vmContentOperationType": 2,
            "vmContent": {
                "children": content
            },
            "subClientEntity": {
                "clientName": self._client_object.client_name,
                "appName": self._agent_object.agent_name,
                "instanceName": self._instance_object.instance_name,
                "backupsetName": self._backupset_object.backupset_name,
                "subclientName": subclient_name
            },
            "commonProperties": {
                "description": kwargs.get('description'),
                "enableBackup": True
            }
        }
    }

    if kwargs.get("customSnapshotResourceGroup"):
        request_json["subClientProperties"]["vsaSubclientProp"] = \
            {"customSnapshotResourceGroup": kwargs.get("customSnapshotResourceGroup")}

    if kwargs.get('plan_name'):
        if not self._commcell_object.plans.has_plan(kwargs['plan_name']):
            raise SDKException(
                'Subclient',
                '102',
                'Plan: "{0}" does not exist in the Commcell'.format(kwargs['plan_name'])
            )
        request_json['subClientProperties']['planEntity'] = {
            "planName": kwargs['plan_name']
        }

    elif kwargs.get('storage_policy'):
        if not self._commcell_object.storage_policies.has_policy(kwargs.get('storage_policy')):
            raise SDKException(
                'Subclient',
                '102',
                'Storage Policy: "{0}" does not exist in the Commcell'.format(kwargs.get('storage_policy'))
            )
        request_json['subClientProperties']['commonProperties']['storageDevice'] = {
            "dataBackupStoragePolicy": {
                "storagePolicyName": kwargs.get('storage_policy')
            }
        }
    else:
        raise SDKException('Subclient', '102', 'Either Plan or Storage policy should be given as input')

    return self._process_add_request(request_json)
def delete(self, subclient_name)

Deletes the subclient specified by the subclient_name from the backupset.

Args

subclient_name (str) – name of the subclient to remove from the backupset

Raises

SDKException: if type of the subclient name argument is not string

if failed to delete subclient

if response is empty

if response is not success

if no subclient exists with the given name
Expand source code Browse git
def delete(self, subclient_name):
    """Deletes the subclient specified by the subclient_name from the backupset.

        Args:
            subclient_name (str)  --  name of the subclient to remove from the backupset

        Raises:
            SDKException:
                if type of the subclient name argument is not string

                if failed to delete subclient

                if response is empty

                if response is not success

                if no subclient exists with the given name
    """
    if not isinstance(subclient_name, str):
        raise SDKException('Subclient', '101')
    else:
        subclient_name = subclient_name.lower()

    if self.has_subclient(subclient_name):
        delete_subclient_service = self._services['SUBCLIENT'] % (
            self._subclients[subclient_name]['id']
        )

        flag, response = self._cvpysdk_object.make_request(
            'DELETE', delete_subclient_service)

        if flag:
            if response.json():
                if 'response' in response.json():
                    response_value = response.json()['response'][0]
                    error_code = str(response_value['errorCode'])
                    error_message = None

                    if 'errorString' in response_value:
                        error_message = response_value['errorString']

                    if error_message:
                        o_str = 'Failed to delete subclient\nError: "{0}"'
                        raise SDKException(
                            'Subclient', '102', o_str.format(error_message))
                    else:
                        if error_code == '0':
                            # initialize the subclients again
                            # so the subclient object has all the
                            # subclients
                            self.refresh()
                        else:
                            o_str = ('Failed to delete subclient with Error Code: "{0}"\n'
                                     'Please check the documentation for '
                                     'more details on the error')
                            raise SDKException(
                                'Subclient', '102', o_str.format(error_code))
            else:
                raise SDKException('Response', '102')
        else:
            raise SDKException(
                'Response', '101', self._update_response_(
                    response.text))
    else:
        raise SDKException(
            'Subclient', '102', 'No subclient exists with name: {0}'.format(
                subclient_name)
        )
def get(self, subclient_name)

Returns a subclient object of the specified backupset name.

Args

subclient_name (str) – name of the subclient

Returns

object - instance of the Subclient class for the given subclient name

Raises

SDKException: if type of the subclient name argument is not string

if no subclient exists with the given name
Expand source code Browse git
def get(self, subclient_name):
    """Returns a subclient object of the specified backupset name.

        Args:
            subclient_name (str)  --  name of the subclient

        Returns:
            object - instance of the Subclient class for the given subclient name

        Raises:
            SDKException:
                if type of the subclient name argument is not string

                if no subclient exists with the given name
    """
    if not isinstance(subclient_name, str):
        raise SDKException('Subclient', '101')
    else:
        subclient_name = subclient_name.lower()

        if self.has_subclient(subclient_name):

            if self._backupset_object is None:
                self._backupset_object = self._instance_object.backupsets.get(
                    self._subclients[subclient_name]['backupset']
                )
            return Subclient(
                self._backupset_object, subclient_name, self._subclients[subclient_name]['id']
            )

        raise SDKException(
            'Subclient', '102', 'No subclient exists with name: {0}'.format(
                subclient_name)
        )
def has_subclient(self, subclient_name)

Checks if a subclient exists in the commcell with the input subclient name.

Args

subclient_name (str) – name of the subclient

Returns

bool - boolean output whether the subclient exists in the backupset or not

Raises

SDKException: if type of the subclient name argument is not string

Expand source code Browse git
def has_subclient(self, subclient_name):
    """Checks if a subclient exists in the commcell with the input subclient name.

        Args:
            subclient_name (str)  --  name of the subclient

        Returns:
            bool - boolean output whether the subclient exists in the backupset or not

        Raises:
            SDKException:
                if type of the subclient name argument is not string
    """
    if not isinstance(subclient_name, str):
        raise SDKException('Subclient', '101')

    return self._subclients and subclient_name.lower() in self._subclients
def refresh(self)

Refresh the subclients associated with the Backupset / Instance.

Expand source code Browse git
def refresh(self):
    """Refresh the subclients associated with the Backupset / Instance."""
    self._subclients = self._get_subclients()