Statistics
| Branch: | Tag: | Revision:

root / snf-cyclades-app / synnefo / volume / snapshots.py @ 83f46e8b

History | View | Annotate | Download (4.8 kB)

1
import logging
2
from django.db import transaction
3
from snf_django.lib.api import faults
4
from synnefo.plankton.utils import image_backend
5
from synnefo.logic import backend
6
from synnefo.volume import util
7

    
8
log = logging.getLogger(__name__)
9

    
10
PLANKTON_DOMAIN = "plankton"
11
PLANKTON_PREFIX = "plankton:"
12

    
13
PROPERTY_PREFIX = "property:"
14

    
15
SNAPSHOT_PREFIX = "snapshot:"
16
SNAPSHOTS_CONTAINER = "snapshots"
17
SNAPSHOTS_TYPE = "application/octet-stream"
18
SNAPSHOTS_MAPFILE_PREFIX = "archip:"
19

    
20

    
21
@transaction.commit_on_success
22
def create(user_id, volume, name, description, metadata, force=False):
23
    """Create a snapshot from a given volume
24

25
    Create a snapshot from a given volume. The snapshot is first created as
26
    a file in Pithos, with specified metadata to indicate that it is a
27
    snapshot. Then a job is sent to Ganeti backend to create the actual
28
    snapshot of the volume.
29

30
    Snapshots are only supported for volumes of ext_ disk template. Also,
31
    the volume must be attached to some server.
32

33
    """
34

    
35
    # Check that taking a snapshot is feasible
36
    if volume.machine is None:
37
        raise faults.BadRequest("Cannot snapshot a detached volume!")
38
    if volume.status not in ["AVAILABLE", "IN_USE"]:
39
        raise faults.BadRequest("Cannot create snapshot while volume is in"
40
                                " '%s' status" % volume.status)
41

    
42
    flavor = volume.machine.flavor
43
    if not flavor.disk_template.startswith("ext_"):
44
        msg = ("Snapshots are supported only for volumes of ext_*"
45
               " disk template")
46
        raise faults.BadRequest(msg)
47

    
48
    # Increase the snapshot counter of the volume that is used in order to
49
    # generate unique snapshot names
50
    volume.snapshot_counter += 1
51
    volume.save()
52
    transaction.commit()
53

    
54
    snapshot_metadata = {
55
        PLANKTON_PREFIX + "name": name,
56
        PLANKTON_PREFIX + "status": "CREATING",
57
        PLANKTON_PREFIX + "disk_format": "diskdump",
58
        PLANKTON_PREFIX + "container_format": "bare",
59
        PLANKTON_PREFIX + "is_snapshot": True,
60
        # Snapshot specific
61
        PLANKTON_PREFIX + "description": description,
62
        PLANKTON_PREFIX + "volume_id": volume.id,
63
    }
64

    
65
    # Snapshots are used as images. We set the most important properties
66
    # that are being used for images. We set 'EXCLUDE_ALL_TASKS' to bypass
67
    # image customization. Also, we get some basic metadata for the volume from
68
    # the server that the volume is attached
69
    metadata.update({"EXCLUDE_ALL_TASKS": "yes",
70
                     "description": description})
71
    vm_metadata = dict(volume.machine.metadata
72
                                     .filter(meta_key__in=["OS", "users"])
73
                                     .values_list("meta_key", "meta_value"))
74
    metadata.update(vm_metadata)
75

    
76
    for key, val in metadata.items():
77
        snapshot_metadata[PLANKTON_PREFIX + PROPERTY_PREFIX + key] = val
78

    
79
    # Generate a name for the Pithos file. Also, generate a name for the
80
    # Archipelago mapfile.
81
    snapshot_pithos_name = generate_snapshot_pithos_name(volume)
82
    mapfile = SNAPSHOTS_MAPFILE_PREFIX + snapshot_pithos_name
83

    
84
    # Convert size from Gbytes to bytes
85
    size = volume.size << 30
86

    
87
    with image_backend(user_id) as pithos_backend:
88
        # move this to plankton backend
89
        snapshot_id = pithos_backend.backend.register_object_map(
90
            user=user_id,
91
            account=user_id,
92
            container=SNAPSHOTS_CONTAINER,
93
            name=snapshot_pithos_name,
94
            size=size,
95
            domain=PLANKTON_DOMAIN,
96
            type=SNAPSHOTS_TYPE,
97
            mapfile=mapfile,
98
            meta=snapshot_metadata,
99
            replace_meta=True,
100
            permissions=None)
101

    
102
    backend.snapshot_instance(volume.machine,
103
                              snapshot_name=snapshot_pithos_name,
104
                              snapshot_id=snapshot_id)
105

    
106
    snapshot = util.get_snapshot(user_id, snapshot_id)
107

    
108
    return snapshot
109

    
110

    
111
def generate_snapshot_pithos_name(volume):
112
    """Helper function to generate a name for the Pithos file."""
113
    # time = isoformat(datetime.datetime.now())
114
    return "snf-snap-%s-%s" % (volume.id,
115
                               volume.snapshot_counter)
116

    
117

    
118
@transaction.commit_on_success
119
def delete(snapshot):
120
    """Delete a snapshot.
121

122
    Delete a snapshot by deleting the corresponding file from Pithos.
123

124
    """
125
    user_id = snapshot["owner"]
126
    log.info("Deleting snapshot '%s'", snapshot["location"])
127
    with image_backend(user_id) as pithos_backend:
128
        pithos_backend.delete_snapshot(snapshot["id"])
129
    return snapshot
130

    
131

    
132
def rename(snapshot, new_name):
133
    # user_id = snapshot["owner"]
134
    raise NotImplemented("Renaming a snapshot is not implemented!")
135

    
136

    
137
def update_description(snapshot, new_description):
138
    # user_id = snapshot["owner"]
139
    raise NotImplemented("Updating snapshot's description is not implemented!")