Statistics
| Branch: | Tag: | Revision:

root / snf-cyclades-app / synnefo / volume / snapshots.py @ 3a3dea88

History | View | Annotate | Download (4.6 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

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

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

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

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

    
73
    for key, val in metadata.items():
74
        snapshot_metadata[PLANKTON_PREFIX + PROPERTY_PREFIX + key] = val
75

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

    
81
    # Convert size from Gbytes to bytes
82
    size = volume.size << 30
83

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

    
99
    backend.snapshot_instance(volume.machine,
100
                              snapshot_name=snapshot_pithos_name,
101
                              snapshot_id=snapshot_id)
102

    
103
    snapshot = util.get_snapshot(user_id, snapshot_id)
104

    
105
    return snapshot
106

    
107

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

    
114

    
115
@transaction.commit_on_success
116
def delete(snapshot):
117
    """Delete a snapshot.
118

119
    Delete a snapshot by deleting the corresponding file from Pithos.
120

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

    
128

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

    
133

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