Skip to content

Commit fe61c7c

Browse files
ed f string format and added documentation
1 parent a8c7c77 commit fe61c7c

File tree

7 files changed

+342
-299
lines changed

7 files changed

+342
-299
lines changed

recipes/python/backup-restore/README.md

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3,13 +3,15 @@
33
## Executing the scripts:
44

55
Pre-requisites:
6-
- NetBackup 8.2.1(Build 209 or higher) or higher
7-
- Python 3.5 or higher
6+
- NetBackup 8.2.1 or higher
7+
- Python 3.6 or higher
88
- Python modules: `requests`
99

1010
Use the following commands to run the scripts.
1111
### - Single VM backup and restore
1212

13+
This single_vm_backup_restore.py script describes how to backup VM using protection plan and restore the single VMware virtual machine that were backuped with NetBackup for VMware.
14+
1315
`python single_vm_backup_restore.py --master_server <master_server> --master_username <master_username> --master_password <master_password> --vcenter_name <vcenter_name> --vcenter_username <vcenter_username> --vcenter_password <vcenter_password> --protection_plan_name <protection_plan_name> --clientvm <client_vm_name> --restore_vmname <restore_vm_name>`
1416

1517
All parameters can also be passed as command line arguments.
@@ -29,7 +31,7 @@ usage: single_vm_backup_restore.py [-h] [--master_server MASTER_SERVER]
2931
3032
Single VM backup and restore scenario
3133
32-
optional arguments:
34+
Arguments:
3335
-h, --help show this help message and exit
3436
--master_server MASTER_SERVER
3537
NetBackup master server name
@@ -67,6 +69,8 @@ Execution flow of single VM backup and restore script:
6769

6870
### - Group VM backup and restore
6971

72+
This group_vm_backup_restore.py script describes how to backup multiple VMs using protection plan and perform bulk restore of VMware virtual machines that were backuped with NetBackup for VMware.
73+
7074
`python group_vm_backup_restore.py --master_server <master_server> --master_username <master_username> --master_password <master_password> --vcenter_name <vcenter_name> --vcenter_username <vcenter_username> --vcenter_password <vcenter_password> --protection_plan_name <protection_plan_name> --querystring <Query_string> --vip_group_name <group_name> --restore_vmname_prefix <restore_vmname_prefix>`
7175

7276
All parameters can also be passed as command line arguments.
@@ -87,7 +91,7 @@ usage: group_vm_backup_restore.py [-h] [--master_server MASTER_SERVER]
8791
8892
Group VM backup and restore scenario
8993
90-
optional arguments:
94+
Arguments:
9195
-h, --help show this help message and exit
9296
--master_server MASTER_SERVER
9397
NetBackup master server
Lines changed: 71 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
1-
## The library contain common functions which are used for both single and group VM backup and restore.
1+
""" The library contain common functions which are used for both single and group VM backup and restore. """
22

3-
## The script can be run with Python 3.5 or higher version.
3+
## The script can be run with Python 3.6 or higher version.
44

5-
## The script requires 'requests' library to make the API calls. The library can be installed using the command: pip install requests.
5+
## The script requires 'requests' library to make the API calls.
66

77
import json
88
import os
@@ -12,97 +12,110 @@
1212

1313
headers = {"Content-Type" : "application/vnd.netbackup+json;version=4.0"}
1414

15-
# Get the base netbackup url
15+
# Get the base NetBackup url
1616
def get_nbu_base_url(host, port):
17+
""" This function return NetBackup base url """
1718
port = f":{str(port)}" if port else ''
1819
baseurl = f"https://{host}{port}/netbackup/"
1920
return baseurl
2021

21-
# Login to the netbackup and get the authorization token
22+
# Login to the NetBackup and get the authorization token
2223
def get_authenticate_token(baseurl, username, password):
24+
""" This function return token of NB master server """
2325
creds = {'userName':username, 'password':password}
24-
url = baseurl + 'login'
26+
url = f"{baseurl}login"
2527
status_code, response_text = rest_request('POST', url, headers, data=creds)
2628
validate_response(status_code, 201, response_text)
2729
token = response_text['token']
2830
return token
2931

