Revision d1e7d2b4

/dev/null
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
from optparse import make_option
36

  
37
from pithos.api.util import get_backend
38

  
39
import logging
40

  
41
logger = logging.getLogger(__name__)
42

  
43
CLIENTKEY = 'pithos'
44

  
45
class Command(NoArgsCommand):
46
    help = "Display unresolved commissions and trigger their recovery"
47

  
48
    option_list = NoArgsCommand.option_list + (
49
        make_option('--fix',
50
                    dest='fix',
51
                    action="store_true",
52
                    default=False,
53
                    help="Fix unresolved commissions"),
54
    )
55

  
56
    def handle_noargs(self, **options):
57
        b = get_backend()
58
        try:
59
            pending_commissions = b.quotaholder.get_pending_commissions(
60
                token=b.quotaholder_token)
61

  
62
            if pending_commissions:
63
                self.stdout.write(
64
                    "Unresolved commissions: %s\n" % pending_commissions
65
                )
66
            else:
67
                self.stdout.write( "No unresolved commissions were found\n")
68
                return
69

  
70
            if options['fix']:
71
                to_accept = b.quotaholder_serials.lookup(pending_commissions)
72
                response = b.quotaholder.resolve_commissions(
73
                    token=b.quotaholder_token,
74
                    accept_serials=to_accept,
75
                    reject_serials=[])
76
                accepted = response['accepted']
77
                self.stdout.write("Accepted commissions: %s\n" %  accepted)
78

  
79
                b.quotaholder_serials.delete_many(to_accept)
80
                self.stdout.write("Deleted serials: %s\n" %  to_accept)
81

  
82
                to_reject = list(set(pending_commissions) - set(to_accept))
83
                response = b.quotaholder.resolve_commissions(
84
                    token=b.quotaholder_token,
85
                    accept_serials=[],
86
                    reject_serials=to_reject)
87
                rejected = response['rejected']
88
                self.stdout.write("Rejected commissions: %s\n" %  rejected)
89
        except Exception, e:
90
            logger.exception(e)
91
            raise CommandError(e)
92
        finally:
93
            b.close()
/dev/null
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 optparse import make_option
38
from sqlalchemy import func
39
from sqlalchemy.sql import select, and_, or_
40

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

  
47
Usage = namedtuple('Usage', ('node', 'path', 'size', 'cluster'))
48
GetQuota = namedtuple('GetQuota', ('entity', 'resource', 'key'))
49

  
50
class ResetHoldingPayload(namedtuple('ResetHoldingPayload', (
51
    'entity', 'resource', 'key', 'imported', 'exported', 'returned', 'released'
52
))):
53
    __slots__ = ()
54

  
55
    def __str__(self):
56
        return '%s: %s' % (self.entity, self.imported)
57

  
58

  
59
ENTITY_KEY = '1'
60

  
61
backend = get_backend()
62
table = {}
63
table['nodes'] = backend.node.nodes
64
table['versions'] = backend.node.versions
65
table['policy'] = backend.node.policy
66
conn = backend.node.conn
67

  
68
def _retrieve_user_nodes(users=()):
69
    s = select([table['nodes'].c.path, table['nodes'].c.node])
70
    s = s.where(and_(table['nodes'].c.node != 0,
71
                     table['nodes'].c.parent == 0))
72
    if users:
73
        s = s.where(table['nodes'].c.path.in_(users))
74
    return conn.execute(s).fetchall()
75

  
76
def _compute_usage(nodes):
77
    usage = []
78
    append = usage.append
79
    for path, node in nodes:
80
        select_children = select(
81
            [table['nodes'].c.node]).where(table['nodes'].c.parent == node)
82
        select_descendants = select([table['nodes'].c.node]).where(
83
            or_(table['nodes'].c.parent.in_(select_children),
84
                table['nodes'].c.node.in_(select_children)))
85
        s = select([table['versions'].c.cluster,
86
                    func.sum(table['versions'].c.size)])
87
        s = s.group_by(table['versions'].c.cluster)
88
        s = s.where(table['nodes'].c.node == table['versions'].c.node)
89
        s = s.where(table['nodes'].c.node.in_(select_descendants))
90
        s = s.where(table['versions'].c.cluster == CLUSTER_NORMAL)
91
        d2 = dict(conn.execute(s).fetchall())
92

  
93
        try:
94
            size = d2[CLUSTER_NORMAL]
95
        except KeyError:
96
            size = 0
97
        append(Usage(
98
            node=node,
99
            path=path,
100
            size=size,
101
            cluster=CLUSTER_NORMAL))
102
    return usage
103

  
104
def _get_quotaholder_usage(usage):
105
    payload = []
