Statistics
| Branch: | Tag: | Revision:

root / snf-pithos-app / pithos / api / management / commands / pithos-usage.py @ 8108c68c

History | View | Annotate | Download (7.9 kB)

1 4c03c942 Sofia Papagiannaki
# Copyright 2012 GRNET S.A. All rights reserved.
2 4c03c942 Sofia Papagiannaki
#
3 4c03c942 Sofia Papagiannaki
# Redistribution and use in source and binary forms, with or
4 4c03c942 Sofia Papagiannaki
# without modification, are permitted provided that the following
5 4c03c942 Sofia Papagiannaki
# conditions are met:
6 4c03c942 Sofia Papagiannaki
#
7 4c03c942 Sofia Papagiannaki
#   1. Redistributions of source code must retain the above
8 4c03c942 Sofia Papagiannaki
#      copyright notice, this list of conditions and the following
9 4c03c942 Sofia Papagiannaki
#      disclaimer.
10 4c03c942 Sofia Papagiannaki
#
11 4c03c942 Sofia Papagiannaki
#   2. Redistributions in binary form must reproduce the above
12 4c03c942 Sofia Papagiannaki
#      copyright notice, this list of conditions and the following
13 4c03c942 Sofia Papagiannaki
#      disclaimer in the documentation and/or other materials
14 4c03c942 Sofia Papagiannaki
#      provided with the distribution.
15 4c03c942 Sofia Papagiannaki
#
16 4c03c942 Sofia Papagiannaki
# THIS SOFTWARE IS PROVIDED BY GRNET S.A. ``AS IS'' AND ANY EXPRESS
17 4c03c942 Sofia Papagiannaki
# OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
18 4c03c942 Sofia Papagiannaki
# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
19 4c03c942 Sofia Papagiannaki
# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL GRNET S.A OR
20 4c03c942 Sofia Papagiannaki
# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
21 4c03c942 Sofia Papagiannaki
# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
22 4c03c942 Sofia Papagiannaki
# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
23 4c03c942 Sofia Papagiannaki
# USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
24 4c03c942 Sofia Papagiannaki
# AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25 4c03c942 Sofia Papagiannaki
# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
26 4c03c942 Sofia Papagiannaki
# ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
27 4c03c942 Sofia Papagiannaki
# POSSIBILITY OF SUCH DAMAGE.
28 4c03c942 Sofia Papagiannaki
#
29 4c03c942 Sofia Papagiannaki
# The views and conclusions contained in the software and
30 4c03c942 Sofia Papagiannaki
# documentation are those of the authors and should not be
31 4c03c942 Sofia Papagiannaki
# interpreted as representing official policies, either expressed
32 4c03c942 Sofia Papagiannaki
# or implied, of GRNET S.A.
33 4c03c942 Sofia Papagiannaki
34 4c03c942 Sofia Papagiannaki
from django.core.management.base import NoArgsCommand, CommandError
35 4c03c942 Sofia Papagiannaki
36 4c03c942 Sofia Papagiannaki
from collections import namedtuple
37 384dee7e Sofia Papagiannaki
from optparse import make_option
38 4c03c942 Sofia Papagiannaki
from sqlalchemy import func
39 4c03c942 Sofia Papagiannaki
from sqlalchemy.sql import select, and_, or_
40 4c03c942 Sofia Papagiannaki
41 4c03c942 Sofia Papagiannaki
from pithos.api.util import get_backend
42 4c03c942 Sofia Papagiannaki
from pithos.backends.modular import (
43 4c03c942 Sofia Papagiannaki
    CLUSTER_NORMAL, CLUSTER_HISTORY, CLUSTER_DELETED
44 4c03c942 Sofia Papagiannaki
)
45 4c03c942 Sofia Papagiannaki
clusters = (CLUSTER_NORMAL, CLUSTER_HISTORY, CLUSTER_DELETED)
46 4c03c942 Sofia Papagiannaki
47 989576a6 Sofia Papagiannaki
Usage = namedtuple('Usage', ('node', 'path', 'size', 'cluster'))
48 989576a6 Sofia Papagiannaki
GetQuota = namedtuple('GetQuota', ('entity', 'resource', 'key'))
49 4c03c942 Sofia Papagiannaki
50 3e1b20d6 Sofia Papagiannaki
class ResetHoldingPayload(namedtuple('ResetHoldingPayload', (
51 3e1b20d6 Sofia Papagiannaki
    'entity', 'resource', 'key', 'imported', 'exported', 'returned', 'released'
52 3e1b20d6 Sofia Papagiannaki
))):
53 3e1b20d6 Sofia Papagiannaki
    __slots__ = ()
