|
| 1 | +""" |
| 2 | +Create clones on a remote array of all volumes in a pgroup. |
| 3 | +
|
| 4 | +Download latest pypureclient release from: |
| 5 | + https://pypi.org/project/py-pure-client |
| 6 | +
|
| 7 | +Example usage: |
| 8 | + $ python clone_all_snapshots_in_pgroup.py --source pure001 --target pure002 --target-id-token eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCIsImtpZCI6ImQ2ODNkOWZjLTY0MzUtNDQwOC1iMjEzLTc0ZGUwNTY0ODEzMSJ9.eyJhdWQiOiIxYTZiNjY1NS02ZTdmLTRkMjMtYTY4NC1lYjZiYzljZWNjYmMiLCJpc3MiOiJwZ0NsaWVudCIsInJvbGUiOiJhcnJheV9hZG1pbiIsImV4cCI6MTU5OTE3Mjg3NywiaWF0IjoxNTY4MDY4ODc3LCJ0eXAiOiJKV1QiLCJzdWIiOiJwdXJldXNlciJ9.G8aNq4Jz0PGnFoZD31Y9d_ux-fdHuzJB4R3QPl-zw4V8htB1MCwvQxKTCQ6F8uuhy61yCL18l6rqKmBcPhrrPDQCLF_lJsMBUNycJVQd-DnfWqYIzAzcpMPAf_zNHekEVXfJZd_VQ3Uv4YC2mVtPe6OKR3JiBy42bJ5tDcax4UrUDEPXg-V1QOWhmtycrYCQp2hfQnqtZMQhtOzrsJhN_9DXCpreasgKimBvWhMfbKZyJGdpELGJhO6ItcmeqdiSDSe-t-os4PYonNgB_we2IlpyDHQpQj1lCF5SSqaqFOrx1lyb-Sc0UXitKrSFs8oS5Rs8UXhN6h9gSb8T-v8sJg --source-api-token dfc11fc6-0287-fe15-c4f9-c4e5a7ce5d7d |
| 9 | + Enter name of source pgroup: pg1 |
| 10 | + Found volumes in pg 'pg1': [u'vol1', u'vol2'] |
| 11 | + Confirmed that script target is in pgroup targets |
| 12 | + Snapshotting pgroup 'pg1' and replicating now to pgroup targets '[{u'name': u'pure002', u'allowed': True}]'... |
| 13 | + Enter clone suffix (hit enter for no suffix): clone2 |
| 14 | + Checking to see that replication has completed... |
| 15 | + Snapshot 'pure001:pg1.23.vol1': started 1568071967000, progress 0.0 |
| 16 | + Snapshot 'pure001:pg1.23.vol1': started 1568071967000, progress 1.0 |
| 17 | + Creating 'vol1-clone2' from 'pure001:pg1.23.vol1' |
| 18 | + Checking to see that replication has completed... |
| 19 | + Snapshot 'pure001:pg1.23.vol2': started 1568071967000, progress 1.0 |
| 20 | + Creating 'vol2-clone2' from 'pure001:pg1.23.vol2' |
| 21 | +
|
| 22 | +If you get "PureError: Could not retrieve a new access token", then your |
| 23 | +id_token is not valid. |
| 24 | +
|
| 25 | +""" |
| 26 | +import argparse |
| 27 | +import purestorage |
| 28 | +import requests |
| 29 | +import sys |
| 30 | +import time |
| 31 | + |
| 32 | +from pypureclient import flasharray |
| 33 | +from requests.packages.urllib3.exceptions import InsecureRequestWarning |
| 34 | +requests.packages.urllib3.disable_warnings(InsecureRequestWarning) |
| 35 | +from util import print_errs |
| 36 | + |
| 37 | +# Making this script work with either python2 or 3 |
| 38 | +try: |
| 39 | + input = raw_input |
| 40 | +except NameError: |
| 41 | + pass |
| 42 | + |
| 43 | +""" |
| 44 | +
|
| 45 | +REST API CALLS |
| 46 | +Separated so I can swap out 1.X and 2.X clients easily. |
| 47 | +
|
| 48 | +In this script, I have to make some calls in 1.X since they |
| 49 | +aren't supported in 2.X yet. |
| 50 | +
|
| 51 | +""" |
| 52 | +def get_volumes_in_pgroup(client_1x, pg_name): |
| 53 | + return client_1x.list_pgroups(names=pg_name)[0]["volumes"] |
| 54 | + |
| 55 | +def get_pgroup_targets(client_1x, pg_name): |
| 56 | + return client_1x.list_pgroups(names=pg_name)[0]["targets"] |
| 57 | + |
| 58 | +def replicate_now(client_1x, pg_name): |
| 59 | + return client_1x.create_pgroup_snapshot(pg_name, replicate_now=True)["name"] |
| 60 | + |
| 61 | +def get_transfer_stats(client_2x, pg_vol_snap_name): |
| 62 | + """ Returns a tuple of (started time, progress) """ |
| 63 | + resp = client_2x.get_volume_snapshots_transfer(names=pg_vol_snap_name) |
| 64 | + assert resp.status_code == 200, print_errs(resp) |
| 65 | + transfer_stats = list(resp.items)[0] |
| 66 | + return (transfer_stats.started, transfer_stats.progress) |
| 67 | + |
| 68 | +def create_clone(client_2x, clone_name, pg_vol_snap_name): |
| 69 | + volume_body = flasharray.Volume(source={"name": pg_vol_snap_name}) |
| 70 | + resp = client_2x.post_volumes(names=clone_name, volume=volume_body) |
| 71 | + assert resp.status_code == 200, print_errs(resp) |
| 72 | + |
| 73 | +""" |
| 74 | +SCRIPT FUNCTIONS |
| 75 | +""" |
| 76 | +def clone_new_volumes(target_client, source_array_name, source_snapshot_name, source_volumes, clone_suffix=None): |
| 77 | + # For each volume in the source pg snap, create a new volume with suffix clone_suffix |
| 78 | + for volume in source_volumes: |
| 79 | + full_pg_vol_snap_name = "{}:{}.{}".format(source_array_name, source_snapshot_name, volume) |
| 80 | + print("Checking to see that replication has completed...") |
| 81 | + progress = 0.0 |
| 82 | + while progress < 1.0: |
| 83 | + transfer_stats = get_transfer_stats(target_client, full_pg_vol_snap_name) |
| 84 | + print("Snapshot '{}': started {}, progress {}".format(full_pg_vol_snap_name, |
| 85 | + transfer_stats[0], transfer_stats[1])) |
| 86 | + progress = transfer_stats[1] |
| 87 | + time.sleep(1) |
| 88 | + |
| 89 | + clone_name = "{}-{}".format(volume, clone_suffix) if clone_suffix else volume |
| 90 | + print("Creating '{}' from '{}'".format(clone_name, full_pg_vol_snap_name)) |
| 91 | + create_clone(target_client, clone_name, full_pg_vol_snap_name) |
| 92 | + |
| 93 | +def main(source, target, source_client, target_client): |
| 94 | + pg_name = input("Enter name of source pgroup: ") |
| 95 | + source_volumes = get_volumes_in_pgroup(source_client, pg_name) |
| 96 | + source_targets = get_pgroup_targets(source_client, pg_name) |
| 97 | + |
| 98 | + print("Found volumes in pg '{}': {}".format(pg_name, source_volumes)) |
| 99 | + target_found = False |
| 100 | + for source_target in source_targets: |
| 101 | + if source_target["name"] == target: |
| 102 | + if not source_target["allowed"]: |
| 103 | + print("Target '{}' is not allowed; exiting...".format( |
| 104 | + target)) |
| 105 | + sys.exit(1) |
| 106 | + target_found = True |
| 107 | + |
| 108 | + if target_found: |
| 109 | + print("Confirmed that script target is in pgroup targets") |
| 110 | + else: |
| 111 | + print("Target '{}' is not in pgroup targets '{}'; exiting...".format( |
| 112 | + target, source_targets)) |
| 113 | + sys.exit(1) |
| 114 | + |
| 115 | + print("Snapshotting pgroup '{}' and replicating now to pgroup targets '{}'...".format( |
| 116 | + pg_name, source_targets)) |
| 117 | + source_snapshot_name = replicate_now(source_client, pg_name) |
| 118 | + |
| 119 | + clone_suffix = input("Enter clone suffix (hit enter for no suffix): ") |
| 120 | + clone_new_volumes(target_client, source, source_snapshot_name, source_volumes, clone_suffix) |
| 121 | + |
| 122 | +if __name__ == "__main__": |
| 123 | + parser = argparse.ArgumentParser(description='Get all volumes in a pgroup, then snap/clone them to a remote target.') |
| 124 | + parser.add_argument('--source', help='Source array. PG Snapshot will be taken here.', required=True) |
| 125 | + parser.add_argument('--target', help='Target array. Clone volumes will be created here.', required=True) |
| 126 | + parser.add_argument('--source-api-token', help='1.X API token for source array', required=True) |
| 127 | + parser.add_argument('--target-id-token', help='OAuth2 identity token for target array', required=True) |
| 128 | + |
| 129 | + args = parser.parse_args() |
| 130 | + source_1x_client = purestorage.FlashArray(args.source, api_token=args.source_api_token) |
| 131 | + target_2x_client = flasharray.Client(target=args.target, id_token=args.target_id_token) |
| 132 | + |
| 133 | + main(args.source, args.target, source_1x_client, target_2x_client) |
0 commit comments