链接复制成功!
通过VMware备份主机批量恢复脚本
场景介绍
针对VMware备份上云场景,云备份(Cloud Backup and Recovery,CBR)增加VMware版本兼容性。但是,VMware混合云备份界面只能操作单个虚拟机进行备份数据恢复,在虚拟机较多时,界面操作步骤繁琐且并发太少。通过该章节内容实现脚本批量执行备份数据恢复,以增加备份副本恢复的并发数,提高效率。
脚本说明
脚本基于Python语言开发,主要实现备份数据批量恢复功能、批量回滚功能等。
config.py | 配置文件 |
main.py | 备份数据批量恢复主流程 |
rollback.py | 批量回滚,用于批量删除云服务器 |
前提条件
- 熟悉Python语言,并有Python环境搭建基础。
- 熟悉华为云备份数据恢复功能。
方案使用到的接口
URL | 所属服务 | 用途 | API文档 |
|---|---|---|---|
POST /v3/auth/tokens | IAM | 认证鉴权 | |
GET /v3/{project_id}/backups/{backup_id} | CBR | 查询备份详情 | |
POST /v1/{project_id}/cloudservers | ECS | 创建ECS | |
GET /v1/{project_id}/jobs/{job_id} | ECS | 查询ECS是否创建完成 | |
GET /v1/{project_id}/cloudservers/{server_id} | ECS | 查询ECS详情,获取创建好的虚拟机的挂载的磁盘信息 | |
POST /v3/{project_id}/backups/{backup_id}/restore | CBR | 使用备份恢复数据 |
参数获取
用户需要收集配置数据,完成config.py中变量的初始赋值,主要包含IAM鉴权参数、项目公共参数、服务器参数、备份数据参数、回滚参数以及监控任务参数。
IAM鉴权参数
- 登录管理控制台。
- 在“控制台”页面,鼠标移动至右上方的用户名,在下拉列表中选择“我的凭证”。
- 在“我的凭证”界面,单击API凭证界面查看。

参数 | 是否必选 | 参数类型 | 描述 |
|---|---|---|---|
iamDomain | 是 | String | IAM用户名 |
iamUser | 是 | String | IAM用户所属账号名 |
iamPassword | 是 | String | IAM用户密码 |
公共参数
- 登录管理控制台。
- 在“控制台”页面,鼠标移动至右上方的用户名,在下拉列表中选择“我的凭证”。
- 在“我的凭证”界面,单击API凭证界面查看。

参数 | 是否必选 | 参数类型 | 描述 |
|---|---|---|---|
projectName | 是 | String | 项目 |
projectId | 是 | String | 项目ID |
服务器参数
- 获取imageRef参数
- 登录云服务器管理控制台。
- 登录管理控制台。
- 单击管理控制台左上角的
,选择区域。 - 单击“
”,选择“计算 > 弹性云服务器”。
- 在左侧导航栏选择“镜像服务”,找到对应镜像并复制镜像ID。
- 登录云服务器管理控制台。
- 获取flavorRef参数
在弹性云服务器控制台界面,单击“购买弹性云服务器”,在“规格”项中找到对应的待创建云服务器的系统规格ID。

- 获取vpcId参数
- 登录云服务器管理控制台。
- 登录管理控制台。
- 单击管理控制台左上角的
,选择区域。 - 单击“
”,选择“网络 > 虚拟私有云”。
- 在左侧导航栏选择“虚拟私有云 > 我的VPC”,找到对应vpcid。

- 登录云服务器管理控制台。
- 获取subnetId参数
在网络控制台界面,左侧导航栏选择“虚拟私有云 > 子网”,找到对应VPC下已创建的子网(subnet)的网络ID。

- 获取securityGroups参数
在网络控制台界面,左侧导航栏选择“访问控制 > 安全组”,找到云服务器对应安全组信息。

