Merge "Billing statistics improved"

This commit is contained in:
Jenkins
2014-04-14 16:01:31 +00:00
committed by Gerrit Code Review
9 changed files with 153 additions and 35 deletions

View File

@@ -30,7 +30,8 @@ Workflow:
Body:
- $.primaryController.deploy()
- $.secondaryControllers.pselect($.deploy())
- $.reportDeployed()
- $.reportDeployed(title => 'MS Active Directory',
unitCount => len(secondaryControllers) + 1)
destroy:
- $.reportDestroyed()

View File

@@ -5,8 +5,15 @@ Name: Application
Workflow:
reportDeployed:
Arguments:
- title:
Contract: $.string()
Default: null
- unitCount:
Contract: $.int()
Default: null
Body:
- $this.find(Environment).instanceNotifier.trackApplication($this)
- $this.find(Environment).instanceNotifier.trackApplication($this, $title, $unitCount)
reportDestroyed:
Body:

View File

@@ -25,14 +25,14 @@ API_NAME = 'EnvironmentStatistics'
class Controller(object):
@request_statistics.stats_count(API_NAME, 'GetForEnvironment')
def get_for_environment(self, request, environment_id):
LOG.debug(_('EnvironmentStatistics:GetForEnvironment'))
@request_statistics.stats_count(API_NAME, 'GetAggregated')
def get_aggregated(self, request, environment_id):
LOG.debug(_('EnvironmentStatistics:GetAggregated'))
# TODO (stanlagun): Check that caller is authorized to access
# tenant's statistics
return instances.InstanceStatsServices.get_environment_stats(
return instances.InstanceStatsServices.get_aggregated_stats(
environment_id)
@request_statistics.stats_count(API_NAME, 'GetForInstance')
@@ -42,9 +42,19 @@ class Controller(object):
# TODO (stanlagun): Check that caller is authorized to access
# tenant's statistics
return instances.InstanceStatsServices.get_environment_stats(
return instances.InstanceStatsServices.get_raw_environment_stats(
environment_id, instance_id)
@request_statistics.stats_count(API_NAME, 'GetForEnvironment')
def get_for_environment(self, request, environment_id):
LOG.debug(_('EnvironmentStatistics:GetForEnvironment'))
# TODO (stanlagun): Check that caller is authorized to access
# tenant's statistics
return instances.InstanceStatsServices.get_raw_environment_stats(
environment_id)
def create_resource():
return wsgi.Resource(Controller())

View File

@@ -15,8 +15,8 @@ import routes
from muranoapi.api.v1 import catalog
from muranoapi.api.v1 import deployments
from muranoapi.api.v1 import environment_statistics
from muranoapi.api.v1 import environments
from muranoapi.api.v1 import instance_statistics
from muranoapi.api.v1 import request_statistics
from muranoapi.api.v1 import services
from muranoapi.api.v1 import sessions
@@ -122,17 +122,23 @@ class API(wsgi.Router):
action='deploy',
conditions={'method': ['POST']})
statistics_resource = environment_statistics.create_resource()
statistics_resource = instance_statistics.create_resource()
mapper.connect(
'/environments/{environment_id}/statistics/{instance_id}',
'/environments/{environment_id}/instance-statistics/raw/'
'{instance_id}',
controller=statistics_resource,
action='get_for_instance',
conditions={'method': ['GET']})
mapper.connect(
'/environments/{environment_id}/statistics',
'/environments/{environment_id}/instance-statistics/raw',
controller=statistics_resource,
action='get_for_environment',
conditions={'method': ['GET']})
mapper.connect(
'/environments/{environment_id}/instance-statistics/aggregated',
controller=statistics_resource,
action='get_aggregated',
conditions={'method': ['GET']})
catalog_resource = catalog.create_resource()
mapper.connect('/catalog/packages/categories',

View File

@@ -117,8 +117,13 @@ def track_instance(payload):
instance_id = payload['instance']
instance_type = payload.get('instance_type', 0)
environment_id = payload['environment']
unit_count = payload.get('unit_count')
type_name = payload['type_name']
type_title = payload.get('type_title')
instances.InstanceStatsServices.track_instance(
instance_id, environment_id, instance_type)
instance_id, environment_id, instance_type,
type_name, type_title, unit_count)
@notification_endpoint_wrapper()

View File

@@ -0,0 +1,43 @@
# Copyright (c) 2013 Mirantis, Inc.
#
# 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.
from sqlalchemy import schema
from sqlalchemy import types
meta = schema.MetaData()
def upgrade(migrate_engine):
meta.bind = migrate_engine
table = schema.Table('instance', meta, autoload=True)
table.rename('instance_stats')
table.delete()
type_name = schema.Column('type_name', types.String(512), nullable=False)
type_name.create(table)
type_title = schema.Column('type_title', types.String(512))
type_title.create(table)
unit_count = schema.Column('unit_count', types.Integer())
unit_count.create(table)
tenant_id = schema.Column('tenant_id', types.String(32), nullable=False)
tenant_id.create(table)
def downgrade(migrate_engine):
meta.bind = migrate_engine
table = schema.Table('instance_stats', meta, autoload=True)
table.rename('instance')
table.c.type_name.drop()
table.c.type_title.drop()
table.c.unit_count.drop()
table.c.tenant_id.drop()

View File

@@ -233,7 +233,7 @@ package_to_tag = sa.Table('package_to_tag',
class Instance(BASE, ModelBase):
__tablename__ = 'instance'
__tablename__ = 'instance_stats'
environment_id = sa.Column(
sa.String(100), primary_key=True, nullable=False)
@@ -242,6 +242,10 @@ class Instance(BASE, ModelBase):
instance_type = sa.Column(sa.Integer, default=0, nullable=False)
created = sa.Column(sa.Integer, nullable=False)
destroyed = sa.Column(sa.Integer, nullable=True)
type_name = sa.Column('type_name', sa.String(512), nullable=False)
type_title = sa.Column('type_title', sa.String(512))
unit_count = sa.Column('unit_count', sa.Integer())
tenant_id = sa.Column('tenant_id', sa.String(32), nullable=False)
def to_dict(self):
dictionary = super(Instance, self).to_dict()

View File

@@ -12,6 +12,8 @@
# License for the specific language governing permissions and limitations
# under the License.
import sqlalchemy
from sqlalchemy.sql import func
from muranoapi.db import models
@@ -27,20 +29,31 @@ OS_INSTANCE = 200
class InstanceStatsServices(object):
@staticmethod
def track_instance(instance_id, environment_id, instance_type):
instance = models.Instance()
instance.instance_id = instance_id
instance.environment_id = environment_id
instance.instance_type = instance_type
instance.created = timeutils.utcnow_ts()
instance.destroyed = None
def track_instance(instance_id, environment_id, instance_type,
type_name, type_title=None, unit_count=None):
unit = db_session.get_session()
try:
with unit.begin():
env = unit.query(models.Environment).get(environment_id)
instance = models.Instance()
instance.instance_id = instance_id
instance.environment_id = environment_id
instance.tenant_id = env.tenant_id
instance.instance_type = instance_type
instance.created = timeutils.utcnow_ts()
instance.destroyed = None
instance.type_name = type_name
instance.type_title = type_title
instance.unit_count = unit_count
unit.add(instance)
except exception.DBDuplicateEntry:
pass # expected behaviour when record already exists
unit.execute(
sqlalchemy.update(models.Instance).where(
models.Instance.instance_id == instance_id and
models.Instance.environment_id == environment_id).values(
unit_count=unit_count))
@staticmethod
def destroy_instance(instance_id, environment_id):
@@ -52,16 +65,13 @@ class InstanceStatsServices(object):
instance.save(unit)
@staticmethod
def get_environment_stats(environment_id, instance_id=None):
def get_aggregated_stats(environment_id):
unit = db_session.get_session()
now = timeutils.utcnow_ts()
query = unit.query(models.Instance.instance_type, func.sum(
func.coalesce(models.Instance.destroyed, now) -
models.Instance.created), func.count()).filter(
models.Instance.environment_id == environment_id)
if instance_id is not None:
query = query.filter(
models.Instance.instance_id == instance_id)
res = query.group_by(models.Instance.instance_type).all()
@@ -70,3 +80,25 @@ class InstanceStatsServices(object):
'duration': int(record[1]),
'count': int(record[2])
} for record in res]
@staticmethod
def get_raw_environment_stats(environment_id, instance_id=None):
unit = db_session.get_session()
now = timeutils.utcnow_ts()
query = unit.query(models.Instance).filter(
models.Instance.environment_id == environment_id)
if instance_id:
query = query.filter(models.Instance.instance_id == instance_id)
res = query.all()
return [{
'type': record.instance_type,
'duration': (record.destroyed or now) - record.created,
'type_name': record.type_name,
'unit_count': record.unit_count,
'instance_id': record.instance_id,
'type_title': record.type_title,
'active': True if not record.destroyed else False
} for record in res]