3032
# Add vCenter credential
3133
def add_vcenter_credential(baseurl, token, vcenter_server, vcenter_username, vcenter_password, vcenter_port, vcenter_server_type):
32-
print(f"Add the vcenter credential:[{vcenter_server}]")
33-
headers.update({'Authorization': token})
34-
url = baseurl + 'config/servers/vmservers'
35-
payload = {'serverName':vcenter_server, 'vmType':vcenter_server_type, 'userId':vcenter_username, 'password':vcenter_password, 'port':vcenter_port}
34+
""" This function add the vCenter into NBU master server """
35+
print(f"Add the vCenter credential:[{vcenter_server}]")
36+
headers.update({'Authorization': token})
37+
url = f"{baseurl}config/servers/vmservers"
38+
payload = {'serverName':vcenter_server, 'vmType':vcenter_server_type, 'userId':vcenter_username,\
39+
'password':vcenter_password, 'port':vcenter_port}
3640
status_code, response_text = rest_request('POST', url, headers, data=payload)
3741
validate_response(status_code, 201, response_text)
38-
print(f"Vcenter credentials added successfully:[{vcenter_server}]")
42+
print(f"vCenter credentials added successfully:[{vcenter_server}]")
3943

4044
# Get Vmware server discovery status
4145
def get_vmware_discovery_status(baseurl, token, workload_type, vcenter_server):
46+
""" This function return the discovery status of vCenter server """
4247
headers.update({'Authorization': token})
43-
url = baseurl + "admin/discovery/workloads/" + workload_type + "/status?filter=serverName eq '" + vcenter_server + "'"
48+
url = f"{baseurl}admin/discovery/workloads/{workload_type}/status?"\
49+
f"filter=serverName eq '{vcenter_server}'"
4450
status_code, response_text = rest_request('GET', url, headers)
4551
validate_response(status_code, 200, response_text)
4652
discovery_status = response_text['data'][0]['attributes']['discoveryStatus']
4753
return discovery_status
4854

49-
# Get Vmware server discovery status
55+
# Verify Vmware server discovery status
5056
def verify_vmware_discovery_status(baseurl, token, workload_type, vcenter_server, timeout=600):
51-
print(f"Wait for Vcenter Discovery :[{vcenter_server}]")
57+
""" This function verify the 'SUCESS' discovery status of vCenter """
58+
print(f"Wait for vCenter Discovery :[{vcenter_server}]")
5259
discovery_status = ''
5360
end_time = time.time() + timeout
5461
while time.time() < end_time:
5562
time.sleep(30)
5663
discovery_status = get_vmware_discovery_status(baseurl, token, workload_type, vcenter_server)
5764
if discovery_status == 'SUCCESS':
58-
print(f"Vcenter added successfully:[{vcenter_server}]")
65+
print(f"vCenter added successfully:[{vcenter_server}]")
5966
break
6067
else:
61-
print(f"Failed to verify VCenter:[{vcenter_server}] discovery with status:[{discovery_status}]")
68+
print(f"Failed to verify vCenter:[{vcenter_server}] discovery with status:[{discovery_status}]")
6269
sys.exit(1)
63-
print(f"Vcenter discovery successful:[{vcenter_server}] with status:[{discovery_status}]")
70+
print(f"vCenter discovery successful:[{vcenter_server}] with status:[{discovery_status}]")
6471

6572
# Get asset info
6673
def get_asset_info(baseurl, token, workload_type, client):
74+
""" This function return the asset info """
6775
print(f"Get client asset info:[{client}]")
6876
headers.update({'Authorization': token})
69-
url = baseurl + "asset-service/workloads/" + workload_type + "/assets?filter=commonAssetAttributes/displayName eq '" + client + "'"
77+
url = f"{baseurl}asset-service/workloads/{workload_type}/assets?"\
78+
f"filter=commonAssetAttributes/displayName eq '{client}'"
7079
status_code, response_text = rest_request('GET', url, headers)
7180
validate_response(status_code, 200, response_text)
81+
7282
asset_id = response_text['data'][0]['id']
7383
uuid = response_text['data'][0]['attributes']['instanceUuid']
7484
exsi_host = response_text['data'][0]['attributes']['host']
85+
7586
print(f"Client asset Id:[{asset_id}]")
7687
print(f"Client uuid Id:[{uuid}]")
7788
print(f"Client exsi host:[{exsi_host}]")
7889
return asset_id, uuid, exsi_host
7990

8091
# Get StorageUnits
8192
def get_storage_units(baseurl, token):
93+
""" This function return the storage unit name """
8294
headers.update({'Authorization': token})
83-
url = baseurl + "storage/storage-units"
95+
url = f"{baseurl}storage/storage-units"
8496
status_code, response_text = rest_request('GET', url, headers)
8597
validate_response(status_code, 200, response_text)
8698
storage_unit_name = response_text['data'][0]['id']
8799
is_instant_access_enable = response_text['data'][0]['attributes']['instantAccessEnabled']
100+
88101
if is_instant_access_enable:
89102
print(f"Storage unit:[{storage_unit_name}] enabled for instant access")
90-
return storage_unit_name
91103
else:
92104
print(f"Storage unit:[{storage_unit_name}] disable for instant access")
93105
raise Exception(f"Storage unit:[{storage_unit_name}] disabled for instant access")
106+
return storage_unit_name
94107