- 获取volumeType参数
云硬盘类型参数,目前支持"SATA","SAS","GPSSD","SSD","ESSD","GPSSD2","ESSD2"七种。
具体信息可参见创建云硬盘volume_type参数说明。
- 获取enterpriseProjectId参数
企业项目ID,默认为'0'。
- 获取keyName参数
密钥名称,默认不需要配置。如果需要使用SSH密钥方式登录云服务器,请指定已创建密钥的名称。
表3 服务器参数说明 参数
是否必选
参数类型
描述
imageRef
是
String
待创建云服务器的系统镜像,需要指定已创建镜像的ID,ID格式为通用唯一识别码(Universally Unique Identifier,简称UUID)。
volumeType
是
String
云服务器系统盘对应的磁盘类型,需要与系统所提供的磁盘类型相匹配。
默认值:"SSD"
flavorRef
是
String
待创建云服务器的系统规格的ID。
示例:"s7n.small.1"
vpcId
是
String
待创建云服务器所属虚拟私有云(简称VPC),需要指定已创建VPC的ID,UUID格式。
subnetId
是
String
待创建云服务器所在的子网信息。
需要指定vpcid对应VPC下已创建的子网(subnet)的网络ID,UUID格式。
enterpriseProjectId
是
Integer
企业项目ID。
默认值:0
securityGroups
是
List
云服务器对应安全组信息。
["xxx", "xxx"]
keyName
否
String
如果需要使用SSH密钥方式登录云服务器,请指定已创建密钥的名称。
备份数据参数
- 登录管理控制台。
- 单击管理控制台左上角的
,选择区域。 - 单击“
”,选择“存储 > 云备份 CBR”。 - 选择“混合云备份 > VMware备份”,在备份副本列表获取备份数据ID。

参数 | 是否必选 | 参数类型 | 描述 |
|---|---|---|---|
backupIds | 是 | List | 备份数据ID列表。 |
回滚参数
执行备份数据恢复脚本后,待数据恢复完成,通过返回的日志中的serverId获取云服务器ID列表。

