ONTAP Rest API Discussions

Snapshot Restore

jsharpe
4,352 Views

Is there a way to restore a volume to a previous snapshot via the Python OnTap API? I am not able to find it documented in the NetApp REST API, either.

1 ACCEPTED SOLUTION

RobertBlackhart
4,346 Views

Yes, you can do this by calling patch() on the volume object, similar to this:

>>> from netapp_ontap import HostConnection
>>> from netapp_ontap.resources import Volume, Snapshot
>>> with HostConnection("10.224.117.128", "admin", "netapp1!", verify=False):
  2     volume = Volume(name="vol1", svm={"name": "vs1"}, aggregates=[{"name": "aggr1"}])
  3     volume.post()
  4     snapshot = Snapshot(volume.uuid, name="my_snap")
  5     snapshot.post()
  6     # do stuff on the volume
  7
  8     # now snap the volume back
  9     volume.patch(**{"restore_to.snapshot.name": "my_snap"})

 

I don't think there are any explict examples of this in the documentation, but you can find the restore_to.snapshot.name argument documented under the PATCH /storage/volumes/{uuid} endpoint in the REST docs: https://library.netapp.com/ecmdocs/ECMLP2862544/html/index.html

View solution in original post

6 REPLIES 6

RobertBlackhart
4,347 Views

Yes, you can do this by calling patch() on the volume object, similar to this:

>>> from netapp_ontap import HostConnection
>>> from netapp_ontap.resources import Volume, Snapshot
>>> with HostConnection("10.224.117.128", "admin", "netapp1!", verify=False):
  2     volume = Volume(name="vol1", svm={"name": "vs1"}, aggregates=[{"name": "aggr1"}])
  3     volume.post()
  4     snapshot = Snapshot(volume.uuid, name="my_snap")
  5     snapshot.post()
  6     # do stuff on the volume
  7
  8     # now snap the volume back
  9     volume.patch(**{"restore_to.snapshot.name": "my_snap"})

 

I don't think there are any explict examples of this in the documentation, but you can find the restore_to.snapshot.name argument documented under the PATCH /storage/volumes/{uuid} endpoint in the REST docs: https://library.netapp.com/ecmdocs/ECMLP2862544/html/index.html

jsharpe
4,339 Views

Thanks for the quick reply! 

jsharpe
4,312 Views

After some testing on our systems, I had better luck selecting the volume by UUID. I am gathering the volume UUID earlier in the script. The rest of the code worked!

 

Below is the working function:

def restore_volume_to_snapshot(vol_name, vol_uuid, snapshot_name):
    """
    Restore given volume to given snapshot.

    :param vol_name: name of volume
    :type vol_name: str
    :param vol_uuid: UUID of volume
    :type vol_uuid: str
    :param snapshot_name: name of snapshot
    :type snapshot_name: str
    """
    logger.info(f'Restoring {vol_name} to {snapshot_name}')
    vol = Volume(uuid=vol_uuid)
    try:
        vol.patch(**{'restore_to.snapshot.name': snapshot_name})
    except NetAppRestError as error:
        logger.critical(f'Exception: {error}')
        raise

 

RobertBlackhart
4,306 Views

Yes, that absolutely works in the case where the volume already exists. In my example, I created the volume so that I could take the initial snapshot with a clean volume.

 

Just a note, in case you don't have the UUID handy but you do have the name, another way to patch an existing volume would be like this:

def restore_volume_to_snapshot(vol_name, snapshot_name):
    """
    Restore given volume to given snapshot.

    :param vol_name: name of volume
    :type vol_name: str
    :param snapshot_name: name of snapshot
    :type snapshot_name: str
    """
    logger.info(f'Restoring {vol_name} to {snapshot_name}')
    try:
        Volume.find(name=vol_name).patch(**{'restore_to.snapshot.name': snapshot_name})
    except NetAppRestError as error:
        logger.critical(f'Exception: {error}')
        raise

This does take 1 more API call than if you have the UUID, but it can be more convienent, so that's the tradeoff. You could also split the find() and patch() calls in two lines so that you keep the intermediate volume object if you need to do other things with it (like return it).

 

If you want to save that extra API call, you could also do it like this:

def restore_volume_to_snapshot(vol_name, snapshot_name):
    """
    Restore given volume to given snapshot.

    :param vol_name: name of volume
    :type vol_name: str
    :param snapshot_name: name of snapshot
    :type snapshot_name: str
    """
    logger.info(f'Restoring {vol_name} to {snapshot_name}')
    try:
        Volume.patch_collection(name=vol_name, **{'restore_to.snapshot.name': snapshot_name})
    except NetAppRestError as error:
        logger.critical(f'Exception: {error}')
        raise

The caveat here is that unless you provide the SVM name as well, this would try to act on all volumes with the same name across all SVMs (assuming you're using a cluster-scoped connection/credentials).

 

Just some other ideas to think about.

jsharpe
4,299 Views

Would my current code and your Volume.patch_collection code have the same number of API calls?

RobertBlackhart
4,297 Views

Yes, they would have the same number of calls.

Public