Statistics
| Branch: | Tag: | Revision:

root / snf-cyclades-app / synnefo / quotas / management / commands / enforce-resources-cyclades.py @ d14155e3

History | View | Annotate | Download (6.7 kB)

1
# Copyright 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
import string
35
from optparse import make_option
36
from django.db import transaction
37

    
38
from synnefo.quotas import util
39
from synnefo.quotas import enforce
40
from synnefo.quotas import errors
41
from snf_django.management.commands import SynnefoCommand, CommandError
42
from snf_django.management.utils import pprint_table
43

    
44

    
45
DEFAULT_RESOURCES = ["cyclades.cpu",
46
                     "cyclades.ram",
47
                     "cyclades.floating_ip",
48
                     ]
49

    
50

    
51
class Command(SynnefoCommand):
52
    help = """Check and fix quota violations for Cyclades resources.
53
    """
54
    option_list = SynnefoCommand.option_list + (
55
        make_option("--users", dest="users",
56
                    help=("Enforce resources only for the specified list "
57
                          "of users, e.g uuid1,uuid2")),
58
        make_option("--resources",
59
                    help="Specify resources to check, default: %s" %
60
                    ",".join(DEFAULT_RESOURCES)),
61
        make_option("--fix",
62
                    default=False,
63
                    action="store_true",
64
                    help="Fix violations"),
65
        make_option("--force",
66
                    default=False,
67
                    action="store_true",
68
                    help=("Confirm actions that may permanently "
69
                          "remove a vm")),
70
    )
71

    
72
    def confirm(self):
73
        self.stderr.write("Confirm? [y/N] ")
74
        response = raw_input()
75
        if string.lower(response) not in ['y', 'yes']:
76
            self.stdout.write("Aborted.\n")
77
            exit()
78

    
79
    def get_handlers(self, resources):
80
        def rem(v):
81
            try:
82
                resources.remove(v)
83
                return True
84
            except ValueError:
85
                return False
86

    
87
        if resources is None:
88
            resources = list(DEFAULT_RESOURCES)
89
        else:
90
            resources = resources.split(",")
91

    
92
        handlers = [h for h in enforce.RESOURCE_HANDLING if rem(h[0])]
93
        if resources:
94
            m = "No such resource '%s'" % resources[0]
95
            raise CommandError(m)
96
        return handlers
97

    
98
    @transaction.commit_on_success
99
    def handle(self, *args, **options):
100
        write = self.stderr.write
101
        fix = options["fix"]
102
        force = options["force"]
103

    
104
        users = options['users']
105
        if users is not None:
106
            users = users.split(',')
107

    
108
        handlers = self.get_handlers(options["resources"])
109
        try:
110
            qh_holdings = util.get_qh_users_holdings(users)
111
        except errors.AstakosClientException as e:
112
            raise CommandError(e)
113

    
114
        resources = set(h[0] for h in handlers)
115
        dangerous = bool(resources.difference(DEFAULT_RESOURCES))
116

    
117
        actions = {}
118
        overlimit = []
119
        viol_id = 0
120
        for resource, handle_resource, resource_type in handlers:
121
            if resource_type not in actions:
122
                actions[resource_type] = {}
123
            actual_resources = enforce.get_actual_resources(resource_type,
124
                                                            users)
125
            for user, user_quota in qh_holdings.iteritems():
126
                for source, source_quota in user_quota.iteritems():
127
                    try:
128
                        qh = util.transform_quotas(source_quota)
129
                        qh_value, qh_limit, qh_pending = qh[resource]
130
                    except KeyError:
131
                        write("Resource '%s' does not exist in Quotaholder"
132
                              " for user '%s' and source '%s'!\n" %
133
                              (resource, user, source))
134
                        continue
135
                    if qh_pending:
136
                        write("Pending commission for user '%s', source '%s', "
137
                              "resource '%s'. Skipping\n" %
138
                              (user, source, resource))
139
                        continue
140
                    diff = qh_value - qh_limit
141
                    if diff > 0:
142
                        viol_id += 1
143
                        overlimit.append((viol_id, user, source, resource,
144
                                          qh_limit, qh_value))
145
                        relevant_resources = actual_resources[user]
146
                        handle_resource(viol_id, resource, relevant_resources,
147
                                        diff, actions)
148

    
149
        if not overlimit:
150
            write("No violations.\n")
151
            return
152

    
153
        headers = ("#", "User", "Source", "Resource", "Limit", "Usage")
154
        pprint_table(self.stderr, overlimit, headers,
155
                     options["output_format"], title="Violations")
156

    
157
        if any(actions.values()):
158
            write("\n")
159
            if fix:
160
                if dangerous and not force:
161
                    write("You are enforcing resources that may permanently "
162
                          "remove a vm.\n")
163
                    self.confirm()
164
                write("Applying actions. Please wait...\n")
165
            title = "Applied Actions" if fix else "Suggested Actions"
166
            log = enforce.perform_actions(actions, fix=fix)
167
            headers = ("Type", "ID", "State", "Action", "Violation")
168
            if fix:
169
                headers += ("Result",)
170
            pprint_table(self.stderr, log, headers,
171
                         options["output_format"], title=title)