~cloudbuilders/nova/os-keypair-integration

« back to all changes in this revision

Viewing changes to nova/api/openstack/contrib/simple_tenant_usage.py

  • Committer: Vishvananda Ishaya
  • Date: 2011-09-01 04:55:12 UTC
  • mfrom: (1455.1.62 nova)
  • Revision ID: vishvananda@gmail.com-20110901045512-ndwp9nu3alwbnnj8
merge trunk, fix tests

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
# vim: tabstop=4 shiftwidth=4 softtabstop=4
 
2
 
 
3
# Copyright 2011 OpenStack LLC.
 
4
# All Rights Reserved.
 
5
#
 
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
 
9
#
 
10
#         http://www.apache.org/licenses/LICENSE-2.0
 
11
#
 
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
 
16
#    under the License.
 
17
 
 
18
import urlparse
 
19
import webob
 
20
 
 
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
 
28
from webob import exc
 
29
 
 
30
 
 
31
FLAGS = flags.FLAGS
 
32
 
 
33
 
 
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")
 
42
 
 
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")
 
47
 
 
48
        if terminated_at and terminated_at < period_start:
 
49
            return 0
 
50
        # nothing if it started after the usage report ended
 
51
        if launched_at and launched_at > period_stop:
 
52
            return 0
 
53
        if launched_at:
 
54
            # if instance launched after period_started, don't charge for first
 
55
            start = max(launched_at, period_start)
 
56
            if terminated_at:
 
57
                # if instance stopped before period_stop, don't charge after
 
58
                stop = min(period_stop, terminated_at)
 
59
            else:
 
60
                # instance is still running, so charge them up to current time
 
61
                stop = period_stop
 
62
            dt = stop - start
 
63
            seconds = dt.days * 3600 * 24 + dt.seconds\
 
64
                      + dt.microseconds / 100000.0
 
65
 
 
66
            return seconds / 3600.0
 
67
        else:
 
68
            # instance hasn't launched, so no charge
 
69
            return 0
 
70
 
 
71
    def _tenant_usages_for_period(self, context, period_start,
 
72
                                  period_stop, tenant_id=None, detailed=True):
 
73
 
 
74
        compute_api = api.API()
 
75
        instances = compute_api.get_active_by_window(context,
 
76
                                                     period_start,
 
77
                                                     period_stop,
 
78
                                                     tenant_id)
 
79
        from nova import log as logging
 
80
        logging.info(instances)
 
81
        rval = {}
 
82
        flavors = {}
 
83
 
 
84
        for instance in instances:
 
85
            info = {}
 
86
            info['hours'] = self._hours_for(instance,
 
87
                                            period_start,
 
88
                                            period_stop)
 
89
            flavor_type = instance['instance_type_id']
 
90
 
 
91
            if not flavors.get(flavor_type):
 
92
                try:
 
93
                    it_ref = compute_api.get_instance_type(context,
 
94
                                                           flavor_type)
 
95
                    flavors[flavor_type] = it_ref
 
96
                except exception.InstanceTypeNotFound:
 
97
                    # can't bill if there is no instance type
 
98
                    continue
 
99
 
 
100
            flavor = flavors[flavor_type]
 
101
 
 
102
            info['name'] = instance['display_name']
 
103
 
 
104
            info['memory_mb'] = flavor['memory_mb']
 
105
            info['local_gb'] = flavor['local_gb']
 
106
            info['vcpus'] = flavor['vcpus']
 
107
 
 
108
            info['tenant_id'] = instance['project_id']
 
109
 
 
110
            info['flavor'] = flavor['name']
 
111
 
 
112
            info['started_at'] = instance['launched_at']
 
113
 
 
114
            info['ended_at'] = instance['terminated_at']
 
115
 
 
116
            if info['ended_at']:
 
117
                info['state'] = 'terminated'
 
118
            else:
 
119
                info['state'] = instance['state_description']
 
120
 
 
121
            now = datetime.utcnow()
 
122
 
 
123
            if info['state'] == 'terminated':
 
124
                delta = info['ended_at'] - info['started_at']
 
125
            else:
 
126
                delta = now - info['started_at']
 
127
 
 
128
            info['uptime'] = delta.days * 24 * 60 + delta.seconds
 
129
 
 
130
            if not info['tenant_id'] in rval:
 
131
                summary = {}
 
132
                summary['tenant_id'] = info['tenant_id']
 
133
                if detailed:
 
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
 
142
 
 
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']\
 
147
                                                * info['hours']
 
148
 
 
149
            summary['total_hours'] += info['hours']
 
150
            if detailed:
 
151
                summary['server_usages'].append(info)
 
152
 
 
153
        return rval.values()
 
154
 
 
155
    def _parse_datetime(self, dtstr):
 
156
        if isinstance(dtstr, datetime):
 
157
            return dtstr
 
158
        try:
 
159
            return datetime.strptime(dtstr, "%Y-%m-%dT%H:%M:%S")
 
160
        except:
 
161
            try:
 
162
                return datetime.strptime(dtstr, "%Y-%m-%dT%H:%M:%S.%f")
 
163
            except:
 
164
                return datetime.strptime(dtstr, "%Y-%m-%d %H:%M:%S.%f")
 
165
 
 
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])
 
173
 
 
174
        detailed = bool(env.get('detailed', False))
 
175
        return (period_start, period_stop, detailed)
 
176
 
 
177
    def index(self, req):
 
178
        """Retrive tenant_usage for all tenants"""
 
179
        context = req.environ['nova.context']
 
180
 
 
181
        if not context.is_admin and FLAGS.allow_admin_api:
 
182
            return webob.Response(status_int=403)
 
183
 
 
184
        (period_start, period_stop, detailed) = self._get_datetime_range(req)
 
185
        usages = self._tenant_usages_for_period(context,
 
186
                                                period_start,
 
187
                                                period_stop,
 
188
                                                detailed=detailed)
 
189
        return {'tenant_usages': usages}
 
190
 
 
191
    def show(self, req, id):
 
192
        """Retrive tenant_usage for a specified tenant"""
 
193
        tenant_id = id
 
194
        context = req.environ['nova.context']
 
195
 
 
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)
 
199
 
 
200
        (period_start, period_stop, ignore) = self._get_datetime_range(req)
 
201
        usage = self._tenant_usages_for_period(context,
 
202
                                               period_start,
 
203
                                               period_stop,
 
204
                                               tenant_id=tenant_id,
 
205
                                               detailed=True)
 
206
        if len(usage):
 
207
            usage = usage[0]
 
208
        else:
 
209
            usage = {}
 
210
        return {'tenant_usage': usage}
 
211
 
 
212
 
 
213
class Simple_tenant_usage(extensions.ExtensionDescriptor):
 
214
    def get_name(self):
 
215
        return "SimpleTenantUsage"
 
216
 
 
217
    def get_alias(self):
 
218
        return "os-simple-tenant-usage"
 
219
 
 
220
    def get_description(self):
 
221
        return "Simple tenant usage extension"
 
222
 
 
223
    def get_namespace(self):
 
224
        return "http://docs.openstack.org/ext/os-simple-tenant-usage/api/v1.1"
 
225
 
 
226
    def get_updated(self):
 
227
        return "2011-08-19T00:00:00+00:00"
 
228
 
 
229
    def get_resources(self):
 
230
        resources = []
 
231
 
 
232
        res = extensions.ResourceExtension('os-simple-tenant-usage',
 
233
                                            SimpleTenantUsageController())
 
234
        resources.append(res)
 
235
 
 
236
        return resources