54 3e1b20d6 Sofia Papagiannaki
55 3e1b20d6 Sofia Papagiannaki
    def __str__(self):
56 3e1b20d6 Sofia Papagiannaki
        return '%s: %s' % (self.entity, self.imported)
57 3e1b20d6 Sofia Papagiannaki
58 3e1b20d6 Sofia Papagiannaki
59 09e69b8a Georgios D. Tsoukalas
ENTITY_KEY = '1'
60 4c03c942 Sofia Papagiannaki
61 4c03c942 Sofia Papagiannaki
backend = get_backend()
62 4c03c942 Sofia Papagiannaki
table = {}
63 4c03c942 Sofia Papagiannaki
table['nodes'] = backend.node.nodes
64 4c03c942 Sofia Papagiannaki
table['versions'] = backend.node.versions
65 6c997921 Sofia Papagiannaki
table['policy'] = backend.node.policy
66 4c03c942 Sofia Papagiannaki
conn = backend.node.conn
67 4c03c942 Sofia Papagiannaki
68 3e1b20d6 Sofia Papagiannaki
def _retrieve_user_nodes(users=()):
69 3e1b20d6 Sofia Papagiannaki
    s = select([table['nodes'].c.path, table['nodes'].c.node])
70 3e1b20d6 Sofia Papagiannaki
    s = s.where(and_(table['nodes'].c.node != 0,
71 3e1b20d6 Sofia Papagiannaki
                     table['nodes'].c.parent == 0))
72 3e1b20d6 Sofia Papagiannaki
    if users:
73 3e1b20d6 Sofia Papagiannaki
        s = s.where(table['nodes'].c.path.in_(users))
74 3e1b20d6 Sofia Papagiannaki
    return conn.execute(s).fetchall()
75 09e69b8a Georgios D. Tsoukalas
76 989576a6 Sofia Papagiannaki
def _compute_usage(nodes):
77 989576a6 Sofia Papagiannaki
    usage = []
78 989576a6 Sofia Papagiannaki
    append = usage.append
79 4c03c942 Sofia Papagiannaki
    for path, node in nodes:
80 4c03c942 Sofia Papagiannaki
        select_children = select(
81 09e69b8a Georgios D. Tsoukalas
            [table['nodes'].c.node]).where(table['nodes'].c.parent == node)
82 4c03c942 Sofia Papagiannaki
        select_descendants = select([table['nodes'].c.node]).where(
83 4c03c942 Sofia Papagiannaki
            or_(table['nodes'].c.parent.in_(select_children),
84 4c03c942 Sofia Papagiannaki
                table['nodes'].c.node.in_(select_children)))
85 4c03c942 Sofia Papagiannaki
        s = select([table['versions'].c.cluster,
86 4c03c942 Sofia Papagiannaki
                    func.sum(table['versions'].c.size)])
87 4c03c942 Sofia Papagiannaki
        s = s.group_by(table['versions'].c.cluster)
88 4c03c942 Sofia Papagiannaki
        s = s.where(table['nodes'].c.node == table['versions'].c.node)
89 4c03c942 Sofia Papagiannaki
        s = s.where(table['nodes'].c.node.in_(select_descendants))
90 9c0c8aa9 Sofia Papagiannaki
        s = s.where(table['versions'].c.cluster == CLUSTER_NORMAL)
91 4c03c942 Sofia Papagiannaki
        d2 = dict(conn.execute(s).fetchall())
92 d776b3c3 Sofia Papagiannaki
93 9c0c8aa9 Sofia Papagiannaki
        try:
94 9c0c8aa9 Sofia Papagiannaki
            size = d2[CLUSTER_NORMAL]
95 9c0c8aa9 Sofia Papagiannaki
        except KeyError:
96 9c0c8aa9 Sofia Papagiannaki
            size = 0
97 989576a6 Sofia Papagiannaki
        append(Usage(
98 9c0c8aa9 Sofia Papagiannaki
            node=node,
99 9c0c8aa9 Sofia Papagiannaki
            path=path,
100 9c0c8aa9 Sofia Papagiannaki
            size=size,
101 9c0c8aa9 Sofia Papagiannaki
            cluster=CLUSTER_NORMAL))
102 989576a6 Sofia Papagiannaki
    return usage
103 989576a6 Sofia Papagiannaki
104 989576a6 Sofia Papagiannaki
def _get_quotaholder_usage(usage):
105 989576a6 Sofia Papagiannaki
    payload = []
