Merge "Billing statistics improved"
This commit is contained in:
@@ -30,7 +30,8 @@ Workflow:
|
|||||||
Body:
|
Body:
|
||||||
- $.primaryController.deploy()
|
- $.primaryController.deploy()
|
||||||
- $.secondaryControllers.pselect($.deploy())
|
- $.secondaryControllers.pselect($.deploy())
|
||||||
- $.reportDeployed()
|
- $.reportDeployed(title => 'MS Active Directory',
|
||||||
|
unitCount => len(secondaryControllers) + 1)
|
||||||
|
|
||||||
destroy:
|
destroy:
|
||||||
- $.reportDestroyed()
|
- $.reportDestroyed()
|
||||||
|
@@ -5,8 +5,15 @@ Name: Application
|
|||||||
|
|
||||||
Workflow:
|
Workflow:
|
||||||
reportDeployed:
|
reportDeployed:
|
||||||
|
Arguments:
|
||||||
|
- title:
|
||||||
|
Contract: $.string()
|
||||||
|
Default: null
|
||||||
|
- unitCount:
|
||||||
|
Contract: $.int()
|
||||||
|
Default: null
|
||||||
Body:
|
Body:
|
||||||
- $this.find(Environment).instanceNotifier.trackApplication($this)
|
- $this.find(Environment).instanceNotifier.trackApplication($this, $title, $unitCount)
|
||||||
|
|
||||||
reportDestroyed:
|
reportDestroyed:
|
||||||
Body:
|
Body:
|
||||||
|
@@ -25,14 +25,14 @@ API_NAME = 'EnvironmentStatistics'
|
|||||||
|
|
||||||
|
|
||||||
class Controller(object):
|
class Controller(object):
|
||||||
@request_statistics.stats_count(API_NAME, 'GetForEnvironment')
|
@request_statistics.stats_count(API_NAME, 'GetAggregated')
|
||||||
def get_for_environment(self, request, environment_id):
|
def get_aggregated(self, request, environment_id):
|
||||||
LOG.debug(_('EnvironmentStatistics:GetForEnvironment'))
|
LOG.debug(_('EnvironmentStatistics:GetAggregated'))
|
||||||
|
|
||||||
# TODO (stanlagun): Check that caller is authorized to access
|
# TODO (stanlagun): Check that caller is authorized to access
|
||||||
# tenant's statistics
|
# tenant's statistics
|
||||||
|
|
||||||
return instances.InstanceStatsServices.get_environment_stats(
|
return instances.InstanceStatsServices.get_aggregated_stats(
|
||||||
environment_id)
|
environment_id)
|
||||||
|
|
||||||
@request_statistics.stats_count(API_NAME, 'GetForInstance')
|
@request_statistics.stats_count(API_NAME, 'GetForInstance')
|
||||||
@@ -42,9 +42,19 @@ class Controller(object):
|
|||||||
# TODO (stanlagun): Check that caller is authorized to access
|
# TODO (stanlagun): Check that caller is authorized to access
|
||||||
# tenant's statistics
|
# tenant's statistics
|
||||||
|
|
||||||
return instances.InstanceStatsServices.get_environment_stats(
|
return instances.InstanceStatsServices.get_raw_environment_stats(
|
||||||
environment_id, instance_id)
|
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():
|
def create_resource():
|
||||||
return wsgi.Resource(Controller())
|
return wsgi.Resource(Controller())
|
@@ -15,8 +15,8 @@ import routes
|
|||||||
|
|
||||||
from muranoapi.api.v1 import catalog
|
from muranoapi.api.v1 import catalog
|
||||||
from muranoapi.api.v1 import deployments
|
from muranoapi.api.v1 import deployments
|
||||||
from muranoapi.api.v1 import environment_statistics
|
|
||||||
from muranoapi.api.v1 import environments
|
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 request_statistics
|
||||||
from muranoapi.api.v1 import services
|
from muranoapi.api.v1 import services
|
||||||
from muranoapi.api.v1 import sessions
|
from muranoapi.api.v1 import sessions
|
||||||
@@ -122,17 +122,23 @@ class API(wsgi.Router):
|
|||||||
action='deploy',
|
action='deploy',
|
||||||
conditions={'method': ['POST']})
|
conditions={'method': ['POST']})
|
||||||
|
|
||||||
statistics_resource = environment_statistics.create_resource()
|
statistics_resource = instance_statistics.create_resource()
|
||||||
mapper.connect(
|
mapper.connect(
|
||||||
'/environments/{environment_id}/statistics/{instance_id}',
|
'/environments/{environment_id}/instance-statistics/raw/'
|
||||||
|
'{instance_id}',
|
||||||
controller=statistics_resource,
|
controller=statistics_resource,
|
||||||
action='get_for_instance',
|
action='get_for_instance',
|
||||||
conditions={'method': ['GET']})
|
conditions={'method': ['GET']})
|
||||||
mapper.connect(
|
mapper.connect(
|
||||||
'/environments/{environment_id}/statistics',
|
'/environments/{environment_id}/instance-statistics/raw',
|
||||||
controller=statistics_resource,
|
controller=statistics_resource,
|
||||||
action='get_for_environment',
|
action='get_for_environment',
|
||||||
conditions={'method': ['GET']})
|
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()
|
catalog_resource = catalog.create_resource()
|
||||||
mapper.connect('/catalog/packages/categories',
|
mapper.connect('/catalog/packages/categories',
|
||||||
|
@@ -117,8 +117,13 @@ def track_instance(payload):
|
|||||||
instance_id = payload['instance']
|
instance_id = payload['instance']
|
||||||
instance_type = payload.get('instance_type', 0)
|
instance_type = payload.get('instance_type', 0)
|
||||||
environment_id = payload['environment']
|
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(
|
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()
|
@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):
|
class Instance(BASE, ModelBase):
|
||||||
__tablename__ = 'instance'
|
__tablename__ = 'instance_stats'
|
||||||
|
|
||||||
environment_id = sa.Column(
|
environment_id = sa.Column(
|
||||||
sa.String(100), primary_key=True, nullable=False)
|
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)
|
instance_type = sa.Column(sa.Integer, default=0, nullable=False)
|
||||||
created = sa.Column(sa.Integer, nullable=False)
|
created = sa.Column(sa.Integer, nullable=False)
|
||||||
destroyed = sa.Column(sa.Integer, nullable=True)
|
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):
|
def to_dict(self):
|
||||||
dictionary = super(Instance, self).to_dict()
|
dictionary = super(Instance, self).to_dict()
|
||||||
|
@@ -12,6 +12,8 @@
|
|||||||
# License for the specific language governing permissions and limitations
|
# License for the specific language governing permissions and limitations
|
||||||
# under the License.
|
# under the License.
|
||||||
|
|
||||||
|
import sqlalchemy
|
||||||
|
|
||||||
from sqlalchemy.sql import func
|
from sqlalchemy.sql import func
|
||||||
|
|
||||||
from muranoapi.db import models
|
from muranoapi.db import models
|
||||||
@@ -27,20 +29,31 @@ OS_INSTANCE = 200
|
|||||||
|
|
||||||
class InstanceStatsServices(object):
|
class InstanceStatsServices(object):
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def track_instance(instance_id, environment_id, instance_type):
|
def track_instance(instance_id, environment_id, instance_type,
|
||||||
instance = models.Instance()
|
type_name, type_title=None, unit_count=None):
|
||||||
instance.instance_id = instance_id
|
|
||||||
instance.environment_id = environment_id
|
|
||||||
instance.instance_type = instance_type
|
|
||||||
instance.created = timeutils.utcnow_ts()
|
|
||||||
instance.destroyed = None
|
|
||||||
|
|
||||||
unit = db_session.get_session()
|
unit = db_session.get_session()
|
||||||
try:
|
try:
|
||||||
with unit.begin():
|
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)
|
unit.add(instance)
|
||||||
except exception.DBDuplicateEntry:
|
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
|
@staticmethod
|
||||||
def destroy_instance(instance_id, environment_id):
|
def destroy_instance(instance_id, environment_id):
|
||||||
@@ -52,16 +65,13 @@ class InstanceStatsServices(object):
|
|||||||
instance.save(unit)
|
instance.save(unit)
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def get_environment_stats(environment_id, instance_id=None):
|
def get_aggregated_stats(environment_id):
|
||||||
unit = db_session.get_session()
|
unit = db_session.get_session()
|
||||||
now = timeutils.utcnow_ts()
|
now = timeutils.utcnow_ts()
|
||||||
query = unit.query(models.Instance.instance_type, func.sum(
|
query = unit.query(models.Instance.instance_type, func.sum(
|
||||||
func.coalesce(models.Instance.destroyed, now) -
|
func.coalesce(models.Instance.destroyed, now) -
|
||||||
models.Instance.created), func.count()).filter(
|
models.Instance.created), func.count()).filter(
|
||||||
models.Instance.environment_id == environment_id)
|
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()
|
res = query.group_by(models.Instance.instance_type).all()
|
||||||
|
|
||||||
@@ -70,3 +80,25 @@ class InstanceStatsServices(object):
|
|||||||
'duration': int(record[1]),
|
'duration': int(record[1]),
|
||||||
'count': int(record[2])
|
'count': int(record[2])
|
||||||
} for record in res]
|
} 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')
|
topic='murano')
|
||||||
self._environment_id = environment.object_id
|
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 = {
|
payload = {
|
||||||
'instance': instance.object_id,
|
'instance': instance.object_id,
|
||||||
'environment': self._environment_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' \
|
self._notifier.info({}, 'murano.track_instance', payload)
|
||||||
if untrack else 'murano.track_instance'
|
|
||||||
|
|
||||||
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._notifier.info({}, 'murano.untrack_instance', payload)
|
||||||
self._track_instance(instance, APPLICATION, False)
|
|
||||||
|
def trackApplication(self, instance, title=None, unitCount=None):
|
||||||
|
self._track_instance(instance, APPLICATION, title, unitCount)
|
||||||
|
|
||||||
def untrackApplication(self, instance):
|
def untrackApplication(self, instance):
|
||||||
self._track_instance(instance, APPLICATION, True)
|
self._untrack_instance(instance, APPLICATION)
|
||||||
|
|
||||||
def trackCloudInstance(self, instance):
|
def trackCloudInstance(self, instance):
|
||||||
self._track_instance(instance, OS_INSTANCE, False)
|
self._track_instance(instance, OS_INSTANCE, None, 1)
|
||||||
|
|
||||||
def untrackCloudInstance(self, instance):
|
def untrackCloudInstance(self, instance):
|
||||||
self._track_instance(instance, OS_INSTANCE, True)
|
self._untrack_instance(instance, OS_INSTANCE)
|
||||||
|
Reference in New Issue
Block a user