Statistics
| Branch: | Tag: | Revision:

root / snf-pithos-app / pithos / api / management / commands / pithos-reset-usage.py @ 7c740d3c

History | View | Annotate | Download (6.1 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
ResetHoldingPayload = namedtuple('ResetHoldingPayload', (
49
                'entity', 'resource', 'key',
50
                'imported', 'exported', 'returned', 'released'))
51
ENTITY_KEY = '1'
52

    
53
backend = get_backend()
54
table = {}
55
table['nodes'] = backend.node.nodes
56
table['versions'] = backend.node.versions
57
table['statistics'] = backend.node.statistics
58
table['policy'] = backend.node.policy
59
conn = backend.node.conn
60

    
61

    
62
def _compute_statistics(nodes):
63
    statistics = []
64
    append = statistics.append
65
    for path, node in nodes:
66
        select_children = select(
67
            [table['nodes'].c.node]).where(table['nodes'].c.parent == node)
68
        select_descendants = select([table['nodes'].c.node]).where(
69
            or_(table['nodes'].c.parent.in_(select_children),
70
                table['nodes'].c.node.in_(select_children)))
71
        s = select([table['versions'].c.cluster,
72
                    func.sum(table['versions'].c.size)])
73
        s = s.group_by(table['versions'].c.cluster)
74
        s = s.where(table['nodes'].c.node == table['versions'].c.node)
75
        s = s.where(table['nodes'].c.node.in_(select_descendants))
76
        d2 = dict(conn.execute(s).fetchall())
77

    
78
        for cluster in clusters:
79
            try:
80
                size = d2[cluster]
81
            except KeyError:
82
                size = 0
83
            append(Statistics(
84
                node=node,
85
                path=path,
86
                size=size,
87
                cluster=cluster))
88
    return statistics
89

    
90

    
91
def _get_verified_usage(statistics):
92
    """Verify statistics and set quotaholder account usage"""
93
    reset_holding = []
94
    append = reset_holding.append
95
    for item in statistics:
96
        s = select([table['statistics'].c.size])
97
        s = s.where(table['statistics'].c.node == item.node)
98
        s = s.where(table['statistics'].c.cluster == item.cluster)
99
        db_item = conn.execute(s).fetchone()
100
        if not db_item:
101
            continue
102
        try:
103
            assert item.size == db_item.size, \
104
                    '%d[%s][%d], size: %d != %d' % (
105
                            item.node, item.path, item.cluster,
106
                            item.size, db_item.size)
107
        except AssertionError, e:
108
            print e
109
        if item.cluster == CLUSTER_NORMAL:
110
            append(ResetHoldingPayload(
111
                    entity=item.path,
112
                    resource='pithos+.diskspace',
113
                    key=ENTITY_KEY,
114
                    imported=item.size,
115
                    exported=0,
116
                    returned=0,
117
                    released=0))
118
    return reset_holding
119

    
120

    
121
class Command(NoArgsCommand):
122
    help = "Set quotaholder account usage"
123

    
124
    def handle_noargs(self, **options):
125
        try:
126
            if not backend.quotaholder_url:
127
                raise CommandError('Quotaholder component url is not set')
128

    
129
            if not backend.quotaholder_token:
130
                raise CommandError('Quotaholder component token is not set')
131

    
132
            # retrieve account nodes
133
            s = select([table['nodes'].c.path, table['nodes'].c.node])
134
            s = s.where(and_(table['nodes'].c.node != 0,
135
                             table['nodes'].c.parent == 0))
136
            account_nodes = conn.execute(s).fetchall()
137

    
138
            # compute account statistics
139
            statistics = _compute_statistics(account_nodes)
140

    
141
            # verify and send usage
142
            reset_holding = _get_verified_usage(statistics)
143

    
144
            while True:
145
                result = backend.quotaholder.reset_holding(
146
                    context={},
147
                    clientkey='pithos',
148
                    reset_holding=reset_holding)
149

    
150
                if not result:
151
                    break
152

    
153
                missing_entities = [reset_holding[x].entity for x in result]
154
                self.stdout.write(
155
                        'Unknown quotaholder accounts: %s\n' %
156
                        ','.join(missing_entities))
157
                m = 'Retrying sending quota usage for the rest...\n'
158
                self.stdout.write(m)
159
                missing_indexes = set(result)
160
                reset_holding = [x for i, x in enumerate(reset_holding)
161
                                 if i not in missing_indexes]
162
        finally:
163
            backend.close()