Hot-keys on this page

r m x p   toggle line displays

j k   next/prev highlighted chunk

0   (zero) top of page

1   (one) first highlighted chunk

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

57

58

59

60

61

62

63

64

65

66

67

68

69

70

71

72

73

74

75

76

77

78

79

80

81

82

83

84

85

86

87

88

89

90

91

92

93

94

95

96

97

98

99

100

101

102

103

104

105

106

107

108

109

110

111

112

113

114

115

116

117

118

119

120

121

122

123

124

125

126

127

128

129

130

131

132

133

134

135

136

137

""" 

Copyright (c) 2012-2013 RockStor, Inc. <http://rockstor.com> 

This file is part of RockStor. 

 

RockStor is free software; you can redistribute it and/or modify 

it under the terms of the GNU General Public License as published 

by the Free Software Foundation; either version 2 of the License, 

or (at your option) any later version. 

 

RockStor is distributed in the hope that it will be useful, but 

WITHOUT ANY WARRANTY; without even the implied warranty of 

MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU 

General Public License for more details. 

 

You should have received a copy of the GNU General Public License 

along with this program. If not, see <http://www.gnu.org/licenses/>. 

""" 

 

from storageadmin.models import (Share, Snapshot) 

from storageadmin.util import handle_exception 

from fs.btrfs import (add_clone, share_id, update_quota, mount_share, 

                      qgroup_create, set_property, remove_share, 

                      share_pqgroup_assign) 

from rest_framework.response import Response 

from storageadmin.serializers import ShareSerializer 

import re 

import shutil 

from django.conf import settings 

 

from system.osi import run_command 

 

 

def create_repclone(share, request, logger, snapshot): 

    """ 

    Variant of create_clone but where the share already exists and is to be 

    supplanted by a snapshot which is effectively moved into the shares prior 

    position, both in the db and on the file system. This is achieved thus: 

    Unmount target share - (via remove_share()). 

    Btrfs subvol delete target share (via remove_share()). 

    Remove prior target share mount point (dir). 

    Move snap source to target share's former location (becomes share on disk). 

    Update existing target share db entry with source snap's qgroup / usage. 

    Remove source snap's db entry: updated share db entry makes it redundant. 

    Remount share (which now represents the prior snap's subvol relocated). 

    :param share: Share object to be supplanted 

    :param request: 

    :param logger: Logger object to reference 

    :param snapshot: Source snapshot/quirk share object to supplant target. 

    :return: response of serialized share (in it's updated form) 

    """ 

    try: 

        logger.debug('Supplanting share ({}) with ' 

                     'snapshot ({}).'.format(share.name, snapshot.name)) 

        # We first strip our snapshot.name of any path as when we encounter the 

        # initially created receive subvol it is identified as a share with a 

        # snapshots location as it's subvol name (current quirk of import sys). 

        # E.g. first receive subvol/share-in-snapdir name example: 

        # ".snapshots/C583C37F-...1712B_sharename/sharename_19_replication_1". 

        # Subsequent more regular snapshots (in db as such) are named thus: 

        # "sharename_19_replication_2" or "sharename_19_replication_2" and on. 

        # The 19 in the above names is the generation of the replication task. 

        # 

        # Normalise source name across initial quirk share & subsequent snaps. 

        source_name = snapshot.name.split('/')[-1] 

        # Note in the above we have to use Object.name for polymorphism, but 

        # our share is passed by it's subvol (potential fragility point). 

        snap_path = '{}{}/.snapshots/{}/{}'.format(settings.MNT_PT, 

                                                   share.pool.name, share.name, 

                                                   source_name) 

        # eg /mnt2/poolname/.snapshots/sharename/snapname 

        share_path = ('{}{}/{}'.format(settings.MNT_PT, share.pool.name, 

                                       share.name)) 

        # eg /mnt2/poolname/sharename 

        # unmount and then subvol deletes our on disk share 

        remove_share(share.pool, share.name, '-1/-1') 

        # Remove read only flag on our snapshot subvol 

        set_property(snap_path, 'ro', 'false', mount=False) 

        # Ensure removed share path is clean, ie remove mount point. 

        run_command(['/usr/bin/rm', '-rf', share_path], throw=False) 

        # Now move snapshot to prior shares location. Given both a share and 

        # a snapshot are subvols, we effectively promote the snap to a share. 

        shutil.move(snap_path, share_path) 

        # This should have re-established our just removed subvol. 

        # Supplant share db info with snap info to reflect new on disk state. 

        share.qgroup = snapshot.qgroup 

        share.rusage = snapshot.rusage 

        share.eusage = snapshot.eusage 

        share.save() 

        # delete our now redundant snapshot/quirky share db entry 

        snapshot.delete() 

        # update our share's quota 

        update_quota(share.pool, share.pqgroup, share.size * 1024) 

        # mount our newly supplanted share 

        mnt_pt = '{}{}'.format(settings.MNT_PT, share.name) 

        mount_share(share, mnt_pt) 

        return Response(ShareSerializer(share).data) 

    except Exception as e: 

        handle_exception(e, request) 

 

 

def create_clone(share, new_name, request, logger, snapshot=None): 

    # if snapshot is None, create clone of the share. 

    # If it's not, then clone it. 

    if (re.match(settings.SHARE_REGEX + '$', new_name) is None): 

        e_msg = ('Clone name is invalid. It must start with a letter and can' 

                 ' contain letters, digits, _, . and - characters') 

        handle_exception(Exception(e_msg), request) 

    if (Share.objects.filter(name=new_name).exists()): 

        e_msg = ('Another Share with name: %s already exists.' % new_name) 

        handle_exception(Exception(e_msg), request) 

    if (Snapshot.objects.filter(share=share, name=new_name).exists()): 

        e_msg = ('Snapshot with name: %s already exists for the ' 

                 'share: %s. Choose a different name' % 

                 (new_name, share.name)) 

        handle_exception(Exception(e_msg), request) 

 

    try: 

        share_name = share.subvol_name 

        snap = None 

        if (snapshot is not None): 

            snap = snapshot.real_name 

        add_clone(share.pool, share_name, new_name, snapshot=snap) 

        snap_id = share_id(share.pool, new_name) 

        qgroup_id = ('0/%s' % snap_id) 

        pqid = qgroup_create(share.pool) 

        new_share = Share(pool=share.pool, qgroup=qgroup_id, pqgroup=pqid, 

                          name=new_name, size=share.size, subvol_name=new_name) 

        new_share.save() 

        if pqid is not settings.MODEL_DEFS['pqgroup']: 

            update_quota(new_share.pool, pqid, new_share.size * 1024) 

            share_pqgroup_assign(pqid, new_share) 

        # Mount our new clone share. 

        mnt_pt = '{}{}'.format(settings.MNT_PT, new_name) 

        mount_share(new_share, mnt_pt) 

        return Response(ShareSerializer(new_share).data) 

    except Exception as e: 

        handle_exception(e, request)