summaryrefslogtreecommitdiff
path: root/openstack_dashboard/usage/views.py
blob: 5d1ea8f201db4e7963fb973537f52c1f02b0510a (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
#      http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.

import collections

from django.contrib.humanize.templatetags import humanize as humanize_filters
from django.utils.translation import pgettext_lazy
from django.utils.translation import ugettext_lazy as _

from horizon import exceptions
from horizon import tables
from horizon.templatetags import sizeformat
from openstack_dashboard import api
from openstack_dashboard.usage import base


class UsageView(tables.DataTableView):
    usage_class = None
    show_deleted = True
    csv_template_name = None
    page_title = _("Overview")

    def __init__(self, *args, **kwargs):
        super(UsageView, self).__init__(*args, **kwargs)
        if not issubclass(self.usage_class, base.BaseUsage):
            raise AttributeError("You must specify a usage_class attribute "
                                 "which is a subclass of BaseUsage.")

    def get_template_names(self):
        if self.request.GET.get('format', 'html') == 'csv':
            return (self.csv_template_name or
                    ".".join((self.template_name.rsplit('.', 1)[0], 'csv')))
        return self.template_name

    def get_content_type(self):
        if self.request.GET.get('format', 'html') == 'csv':
            return "text/csv"
        return "text/html"

    def get_data(self):
        try:
            project_id = self.kwargs.get('project_id',
                                         self.request.user.tenant_id)
            self.usage = self.usage_class(self.request, project_id)
            self.usage.summarize(*self.usage.get_date_range())
            self.kwargs['usage'] = self.usage
            return self.usage.usage_list
        except Exception:
            exceptions.handle(self.request,
                              _('Unable to retrieve usage information.'))
            return []

    def get_context_data(self, **kwargs):
        context = super(UsageView, self).get_context_data(**kwargs)
        context['table'].kwargs['usage'] = self.usage
        context['form'] = self.usage.form
        context['usage'] = self.usage

        try:
            context['simple_tenant_usage_enabled'] = \
                api.nova.extension_supported('SimpleTenantUsage', self.request)
        except Exception:
            context['simple_tenant_usage_enabled'] = True
        return context

    def render_to_response(self, context, **response_kwargs):
        if self.request.GET.get('format', 'html') == 'csv':
            render_class = self.csv_response_class
            response_kwargs.setdefault("filename", "usage.csv")
        else:
            render_class = self.response_class
        context = self.render_context_with_title(context)
        resp = render_class(request=self.request,
                            template=self.get_template_names(),
                            context=context,
                            content_type=self.get_content_type(),
                            **response_kwargs)
        return resp


ChartDef = collections.namedtuple(
    'ChartDef',
    ('quota_key', 'label', 'used_phrase', 'filters'))
# Each ChartDef should contains the following fields:
# - quota key:
#   The key must be included in a response of tenant_quota_usages().
# - Human Readable Name:
# - text to display when describing the quota.
#   If None is specified, the default value 'Used' will be used.
# - filters to be applied to the value
#   If None is specified, the default filter 'intcomma' will be applied.
#   if you want to apply no filters, specify an empty tuple or list.
CHART_DEFS = [
    {
        'title': _("Compute"),
        'charts': [
            ChartDef("instances", _("Instances"), None, None),
            ChartDef("cores", _("VCPUs"), None, None),
            ChartDef("ram", _("RAM"), None, (sizeformat.mb_float_format,)),
        ]
    },
    {
        'title': _("Volume"),
        'charts': [
            ChartDef("volumes", _("Volumes"), None, None),
            ChartDef("snapshots", _("Volume Snapshots"), None, None),
            ChartDef("gigabytes", _("Volume Storage"), None,
                     (sizeformat.diskgbformat,)),
        ]
    },
    {
        'title': _("Network"),
        'charts': [
            ChartDef("floatingip", _("Floating IPs"),
                     pgettext_lazy('Label in the limit summary', "Allocated"),
                     None),
            ChartDef("security_group", _("Security Groups"), None, None),
            ChartDef("security_group_rule", _("Security Group Rules"),
                     None, None),
            ChartDef("network", _("Networks"), None, None),
            ChartDef("port", _("Ports"), None, None),
            ChartDef("router", _("Routers"), None, None),
        ]
    },
]


def _apply_filters(value, filters):
    if not filters:
        return value
    for f in filters:
        value = f(value)
    return value


class ProjectUsageView(UsageView):

    def _get_charts_data(self):
        chart_sections = []
        for section in CHART_DEFS:
            chart_data = self._process_chart_section(section['charts'])
            chart_sections.append({
                'title': section['title'],
                'charts': chart_data
            })
        return chart_sections

    def _process_chart_section(self, chart_defs):
        charts = []
        for t in chart_defs:
            if t.quota_key not in self.usage.limits:
                continue
            key = t.quota_key
            used = self.usage.limits[key]['used']
            quota = self.usage.limits[key]['quota']
            text = t.used_phrase
            if text is None:
                text = pgettext_lazy('Label in the limit summary', 'Used')

            filters = t.filters
            if filters is None:
                filters = (humanize_filters.intcomma,)
            used_display = _apply_filters(used, filters)
            # When quota is float('inf'), we don't show quota
            # so filtering is unnecessary.
            quota_display = None
            if quota != float('inf'):
                quota_display = _apply_filters(quota, filters)
            else:
                quota_display = quota

            charts.append({
                'type': key,
                'name': t.label,
                'used': used,
                'quota': quota,
                'used_display': used_display,
                'quota_display': quota_display,
                'text': text
            })
        return charts

    def get_context_data(self, **kwargs):
        context = super(ProjectUsageView, self).get_context_data(**kwargs)
        context['charts'] = self._get_charts_data()
        return context

    def get_data(self):
        data = super(ProjectUsageView, self).get_data()
        try:
            self.usage.get_limits()
        except Exception:
            exceptions.handle(self.request,
                              _('Unable to retrieve limits information.'))
        return data