-
Notifications
You must be signed in to change notification settings - Fork 24
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Add test script to check volume backup API
Signed-off-by: Markus Hentsch <[email protected]>
- Loading branch information
1 parent
47957ab
commit 8e5eb04
Showing
1 changed file
with
241 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,241 @@ | ||
"""Volume Backup API tester for Block Storage API | ||
This test script executes basic operations on the Block Storage API centered | ||
around volume backups. Its purpose is to verify that the Volume Backup API is | ||
available and working as expected using simple operations such as creating and | ||
restoring volume backups. | ||
It verifies that a properly configured backup driver is present to the extent | ||
that aforementioned operations succeed on the API level. It does not by any | ||
means verify that the backup and restore procedures actual handle the data | ||
correctly (it only uses empty volumes and does not look at data for the sake | ||
of simplicity). | ||
""" | ||
|
||
import argparse | ||
import getpass | ||
import os | ||
import time | ||
import typing | ||
|
||
import openstack | ||
|
||
# prefix to be included in the names of any Keystone resources created | ||
# used by the cleanup routine to identify resources that can be safely deleted | ||
DEFAULT_PREFIX = "scs-test-" | ||
|
||
# timeout in seconds for resource availability checks | ||
# (e.g. a volume becoming available) | ||
WAIT_TIMEOUT = 60 | ||
|
||
|
||
def connect(cloud_name: str, password: typing.Optional[str] = None | ||
) -> openstack.connection.Connection: | ||
"""Create a connection to an OpenStack cloud | ||
:param string cloud_name: | ||
The name of the configuration to load from clouds.yaml. | ||
:param string password: | ||
Optional password override for the connection. | ||
:returns: openstack.connnection.Connection | ||
""" | ||
|
||
if password: | ||
return openstack.connect( | ||
cloud=cloud_name, | ||
password=password | ||
) | ||
else: | ||
return openstack.connect( | ||
cloud=cloud_name, | ||
) | ||
|
||
|
||
def test_backup(conn: openstack.connection.Connection, | ||
prefix=DEFAULT_PREFIX, timeout=WAIT_TIMEOUT) -> None: | ||
"""Execute volume backup tests on the connection | ||
This will create an empty volume, a backup of that empty volume and then | ||
attempt to restore the backup onto a new volume. | ||
Purpose of these tests is to verify that the volume backup API is working | ||
correctly. | ||
""" | ||
|
||
# CREATE VOLUME | ||
print("Creating volume ...") | ||
volume = conn.block_storage.create_volume( | ||
name=f"{prefix}volume", | ||
size=1 | ||
) | ||
assert volume is not None, ( | ||
"Initial volume creation failed" | ||
) | ||
volume_id = volume.id | ||
assert conn.block_storage.get_volume(volume_id) is not None, ( | ||
"Retrieving initial volume by ID failed" | ||
) | ||
|
||
print( | ||
f"↳ waiting for volume with ID '{volume_id}' to reach status " | ||
f"'available' ..." | ||
) | ||
seconds_waited = 0 | ||
while conn.block_storage.get_volume(volume_id).status != "available": | ||
time.sleep(1.0) | ||
seconds_waited += 1 | ||
assert seconds_waited < timeout, ( | ||
f"Timeout reached while waiting for volume to reach status " | ||
f"'available' (volume id: {volume_id}) after {seconds_waited} " | ||
f"seconds" | ||
) | ||
print("Create empty volume: PASS") | ||
|
||
# CREATE BACKUP | ||
print("Creating backup from volume ...") | ||
backup = conn.block_storage.create_backup( | ||
name=f"{prefix}volume-backup", | ||
volume_id=volume_id | ||
) | ||
assert backup is not None, ( | ||
"Backup creation failed" | ||
) | ||
backup_id = backup.id | ||
assert conn.block_storage.get_backup(backup_id) is not None, ( | ||
"Retrieving backup by ID failed" | ||
) | ||
|
||
print(f"↳ waiting for backup '{backup_id}' to become available ...") | ||
seconds_waited = 0 | ||
while conn.block_storage.get_backup(backup_id).status != "available": | ||
time.sleep(1.0) | ||
seconds_waited += 1 | ||
assert seconds_waited < timeout, ( | ||
f"Timeout reached while waiting for backup to reach status " | ||
f"'available' (backup id: {backup_id}) after {seconds_waited} " | ||
f"seconds" | ||
) | ||
print("Create backup from volume: PASS") | ||
|
||
# RESTORE BACKUP | ||
print("Restoring backup to volume ...") | ||
restored_volume_name = f"{prefix}restored-backup" | ||
conn.block_storage.restore_backup( | ||
backup_id, | ||
name=restored_volume_name | ||
) | ||
|
||
print( | ||
f"↳ waiting for restoration target volume '{restored_volume_name}' " | ||
f"to be created ..." | ||
) | ||
seconds_waited = 0 | ||
while conn.block_storage.find_volume(restored_volume_name) is None: | ||
time.sleep(1.0) | ||
seconds_waited += 1 | ||
assert seconds_waited < timeout, ( | ||
f"Timeout reached while waiting for restored volume to be created " | ||
f"(volume name: {restored_volume_name}) after {seconds_waited} " | ||
f"seconds" | ||
) | ||
# wait for the volume restoration to finish | ||
print( | ||
f"↳ waiting for restoration target volume '{restored_volume_name}' " | ||
f"to reach 'available' status ..." | ||
) | ||
volume_id = conn.block_storage.find_volume(restored_volume_name).id | ||
while conn.block_storage.get_volume(volume_id).status != "available": | ||
time.sleep(1.0) | ||
seconds_waited += 1 | ||
assert seconds_waited < timeout, ( | ||
f"Timeout reached while waiting for restored volume reach status " | ||
f"'available' (volume id: {volume_id}) after {seconds_waited} " | ||
f"seconds" | ||
) | ||
print("Restore volume from backup: PASS") | ||
|
||
|
||
def cleanup(conn: openstack.connection.Connection, prefix=DEFAULT_PREFIX): | ||
""" | ||
Looks up volume and volume backup resources matching the given prefix and | ||
deletes them. | ||
""" | ||
print(f"\nPerforming cleanup for resources with the " | ||
f"'{prefix}' prefix ...") | ||
|
||
backups = conn.block_storage.backups() | ||
for backup in backups: | ||
if backup.name.startswith(prefix): | ||
print(f"↳ deleting volume backup '{backup.id}' ...") | ||
conn.block_storage.delete_backup(backup.id) | ||
|
||
volumes = conn.block_storage.volumes() | ||
for volume in volumes: | ||
if volume.name.startswith(prefix): | ||
print(f"↳ deleting volume '{volume.id}' ...") | ||
conn.block_storage.delete_volume(volume.id) | ||
|
||
|
||
def main(): | ||
parser = argparse.ArgumentParser( | ||
description="SCS Domain Manager Conformance Checker") | ||
parser.add_argument( | ||
"--os-cloud", type=str, | ||
help="Name of the cloud from clouds.yaml, alternative " | ||
"to the OS_CLOUD environment variable" | ||
) | ||
parser.add_argument( | ||
"--ask", | ||
help="Ask for password interactively instead of reading it from the " | ||
"clouds.yaml", | ||
action="store_true" | ||
) | ||
parser.add_argument( | ||
"--debug", action="store_true", | ||
help="Enable OpenStack SDK debug logging" | ||
) | ||
parser.add_argument( | ||
"--prefix", type=str, | ||
default=DEFAULT_PREFIX, | ||
help=f"OpenStack resource name prefix for all resources to be created " | ||
f"and/or cleaned up by this script within the configured domains " | ||
f"(default: '{DEFAULT_PREFIX}')" | ||
) | ||
parser.add_argument( | ||
"--timeout", type=int, | ||
default=WAIT_TIMEOUT, | ||
help=f"Timeout in seconds for operations waiting for resources to " | ||
f"become available such as creating volumes and volume backups " | ||
f"(default: '{WAIT_TIMEOUT}')" | ||
) | ||
parser.add_argument( | ||
"--cleanup-only", action="store_true", | ||
help="Instead of executing tests, cleanup all resources " | ||
"with the prefix specified via '--prefix' (or its default)" | ||
) | ||
args = parser.parse_args() | ||
openstack.enable_logging(debug=args.debug) | ||
|
||
# parse cloud name for lookup in clouds.yaml | ||
cloud = os.environ.get("OS_CLOUD", None) | ||
if args.os_cloud: | ||
cloud = args.os_cloud | ||
assert cloud, ( | ||
"You need to have the OS_CLOUD environment variable set to your " | ||
"cloud name or pass it via --os-cloud" | ||
) | ||
conn = connect( | ||
cloud, | ||
password=getpass.getpass("Enter password: ") if args.ask else None | ||
) | ||
if args.cleanup_only: | ||
cleanup(conn, prefix=args.prefix) | ||
else: | ||
cleanup(conn, prefix=args.prefix) | ||
test_backup(conn, prefix=args.prefix, timeout=args.timeout) | ||
cleanup(conn, prefix=args.prefix) | ||
|
||
|
||
if __name__ == "__main__": | ||
main() |