1
# vim: tabstop=4 shiftwidth=4 softtabstop=4
3
# Copyright 2011 OpenStack LLC.
6
# Licensed under the Apache License, Version 2.0 (the "License"); you may
7
# not use this file except in compliance with the License. You may obtain
8
# a copy of the License at
10
# http://www.apache.org/licenses/LICENSE-2.0
12
# Unless required by applicable law or agreed to in writing, software
13
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
14
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
15
# License for the specific language governing permissions and limitations
21
from datetime import datetime
22
from nova import exception
23
from nova import flags
24
from nova.compute import api
25
from nova.api.openstack import extensions
26
from nova.api.openstack import views
27
from nova.db.sqlalchemy.session import get_session
34
class SimpleTenantUsageController(object):
35
def _hours_for(self, instance, period_start, period_stop):
36
launched_at = instance['launched_at']
37
terminated_at = instance['terminated_at']
38
if terminated_at is not None:
39
if not isinstance(terminated_at, datetime):
40
terminated_at = datetime.strptime(terminated_at,
41
"%Y-%m-%d %H:%M:%S.%f")
43
if launched_at is not None:
44
if not isinstance(launched_at, datetime):
45
launched_at = datetime.strptime(launched_at,
46
"%Y-%m-%d %H:%M:%S.%f")
48
if terminated_at and terminated_at < period_start:
50
# nothing if it started after the usage report ended
51
if launched_at and launched_at > period_stop:
54
# if instance launched after period_started, don't charge for first
55
start = max(launched_at, period_start)
57
# if instance stopped before period_stop, don't charge after
58
stop = min(period_stop, terminated_at)
60
# instance is still running, so charge them up to current time
63
seconds = dt.days * 3600 * 24 + dt.seconds\
64
+ dt.microseconds / 100000.0
66
return seconds / 3600.0
68
# instance hasn't launched, so no charge
71
def _tenant_usages_for_period(self, context, period_start,
72
period_stop, tenant_id=None, detailed=True):
74
compute_api = api.API()
75
instances = compute_api.get_active_by_window(context,
79
from nova import log as logging
80
logging.info(instances)
84
for instance in instances:
86
info['hours'] = self._hours_for(instance,
89
flavor_type = instance['instance_type_id']
91
if not flavors.get(flavor_type):
93
it_ref = compute_api.get_instance_type(context,
95
flavors[flavor_type] = it_ref
96
except exception.InstanceTypeNotFound:
97
# can't bill if there is no instance type
100
flavor = flavors[flavor_type]
102
info['name'] = instance['display_name']
104
info['memory_mb'] = flavor['memory_mb']
105
info['local_gb'] = flavor['local_gb']
106
info['vcpus'] = flavor['vcpus']
108
info['tenant_id'] = instance['project_id']
110
info['flavor'] = flavor['name']
112
info['started_at'] = instance['launched_at']
114
info['ended_at'] = instance['terminated_at']
117
info['state'] = 'terminated'
119
info['state'] = instance['state_description']
121
now = datetime.utcnow()
123
if info['state'] == 'terminated':
124
delta = info['ended_at'] - info['started_at']
126
delta = now - info['started_at']
128
info['uptime'] = delta.days * 24 * 60 + delta.seconds
130
if not info['tenant_id'] in rval:
132
summary['tenant_id'] = info['tenant_id']
134
summary['server_usages'] = []
135
summary['total_local_gb_usage'] = 0
136
summary['total_vcpus_usage'] = 0
137
summary['total_memory_mb_usage'] = 0
138
summary['total_hours'] = 0
139
summary['start'] = period_start
140
summary['stop'] = period_stop
141
rval[info['tenant_id']] = summary
143
summary = rval[info['tenant_id']]
144
summary['total_local_gb_usage'] += info['local_gb'] * info['hours']
145
summary['total_vcpus_usage'] += info['vcpus'] * info['hours']
146
summary['total_memory_mb_usage'] += info['memory_mb']\
149
summary['total_hours'] += info['hours']
151
summary['server_usages'].append(info)
155
def _parse_datetime(self, dtstr):
156
if isinstance(dtstr, datetime):
159
return datetime.strptime(dtstr, "%Y-%m-%dT%H:%M:%S")
162
return datetime.strptime(dtstr, "%Y-%m-%dT%H:%M:%S.%f")
164
return datetime.strptime(dtstr, "%Y-%m-%d %H:%M:%S.%f")
166
def _get_datetime_range(self, req):
167
qs = req.environ.get('QUERY_STRING', '')
168
env = urlparse.parse_qs(qs)
169
period_start = self._parse_datetime(env.get('start',
170
[datetime.utcnow().isoformat()])[0])
171
period_stop = self._parse_datetime(env.get('end',
172
[datetime.utcnow().isoformat()])[0])
174
detailed = bool(env.get('detailed', False))
175
return (period_start, period_stop, detailed)
177
def index(self, req):
178
"""Retrive tenant_usage for all tenants"""
179
context = req.environ['nova.context']
181
if not context.is_admin and FLAGS.allow_admin_api:
182
return webob.Response(status_int=403)
184
(period_start, period_stop, detailed) = self._get_datetime_range(req)
185
usages = self._tenant_usages_for_period(context,
189
return {'tenant_usages': usages}
191
def show(self, req, id):
192
"""Retrive tenant_usage for a specified tenant"""
194
context = req.environ['nova.context']
196
if not context.is_admin and FLAGS.allow_admin_api:
197
if tenant_id != context.project_id:
198
return webob.Response(status_int=403)
200
(period_start, period_stop, ignore) = self._get_datetime_range(req)
201
usage = self._tenant_usages_for_period(context,
210
return {'tenant_usage': usage}
213
class Simple_tenant_usage(extensions.ExtensionDescriptor):
215
return "SimpleTenantUsage"
218
return "os-simple-tenant-usage"
220
def get_description(self):
221
return "Simple tenant usage extension"
223
def get_namespace(self):
224
return "http://docs.openstack.org/ext/os-simple-tenant-usage/api/v1.1"
226
def get_updated(self):
227
return "2011-08-19T00:00:00+00:00"
229
def get_resources(self):
232
res = extensions.ResourceExtension('os-simple-tenant-usage',
233
SimpleTenantUsageController())
234
resources.append(res)