106
    append = payload.append
107
    [append(GetQuota(
108
        entity=item.path,
109
        resource='pithos+.diskspace',
110
        key=ENTITY_KEY
111
    )) for item in usage]
112

  
113
    result = backend.quotaholder.get_quota(
114
        context={}, clientkey='pithos', get_quota=payload
115
    )
116
    return dict((entity, imported - exported + returned - released) for (
117
        entity, resource, quantity, capacity, import_limit, export_limit,
118
        imported, exported, returned, released, flags
119
    ) in result)
120

  
121

  
122
def _prepare_reset_holding(usage, only_diverging=False):
123
    """Verify usage and set quotaholder user usage"""
124
    reset_holding = []
125
    append = reset_holding.append
126

  
127
    quotaholder_usage = {}
128
    if only_diverging:
129
        quotaholder_usage = _get_quotaholder_usage(usage)
130

  
131
    for item in(usage):
132
        if only_diverging and quotaholder_usage.get(item.path) == item.size:
133
            continue
134

  
135
        if item.cluster == CLUSTER_NORMAL:
136
            append(ResetHoldingPayload(
137
                    entity=item.path,
138
                    resource='pithos+.diskspace',
139
                    key=ENTITY_KEY,
140
                    imported=item.size,
141
                    exported=0,
142
                    returned=0,
143
                    released=0))
144
    return reset_holding
145

  
146

  
147
class Command(NoArgsCommand):
148
    help = "List and reset pithos usage"
149

  
150
    option_list = NoArgsCommand.option_list + (
151
        make_option('--list',
152
                    dest='list',
153
                    action="store_true",
154
                    default=True,
155
                    help="List usage for all or specified user"),
156
        make_option('--reset',
157
                    dest='reset',
158
                    action="store_true",
159
                    default=False,
160
                    help="Reset usage for all or specified users"),
161
        make_option('--diverging',
162
                    dest='diverging',
163
                    action="store_true",
164
                    default=False,
165
                    help=("List or reset diverging usages")),
166
        make_option('--user',
167
                    dest='users',
168
                    action='append',
169
                    metavar='USER_UUID',
170
                    help=("Specify which users --list or --reset applies."
171
                          "This option can be repeated several times."
172
                          "If no user is specified --list or --reset will be applied globally.")),
173
    )
174

  
175
    def handle_noargs(self, **options):
176
        try:
177
            user_nodes = _retrieve_user_nodes(options['users'])
178
            if not user_nodes:
179
                raise CommandError('No users found.')
180
            usage = _compute_usage(user_nodes)
181
            reset_holding = _prepare_reset_holding(
182
                usage, only_diverging=options['diverging']
183
            )
184

  
185
            if options['list']:
186
                print '\n'.join([str(i) for i in reset_holding])
187

  
188
            if options['reset']:
189
                if not backend.quotaholder_enabled:
190
                    raise CommandError('Quotaholder component is not enabled')
191

  
192
                if not backend.quotaholder_url:
193
                    raise CommandError('Quotaholder url is not set')
194

  
195
                if not backend.quotaholder_token:
196
                    raise CommandError('Quotaholder token is not set')
197

  
198
                while True:
199
                    result = backend.quotaholder.reset_holding(
200
                        context={},
201
                        clientkey='pithos',
202
                        reset_holding=reset_holding)
203

  
204
                    if not result:
205
                        break
206

  
207
                    missing_entities = [reset_holding[x].entity for x in result]
208
                    self.stdout.write(
209
                            'Unknown quotaholder users: %s\n' %
210
                            ', '.join(missing_entities))
211
                    m = 'Retrying sending quota usage for the rest...\n'
212
                    self.stdout.write(m)
213
                    missing_indexes = set(result)
214
                    reset_holding = [x for i, x in enumerate(reset_holding)
215
                                     if i not in missing_indexes]
216
        finally:
217
            backend.close()
b/snf-pithos-app/pithos/api/management/commands/reconcile-commissions-pithos.py
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
from optparse import make_option
36

  
37
from pithos.api.util import get_backend
38

  
39
import logging
40

  
41
logger = logging.getLogger(__name__)
42

  
43
CLIENTKEY = 'pithos'
44

  
45
class Command(NoArgsCommand):
46
    help = "Display unresolved commissions and trigger their recovery"
47

  
48
    option_list = NoArgsCommand.option_list + (
49
        make_option('--fix',
50
                    dest='fix',
51
                    action="store_true",
52
                    default=False,
53
                    help="Fix unresolved commissions"),
54
    )
55

  
56
    def handle_noargs(self, **options):
57
        b = get_backend()
58
        try:
59
            pending_commissions = b.quotaholder.get_pending_commissions(
60
                token=b.quotaholder_token)
61

  
62
            if pending_commissions:
63
                self.stdout.write(
64
                    "Unresolved commissions: %s\n" % pending_commissions
65
                )
66
            else:
67
                self.stdout.write( "No unresolved commissions were found\n")
68
                return
69

  
70
            if options['fix']:
71
                to_accept = b.quotaholder_serials.lookup(pending_commissions)
72
                to_reject = list(set(pending_commissions) - set(to_accept))
73
                response = b.quotaholder.resolve_commissions(
74
                    token=b.quotaholder_token,
75
                    accept_serials=to_accept,
76
                    reject_serials=to_reject
77
                )
78
                accepted = response['accepted']
79
                rejected = response['rejected']
80
                failed = response['failed']
81
                self.stdout.write("Accepted commissions: %s\n" %  accepted)
82
                self.stdout.write("Rejected commissions: %s\n" %  rejected)
83
                self.stdout.write("Failed commissions:\n")
84
                for i in failed:
85
                    self.stdout.write('%s\n' % i)
86

  
87
                b.quotaholder_serials.delete_many(accepted)
88
        except Exception, e:
89
            logger.exception(e)
90
            raise CommandError(e)
91
        finally:
92
            b.close()
b/snf-pithos-app/pithos/api/management/commands/reconcile-resources-pithos.py
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 optparse import make_option
37

  
38
from pithos.api.util import get_backend
39
from pithos.backends.modular import CLUSTER_NORMAL, DEFAULT_SOURCE
40
from synnefo.webproject.management import utils
41
from astakosclient.errors import AstakosClientException
42

  
43
ENTITY_KEY = '1'
44

  
45
backend = get_backend()
46

  
47
class Command(NoArgsCommand):
48
    help = "List and reset pithos usage"
49

  
50
    option_list = NoArgsCommand.option_list + (
51
        make_option('--list',
52
                    dest='list',
53
                    action="store_true",
54
                    default=True,
55
                    help="List usage for all or specified user"),
56
        make_option('--reset',
57
                    dest='reset',
58
                    action="store_true",
59
                    default=False,
60
                    help="Reset usage for all or specified users"),
61
        make_option('--diverging',
62
                    dest='diverging',
63
                    action="store_true",
64
                    default=False,
65
                    help=("List or reset diverging usages")),
66
        make_option('--user',
67
                    dest='users',
68
                    action='append',
69
                    metavar='USER_UUID',
70
                    help=("Specify which users --list or --reset applies."
71
                          "This option can be repeated several times."
72
                          "If no user is specified --list or --reset "
73
                          "will be applied globally.")),
74
        make_option(
75
            "--no-headers",
76
            dest="headers",
77
            action="store_false",
78
            default=True,
79
            help="Do not display headers"),
80
        make_option(
81
            "--output-format",
82
            dest="output_format",
83
            metavar="[pretty, csv, json]",
84
            default="pretty",
85
            choices=["pretty", "csv", "json"],
86
            help="Select the output format: pretty [the default], tabs"
87
                 " [tab-separated output], csv [comma-separated output]"),
88

  
89
    )
90

  
91
    def handle_noargs(self, **options):
92
        try:
93
            account_nodes = backend.node.node_accounts(options['users'])
94
            if not account_nodes:
95
                raise CommandError('No users found.')
96

  
97
            db_usage = {}
98
            for path, node in account_nodes:
99
                size = backend.node.node_account_usage(node, CLUSTER_NORMAL)
100
                db_usage[path] = size or 0
101

  
102
            result = backend.quotaholder.service_get_quotas(
103
                backend.quotaholder_token,
104
            )
105

  
106
            qh_usage = {}
107
            resource = 'pithos.diskspace'
108
            pending_list = []
109
            for uuid, d in result.iteritems():
110
                pithos_dict = d.get(DEFAULT_SOURCE, {}).get(resource, {})
111
                pending = pithos_dict.get('pending', 0)
112
                if pending != 0:
113
                    pending_list.append(pending)
114
                    continue
115
                qh_usage[uuid] = pithos_dict.get('usage', 0)
116

  
117
            if pending_list:
118
                self.stdout.write((
119
                    "There are pending commissions for: %s.\n"
120
                    "Reconcile commissions and retry"
121
                    "in order to list/reset their quota.\n"
122
                ) % pending_list)
123

  
124
            headers = ['uuid', 'usage']
125
            table = []
126
            provisions = []
127
            for uuid in db_usage.keys():
128
                try:
129
                    delta = db_usage[uuid] - qh_usage[uuid]
130
                except KeyError:
131
                    self.stdout.write('Unknown holder: %s\n' % uuid)
