Revision 075b91de

b/docs/admin-guide.rst
140 140
Setting quota limits
141 141
~~~~~~~~~~~~~~~~~~~~
142 142

  
143
Set default quotas
144
``````````````````
143
Set default quota
144
`````````````````
145 145

  
146 146
In 20-snf-astakos-app-settings.conf, 
147 147
uncomment the default setting ``ASTAKOS_SERVICES``
148 148
and customize the ``'uplimit'`` values.
149
These are the default base quotas for all users.
149
These are the default base quota for all users.
150 150

  
151 151
To apply your configuration run::
152 152

  
153 153
    # snf-manage astakos-init --load-service-resources
154
    # snf-manage astakos-quota --sync
154
    # snf-manage quota --sync
155 155

  
156
Set base quotas for individual users
157
````````````````````````````````````
156
Set base quota for individual users
157
```````````````````````````````````
158 158

  
159
For individual users that need different quotas than the default
159
For individual users that need different quota than the default
160 160
you can set it for each resource like this::
161 161

  
162
    # use this to display quotas / uuid
163
    # snf-manage user-show 'uuid or email' --quotas
162
    # use this to display quota / uuid
163
    # snf-manage user-show 'uuid or email' --quota
164 164

  
165
    # snf-manage user-modify 'user-uuid' --set-quota 'cyclades.vm' 10
165
    # snf-manage user-modify 'user-uuid' --set-base-quota 'cyclades.vm' 10
166 166

  
167 167

  
168 168
Enable the Projects feature
......
182 182

  
183 183
You can also set a user-specific limit with::
184 184

  
185
    # snf-manage user-modify 'user-uuid' --set-quota 'astakos.pending_app' 5
185
    # snf-manage user-modify 'user-uuid' --set-base-quota 'astakos.pending_app' 5
186 186

  
187 187
When users apply for projects they are not automatically granted
188 188
the resources. They must first be approved by the administrator.
b/docs/quick-install-admin-guide.rst
2001 2001
.. code-block:: console
2002 2002

  
2003 2003
   node1 # snf-manage astakos-init --load-service-resources
2004
   node1 # snf-manage astakos-quota --verify
2005
   node1 # snf-manage astakos-quota --sync
2004
   node1 # snf-manage quota --verify
2005
   node1 # snf-manage quota --sync
2006 2006
   node2 # snf-manage pithos-reset-usage
2007 2007
   node1 # snf-manage reconcile-resources-cyclades --fix
2008 2008

  
b/docs/scale/i-astakos.rst
140 140
   # snf-manage service-add "cyclades" https://cyclades.example.com/ui/
141 141
   # snf-manage service-add "pithos+" https://pithos.example.com/ui/
142 142
   # snf-manage astakos-init --load-service-resources
143
   # snf-manage astakos-quota --sync
143
   # snf-manage quota --sync
144 144
   # /etc/init.d/gunicorn restart
145 145
   # /etc/init.d/apache2 restart
146 146

  
b/snf-astakos-app/README
165 165
============================  ===========================
166 166
Name                          Description
167 167
============================  ===========================
168
astakos-quota                 Inspect quotaholder status
169 168
fix-superusers                Transform superusers created by syncdb into AstakosUser instances
170 169
full-cleanup                  Cleanup sessions and session catalog
171 170
invitation-list               List invitation
......
173 172
project-control               Manage projects and applications
174 173
project-list                  List projects
175 174
project-show                  Show project details
175
quota                         List and check the integrity of user quota
176 176
reconcile-resources-astakos   Reconcile resource usage of Quotaholder with Astakos DB
177 177
resource-add                  Add resource
178 178
resource-export-astakos       Export astakos resources in json format
/dev/null
1
# Copyright 2012, 2013 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 optparse import make_option
35
from django.core.management.base import CommandError
36

  
37
from astakos.im.models import AstakosUser
38
from astakos.im.quotas import set_user_quota, list_user_quotas, add_base_quota
39
from astakos.im.functions import get_user_by_uuid
40
from astakos.im.management.commands._common import is_uuid, is_email
41
from snf_django.lib.db.transaction import commit_on_success_strict
42
from synnefo.webproject.management.commands import SynnefoCommand
43
from synnefo.webproject.management import utils
44
from ._common import show_quotas
45

  
46
import logging
47
logger = logging.getLogger(__name__)
48

  
49

  
50
class Command(SynnefoCommand):
51
    help = "Inspect quotaholder status"