参数 | 是否必选 | 参数类型 | 描述 |
|---|---|---|---|
serverIds | 是 | List | 云服务器ID列表。 备份数据恢复完成后获取。 |
deletePublicip | 否 | Boolean | 配置删除云服务器是否删除云服务器绑定的弹性公网IP。 默认:False |
deleteVolume | 否 | Boolean | 配置删除云服务器是否删除云服务器对应的数据盘。 默认:True |
监控任务参数
参数 | 是否必选 | 参数类型 | 描述 |
|---|---|---|---|
delayInSeconds | 是 | Integer | 循环监控任务状态时间间隔,单位为秒。 默认:10 |
备份数据恢复操作
前提条件
- 已准备好备份数据。
- 完成config.py脚本中的配置项赋值。 config.py示例如下:
# IAM鉴权参数 iamDomain = "" iamUser = "" iamPassword = "" # 项目参数 projectName = "" projectId = "" # 服务器参数 # CentOS 7.9 64bit imageRef = "" volumeType = "SSD" flavorRef = "s7n.small.1" vpcId = "" subnetId = "" enterpriseProjectId = 0 securityGroups = [ "" ] keyName = "" # 备份数据参数 backupIds = ["", "", "", "" ] # 备份数据回退参数 serverIds = ["", ""] deletePublicip = False deleteVolume = True # 监控任务参数 delayInSeconds = 10
执行备份数据恢复脚本
通过命令python .\main.py执行main.py文件,开始备份数据恢复主流程。main.py示例如下:
import requests
import json
import time
import config
class IamLogin:
"""
login by iam, return token
"""
def __init__(self, iamDomain: str, iamUser: str, iamPassword: str, projectName: str):
self.iamDomain = iamDomain
self.iamUser = iamUser
self.iamPassword = iamPassword
self.projectName = projectName
self.iamTokenUrl = "https://iam.{}.myhuaweicloud.com/v3/auth/tokens".format(self.projectName)
def get_auth_token(self) -> dict:
"""
return token
"""
payload = {
"auth": {
"identity": {
"methods": [
"password"
],
"password": {
"user": {
"domain": {
"name": self.iamDomain
},
"name": self.iamUser,
"password": self.iamPassword
}
}
},
"scope": {
"project": {
"name": self.projectName
}
}
}
}
json_data = json.dumps(payload)
try:
response = requests.post(self.iamTokenUrl,
data=json_data,
headers={'Content-Type': 'application/json'},
verify=False)
# 检查请求是否成功
response.raise_for_status()
# 获取响应头的token
access_token = response.headers.get("X-Subject-Token")
return {
"token": access_token,
"code": 0
}
except requests.exceptions.RequestException as e:
print("get token error: ", e)
return {
"code": 1,
"msg": "get token error"
}
class GetBackup:
"""
获取Backup信息
"""
def __init__(self, token, projectName, projectId, backupId) -> None:
self.token = token
self.header = {
'Content-Type': 'application/json', 'X-Auth-Token': self.token
}
self.projectName = projectName
self.projectId = projectId
self.backupId = backupId
def get_backup(self) -> dict:
"""
根据id查询备份数据
"""
print("get backup data")
self.getBackupDataUrl = "https://cbr.{}.myhuaweicloud.com/v3/{}/backups/{}".format(self.projectName,
self.projectId,
self.backupId)
try:
response = requests.get(url=self.getBackupDataUrl, headers=self.header, verify=False)
# 检查请求是否成功
response.raise_for_status()
# 获取响应数据
result = json.loads(str(response.content, encoding="utf-8"))
# 解析响应数据
return {
"code": 0,
"backups": result
}
except requests.exceptions.RequestException as e:
print("get backup data error: ", e)
return {
"code": 1,
"msg": "get backup data error"
}
class CreateServer:
"""
创建服务器
"""
def __init__(self, token, projectName, projectId, backupId, backups, imageRef, flavorRef, vpcId,
subnetId, enterpriseProjectId, securityGroups, keyName) -> None:
self.token = token
self.header = {
'Content-Type': 'application/json', 'X-Auth-Token': self.token
}
self.projectName = projectName
self.projectId = projectId
self.backupId = backupId
self.backups = backups
self.imageRef = imageRef
self.flavorRef = flavorRef
self.vpcId = vpcId
self.subnetId = subnetId
self.enterpriseProjectId = enterpriseProjectId
self.securityGroups = securityGroups
self.keyName = keyName
def transfor_metadata_to_server(self) -> None:
"""
构造server参数,返回backup_volumes_attached
"""
print("transfor metadata to server")
backup = self.backups["backup"]
root_volume, data_volumes = handle_backup_volumes(backup["children"])
# 构建创建服务器的参数
payload = {
"server": {
"name": backup["name"],
"imageRef": self.imageRef,
"root_volume": root_volume,
"data_volumes": data_volumes,
"flavorRef": self.flavorRef,
"vpcid": self.vpcId,
"security_groups": self.securityGroups,
"nics": [{
"subnet_id": self.subnetId
}
],
"key_name": self.keyName,
"count": 1,
"extendparam": {
"enterprise_project_id": self.enterpriseProjectId
}
}
}
self.payload = payload
print(payload)
def create_server(self) -> dict:
print("create server")
self.createServerUrl = "https://ecs.{}.myhuaweicloud.com/v1/{}/cloudservers".format(self.projectName, self.projectId)
json_data = json.dumps(self.payload)
try:
response = requests.post(self.createServerUrl,
data=json_data,
headers=self.header,
verify=False)
print(json.loads(str(response.content, encoding="utf-8")))
# 检查请求是否成功
response.raise_for_status()
# 获取响应数据
result = json.loads(str(response.content, encoding="utf-8"))
# {'job_id': 'ff8080828ee22cea018f27bdd23c6477', 'serverIds': ['6cb36d34-b111-48be-9577-b52dbb74adae']}
return {
"job_id": result["job_id"],
"serverIds": result["serverIds"],
"code": 0
}
except requests.exceptions.RequestException as e:
print("create server error: ", e)
return {
"msg": "create server error",
"code": 1
}
def get_server(self, serverId):
"""
根据id查询ecs信息
"""
print("get server, serverId is ", serverId)
getServerdataUrl = "https://ecs.{}.myhuaweicloud.com/v1/{}/cloudservers/{}".format(self.projectName,
self.projectId,
serverId)
try:
response = requests.get(url=getServerdataUrl, headers=self.header, verify=False)
# 检查请求是否成功
response.raise_for_status()
# 获取响应数据
result = json.loads(str(response.content, encoding="utf-8"))
# 解析响应数据
server_volumes_attached = result["server"]["os-extended-volumes:volumes_attached"]
# [{'id': '66fc89cd-874c-480a-8e49-bd1d361ff0a0', 'delete_on_termination': 'true', 'device': '/dev/vda', 'bootIndex': '0'}]
return server_volumes_attached
except requests.exceptions.RequestException as e:
print("get server data error: ", e)
def handle_backup_volumes(backupChildren):
# 单独处理用于构造root_volume和data_volumes
root_volume = {}
data_volumes = []
# 根据Children顺序赋值root_volume和data_volumes
for child in backupChildren:
bootable = child["extend_info"]["bootable"]
volumeSize = max(40, child["resource_size"])
if bootable:
root_volume["volumetype"] = config.volumeType
root_volume["size"] = volumeSize
else:
data_volumes.append({
"volumetype": config.volumeType,
"size": volumeSize
})
return root_volume, data_volumes
def build_restore_mappings(server_volumes_attached, backup_children):
print("build restore mappings")
print(server_volumes_attached)
print(backup_children)
# 根据bootIndex构建restore的mappings
restore_mappings = []
for server_volume in server_volumes_attached:
server_volume_id = server_volume["id"]
server_bootIndex = server_volume["bootIndex"]
backup_child = backup_children[int(server_bootIndex)]
restore_mappings.append({
"backup_id": backup_child["id"],
"volume_id": server_volume_id
})
return restore_mappings
class Job:
"""
获取Job信息
"""
def __init__(self, token, projectName, projectId, jobId) -> None:
self.token = token
self.header = {
'Content-Type': 'application/json', 'X-Auth-Token': self.token
}
self.projectName = projectName
self.projectId = projectId
self.jobId = jobId
self.getJobUrl = "https://ecs.{}.myhuaweicloud.com/v1/{}/jobs/{}".format(self.projectName,
self.projectId,
self.jobId)
def get_job_status(self) -> dict:
"""
根据jobId查询任务信息
"""
print("get job status, jobId is ", self.jobId)
try:
response = requests.get(url=self.getJobUrl, headers=self.header, verify=False)
# 检查请求是否成功
response.raise_for_status()
# 获取响应数据
result = json.loads(str(response.content, encoding="utf-8"))
# 解析响应数据
return {
"code": 0,
"status": result["status"],
}
except requests.exceptions.RequestException as e:
print("get job status error: ", e)
return {
"code": 1,
"msg": "get job status error",
}
class Restore:
"""
恢复备份数据
"""
def __init__(self, token, projectName, projectId, backupId, serverId, restore_mappings) -> dict:
self.token = token
self.header = {
'Content-Type': 'application/json', 'X-Auth-Token': self.token
}
self.projectName = projectName
self.projectId = projectId
self.backupId = backupId
self.serverId = serverId
self.restore_mappings = restore_mappings
self.backupRestoreUrl = "https://cbr.{}.myhuaweicloud.com/v3/{}/backups/{}/restore".format(self.projectName,
self.projectId,
self.backupId)
def backup_restore(self) -> dict:
"""
恢复备份数据
"""
print("backup restore, backupId is {}, serverId is {}".format(self.backupId, self.serverId))
payload = {
"restore": {
"mappings": self.restore_mappings,
"power_on": True,
"server_id": self.serverId
}
}
json_data = json.dumps(payload)
try:
response = requests.post(self.backupRestoreUrl,
data=json_data,
headers=self.header,
verify=False)
# 检查请求是否成功
response.raise_for_status()
# 获取响应数据
result = json.loads(str(response.content, encoding="utf-8"))
# {}
return {
"code": 0
}
except requests.exceptions.RequestException as e:
print("backup restore error: ", e)
return {
"code": 1,
"msg": "backup restore error",
}
def http_error(code):
return code == 1
if __name__ == '__main__':
# IAM鉴权获取token
print("start to get token")
tokenObj = IamLogin(config.iamDomain, config.iamUser, config.iamPassword, config.projectName).get_auth_token()
if http_error(tokenObj["code"]):
print("backup failed, msg is {}".format(tokenObj["msg"]))
else:
token = tokenObj["token"]
# 记录创建服务器的job信息
jobInfos = []
# 根据备份副本ID列表,批量恢复数据
for backupId in config.backupIds:
print("start the backup process, backup id is ", backupId)
resultData = {"backupId": backupId}
# 初始化GetBackup参数
get_backup = GetBackup(token, config.projectName, config.projectId, backupId)
# 根据backupId查询备份元数据
backupsObj = get_backup.get_backup()
if http_error(backupsObj["code"]):
resultData["status"] = "FAILED"
resultData["msg"] = backupsObj["msg"]
print("backup process failed, msg is {}".format(backupsObj["msg"]))
jobInfos.append(resultData)
continue
else:
backups = backupsObj["backups"]
# 初始化CreateServer参数
create_server = CreateServer(token, config.projectName, config.projectId, backupId, backups,
config.imageRef, config.flavorRef, config.vpcId,
config.subnetId, config.enterpriseProjectId, config.securityGroups,
config.keyName)
create_server.transfor_metadata_to_server()
# 创建服务器
createServerData = create_server.create_server()
if http_error(createServerData["code"]):
resultData["status"] = "FAILED"
resultData["msg"] = createServerData["msg"]
print("backup process failed, msg is {}".format(createServerData["msg"]))
jobInfos.append(resultData)
continue
else:
serverId = createServerData["serverIds"][0]
resultData["serverId"] = serverId
resultData["status"] = "START"
# 监控创建服务器任务状态
print("start to get create server job status, please wait...")
get_job = Job(token, config.projectName, config.projectId, createServerData["job_id"])
while True:
time.sleep(config.delayInSeconds)
jobStatusObj = get_job.get_job_status()
if http_error(jobStatusObj["code"]):
resultData["status"] = "FAILED"
resultData["msg"] = createServerData["msg"]
print("backup process failed, msg is {}".format(createServerData["msg"]))
jobInfos.append(resultData)
break
else:
jobStatus = jobStatusObj["status"]
if jobStatus == "SUCCESS":
createServerData["status"] = "SUCCESS"
print("The create server job is executed successfully.")
break
elif jobStatus == "RUNNING" or jobStatus == "INIT":
print("job status is {}, please wait...".format(jobStatus))
if jobStatus is None or jobStatus == "FAIL":
print("The job failed.")
break
# 恢复备份数据
if createServerData["status"] == "SUCCESS":
# 根据bootIndex构建restore的mappings
server_volumes_attached = create_server.get_server(serverId)
backup_children = backups["backup"]["children"]
restore_mappings = build_restore_mappings(server_volumes_attached, backup_children)
restore = Restore(token, config.projectName, config.projectId, backupId, serverId, restore_mappings)
restoreObj = restore.backup_restore()
if http_error(restoreObj["code"]):
resultData["status"] = "FAILED"
resultData["msg"] = restoreObj["msg"]
print("backup process failed, msg is {}".format(restoreObj["msg"]))
jobInfos.append(resultData)
else:
resultData["status"] = "SUCCESS"
jobInfos.append(resultData)
else:
resultData["status"] = "FAILED"
resultData["msg"] = createServerData["msg"]
print("backup process failed, msg is {}".format(createServerData["msg"]))
jobInfos.append(resultData)
break
# 输出成功和失败的数据
print(jobInfos)
successJobList = []
failedJobList = []
for jobInfo in jobInfos:
if jobInfo["status"] == "SUCCESS":
successJobList.append(jobInfo)
else:
failedJobList.append(jobInfo)
print("backup process end")
print({
"successJobList": successJobList,
"failedJobList": failedJobList
}) 执行完成后,回显信息如下:


其中,successJobList为触发备份数据恢复成功的列表,failedJobList为触发备份数据恢复失败的列表。
备份恢复结果验证操作
- 备份副本列表
- 登录管理控制台。
- 单击管理控制台左上角的
,选择区域。 - 单击“
”,选择“存储 > 云备份 CBR”。 - 左侧导航栏选择“混合云备份 > VMware备份”,备份副本列表的状态下显示“正在恢复”。恢复完成后,状态变为“可用”。
- 云服务器列表
- 云服务器磁盘详情
在弹性云服务器控制台界面,点击对应的云服务器名称,查看云服务器磁盘数据均正常。


执行回滚
前提条件
- config.py中的回滚参数(serverIds参数)已经配置完毕。
- 备份数据恢复流程已经执行完毕。
执行回滚操作脚本
通过命令python .\rollback.py执行rollback.py文件,开始备份数据恢复主流程。
rollback.py示例如下:
import requests
import json
import time
import config
class IamLogin:
"""
login by iam, return token
"""
def __init__(self, iamDomain: str, iamUser: str, iamPassword: str, projectName: str):
self.iamDomain = iamDomain
self.iamUser = iamUser
self.iamPassword = iamPassword
self.projectName = projectName
self.iamTokenUrl = "https://iam.{}.myhuaweicloud.com/v3/auth/tokens".format(self.projectName)
def get_auth_token(self) -> dict:
"""
return token
"""
payload = {
"auth": {
"identity": {
"methods": [
"password"
],
"password": {
"user": {
"domain": {
"name": self.iamDomain
},
"name": self.iamUser,
"password": self.iamPassword
}
}
},
"scope": {
"project": {
"name": self.projectName
}
}
}
}
json_data = json.dumps(payload)
try:
response = requests.post(self.iamTokenUrl,
data=json_data,
headers={'Content-Type': 'application/json'},
verify=False)
# 检查请求是否成功
response.raise_for_status()
# 获取响应头的token
access_token = response.headers.get("X-Subject-Token")
return {
"token": access_token,
"code": 0
}
except requests.exceptions.RequestException as e:
print("get token error: ", e)
return {
"msg": "get token error",
"code": 1
}
class Rollback:
"""
服务器回滚
"""
def __init__(self, token, projectName, projectId, serverIds, deletePublicIp, deleteVolume) -> None:
self.token = token
self.projectName = projectName
self.projectId = projectId
self.header = {
'Content-Type': 'application/json',
'X-Project-Id': self.projectId,
'X-Auth-Token': self.token
}
self.serverIds = serverIds
self.deletePublicIp = deletePublicIp
self.deleteVolume = deleteVolume
self.deleteServerUrl = "https://ecs.{}.myhuaweicloud.com/v1/{}/cloudservers/delete".format(self.projectName,
self.projectId)
def delete_cloudservers(self) -> dict:
"""
根据指定的云服务器ID列表,删除云服务器。可以单个删除,也可以批量删除。
"""
print("batch delete servers, serverIds are ", self.serverIds)
if len(self.serverIds) == 0:
return {
"code": 1,
"msg": "serverIds is empty"
}
servers = []
for serverId in self.serverIds:
servers.append({"id": serverId})
payload = {
"servers": servers,
"delete_publicip": self.deletePublicIp,
"delete_volume": self.deleteVolume
}
json_data = json.dumps(payload)
try:
response = requests.post(self.deleteServerUrl,
data=json_data,
headers=self.header,
verify=False)
# 检查请求是否成功
response.raise_for_status()
# 获取响应数据
result = json.loads(str(response.content, encoding="utf-8"))
# {'job_id': 'ff8080828ee21983018f27b42f310e0f'}
# 解析响应数据
return {
"code": 0,
"job_id": result["job_id"]
}
except requests.exceptions.RequestException as e:
print("batch delete servers error: ", e)
return {
"code": 1,
"msg": "batch delete servers error"
}
class Job:
"""
获取Job信息
"""
def __init__(self, token, projectName, projectId, jobId) -> None:
self.token = token
self.header = {
'Content-Type': 'application/json', 'X-Auth-Token': self.token
}
self.projectName = projectName
self.projectId = projectId
self.jobId = jobId
self.getJobUrl = "https://ecs.{}.myhuaweicloud.com/v1/{}/jobs/{}".format(self.projectName,
self.projectId,
self.jobId)
def get_job_status(self) -> dict:
"""
根据jobId查询任务信息
"""
print("get job status, jobId is ", self.jobId)
try:
response = requests.get(url=self.getJobUrl, headers=self.header, verify=False)
# 检查请求是否成功
response.raise_for_status()
# 获取响应数据
result = json.loads(str(response.content, encoding="utf-8"))
# 解析响应数据
return {
"code": 0,
"status": result["status"],
}
except requests.exceptions.RequestException as e:
print("get job status error: ", e)
return {
"code": 1,
"msg": "get job status error",
}
def http_error(code):
return code == 1
if __name__ == '__main__':
# IAM鉴权获取token
print("start to get token")
tokenObj = IamLogin(config.iamDomain, config.iamUser, config.iamPassword, config.projectName).get_auth_token()
if http_error(tokenObj["code"]):
print("rollback failed, msg is {}".format(tokenObj["msg"]))
else:
token = tokenObj["token"]
# 根据服务器ID列表,批量删除数据
print("start to rollback")
rollback = Rollback(token, config.projectName, config.projectId, config.serverIds, config.deletePublicip,
config.deleteVolume)
jobObj = rollback.delete_cloudservers()
if http_error(jobObj["code"]):
print("rollback failed, msg is {}".format(jobObj["msg"]))
else:
get_job = Job(token, config.projectName, config.projectId, jobObj["job_id"])
# 监控任务状态
print("start to get rollback job status, please wait...")
while True:
time.sleep(config.delayInSeconds)
jobStatusObj = get_job.get_job_status()
if http_error(jobStatusObj["code"]):
print("rollback failed, msg is {}".format(jobStatusObj["msg"]))
break
else:
jobStatus = jobStatusObj["status"]
if jobStatus == "SUCCESS":
print("The job is executed successfully.")
break
elif jobStatus == "RUNNING" or jobStatus == "INIT":
print("job status is {}, please wait...".format(jobStatus))
if jobStatus is None or jobStatus == "FAIL":
print("The job failed.")
break 执行完成后,回显信息如下

回滚结果验证
在弹性云服务器控制台界面,左侧导航栏选择“弹性云服务器 > 弹性云服务器”,云服务器已被清退。






