Merge "Billing statistics improved"
This commit is contained in:
@@ -30,7 +30,8 @@ Workflow:
|
||||
Body:
|
||||
- $.primaryController.deploy()
|
||||
- $.secondaryControllers.pselect($.deploy())
|
||||
- $.reportDeployed()
|
||||
- $.reportDeployed(title => 'MS Active Directory',
|
||||
unitCount => len(secondaryControllers) + 1)
|
||||
|
||||
destroy:
|
||||
- $.reportDestroyed()
|
||||
|
@@ -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:
|
||||
|
@@ -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())
|
@@ -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',
|
||||
|
@@ -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()
|
||||
|
@@ -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()
|
@@ -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()
|
||||
|
@@ -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]
|
||||
|
@@ -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)
|
||||
|
Reference in New Issue
Block a user