52

  
53
    option_list = SynnefoCommand.option_list + (
54
        make_option('--list',
55
                    action='store_true',
56
                    dest='list',
57
                    default=False,
58
                    help="List all quotas (default)"),
59
        make_option('--verify',
60
                    action='store_true',
61
                    dest='verify',
62
                    default=False,
63
                    help="Check if quotaholder is in sync with astakos"),
64
        make_option('--sync',
65
                    action='store_true',
66
                    dest='sync',
67
                    default=False,
68
                    help="Sync quotaholder"),
69
        make_option('--user',
70
                    metavar='<uuid or email>',
71
                    dest='user',
72
                    help="List quotas for a specified user"),
73
        make_option('--import-base-quota',
74
                    dest='import_base_quota',
75
                    metavar='<exported-quotas.txt>',
76
                    help=("Import base quotas from file. "
77
                          "The file must contain non-empty lines, and each "
78
                          "line must contain a single-space-separated list "
79
                          "of values: <user> <resource name> <capacity>")
80
                    ),
81
    )
82

  
83
    @commit_on_success_strict()
84
    def handle(self, *args, **options):
85
        sync = options['sync']
86
        verify = options['verify']
87
        user_ident = options['user']
88
        list_ = options['list']
89
        import_base_quota = options['import_base_quota']
90

  
91
        if import_base_quota:
92
            if any([sync, verify, list_]):
93
                m = "--from-file cannot be combined with other options."
94
                raise CommandError(m)
95
            self.import_from_file(import_base_quota)
96
        else:
97
            self.quotas(sync, verify, user_ident, options["output_format"])
98

  
99
    def quotas(self, sync, verify, user_ident, output_format):
100
        list_only = not sync and not verify
101

  
102
        if user_ident is not None:
103
            users = [self.get_user(user_ident)]
104
        else:
105
            users = AstakosUser.objects.verified()
106

  
107
        try:
108
            qh_limits, qh_quotas, astakos_i, diff_q = list_user_quotas(users)
109
        except BaseException as e:
110
            logger.exception(e)
111
            raise CommandError("Failed to compute quotas.")
112

  
113
        info = {}
114
        for user in users:
115
            info[user.uuid] = user.email
116

  
117
        if list_only:
118
            print_data, labels = show_quotas(qh_quotas, astakos_i, info)
119
            utils.pprint_table(self.stdout, print_data, labels,
120
                               output_format)
121

  
122
        else:
123
            if verify:
124
                self.print_verify(qh_limits, diff_q)
125
            if sync:
126
                try:
127
                    set_user_quota(diff_q)
128
                except BaseException as e:
129
                    logger.exception(e)
130
                    raise CommandError("Failed to sync quotas.")
131
                self.print_sync(diff_q)
132

  
133
    def get_user(self, user_ident):
134
        if is_uuid(user_ident):
135
            try:
136
                user = AstakosUser.objects.get(uuid=user_ident)
137
            except AstakosUser.DoesNotExist:
138
                raise CommandError('Not found user having uuid: %s' %
139
                                   user_ident)
140
        elif is_email(user_ident):
141
            try:
142
                user = AstakosUser.objects.get(username=user_ident)
143
            except AstakosUser.DoesNotExist:
144
                raise CommandError('Not found user having email: %s' %
145
                                   user_ident)
146
        else:
147
            raise CommandError('Please specify user by uuid or email')
148

  
149
        if not user.email_verified and sync:
150
            raise CommandError('User %s is not verified.' % user.uuid)
151

  
152
        return user
153

  
154
    def print_sync(self, diff_quotas):
155
        size = len(diff_quotas)
156
        if size == 0:
157
            self.stdout.write("No sync needed.\n")
158
        else:
159
            self.stdout.write("Synced %s users:\n" % size)
160
            for holder in diff_quotas.keys():
161
                user = get_user_by_uuid(holder)
162
                self.stdout.write("%s (%s)\n" % (holder, user.username))
163

  
164
    def print_verify(self,
165
                     qh_limits,
166
                     diff_quotas):
167

  
168
            for holder, local in diff_quotas.iteritems():
169
                registered = qh_limits.pop(holder, None)
170
                user = get_user_by_uuid(holder)