106 989576a6 Sofia Papagiannaki
    append = payload.append
107 989576a6 Sofia Papagiannaki
    [append(GetQuota(
108 989576a6 Sofia Papagiannaki
        entity=item.path,
109 989576a6 Sofia Papagiannaki
        resource='pithos+.diskspace',
110 989576a6 Sofia Papagiannaki
        key=ENTITY_KEY
111 989576a6 Sofia Papagiannaki
    )) for item in usage]
112 989576a6 Sofia Papagiannaki
113 989576a6 Sofia Papagiannaki
    result = backend.quotaholder.get_quota(
114 989576a6 Sofia Papagiannaki
        context={}, clientkey='pithos', get_quota=payload
115 989576a6 Sofia Papagiannaki
    )
116 989576a6 Sofia Papagiannaki
    return dict((entity, imported - exported + returned - released) for (
117 989576a6 Sofia Papagiannaki
        entity, resource, quantity, capacity, import_limit, export_limit,
118 989576a6 Sofia Papagiannaki
        imported, exported, returned, released, flags
119 989576a6 Sofia Papagiannaki
    ) in result)
120 989576a6 Sofia Papagiannaki
121 989576a6 Sofia Papagiannaki
122 989576a6 Sofia Papagiannaki
def _prepare_reset_holding(usage, only_diverging=False):
123 989576a6 Sofia Papagiannaki
    """Verify usage and set quotaholder user usage"""
124 a7447c76 Sofia Papagiannaki
    reset_holding = []
125 a7447c76 Sofia Papagiannaki
    append = reset_holding.append
126 989576a6 Sofia Papagiannaki
127 989576a6 Sofia Papagiannaki
    quotaholder_usage = {}
128 989576a6 Sofia Papagiannaki
    if only_diverging:
129 989576a6 Sofia Papagiannaki
        quotaholder_usage = _get_quotaholder_usage(usage)
130 989576a6 Sofia Papagiannaki
131 989576a6 Sofia Papagiannaki
    for item in(usage):
132 989576a6 Sofia Papagiannaki
        if only_diverging and quotaholder_usage.get(item.path) == item.size:
133 989576a6 Sofia Papagiannaki
            continue
134 989576a6 Sofia Papagiannaki
135 6674f31d Sofia Papagiannaki
        if item.cluster == CLUSTER_NORMAL:
136 6674f31d Sofia Papagiannaki
            append(ResetHoldingPayload(
137 6674f31d Sofia Papagiannaki
                    entity=item.path,
138 6674f31d Sofia Papagiannaki
                    resource='pithos+.diskspace',
139 6674f31d Sofia Papagiannaki
                    key=ENTITY_KEY,
140 6674f31d Sofia Papagiannaki
                    imported=item.size,
141 6674f31d Sofia Papagiannaki
                    exported=0,
142 6674f31d Sofia Papagiannaki
                    returned=0,
143 6674f31d Sofia Papagiannaki
                    released=0))
144 a7447c76 Sofia Papagiannaki
    return reset_holding
145 d776b3c3 Sofia Papagiannaki
146 d776b3c3 Sofia Papagiannaki
147 4c03c942 Sofia Papagiannaki
class Command(NoArgsCommand):
148 3e1b20d6 Sofia Papagiannaki
    help = "List and reset pithos usage"
149 4c03c942 Sofia Papagiannaki
150 384dee7e Sofia Papagiannaki
    option_list = NoArgsCommand.option_list + (
151 3e1b20d6 Sofia Papagiannaki
        make_option('--list',
152 3e1b20d6 Sofia Papagiannaki
                    dest='list',
153 3e1b20d6 Sofia Papagiannaki
                    action="store_true",
154 3e1b20d6 Sofia Papagiannaki
                    default=True,
155 3e1b20d6 Sofia Papagiannaki
                    help="List usage for all or specified user"),
156 3e1b20d6 Sofia Papagiannaki
        make_option('--reset',
157 3e1b20d6 Sofia Papagiannaki
                    dest='reset',
158 3e1b20d6 Sofia Papagiannaki
                    action="store_true",
159 3e1b20d6 Sofia Papagiannaki
                    default=False,
160 3e1b20d6 Sofia Papagiannaki
                    help="Reset usage for all or specified users"),
161 989576a6 Sofia Papagiannaki
        make_option('--diverging',
162 989576a6 Sofia Papagiannaki
                    dest='diverging',
163 9c0c8aa9 Sofia Papagiannaki
                    action="store_true",
164 9c0c8aa9 Sofia Papagiannaki
                    default=False,
165 989576a6 Sofia Papagiannaki
                    help=("List or reset diverging usages")),
166 3e1b20d6 Sofia Papagiannaki
        make_option('--user',
167 3e1b20d6 Sofia Papagiannaki
                    dest='users',
168 384dee7e Sofia Papagiannaki
                    action='append',
169 3e1b20d6 Sofia Papagiannaki
                    metavar='USER_UUID',
170 8108c68c Sofia Papagiannaki
                    help=("Specify which users --list or --reset applies."
171 8108c68c Sofia Papagiannaki
                          "This option can be repeated several times."
172 989576a6 Sofia Papagiannaki
                          "If no user is specified --list or --reset will be applied globally.")),
173 384dee7e Sofia Papagiannaki
    )
