from __future__ import division import datetime import logging from dateutil.relativedelta import relativedelta from django.contrib import messages from django.utils.translation import ugettext as _ from horizon import api from horizon import exceptions from horizon import forms from horizon import time LOG = logging.getLogger(__name__) class BaseUsage(object): show_terminated = False def __init__(self, request, tenant_id=None): self.tenant_id = tenant_id or request.user.tenant_id self.request = request self.summary = {} self.usage_list = [] @property def today(self): return time.today() @staticmethod def get_datetime(date, now=False): if now: now = time.utcnow() current_time = time.time(now.hour, now.minute, now.second) else: current_time = time.time() return datetime.datetime.combine(date, current_time) @staticmethod def get_start(year, month, day=1): return datetime.date(year, month, day) @staticmethod def get_end(year, month, day=1): period = relativedelta(months=1) date_end = BaseUsage.get_start(year, month, day) + period if date_end > time.today(): date_end = time.today() return date_end def get_instances(self): instance_list = [] [instance_list.extend(u.server_usages) for u in self.usage_list] return instance_list def get_date_range(self): if not hasattr(self, "start") or not hasattr(self, "end"): args = (self.today.year, self.today.month) form = self.get_form() if form.is_valid(): args = (int(form.cleaned_data['year']), int(form.cleaned_data['month'])) self.start = self.get_start(*args) self.end = self.get_end(*args) return self.start, self.end def get_form(self): if not hasattr(self, 'form'): if (any(key in ['month', 'year'] for key in self.request.GET.keys())): # bound form self.form = forms.DateForm(self.request.GET) else: # non-bound form self.form = forms.DateForm(initial={ 'month': self.today.month, 'year': self.today.year}) return self.form def get_usage_list(self, start, end): raise NotImplementedError("You must define a get_usage method.") def summarize(self, start, end): if start <= end <= time.today(): # Convert to datetime.datetime just for API call. start = BaseUsage.get_datetime(start) end = BaseUsage.get_datetime(end, now=True) try: self.usage_list = self.get_usage_list(start, end) except: exceptions.handle(self.request, _('Unable to retrieve usage information.')) else: messages.info(self.request, _("You are viewing data for the future, " "which may or may not exist.")) for tenant_usage in self.usage_list: tenant_summary = tenant_usage.get_summary() for key, value in tenant_summary.items(): self.summary.setdefault(key, 0) self.summary[key] += value def csv_link(self): return "?date_month=%s&date_year=%s&format=csv" % self.get_date_range() class GlobalUsage(BaseUsage): show_terminated = True def get_usage_list(self, start, end): return api.usage_list(self.request, start, end) class TenantUsage(BaseUsage): attrs = ('memory_mb', 'vcpus', 'uptime', 'hours', 'local_gb') def get_usage_list(self, start, end): show_terminated = self.request.GET.get('show_terminated', self.show_terminated) instances = [] terminated_instances = [] usage = api.usage_get(self.request, self.tenant_id, start, end) # Attribute may not exist if there are no instances if hasattr(usage, 'server_usages'): now = datetime.datetime.now() for server_usage in usage.server_usages: # This is a way to phrase uptime in a way that is compatible # with the 'timesince' filter. (Use of local time intentional.) server_uptime = server_usage['uptime'] total_uptime = now - datetime.timedelta(seconds=server_uptime) server_usage['uptime_at'] = total_uptime if server_usage['ended_at'] and not show_terminated: terminated_instances.append(server_usage) else: instances.append(server_usage) usage.server_usages = instances return (usage,)