171
                if registered is None:
172
                    self.stdout.write(
173
                        "No quotas for %s (%s) in quotaholder.\n" %
174
                        (holder, user.username))
175
                else:
176
                    self.stdout.write("Quotas differ for %s (%s):\n" %
177
                                      (holder, user.username))
178
                    self.stdout.write("Quotas according to quotaholder:\n")
179
                    self.stdout.write("%s\n" % (registered))
180
                    self.stdout.write("Quotas according to astakos:\n")
181
                    self.stdout.write("%s\n\n" % (local))
182

  
183
            diffs = len(diff_quotas)
184
            if diffs:
185
                self.stdout.write("Quotas differ for %d users.\n" % (diffs))
186

  
187
    def import_from_file(self, location):
188
        users = set()
189
        with open(location) as f:
190
            for line in f.readlines():
191
                try:
192
                    t = line.rstrip('\n').split(' ')
193
                    user = t[0]
194
                    resource = t[1]
195
                    capacity = t[2]
196
                except(IndexError, TypeError):
197
                    self.stdout.write('Invalid line format: %s:\n' % t)
198
                    continue
199
                else:
200
                    try:
201
                        user = self.get_user(user)
202
                        users.add(user.id)
203
                    except CommandError:
204
                        self.stdout.write('Not found user: %s\n' % user)
205
                        continue
206
                    else:
207
                        try:
208
                            add_base_quota(user, resource, capacity)
209
                        except Exception, e:
210
                            self.stdout.write('Failed to add quota: %s\n' % e)
211
                            continue
b/snf-astakos-app/astakos/im/management/commands/quota.py
1
# Copyright 2012, 2013 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 optparse import make_option
35
from django.core.management.base import CommandError
36

  
37
from astakos.im.models import AstakosUser
38
from astakos.im.quotas import set_user_quota, list_user_quotas, add_base_quota
39
from astakos.im.functions import get_user_by_uuid
40
from astakos.im.management.commands._common import is_uuid, is_email
41
from snf_django.lib.db.transaction import commit_on_success_strict
42
from synnefo.webproject.management.commands import SynnefoCommand
43
from synnefo.webproject.management import utils
44
from ._common import show_quotas
45

  
46
import logging
47
logger = logging.getLogger(__name__)
48

  
49

  
50
class Command(SynnefoCommand):
51
    help = "List and check the integrity of user quota"
52

  
53
    option_list = SynnefoCommand.option_list + (
54
        make_option('--list',
55
                    action='store_true',
56
                    dest='list',
57
                    default=False,
58
                    help="List all quota (default)"),
59
        make_option('--verify',
60
                    action='store_true',
61
                    dest='verify',
62
                    default=False,
63
                    help="Check if quotaholder is in sync with astakos"),
64
        make_option('--sync',
65
                    action='store_true',
66
                    dest='sync',
67
                    default=False,
68
                    help="Sync quotaholder"),
69
        make_option('--user',
70
                    metavar='<uuid or email>',
71
                    dest='user',
72
                    help="List quota for a specified user"),
73
        make_option('--import-base-quota',
74
                    dest='import_base_quota',
75
                    metavar='<exported-quota.txt>',
76
                    help=("Import base quota from file. "
77
                          "The file must contain non-empty lines, and each "
78
                          "line must contain a single-space-separated list "
79
                          "of values: <user> <resource name> <capacity>")
80
                    ),
81
    )
82

  
83
    @commit_on_success_strict()
84
    def handle(self, *args, **options):
85
        sync = options['sync']
86
        verify = options['verify']
87
        user_ident = options['user']
88
        list_ = options['list']
89
        import_base_quota = options['import_base_quota']
90

  
91
        if import_base_quota:
92
            if any([sync, verify, list_]):
93
                m = "--from-file cannot be combined with other options."
94
                raise CommandError(m)
95
            self.import_from_file(import_base_quota)
96
        else:
97
            self.quotas(sync, verify, user_ident, options["output_format"])
98

  
99
    def quotas(self, sync, verify, user_ident, output_format):
100
        list_only = not sync and not verify
101

  
102
        if user_ident is not None:
103
            users = [self.get_user(user_ident)]
104
        else:
105
            users = AstakosUser.objects.verified()
106

  
107
        try:
108
            qh_limits, qh_quotas, astakos_i, diff_q = list_user_quotas(users)
109
        except BaseException as e:
110
            logger.exception(e)
111
            raise CommandError("Failed to compute quota.")
112

  
113
        info = {}
114
        for user in users:
115
            info[user.uuid] = user.email
116

  
117
        if list_only:
118
            print_data, labels = show_quotas(qh_quotas, astakos_i, info)
119
            utils.pprint_table(self.stdout, print_data, labels,
120
                               output_format)
121

  
122
        else:
123
            if verify:
124
                self.print_verify(qh_limits, diff_q)
125
            if sync:
126
                try:
127
                    set_user_quota(diff_q)
128
                except BaseException as e:
129
                    logger.exception(e)
130
                    raise CommandError("Failed to sync quota.")
131
                self.print_sync(diff_q)
132

  
133
    def get_user(self, user_ident):
134
        if is_uuid(user_ident):
135
            try:
136
                user = AstakosUser.objects.get(uuid=user_ident)
137
            except AstakosUser.DoesNotExist:
138
                raise CommandError('Not found user having uuid: %s' %
139
                                   user_ident)
140
        elif is_email(user_ident):
141
            try:
142
                user = AstakosUser.objects.get(username=user_ident)
143
            except AstakosUser.DoesNotExist:
144
                raise CommandError('Not found user having email: %s' %
145
                                   user_ident)
146
        else:
147
            raise CommandError('Please specify user by uuid or email')
148

  
149
        if not user.email_verified and sync:
150
            raise CommandError('User %s is not verified.' % user.uuid)
151

  
152
        return user
153

  
154
    def print_sync(self, diff_quotas):
155
        size = len(diff_quotas)
156
        if size == 0:
157
            self.stdout.write("No sync needed.\n")
158
        else:
159
            self.stdout.write("Synced %s users:\n" % size)
160
            for holder in diff_quotas.keys():
161
                user = get_user_by_uuid(holder)
162
                self.stdout.write("%s (%s)\n" % (holder, user.username))
163

  
164
    def print_verify(self,
165
                     qh_limits,
166
                     diff_quotas):
167

  
168
            for holder, local in diff_quotas.iteritems():
169
                registered = qh_limits.pop(holder, None)
170
                user = get_user_by_uuid(holder)
171
                if registered is None:
172
                    self.stdout.write(
173
                        "No quota for %s (%s) in quotaholder.\n" %
174
                        (holder, user.username))
175
                else:
176
                    self.stdout.write("Quota differ for %s (%s):\n" %
177
                                      (holder, user.username))
178
                    self.stdout.write("Quota according to quotaholder:\n")
179
                    self.stdout.write("%s\n" % (registered))
180
                    self.stdout.write("Quota according to astakos:\n")
181
                    self.stdout.write("%s\n\n" % (local))
182

  
183
            diffs = len(diff_quotas)
184
            if diffs:
185
                self.stdout.write("Quota differ for %d users.\n" % (diffs))
186

  
187
    def import_from_file(self, location):
188
        users = set()
189
        with open(location) as f:
190
            for line in f.readlines():
191
                try:
192
                    t = line.rstrip('\n').split(' ')
193
                    user = t[0]
194
                    resource = t[1]
195
                    capacity = t[2]
196
                except(IndexError, TypeError):
197
                    self.stdout.write('Invalid line format: %s:\n' % t)
198
                    continue
199
                else:
200
                    try:
201
                        user = self.get_user(user)
202
                        users.add(user.id)
203
                    except CommandError:
204
                        self.stdout.write('Not found user: %s\n' % user)
205
                        continue
206
                    else:
207
                        try:
208
                            add_base_quota(user, resource, capacity)
209
                        except Exception, e:
210
                            self.stdout.write('Failed to add quota: %s\n' % e)
211
                            continue
b/snf-astakos-app/astakos/im/management/commands/user-show.py
51 51
    help = "Show user info"
52 52

  
53 53
    option_list = SynnefoCommand.option_list + (
54
        make_option('--quotas',
54
        make_option('--quota',
55 55
                    action='store_true',
56 56
                    dest='list_quotas',
57 57
                    default=False,
58
                    help="Also list user quotas"),
58
                    help="Also list user quota"),
59 59
    )
60 60

  
61 61
    def handle(self, *args, **options):

Also available in: Unified diff