# Restore a virtual machine (instance) from snapshot

> Learn how to restore an OpenStack volume from a Ceph RBD snapshot, including prerequisites, restore steps, verification, and troubleshooting.

This guide explains how to use the `snapshot-restore.sh` script to restore an attached OpenStack volume from one of its Cinder snapshots.

The script prompts you to select a project, server, attached volume, and snapshot. It then stops the server, rolls back the selected Ceph RBD snapshot, and starts the server again. For a complete sample restoration workflow, reference [Example output from a complete restore session](#example-output-from-a-complete-restore-session).

:::danger[Data loss risk]

Restoring a volume from a snapshot overwrites the current volume data. You lose any changes made after the snapshot was created.

:::

## Before you begin {#before-you-begin}

Verify that the host where you run the script has the required OpenStack and Ceph access.

## Restore a volume from a snapshot {#restore-a-volume-from-a-snapshot}

1. Save the script as `snapshot-restore.sh`. Obtain the script from [Full restore script](#full-restore-script).

2. Make the script executable.

   ```bash title="Make the restore script executable"
   chmod +x snapshot-restore.sh
   ```

3. Run the script.

   ```bash title="Run snapshot-restore.sh"
   ./snapshot-restore.sh
   ```

4. Enter the project number where the virtual machine to be restored is located.

   ```bash title="Project selection"
   Select a project by number:
   1. admin
   2. _diagnostics
   3. bigstack
   Enter the number: 3
   ```

5. Enter the number of the instance that you want to restore. You can identify instances using either the instance name or the UUID.

   ```bash title="Server selection"
   Select a server by number:
   1. vm-snap-restore (52d7f3fa-4d6f-4868-b271-e018c78a061f)
   Enter the number: 1
   ```

6. Enter the number of the volume to restore.

   The root disk that hosts the operating system is usually `/dev/sda`, while additional data disks are on lower indexes. To identify each volume, navigate to **Compute > Instances**, select the instance to restore, and then click the **Volumes** tab to view information about the volumes.

   ```bash title="Volume selection"
   Select a volume by number:
   1. d11dfee8-3067-4031-8e4d-da3bc867c913 (Device: /dev/sda)
   2. 684483a5-4bd6-448f-810a-81517d82754f (Device: /dev/sdb)
   Enter the number: 1
   ```

7. Enter the number of the snapshot to restore from.

   ```bash title="Snapshot selection"
   Select a snapshot by number:
   1. snapshot for add-data-v3 - Created: 2025-02-06 05:42:57
   2. snapshot for add-data-v1 - Created: 2025-02-06 05:42:11
   3. snapshot for snap-data - Created: 2025-02-06 02:01:19
   Enter the number: 1
   ```

8. Confirm the restore.

   The script displays the selected volume and snapshot IDs and prompts for confirmation:

   :::danger[Data loss]

   Restoring a volume from a snapshot overwrites the current volume data. You lose any changes made after the snapshot was created.

   :::

   ```bash title="Restore confirmation prompt"
   WARNING: This operation will revert the volume
   (d11dfee8-3067-4031-8e4d-da3bc867c913) to the selected snapshot
   (50927e68-9330-46d6-bf8a-1dac0db8e652).
   # highlight-start
   The associated server will be SHUTDOWN, and all current disk data will be LOST.
   # highlight-end
   Type 'YES' to confirm:
   ```

   - To proceed, type `YES`.
   - To cancel, type anything else.

## Verify that the server and volume are healthy after the restore {#verify-that-the-server-and-volume-are-healthy-after-the-restore}

After the script finishes, verify that the server and the restored volume are healthy.

1. Check that the server status is **Active**.
2. Confirm that the expected volume is still attached and volume status is **In-use**.
3. Log in to the server and check the restored data or filesystem state.

## Full restore script {#full-restore-script}

```bash title="snapshot-restore.sh"
#!/bin/bash
source /etc/admin-openrc.sh

# Get the list of project names
projects=($(openstack project list -c Name -f value))

# Display the numbered list
echo "Select a project by number:"
for i in "${!projects[@]}"; do
    echo "$((i+1)). ${projects[i]}"
done

# Read user input
read -p "Enter the number: " choice

# Validate input
if [[ $choice -ge 1 && $choice -le ${#projects[@]} ]]; then
    project_name=(${projects[$((choice-1))]})
    echo "You selected: ${projects[$((choice-1))]}"
else
    echo "Invalid choice. Exiting."
    exit 1
fi
## - End of Project -

# Get the list of servers (Name and ID)
mapfile -t servers < <(openstack server list --project="$project_name" --long -c Name -c ID -f value)

# Check if there are any servers
if [ ${#servers[@]} -eq 0 ]; then
    echo "No servers found for project $project_name."
    exit 1
fi

# Display the numbered list
echo "Select a server by number:"
for i in "${!servers[@]}"; do
    selected_server_id=$(echo "${servers[i]}" | awk '{print $1}')     # Extract server ID
    server_name=$(echo "${servers[i]}" | awk '{$1=""; print $0}' | sed 's/^ *//') # Extract server name
    echo "$((i+1)). $server_name ($selected_server_id)"
done

# Read user input
read -p "Enter the number: " choice

# Validate input
if [[ $choice -ge 1 && $choice -le ${#servers[@]} ]]; then
    selected_server="${servers[$((choice-1))]}"
    selected_server_id=$(echo "$selected_server" | awk '{print $1}')  # Extract server ID
    server_name=$(echo "$selected_server" | awk '{$1=""; print $0}' | sed 's/^ *//') # Extract server name
    echo "You selected: $server_name ($selected_server_id)"
else
    echo "Invalid choice. Exiting."
    exit 1
fi
## - End of Server -

volumes_json=$(openstack server show "$selected_server_id" -c attached_volumes -f json)

# Extract volume IDs
mapfile -t volume_ids < <(echo "$volumes_json" | jq -r '.attached_volumes[].id')

# Check if there are any attached volumes
if [ ${#volume_ids[@]} -eq 0 ]; then
    echo "No attached volumes found for server $selected_server_id."
    exit 1
fi

# Get details of each attached volume
volume_info=()
for volume_id in "${volume_ids[@]}"; do
    volume_json=$(openstack volume show "$volume_id" -c attachments -f json)
    device=$(echo "$volume_json" | jq -r '.attachments[0].device')

    # Store volume info (ID and device)
    volume_info+=("$volume_id $device")
done

# Display numbered list
echo "Select a volume by number:"
for i in "${!volume_info[@]}"; do
    volume_id=$(echo "${volume_info[i]}" | awk '{print $1}')
    device=$(echo "${volume_info[i]}" | awk '{print $2}')
    echo "$((i+1)). $volume_id (Device: $device)"
done

# Get user input
read -p "Enter the number: " choice

# Validate input
if [[ $choice -ge 1 && $choice -le ${#volume_info[@]} ]]; then
    selected_volume_id=$(echo "${volume_info[$((choice-1))]}" | awk '{print $1}')
    echo "You selected volume: $selected_volume_id"
else
    echo "Invalid choice. Exiting."
    exit 1
fi

## - End of Volume -

# Get all snapshots for the project "bigstack"
snapshots_json=$(openstack volume snapshot list --project="$project_name" --long -f json)

# Extract snapshots related to the selected volume
mapfile -t snapshot_info < <(echo "$snapshots_json" | jq -c --arg vol "$selected_volume_id" \
    '.[] | select(.Volume == $vol) | {name: .Name, id: .ID, created_at: .["Created At"]}')

# Check if there are any snapshots
if [ ${#snapshot_info[@]} -eq 0 ]; then
    echo "No snapshots found for volume: $selected_volume_id"
    exit 1
fi

# Convert "Created At" to human-readable format and display options
echo "Select a snapshot by number:"
for i in "${!snapshot_info[@]}"; do
    name=$(echo "${snapshot_info[i]}" | jq -r '.name')        # Extract snapshot name
    id=$(echo "${snapshot_info[i]}" | jq -r '.id')            # Extract snapshot ID
    created_at=$(echo "${snapshot_info[i]}" | jq -r '.created_at')  # Extract Created At

    # Convert to human-readable format
    human_date=$(date -d "$created_at" '+%Y-%m-%d %H:%M:%S' 2>/dev/null)

    # If date conversion fails, set a fallback
    if [[ -z "$human_date" ]]; then
        human_date="Invalid Date"
    fi

    echo "$((i+1)). $name - Created: $human_date"
done

# Get user selection
read -p "Enter the number: " choice

# Validate input
if [[ $choice -ge 1 && $choice -le ${#snapshot_info[@]} ]]; then
    selected_snapshot_id=$(echo "${snapshot_info[$((choice-1))]}" | jq -r '.id')  # Extract snapshot ID
    echo "You selected snapshot: $selected_snapshot_id"
else
    echo "Invalid choice. Exiting."
    exit 1
fi

## - End of Snapshot -

# Ask for final confirmation
echo ""
echo "WARNING: This operation will revert the volume ($selected_volume_id) to the selected snapshot ($selected_snapshot_id)."
echo "The associated server will be SHUTDOWN, and all current disk data will be LOST."
echo ""
read -p "Type 'YES' to confirm: " confirmation

# Check if the user entered exactly "YES"
if [[ "$confirmation" != "YES" ]]; then
    echo "Operation canceled. No changes were made."
    exit 1
fi

# Proceed with the revert operation, ensure the server is SHUTOFF before proceeding
echo "Stopping the server ($selected_server_id)..."
openstack server stop "$selected_server_id"

# Wait until the server is fully stopped
echo "Waiting for server ($selected_server_id) to shut down..."
while true; do
    vm_state=$(openstack server show "$selected_server_id" -c vm_state -f value)
    if [[ "$vm_state" == "stopped" ]]; then
        echo "✅ Server is now stopped."
        break
    fi
    echo "⏳ Server is still stopping... checking again in 5 seconds."
    sleep 5
done

echo "Reverting volume $selected_volume_id to snapshot $selected_snapshot_id..."
rbd snap rollback cinder-volumes/volume-"$selected_volume_id"@snapshot-"$selected_snapshot_id"

echo "Revert operation completed successfully."

echo "Starting the server ($selected_server_id)..."
openstack server start "$selected_server_id"

echo "Server started successfully."
```

## Example output from a complete restore session {#example-output-from-a-complete-restore-session}

```bash title="Example restore session"
Select a project by number:
1. admin
2. _diagnostics
3. bigstack
Enter the number: 3
You selected: bigstack
Select a server by number:
1. vm-snap-restore (52d7f3fa-4d6f-4868-b271-e018c78a061f)
Enter the number: 1
You selected: vm-snap-restore (52d7f3fa-4d6f-4868-b271-e018c78a061f)
Select a volume by number:
1. d11dfee8-3067-4031-8e4d-da3bc867c913 (Device: /dev/sda)
2. 684483a5-4bd6-448f-810a-81517d82754f (Device: /dev/sdb)
Enter the number: 1
You selected volume: d11dfee8-3067-4031-8e4d-da3bc867c913
Select a snapshot by number:
1. snapshot for add-data-v3 - Created: 2025-02-06 05:42:57
2. snapshot for add-data-v1 - Created: 2025-02-06 05:42:11
3. snapshot for snap-data - Created: 2025-02-06 02:01:19
Enter the number: 1
You selected snapshot: 50927e68-9330-46d6-bf8a-1dac0db8e652

WARNING: This operation will revert the volume
(d11dfee8-3067-4031-8e4d-da3bc867c913) to the selected snapshot
(50927e68-9330-46d6-bf8a-1dac0db8e652).
The associated server will be SHUTDOWN, and all current disk data will be LOST.

Type 'YES' to confirm: YES
Stopping the server (52d7f3fa-4d6f-4868-b271-e018c78a061f)...
Waiting for server (52d7f3fa-4d6f-4868-b271-e018c78a061f) to shut down...
Server is still stopping. Checking again in 5 seconds.
Server is now stopped.
Reverting volume d11dfee8-3067-4031-8e4d-da3bc867c913 to snapshot 50927e68-9330-46d6-bf8a-1dac0db8e652...
Rolling back to snapshot: 100% complete...done.
Revert operation completed successfully.
Starting the server (52d7f3fa-4d6f-4868-b271-e018c78a061f)...
Server started successfully.
```

## Troubleshooting common restore failures {#troubleshooting-common-restore-failures}

### The script can't list projects {#the-script-cant-list-projects}

Confirm that `/etc/admin-openrc.sh` exists and contains valid OpenStack credentials.

To test authentication:

```bash title="Test OpenStack authentication"
openstack project list
```

### No servers are listed {#no-servers-are-listed}

Confirm that the selected project contains servers and that your credentials can read them:

```bash title="List servers in a project"
openstack server list --project PROJECT_NAME --long
```

### No attached volumes are found {#no-attached-volumes-are-found}

Confirm that the selected server has attached volumes:

```bash title="Show attached server volumes"
openstack server show SERVER_ID -c attached_volumes
```

### No snapshots are found {#no-snapshots-are-found}

Confirm that the selected volume has snapshots in the selected project:

```bash title="List volume snapshots"
openstack volume snapshot list --project PROJECT_NAME --long
```

### The server doesn't stop {#the-server-doesnt-stop}

Check the server state and hypervisor status:

```bash title="Check server state"
openstack server show SERVER_ID -c status -c vm_state -c OS-EXT-SRV-ATTR:host
```

Don't run the RBD rollback until the server has stopped.

### The rollback fails {#the-rollback-fails}

Confirm that the Ceph pool, volume ID, and snapshot ID are correct:

```bash title="List RBD snapshots for a volume"
rbd snap ls cinder-volumes/volume-VOLUME_ID
```

Then verify that the rollback target exists:

```bash title="Verify the rollback target"
rbd info cinder-volumes/volume-VOLUME_ID@snapshot-SNAPSHOT_ID
```