174 384dee7e Sofia Papagiannaki
175 4c03c942 Sofia Papagiannaki
    def handle_noargs(self, **options):
176 4c03c942 Sofia Papagiannaki
        try:
177 989576a6 Sofia Papagiannaki
            user_nodes = _retrieve_user_nodes(options['users'])
178 989576a6 Sofia Papagiannaki
            if not user_nodes:
179 989576a6 Sofia Papagiannaki
                raise CommandError('No users found.')
180 989576a6 Sofia Papagiannaki
            usage = _compute_usage(user_nodes)
181 989576a6 Sofia Papagiannaki
            reset_holding = _prepare_reset_holding(
182 989576a6 Sofia Papagiannaki
                usage, only_diverging=options['diverging']
183 989576a6 Sofia Papagiannaki
            )
184 989576a6 Sofia Papagiannaki
185 3e1b20d6 Sofia Papagiannaki
            if options['list']:
186 3e1b20d6 Sofia Papagiannaki
                print '\n'.join([str(i) for i in reset_holding])
187 3e1b20d6 Sofia Papagiannaki
188 3e1b20d6 Sofia Papagiannaki
            if options['reset']:
189 3e1b20d6 Sofia Papagiannaki
                if not backend.quotaholder_enabled:
190 3e1b20d6 Sofia Papagiannaki
                    raise CommandError('Quotaholder component is not enabled')
191 3e1b20d6 Sofia Papagiannaki
192 3e1b20d6 Sofia Papagiannaki
                if not backend.quotaholder_url:
193 3e1b20d6 Sofia Papagiannaki
                    raise CommandError('Quotaholder url is not set')
194 3e1b20d6 Sofia Papagiannaki
195 3e1b20d6 Sofia Papagiannaki
                if not backend.quotaholder_token:
196 3e1b20d6 Sofia Papagiannaki
                    raise CommandError('Quotaholder token is not set')
197 3e1b20d6 Sofia Papagiannaki
198 3e1b20d6 Sofia Papagiannaki
                while True:
199 3e1b20d6 Sofia Papagiannaki
                    result = backend.quotaholder.reset_holding(
200 3e1b20d6 Sofia Papagiannaki
                        context={},
201 3e1b20d6 Sofia Papagiannaki
                        clientkey='pithos',
202 3e1b20d6 Sofia Papagiannaki
                        reset_holding=reset_holding)
203 3e1b20d6 Sofia Papagiannaki
204 3e1b20d6 Sofia Papagiannaki
                    if not result:
205 3e1b20d6 Sofia Papagiannaki
                        break
206 3e1b20d6 Sofia Papagiannaki
207 3e1b20d6 Sofia Papagiannaki
                    missing_entities = [reset_holding[x].entity for x in result]
208 3e1b20d6 Sofia Papagiannaki
                    self.stdout.write(
209 3e1b20d6 Sofia Papagiannaki
                            'Unknown quotaholder users: %s\n' %
210 3e1b20d6 Sofia Papagiannaki
                            ', '.join(missing_entities))
211 3e1b20d6 Sofia Papagiannaki
                    m = 'Retrying sending quota usage for the rest...\n'
212 3e1b20d6 Sofia Papagiannaki
                    self.stdout.write(m)
213 3e1b20d6 Sofia Papagiannaki
                    missing_indexes = set(result)
214 3e1b20d6 Sofia Papagiannaki
                    reset_holding = [x for i, x in enumerate(reset_holding)
215 3e1b20d6 Sofia Papagiannaki
                                     if i not in missing_indexes]
216 d776b3c3 Sofia Papagiannaki
        finally:
217 4c03c942 Sofia Papagiannaki
            backend.close()