View File

@@ -42,26 +42,36 @@ class InstanceReportNotifier(object):
topic='murano')
self._environment_id = environment.object_id
def _track_instance(self, instance, instance_type, untrack=False):
def _track_instance(self, instance, instance_type,
type_title, unit_count):
payload = {
'instance': instance.object_id,
'environment': self._environment_id,
'instance_type': instance_type
'instance_type': instance_type,
'type_name': instance.type.name,
'type_title': type_title,
'unit_count': unit_count
}
event_type = 'murano.untrack_instance' \
if untrack else 'murano.track_instance'
self._notifier.info({}, 'murano.track_instance', payload)
self._notifier.info({}, event_type, payload)
def _untrack_instance(self, instance, instance_type):
payload = {
'instance': instance.object_id,
'environment': self._environment_id,
'instance_type': instance_type,
}
def trackApplication(self, instance):
self._track_instance(instance, APPLICATION, False)
self._notifier.info({}, 'murano.untrack_instance', payload)
def trackApplication(self, instance, title=None, unitCount=None):
self._track_instance(instance, APPLICATION, title, unitCount)
def untrackApplication(self, instance):
self._track_instance(instance, APPLICATION, True)
self._untrack_instance(instance, APPLICATION)
def trackCloudInstance(self, instance):
self._track_instance(instance, OS_INSTANCE, False)
self._track_instance(instance, OS_INSTANCE, None, 1)
def untrackCloudInstance(self, instance):
self._track_instance(instance, OS_INSTANCE, True)
self._untrack_instance(instance, OS_INSTANCE)