95108
# Create protection plan
96109
def create_protection_plan(baseurl, token, protection_plan_name, storage_unit_name):
110+
""" This function create the protection plan """
97111
print(f"Create protection plan:[{protection_plan_name}]")
98112
headers.update({'Authorization': token})
99-
payload = {}
100-
url = baseurl + 'servicecatalog/slos?meta=accessControlId'
113+
url = f"{baseurl}servicecatalog/slos?meta=accessControlId"
101114

102115
cur_dir = os.path.dirname(os.path.abspath(__file__))
103116
file_name = os.path.join(cur_dir, "create_protection_plan_template.json")
104-
f = open(file_name)
105-
data = json.load(f)
117+
file_handle = open(file_name)
118+
data = json.load(file_handle)
106119
data['data']['attributes']['name'] = protection_plan_name
107120
data['data']['attributes']['policyNamePrefix'] = protection_plan_name
108121
data['data']['attributes']['schedules'][0]['backupStorageUnit'] = storage_unit_name
@@ -115,12 +128,14 @@ def create_protection_plan(baseurl, token, protection_plan_name, storage_unit_na
115128
return protection_plan_id
116129

117130
# Subscription asset to SLO
118-
def subscription_asset_to_slo(baseurl, token, protection_plan_id, asset_id, is_vm_group = 0):
131+
def subscription_asset_to_slo(baseurl, token, protection_plan_id, asset_id, is_vm_group=0):
132+
""" This function subscribe the asset/group asset to protection plan """
119133
print(f"Subscribe client to protection plan id: [{protection_plan_id}]")
120134
headers.update({'Authorization': token})
121-
url = baseurl + "servicecatalog/slos/" + protection_plan_id + "/subscriptions"
135+
url = f"{baseurl}servicecatalog/slos/{protection_plan_id}/subscriptions"
122136
selection_type = "ASSETGROUP" if is_vm_group else "ASSET"
123-
payload = {"data": {"type": "subscription","attributes": {"selectionType": selection_type,"selectionId": asset_id}}}
137+
payload = {"data": {"type": "subscription", "attributes": \
138+
{"selectionType": selection_type, "selectionId": asset_id}}}
124139
status_code, response_text = rest_request('POST', url, headers, data=payload)
125140
validate_response(status_code, 201, response_text)
126141
subscription_id = response_text['data']['id']
@@ -130,22 +145,25 @@ def subscription_asset_to_slo(baseurl, token, protection_plan_id, asset_id, is_v
130145

131146
# Get subscription
132147
def get_subscription(baseurl, token, protection_plan_id, subscription_id):
148+
""" This function return the subscription info """
133149
headers.update({'Authorization': token})
134-
url = baseurl + "servicecatalog/slos/" + protection_plan_id + "/subscriptions/" + subscription_id
150+
url = f"{baseurl}servicecatalog/slos/{protection_plan_id}/subscriptions/{subscription_id}"
135151
status_code, response_text = rest_request('GET', url, headers)
136152
validate_response(status_code, 200, response_text)
137153
print(f"Sucessfully fetched the subscription:[{subscription_id}] details.")
138-
154+
139155
# Get job details
140156
def get_job_details(baseurl, token, jobid):
157+
""" This function return the job details """
141158
headers.update({'Authorization': token})
142-
url = baseurl + "admin/jobs/" + str(jobid)
159+
url = f"{baseurl}admin/jobs/{str(jobid)}"
143160
status_code, response_text = rest_request('GET', url, headers)
144161
validate_response(status_code, 200, response_text)
145162
return response_text
146163

147164
# Verify given job state and status
148165
def verify_job_state(baseurl, token, jobid, expected_state, expected_status=0, timeout=1200):
166+
""" This function verify the job status and state with expected status and state """
149167
if jobid:
150168
print(f"Wait for backup job to complete. Backup jobid:[{jobid}]")
151169
# Verify backup job status
@@ -158,7 +176,7 @@ def verify_job_state(baseurl, token, jobid, expected_state, expected_status=0, t
158176
print(f"Job:[{jobid}] completed with expected state:[{expected_state}]")
159177
status = response_text['data']['attributes']['status']
160178
print(f"Actual status:[{status}] and expected status:[{expected_status}]")
161-
if status == expected_status or status == 1:
179+
if status == expected_status:
162180
print(f"Job:[{jobid}] completed with expected status:[{expected_status}]")
163181
break
164182
else:
@@ -168,37 +186,40 @@ def verify_job_state(baseurl, token, jobid, expected_state, expected_status=0, t
168186
print(f"Failed backup jobid:[{jobid}] with state:[{state}]")
169187
raise Exception(f"Failed backup jobid:[{jobid}] with state:[{state}]")
170188

171-
172-
173189
# Remove protection plan
174190
def remove_protectionplan(baseurl, token, protection_plan_id):
191+
""" This function remove the given protection plan """
175192
if protection_plan_id:
176193
headers.update({'Authorization': token})
177-
url = baseurl + 'servicecatalog/slos/' +protection_plan_id
194+
url = f"{baseurl}servicecatalog/slos/{protection_plan_id}"
178195
status_code, response_text = rest_request('DELETE', url, headers)
179-
validate_response(status_code, 204, response_text)
196+
validate_response(status_code, 204, response_text)
180197
print(f"Successfully removed protection plan:[{protection_plan_id}]")
181198

182199
# Remove vm subscription from protection plan
183200
def remove_subscription(baseurl, token, protection_plan_id, subscription_id):
201+
""" This function remove subscription from protection plan """
184202
if protection_plan_id and subscription_id:
185203
headers.update({'Authorization': token})
186-
url = baseurl + 'servicecatalog/slos/' +protection_plan_id+ '/subscriptions/' +subscription_id
204+
url = f"{baseurl}servicecatalog/slos/{protection_plan_id}/subscriptions/{subscription_id}"
187205
status_code, response_text = rest_request('DELETE', url, headers)
188-
validate_response(status_code, 204, response_text)
189-
print(f"Successfully removed asset subscription:[{subscription_id}] from protection plan:[{protection_plan_id}]")
206+
validate_response(status_code, 204, response_text)
207+
print(f"Successfully removed asset subscription:[{subscription_id}] "\
208+
f"from protection plan:[{protection_plan_id}]")
190209

191-
# Remove vcenter creds from netbackup master
210+
# Remove vCenter creds from NetBackup master
192211
def remove_vcenter_creds(baseurl, token, vcenter_name):
212+
""" This function remove the vCenter from NBU master """
193213
if vcenter_name:
194214
headers.update({'Authorization': token})
195-
url = baseurl + 'config/servers/vmservers/' +vcenter_name
215+
url = f"{baseurl}config/servers/vmservers/{vcenter_name}"
196216
status_code, response_text = rest_request('DELETE', url, headers)
197217
validate_response(status_code, 204, response_text)
198-
print(f"Successfully removed vcenter:[{vcenter_name}] credential")
218+
print(f"Successfully removed vCenter:[{vcenter_name}] from NBU master")
199219

200220
# Execute REST API request
201221
def rest_request(request_type, uri, header=None, **kwargs):
222+
""" This function make call to the REST API """
202223
session = requests.session()
203224
payload = kwargs.get('data')
204225
if request_type == 'POST':
@@ -233,17 +254,18 @@ def rest_request(request_type, uri, header=None, **kwargs):
233254
except json.decoder.JSONDecodeError:
234255
print(f"Could not parse json from [{response_text}]")
235256

236-
print(f"Sucessfully sent REST request:[{uri}]")
257+
print(f"Successfully sent REST request:[{uri}]")
237258
print(f"Status code:[{response.status_code}]")
238259
print(f"Response text:[{response.text}]")
239260
return response.status_code, response_text
240261

262+
# Validate the response code of the request
241263
def validate_response(actual_status_code, expected_status_code, response_text):
242-
# Validate the response code of the request
243-
if(actual_status_code == expected_status_code):
244-
print(f"Sucessfully validate the response status code:[{expected_status_code}]")
245-
return True
264+
""" This function validate the response status code with expected response code """
265+
if actual_status_code == expected_status_code:
266+
print(f"Successfully validate the response status code:[{expected_status_code}]")
246267
else:
247-
print(f"Actual status code:[{actual_status_code}] not match with expected status code:[{expected_status_code}]")
248-
raise Exception(f"Response Error:[{response_text['errorMessage']}] and details:[{response_text['errorDetails']}]")
249-
return False
268+
print(f"Actual status code:[{actual_status_code}] not match "\
269+
f"with expected status code:[{expected_status_code}]")
270+
raise Exception(f"Response Error:[{response_text['errorMessage']}] and "\
271+
f"details:[{response_text['errorDetails']}]")

0 commit comments

Comments
 (0)