Statistics
| Branch: | Tag: | Revision:

root / snf-pithos-app / pithos / api / management / commands / pithos-set-quota.py @ 6c997921

History | View | Annotate | Download (6.2 kB)

1
# Copyright 2012 GRNET S.A. All rights reserved.
2
#
3
# Redistribution and use in source and binary forms, with or
4
# without modification, are permitted provided that the following
5
# conditions are met:
6
#
7
#   1. Redistributions of source code must retain the above
8
#      copyright notice, this list of conditions and the following
9
#      disclaimer.
10
#
11
#   2. Redistributions in binary form must reproduce the above
12
#      copyright notice, this list of conditions and the following
13
#      disclaimer in the documentation and/or other materials
14
#      provided with the distribution.
15
#
16
# THIS SOFTWARE IS PROVIDED BY GRNET S.A. ``AS IS'' AND ANY EXPRESS
17
# OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
18
# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
19
# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL GRNET S.A OR
20
# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
21
# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
22
# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
23
# USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
24
# AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25
# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
26
# ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
27
# POSSIBILITY OF SUCH DAMAGE.
28
#
29
# The views and conclusions contained in the software and
30
# documentation are those of the authors and should not be
31
# interpreted as representing official policies, either expressed
32
# or implied, of GRNET S.A.
33

    
34
from django.core.management.base import NoArgsCommand, CommandError
35

    
36
from collections import namedtuple
37
from sqlalchemy import func
38
from sqlalchemy.sql import select, and_, or_
39

    
40
from pithos.api.util import get_backend
41
from pithos.backends.modular import (
42
    CLUSTER_NORMAL, CLUSTER_HISTORY, CLUSTER_DELETED
43
)
44
clusters = (CLUSTER_NORMAL, CLUSTER_HISTORY, CLUSTER_DELETED)
45

    
46
Statistics = namedtuple('Statistics', ('node', 'path', 'size', 'cluster'))
47

    
48
AddQuotaPayload = namedtuple('AddQuotaPayload', ('holder',
49
                                                 'resource',
50
                                                 'key',
51
                                                 'quantity',
52
                                                 'capacity',
53
                                                 'import_limit',
54
                                                 'export_limit'))
55

    
56
ENTITY_KEY= '1'
57

    
58
backend = get_backend()
59
table = {}
60
table['nodes'] = backend.node.nodes
61
table['versions'] = backend.node.versions
62
table['statistics'] = backend.node.statistics
63
table['policy'] = backend.node.policy
64
conn = backend.node.conn
65

    
66
def _compute_statistics(nodes):
67
    statistics = []
68
    append = statistics.append
69
    for path, node in nodes:
70
        select_children = select(
71
            [table['nodes'].c.node]).where(table['nodes'].c.parent==node)
72
        select_descendants = select([table['nodes'].c.node]).where(
73
            or_(table['nodes'].c.parent.in_(select_children),
74
                table['nodes'].c.node.in_(select_children)))
75
        s = select([table['versions'].c.cluster,
76
                    func.sum(table['versions'].c.size)])
77
        s = s.group_by(table['versions'].c.cluster)
78
        s = s.where(table['nodes'].c.node == table['versions'].c.node)
79
        s = s.where(table['nodes'].c.node.in_(select_descendants))
80
        d2 = dict(conn.execute(s).fetchall())
81
        
82
        for cluster in clusters: 
83
            try:
84
                size = d2[cluster]
85
            except KeyError:
86
                size = 0 
87
            append(Statistics(
88
                node=node,
89
                path=path,
90
                size=size,
91
            cluster=cluster))
92
    return statistics
93

    
94
def _get_verified_quota(statistics, default_quota=0):
95
    """ Verify statistics and set quotaholder account quota """
96
    add_quota = []
97
    append = add_quota.append
98
    for item in statistics:
99
        s = select([table['statistics'].c.size])
100
        s = s.where(table['statistics'].c.node == item.node)
101
        s = s.where(table['statistics'].c.cluster == item.cluster)
102
        db_item = conn.execute(s).fetchone()
103
        if not db_item:
104
            continue
105
        try:
106
            assert item.size == db_item.size, \
107
                '%d[%d], size: %d != %d' % (
108
                    item.node, item.cluster, item.size, db_item.size)
109
        except AssertionError, e:
110
            print e
111
            continue
112
        else:
113
            s = select([table['policy'].c.value])
114
            s = s.where(table['policy'].c.node == item.node)
115
            s = s.where(table['policy'].c.key == 'quota')
116
            policy = conn.execute(s).fetchone()
117
            capacity = policy.value if policy else 0
118
            if capacity:
119
                capacity -= default_quota
120
            append(AddQuotaPayload(
121
                holder=item.path,
122
                resource='pithos+.diskspace',
123
                key=ENTITY_KEY,
124
                quantity=db_item.size,
125
                capacity=capacity,
126
                import_limit=0,
127
                export_limit=0))
128
    return add_quota
129
             
130
    
131
class Command(NoArgsCommand):
132
    help = "Set quotaholder account quotas"
133

    
134
    def handle_noargs(self, **options):
135
        try:
136
            if not backend.quotaholder_url:
137
                raise CommandError('Quotaholder component url is not set')
138
            
139
            if not backend.quotaholder_token:
140
                raise CommandError('Quotaholder component token is not set')
141

    
142
            # retrieve account nodes
143
            s = select([table['nodes'].c.path, table['nodes'].c.node])
144
            s = s.where(and_(table['nodes'].c.node != 0,
145
                             table['nodes'].c.parent == 0))
146
            account_nodes = conn.execute(s).fetchall()
147
            
148
            # compute account statistics
149
            statistics = _compute_statistics(
150
                account_nodes, default_quota=backend.DEFAULT_QUOTA)
151

    
152
            # verify and send quota
153
            add_quota = _get_verified_quota(statistics)
154
            result = backend.quotaholder.add_quota(
155
                context={},
156
                clientkey='pithos',
157
            serial=42,
158
            sub_quota=[],
159
            add_quota=add_quota)
160
            if result:
161
                raise CommandError(result)
162
        finally:      
163
            backend.close()