def create_mssql_recovery_request(baseurl, token, mssql_recovery_input, rp_id, asset_id, mssql_sysadm_user, mssql_sysadm_domain, mssql_sysadm_pwd, mssql_alt_db_name, mssql_alt_db_path, mssql_instance_name, mssql_server_name, recover_from_copy): """ This function is for mssql recovery request""" print(f"create mssql recovery request:[{mssql_recovery_input}]") headers.update({'Authorization': token}) url = f"{baseurl}/recovery/workloads/mssql/scenarios/database-complete-recovery/recover" cur_dir = os.path.dirname(os.path.abspath(__file__)) cur_dir = cur_dir + os.sep + "sample-payloads" + os.sep file_name = os.path.join(cur_dir, mssql_recovery_input) with open(file_name, 'r') as file_handle: data = json.load(file_handle) data['data']['attributes']['recoveryPoint'] = rp_id data['data']['attributes']['recoveryObject']['assetId'] = asset_id data['data']['attributes']['recoveryObject']['credentials'][ 'domain'] = mssql_sysadm_domain data['data']['attributes']['recoveryObject']['credentials'][ 'userName'] = mssql_sysadm_user data['data']['attributes']['recoveryObject']['credentials'][ 'password'] = mssql_sysadm_pwd data['data']['attributes']['alternateRecoveryOptions'][ 'databaseName'] = mssql_alt_db_name data['data']['attributes']['alternateRecoveryOptions'][ 'instanceName'] = mssql_instance_name data['data']['attributes']['alternateRecoveryOptions'][ 'client'] = mssql_server_name data['data']['attributes']['alternateRecoveryOptions'][ 'alternateFileLocation'][ 'renameAllFilesToSameLocation'] = mssql_alt_db_path if (recover_from_copy): info = common.get_recovery_point_copy_info(baseurl, token, 'mssql', rp_id) if (len(info) > 0): data['data']['attributes']['recoveryOptions'][ 'mssqlRecoveryCopyInfo'] = {} data['data']['attributes']['recoveryOptions'][ 'mssqlRecoveryCopyInfo']['fullRecoveryCollection'] = [{}] for copy in info[0]['copies']: if (copy['copyNumber'] == recover_from_copy): copy['storage'].pop('sType', None) data['data']['attributes']['recoveryOptions'][ 'mssqlRecoveryCopyInfo']['fullRecoveryCollection'][0][ 'backupId'] = info[0]['backupId'] data['data']['attributes']['recoveryOptions'][ 'mssqlRecoveryCopyInfo']['fullRecoveryCollection'][0][ 'storage'] = copy['storage'] status_code, response_text = common.rest_request('POST', url, headers, data=data) common.validate_response(status_code, 201, response_text) recovery_job_id = response_text['data']['id'] print( f"MSSQL Recovery Request started successfully jobid :[{recovery_job_id}]" ) return recovery_job_id
def create_instant_access_vm(baseurl, token, workload_type, backup_id, vcenter_name, exsi_host, resource_pool, client_restore_name): """ This function create the instant access VM """ print(f"Instant restore is initiated:[{client_restore_name}]") headers.update({'Authorization': token}) payload = { "data": { "type": "instantAccessVmV3", "attributes": { "backupId": backup_id, "copyNumber": 1, "vCenter": vcenter_name, "esxiHost": exsi_host, "resourcePoolOrVapp": resource_pool, "vmName": client_restore_name, "powerOn": "True", "removeEthCards": "False", "retention": { "value": 30, "unit": "DAYS" }, }, } } url = f"{baseurl}recovery/workloads/{workload_type}/instant-access-vms" status_code, response_text = common.rest_request('POST', url, headers, data=payload) common.validate_response(status_code, 201, response_text) mount_id = response_text['data']['id'] return mount_id
def add_mssql_credential(baseurl, token, mssql_use_localcreds, mssql_domain, mssql_username, mssql_password): """ This function add the MSSQL into NBU master server """ print(f"Add MSSQL credential") x = uuid.uuid1() headers.update({'Authorization': token}) url = f"{baseurl}/config/credentials" cur_dir = os.path.dirname(os.path.abspath(__file__)) cur_dir = cur_dir + os.sep + "sample-payloads" + os.sep file_name = os.path.join(cur_dir, 'post_mssql_credential.json') with open(file_name, 'r') as file_handle: data = json.load(file_handle) data['data']['id'] = str(x) data['data']['type'] = "credentialRequest" data['data']['attributes']['name'] = "pyname" data['data']['attributes']['tag'] = "pytag" if mssql_use_localcreds: data['data']['attributes']['contents']['useLocalCredentials'] = True else: data['data']['attributes']['contents']['domain'] = mssql_domain data['data']['attributes']['contents']['username'] = mssql_username data['data']['attributes']['contents']['password'] = mssql_password data['data']['attributes']['description'] = "pydesc" status_code, response_text = common.rest_request('POST', url, headers, data=data) common.validate_response(status_code, 201, response_text) print(f"MSSQL credentials added successfully") return response_text['data']['id'], response_text['data']['attributes'][ 'name']
def create_netbackup_policy(base_url, token, policy_name, client, storage_unit_name, copy_storage_unit_name): print(f"Create policy:[{policy_name}]") headers.update({'Authorization': token}) url = base_url + "/config/policies/" cur_dir = os.path.dirname(os.path.abspath(__file__)) cur_dir = cur_dir + os.sep + "sample-payloads" + os.sep file_name = os.path.join(cur_dir, "create_mssql_policy_template.json") with open(file_name, 'r') as file_handle: data = json.load(file_handle) data['data']['attributes']['policy']['policyName'] = policy_name data['data']['id'] = policy_name data['data']['attributes']['policy']['clients'][0]['hostName'] = client data['data']['attributes']['policy']['policyAttributes'][ 'storage'] = storage_unit_name data['data']['attributes']['policy']['schedules'][0]['backupCopies'][ 'copies'][0]['storage'] = storage_unit_name data['data']['attributes']['policy']['schedules'][0]['backupCopies'][ 'copies'][1]['storage'] = copy_storage_unit_name status_code, response_text = common.rest_request('POST', url, headers, data=data) common.validate_response(status_code, 204, response_text) print(f"Policy created successfully:[{policy_name}]")
def create_and_register_mssql_instance(baseurl, token, instance_name, server_name, credential_name): """ This function is for mssql create instance and register request""" print(f"create mssql instance and register request:[{instance_name}]") headers.update({'Authorization': token}) url = f"{baseurl}asset-service/queries" cur_dir = os.path.dirname(os.path.abspath(__file__)) cur_dir = cur_dir + os.sep + "sample-payloads" + os.sep file_name = os.path.join(cur_dir, 'post_mssql_create_instance.json') with open(file_name, 'r') as file_handle: data = json.load(file_handle) data['data']['attributes']['parameters']['objectList'][0]['asset'][ 'commonAssetAttributes']['displayName'] = instance_name data['data']['attributes']['parameters']['objectList'][0]['asset'][ 'commonAssetAttributes']['credentials'][0][ 'credentialName'] = credential_name data['data']['attributes']['parameters']['objectList'][0]['asset'][ 'clientName'] = server_name status_code, response_text = common.rest_request('POST', url, headers, data=data) common.validate_response(status_code, 201, response_text) get_query_url = response_text['data']['links']['self']['href'] url = f"{baseurl}{get_query_url}" status_code, response_text = common.rest_request('GET', url, headers)
def get_instantaccess_vmstate(baseurl, token, workload_type, mount_id): """ This function return state of instant access VM """ headers.update({'Authorization': token}) url = f"{baseurl}recovery/workloads/{workload_type}/instant-access-vms/{mount_id}" status_code, response_text = common.rest_request('GET', url, headers) common.validate_response(status_code, 200, response_text) status = response_text['data']['attributes']['status'] return status
def remove_mssql_credential(baseurl, token, credential_id): """ This function is for mssql remove credential request""" print(f"remove mssql credential:[{credential_id}]") headers.update({'Authorization': token}) url = f"{baseurl}/config/credentials/{credential_id}" status_code, response_text = common.rest_request('DELETE', url, headers) common.validate_response(status_code, 204, response_text)
def remove_instantaccess_vm(baseurl, token, mount_id): """ This function remove the instant access VM""" if mount_id: headers.update({'Authorization': token}) url = f"{baseurl}recovery/workloads/vmware/instant-access-vms/{mount_id}" status_code, response_text = common.rest_request( 'DELETE', url, headers) common.validate_response(status_code, 204, response_text) print(f"Successfully removed instant access vm:[{mount_id}]")
def get_resource_pool(baseurl, token, workload_type, vcenter_name, exsi_host): """ This function return the resource pool info of vcenter and exsi host """ headers.update({'Authorization': token}) url = f"{baseurl}/config/workloads/{workload_type}/vcenters/"\ f"{vcenter_name}/esxiservers/{exsi_host}/resource-pools" status_code, response_text = common.rest_request('GET', url, headers) common.validate_response(status_code, 200, response_text) resource_pool = response_text['data']['attributes']['resourcePools'][0][ 'path'] return resource_pool
def get_recovery_points(baseurl, token, workload_type, asset_id): """ This function return the recovery point of given asset """ print(f"Get the recovery points for asset:[{asset_id}]") headers.update({'Authorization': token}) url = f"{baseurl}recovery-point-service/workloads/{workload_type}/"\ f"recovery-points?filter=assetId eq '{asset_id}'" status_code, response_text = common.rest_request('GET', url, headers) common.validate_response(status_code, 200, response_text) backup_id = response_text['data'][0]['id'] return backup_id
def create_mssql_protection_plan(baseurl, token, protection_plan_name, storage_unit_name, workload_type): """ This function will create the version 3 protection plan """ print(f"Create protection plan:[{protection_plan_name}]") headers.update({'Authorization': token}) url = f"{baseurl}servicecatalog/slos?meta=accessControlId" cur_dir = os.path.dirname(os.path.abspath(__file__)) cur_dir = cur_dir + os.sep + "sample-payloads" + os.sep file_name = os.path.join(cur_dir, "create_mssql_protection_plan_template.json") with open(file_name, 'r') as file_handle: data = json.load(file_handle) data['data']['attributes'][ 'description'] = "Protection Plan for MSSQL Workload" data['data']['attributes']['name'] = protection_plan_name data['data']['attributes']['policyNamePrefix'] = protection_plan_name data['data']['attributes']['workloadType'] = workload_type data['data']['attributes']['schedules'][0][ 'backupStorageUnit'] = storage_unit_name #adjust the dayOfWeek for the current day, and startSeconds and duration in seconds for an hour dow = datetime.datetime.today().isoweekday() now = datetime.datetime.now() midnight = now.replace(hour=0, minute=0, second=0, microsecond=0) seconds = (now - midnight).seconds if (dow == 7): dow = 0 else: dow = dow + 1 data['data']['attributes']['schedules'][0]['backupWindows'][0][ 'dayOfWeek'] = dow seconds = seconds - 600 data['data']['attributes']['schedules'][0]['backupWindows'][0][ 'startSeconds'] = seconds seconds = 3600 data['data']['attributes']['schedules'][0]['backupWindows'][0][ 'durationSeconds'] = seconds #FULL schedule type with 4 week retention and a frequency of everyday data['data']['attributes']['schedules'][0]['scheduleType'] = "FULL" data['data']['attributes']['schedules'][0]['frequencySeconds'] = 86400 data['data']['attributes']['schedules'][0]['retention']['value'] = 4 data['data']['attributes']['schedules'][0]['retention']['unit'] = "WEEKS" status_code, response_text = common.rest_request('POST', url, headers, data=data) common.validate_response(status_code, 201, response_text) protection_plan_id = response_text['data']['id'] print(f"Protection plan created successfully:[{protection_plan_id}]") return protection_plan_id
def perform_vm_backup(baseurl, token, protection_plan_id, asset_id, is_vm_group=0): """ This function will trigger the backup of given asset using protection plan""" headers.update({'Authorization': token}) url = f"{baseurl}servicecatalog/slos/{protection_plan_id}/backup-now" selection_type = "ASSETGROUP" if is_vm_group else "ASSET" payload = {"data": {"type": "backupNowRequest", "attributes": {"selectionType": selection_type, "selectionId": asset_id}}} status_code, response_text = common.rest_request('POST', url, headers, data=payload) common.validate_response(status_code, 202, response_text) backup_job_id = response_text['data'][0]['id'] print(f"Started backup for asset:[{asset_id}] and backup id is:[{backup_job_id}]") return backup_job_id
def create_mssql_recovery_request(baseurl, token, mssql_recovery_input, rp_id, asset_id, mssql_sysadm_user, mssql_sysadm_domain, mssql_sysadm_pwd, mssql_alt_db_name, mssql_alt_db_path, mssql_instance_name, mssql_server_name): """ This function is for mssql recovery request""" print(f"create mssql recovery request:[{mssql_recovery_input}]") headers.update({'Authorization': token}) url = f"{baseurl}/recovery/workloads/mssql/scenarios/database-complete-recovery/recover" cur_dir = os.path.dirname(os.path.abspath(__file__)) cur_dir = cur_dir + os.sep + "sample-payloads" + os.sep file_name = os.path.join(cur_dir, mssql_recovery_input) with open(file_name, 'r') as file_handle: data = json.load(file_handle) data['data']['attributes']['recoveryPoint'] = rp_id data['data']['attributes']['recoveryObject']['assetId'] = asset_id data['data']['attributes']['recoveryObject']['credentials'][ 'domain'] = mssql_sysadm_domain data['data']['attributes']['recoveryObject']['credentials'][ 'userName'] = mssql_sysadm_user data['data']['attributes']['recoveryObject']['credentials'][ 'password'] = mssql_sysadm_pwd data['data']['attributes']['alternateRecoveryOptions'][ 'databaseName'] = mssql_alt_db_name data['data']['attributes']['alternateRecoveryOptions'][ 'instanceName'] = mssql_instance_name data['data']['attributes']['alternateRecoveryOptions'][ 'client'] = mssql_server_name data['data']['attributes']['alternateRecoveryOptions'][ 'alternateFileLocation'][ 'renameAllFilesToSameLocation'] = mssql_alt_db_path status_code, response_text = common.rest_request('POST', url, headers, data=data) common.validate_response(status_code, 201, response_text) recovery_job_id = response_text['data']['id'] print( f"MSSQL Recovery Request started successfully jobid :[{recovery_job_id}]" ) return recovery_job_id
def mssql_instance_deepdiscovery(baseurl, token, mssql_instance_id): """ This function will invoke deep discovery for databases on the instance""" headers.update({'Authorization': token}) url = f"{baseurl}/asset-service/queries" cur_dir = os.path.dirname(os.path.abspath(__file__)) cur_dir = cur_dir + os.sep + "sample-payloads" + os.sep file_name = os.path.join(cur_dir, 'post_mssql_instance_deepdiscovery.json') with open(file_name, 'r') as file_handle: data = json.load(file_handle) data['data']['attributes']['parameters']['objectList'][0][ 'instanceId'] = mssql_instance_id print(f"MSSQL deep discovery for databases ") status_code, response_text = common.rest_request('POST', url, headers, data=data) common.validate_response(status_code, 201, response_text) print(f"MSSQL deep discovery for databases started successfully")
def validate_mssql_credential(baseurl, token, instance_id): """ This function validates MSSQL credential assigned to the INSTANCE asset """ print(f"Validate MSSQL credential") headers.update({'Authorization': token}) url = f"{baseurl}/asset-service/queries" cur_dir = os.path.dirname(os.path.abspath(__file__)) cur_dir = cur_dir + os.sep + "sample-payloads" + os.sep file_name = os.path.join(cur_dir, 'validate_mssql_instance_credentials.json') with open(file_name, 'r') as file_handle: data = json.load(file_handle) data['data']['attributes']['parameters']['objectList'][0][ 'assetId'] = instance_id status_code, response_text = common.rest_request('POST', url, headers, data=data) common.validate_response(status_code, 201, response_text) print(f"MSSQL credentials validated successfully")
def get_backup_job_id(baseurl, token, backup_job_id, protection_plan_name): """ This function will return the catalog and protection plan backup id""" print("Find the protection plan and catalog backup job IDs") protection_backup_job_id = '' catalog_backup_job_id = '' headers.update({'Authorization': token}) url = f"{baseurl}admin/jobs/?filter=jobId gt {str(backup_job_id)}" status_code, response_text = common.rest_request('GET', url, headers) common.validate_response(status_code, 200, response_text) for jobdata in response_text['data']: job_id = str(jobdata['id']).strip() parent_job_id = str(jobdata['attributes']['parentJobId']).strip() if jobdata['attributes']['policyName'].startswith(protection_plan_name) and job_id == parent_job_id: protection_backup_job_id = parent_job_id print(f"Protection backup Job Id:[{protection_backup_job_id}]") if jobdata['attributes']['policyName'].startswith('NBU_Catalog_Default') \ and job_id == parent_job_id: catalog_backup_job_id = parent_job_id print(f"NBU Catalog backup Job Id:[{catalog_backup_job_id}]") return protection_backup_job_id, catalog_backup_job_id
def get_mssql_asset_info(baseurl, token, asset_type, host_name, display_name, instance_name='MSSQLSERVER'): """ This function return the asset info """ print(f"Get client asset info for MSSQL type :[asset_type]") headers.update({'Authorization': token}) if (asset_type == "database"): url = f"{baseurl}asset-service/workloads/mssql/assets?"\ f"filter=assetType eq '{asset_type}' and displayName eq '{display_name}' and clientName eq '{host_name}' and instanceName eq '{instance_name}'" else: url = f"{baseurl}asset-service/workloads/mssql/assets?"\ f"filter=assetType eq '{asset_type}' and displayName eq '{display_name}' and clientName eq '{host_name}'" status_code, response_text = common.rest_request('GET', url, headers) common.validate_response(status_code, 200, response_text) asset_id = response_text['data'][0]['id'] print(f"Client asset Id:[{asset_id}]") return asset_id
def update_protection_plan_mssql_attr(baseurl, token, protection_plan_name, protection_plan_id, skip_offline_db=0): """ Update SLO with mssql attributes """ headers.update({'Authorization': token}) url = f"{baseurl}servicecatalog/slos/{protection_plan_id}" status_code, response_text = common.rest_request('GET', url, headers) common.validate_response(status_code, 200, response_text) response_text['data']['attributes']['policyDefinition']['policy'][ 'policyAttributes']['databaseOptions'][ 'skipUnavailableDatabases'] = True response_text['data']['attributes']['policyDefinition']['policy'][ 'policyAttributes']['databaseOptions']['parallelBackupOps'] = 10 response_text['data']['attributes']['policyDefinition']['policy'][ 'schedules'][0]['storageIsSLP'] = False payload = response_text status_code, response_text2 = common.rest_request('PUT', url, headers, data=payload) common.validate_response(status_code, 204, response_text2)
def perform_bulk_restore(baseurl, token, bulk_backup_job_id, workload_type, vcenter_name, client_restore_vm_prefix): """ This function perform the bulk restore """ headers.update({'Authorization': token}) jobid_list = [] mount_id_list = [] job_mount_dict = {} error_msg = '' is_error = False url = f"{baseurl}admin/jobs/?filter=parentJobId eq {str(bulk_backup_job_id)} and "\ f"jobId ne {str(bulk_backup_job_id)} and jobType eq 'SNAPSHOT' and "\ f"state eq 'DONE' and (status eq 0 or status eq 1)" status_code, response_text = common.rest_request('GET', url, headers) common.validate_response(status_code, 200, response_text) for data in response_text["data"]: jobid_list.append(data["id"]) print(f"Snapshot jobid list:[{(','.join(jobid_list))}]") for jobid in jobid_list: mount_id = '' url = f"{baseurl}admin/jobs/?filter=parentJobId eq {str(jobid)} and state eq 'DONE'" status_code, response_text = common.rest_request('GET', url, headers) common.validate_response(status_code, 200, response_text) backup_id = response_text['data'][0]['attributes']['backupId'] asset_id = response_text['data'][0]['attributes']['assetID'] asset_name = response_text['data'][0]['attributes'][ 'assetDisplayableName'] print(f"Backup id for job:[{jobid}] is:[{backup_id}]") print(f"asset id for job:[{jobid}] is:[{asset_id}]") print(f"asset display name for job:[{jobid}] is:[{asset_name}]") # Get asset info asset_id, _, exsi_host = common.get_asset_info(baseurl, token, workload_type, asset_name) resource_pool = get_resource_pool(baseurl, token, workload_type, vcenter_name, exsi_host) print(f"Resource pool:[{resource_pool}]") restore_vmname = f"{client_restore_vm_prefix}_{jobid}" print(f"Restore vm name for jobid:[{jobid}] is:[{restore_vmname}]") try: mount_id = create_instant_access_vm(baseurl, token, workload_type,\ backup_id, vcenter_name, exsi_host, resource_pool, restore_vmname) if mount_id: mount_id_list.append(mount_id) else: error_msg = f"{error_msg} Unable to create the the instant VM for jobid:[{jobid}]" is_error = True except Exception as exc: error_msg = f"{error_msg} Instant VM creation Exception for jobid:[{jobid}] is: {exc}" is_error = True for jobid, mount_id in job_mount_dict.items(): try: verify_instant_access_vmstate(baseurl, token, workload_type, backup_id, mount_id) except Exception as exc: error_msg = f"{error_msg} Instant VM verification Exception for jobid:[{jobid}] is:{exc}" is_error = True mount_id_list_str = ",".join(mount_id_list) print(f"Mount id list:[{mount_id_list_str}]") if is_error: raise Exception(error_msg) return mount_id_list_str