132
                    continue
133
                else:
134
                    if options['diverging'] and delta == 0:
135
                        continue
136
                    table.append((uuid, db_usage[uuid]))
137
                    provisions.append({"holder": uuid,
138
                                       "source": DEFAULT_SOURCE,
139
                                       "resource": resource,
140
                                       "quantity": delta
141
                    })
142

  
143

  
144
            if options['reset']:
145
                if not provisions:
146
                    raise CommandError('Nothing to reset')
147
                request = {}
148
                request['force'] = True
149
                request['auto_accept'] = True
150
                request['provisions'] = provisions
151
                try:
152
                    serial = backend.quotaholder.issue_commission(
153
                        backend.quotaholder_token, request
154
                    )
155
                except AstakosClientException, e:
156
                    self.stdout.write(e.details)
157
                else:
158
                    backend.quotaholder_serials.insert_many([serial])
159
            elif options['list'] and table:
160
                output_format = options["output_format"]
161
                if output_format != "json" and not options["headers"]:
162
                    headers = None
163
                utils.pprint_table(self.stdout, table, headers, output_format)
164
        finally:
165
            backend.close()
b/snf-pithos-app/pithos/api/swiss_army/__init__.py
56 56
        self.backend.close()
57 57

  
58 58
    def existing_accounts(self):
59
        return self.backend.node.node_accounts()
59
        return sorted([path for path, _ in self.backend.node.node_accounts()])
60 60

  
61 61
    def duplicate_accounts(self):
62 62
        accounts = self.existing_accounts()
b/snf-pithos-backend/pithos/backends/lib/sqlalchemy/node.py
449 449
        self.conn.execute(s).close()
450 450
        return True
451 451

  
452
    def node_accounts(self):
453
        s = select([self.nodes.c.path])
454
        s = s.where(and_(self.nodes.c.node != 0, self.nodes.c.parent == 0))
455
        account_nodes = self.conn.execute(s).fetchall()
456
        return sorted(i[0] for i in account_nodes)
452
    def node_accounts(self, accounts=()):
453
        s = select([self.nodes.c.path, self.nodes.c.node])
454
        s = s.where(and_(self.nodes.c.node != 0,
455
                         self.nodes.c.parent == 0))
456
        if accounts:
457
            s = s.where(self.nodes.c.path.in_(accounts))
458
        r = self.conn.execute(s)
459
        rows = r.fetchall()
460
        r.close()
461
        return rows
462

  
463
    def node_account_usage(self, account_node, cluster):
464
        select_children = select(
465
            [self.nodes.c.node]).where(self.nodes.c.parent == account_node)
466
        select_descendants = select([self.nodes.c.node]).where(
467
            or_(self.nodes.c.parent.in_(select_children),
468
                self.nodes.c.node.in_(select_children)))
469
        s = select([func.sum(self.versions.c.size)])
470
        s = s.group_by(self.versions.c.cluster)
471
        s = s.where(self.nodes.c.node == self.versions.c.node)
472
        s = s.where(self.nodes.c.node.in_(select_descendants))
473
        s = s.where(self.versions.c.cluster == cluster)
474
        r = self.conn.execute(s)
475
        usage = r.fetchone()[0]
476
        r.close()
477
        return usage
457 478

  
458 479
    def policy_get(self, node):
459 480
        s = select([self.policy.c.key, self.policy.c.value],
b/snf-pithos-backend/pithos/backends/lib/sqlite/node.py
384 384
        self.execute(q, (node,))
385 385
        return True
386 386

  
387
    def node_accounts(self, accounts=()):
388
        q = ("select path, node from nodes where node != 0 and parent == 0 ")
389
        args = []
390
        if accounts:
391
            placeholders = ','.join('?' for a in accounts)
392
            q += ("and path in (%s)" % placeholders)
393
            args += accounts
394
        return self.execute(q, args).fetchall()
395

  
396
    def node_account_usage(self, account_node, cluster):
397
        select_children = ("select node from nodes where parent = ?")
398
        select_descedents = ("select node from nodes "
399
                             "where parent in (%s) "
400
                             "or node in (%s) ") % ((select_children,)*2)
401
        args = [account_node]*2
402
        q = ("select sum(v.size) from versions v, nodes n "
403
             "where v.node = n.node "
404
             "and n.node in (%s) "
405
             "and v.cluster = ?") % select_descedents
406
        args += [cluster]
407

  
408
        self.execute(q, args)
409
        return self.fetchone()[0]
410

  
387 411
    def policy_get(self, node):
388 412
        q = "select key, value from policy where node = ?"
389 413
        self.execute(q, (node,))

Also available in: Unified diff