Merge "Added /reports, /reports/domains, /reports/records & /reports/tenants"

This commit is contained in:
Jenkins 2013-03-25 22:48:31 +00:00 committed by Gerrit Code Review
commit 1d1fcd608b
10 changed files with 277 additions and 1 deletions

View File

@ -62,7 +62,7 @@ default_log_levels = amqplib=WARN, sqlalchemy=WARN, boto=WARN, suds=INFO, keysto
#auth_strategy = noauth
# Enabled API Version 1 extensions
#enabled_extensions_v1 = diagnostics, sync, import, export
#enabled_extensions_v1 = diagnostics, sync, import, export, reports
#-----------------------
# Agent Service

View File

@ -33,6 +33,10 @@
"use_sudo": "rule:admin",
"use_blacklisted_domain": "rule:admin",
"count_domains": "rule:admin",
"count_records": "rule:admin",
"count_tenants": "rule:admin",
"diagnostics_ping": "rule:admin",
"diagnostics_sync_domains": "rule:admin",
"diagnostics_sync_domain": "rule:admin",

View File

@ -0,0 +1,92 @@
# Copyright 2012 Hewlett-Packard Development Company, L.P. All Rights Reserved.
#
# Author: Simon McCartney <simon.mccartney@hp.com>
#
# 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.
import flask
from moniker.openstack.common import log as logging
from moniker.openstack.common.rpc import common as rpc_common
from moniker import exceptions
from moniker.central import api as central_api
LOG = logging.getLogger(__name__)
blueprint = flask.Blueprint('reports', __name__)
@blueprint.route('/reports', methods=['GET'])
def reports():
context = flask.request.environ.get('context')
try:
domains = central_api.count_domains(context)
records = central_api.count_records(context)
tenants = central_api.count_tenants(context)
except exceptions.Forbidden:
return flask.Response(status=401)
except exceptions.DomainNotFound:
return flask.Response(status=404)
except rpc_common.Timeout:
return flask.Response(status=504)
else:
return flask.jsonify(domains=int(domains), records=int(records),
tenants=int(tenants))
@blueprint.route('/reports/domains', methods=['GET'])
def reports_domains():
context = flask.request.environ.get('context')
try:
count = central_api.count_domains(context)
except exceptions.Forbidden:
return flask.Response(status=401)
except exceptions.DomainNotFound:
return flask.Response(status=404)
except rpc_common.Timeout:
return flask.Response(status=504)
else:
return flask.jsonify(domains=int(count))
@blueprint.route('/reports/records', methods=['GET'])
def reports_records():
context = flask.request.environ.get('context')
try:
count = central_api.count_records(context)
except exceptions.Forbidden:
return flask.Response(status=401)
except exceptions.DomainNotFound:
return flask.Response(status=404)
except rpc_common.Timeout:
return flask.Response(status=504)
else:
return flask.jsonify(records=int(count))
@blueprint.route('/reports/tenants', methods=['GET'])
def reports_tenants():
context = flask.request.environ.get('context')
try:
count = central_api.count_tenants(context)
except exceptions.Forbidden:
return flask.Response(status=401)
except exceptions.DomainNotFound:
return flask.Response(status=404)
except rpc_common.Timeout:
return flask.Response(status=504)
else:
LOG.debug(count)
return flask.jsonify(tenants=int(count))

View File

@ -206,6 +206,17 @@ def delete_domain(context, domain_id):
return RPC.call(context, msg)
def count_domains(context, criterion=None):
msg = {
'method': 'count_domains',
'args': {
'criterion': criterion,
},
}
return RPC.call(context, msg)
def get_domain_servers(context, domain_id):
msg = {
'method': 'get_domain_servers',
@ -283,6 +294,25 @@ def delete_record(context, domain_id, record_id, increment_serial=True):
return RPC.call(context, msg)
def count_records(context, criterion=None):
msg = {
'method': 'count_records',
'args': {
'criterion': criterion,
},
}
return RPC.call(context, msg)
def count_tenants(context):
msg = {
'method': 'count_tenants',
}
return RPC.call(context, msg)
def sync_domains(context):
msg = {
'method': 'sync_domains',

View File

@ -468,6 +468,10 @@ class Service(rpc_service.Service):
return self.storage.delete_domain(context, domain_id)
def count_domains(self, context, criterion=None):
policy.check('count_domains', context)
return self.storage.count_domains(context, criterion)
def get_domain_servers(self, context, domain_id, criterion=None):
domain = self.storage.get_domain(context, domain_id)
@ -642,6 +646,14 @@ class Service(rpc_service.Service):
return self.storage.delete_record(context, record_id)
def count_records(self, context, criterion=None):
policy.check('count_records', context)
return self.storage.count_records(context, criterion)
def count_tenants(self, context):
policy.check('count_tenants', context)
return self.storage.count_tenants(context)
# Diagnostics Methods
def sync_domains(self, context):
policy.check('diagnostics_sync_domains', context)

View File

@ -113,6 +113,14 @@ class Storage(Plugin):
:param context: RPC Context.
:param domain_id: Domain ID to delete.
"""
@abc.abstractmethod
def count_domains(self, context, criterion=None):
"""
Count all Domains, across all accounts, applying criteria
:param context: RPC Context.
:param criterion: Criteria to filter by.
"""
@abc.abstractmethod
def create_record(self, context, domain_id, values):
@ -161,6 +169,23 @@ class Storage(Plugin):
:param record_id: Record ID to delete
"""
@abc.abstractmethod
def count_records(self, context, criterion=None):
"""
Count records, across all domains unless otherwise specified
:param context: RPC Context.
:param criterion: Criteria to filter by.
"""
@abc.abstractmethod
def count_tenants(self, context):
"""
Count tenants, across all domains
:param context: RPC Context.
"""
def ping(self, context):
""" Ping the Storage connection """
return {

View File

@ -15,6 +15,7 @@
# under the License.
import time
from sqlalchemy.orm import exc
from sqlalchemy import distinct
from moniker.openstack.common import cfg
from moniker.openstack.common import log as logging
from moniker import exceptions
@ -243,6 +244,11 @@ class SQLAlchemyStorage(base.Storage):
domain.delete(self.session)
def count_domains(self, context, criterion=None):
query = self.session.query(models.Domain)
query = self._apply_criterion(models.Domain, query, criterion)
return query.count()
# Record Methods
def create_record(self, context, domain_id, values):
record = models.Record()
@ -302,6 +308,17 @@ class SQLAlchemyStorage(base.Storage):
record.delete(self.session)
def count_records(self, context, criterion=None):
query = self.session.query(models.Record)
query = self._apply_criterion(models.Record, query, criterion)
return query.count()
# tenants are the owner of domains, count the number of unique tenants
# select count(distinct tenant_id) from domains
def count_tenants(self, context):
return self.session.query(distinct(models.Domain.tenant_id)).count()
# diagnostics
def ping(self, context):
start_time = time.time()

View File

@ -899,3 +899,59 @@ class CentralServiceTest(CentralTestCase):
with self.assertRaises(exceptions.RecordNotFound):
self.central_service.delete_record(context, other_domain['id'],
record['id'])
def test_count_domains(self):
# in the beginning, there should be nothing
domains = self.central_service.count_domains(self.admin_context)
self.assertEqual(domains, 0)
# Create a single domain
self.create_domain()
# count 'em up
domains = self.central_service.count_domains(self.admin_context)
# well, did we get 1?
self.assertEqual(domains, 1)
def test_count_domains_admin_only(self):
with self.assertRaises(exceptions.Forbidden):
self.central_service.count_domains(self.get_context())
def test_count_records(self):
# in the beginning, there should be nothing
records = self.central_service.count_records(self.admin_context)
self.assertEqual(records, 0)
# Create a domain to put our record in
domain = self.create_domain()
# Create a record
self.create_record(domain)
# we should have 1 record now
records = self.central_service.count_domains(self.admin_context)
self.assertEqual(records, 1)
def test_count_records_admin_only(self):
with self.assertRaises(exceptions.Forbidden):
self.central_service.count_records(self.get_context())
def test_count_tenants(self):
context = self.get_admin_context()
# in the beginning, there should be nothing
tenants = self.central_service.count_tenants(self.admin_context)
self.assertEqual(tenants, 0)
# Explicitly set a tenant_id
context.tenant_id = '1'
self.create_domain(fixture=0, context=context)
context.tenant_id = '2'
self.create_domain(fixture=1, context=context)
tenants = self.central_service.count_tenants(self.admin_context)
self.assertEqual(tenants, 2)
def test_count_tenants_admin_only(self):
with self.assertRaises(exceptions.Forbidden):
self.central_service.count_tenants(self.get_context())

View File

@ -607,3 +607,42 @@ class StorageTestCase(TestCase):
self.assertEqual(pong['status'], True)
self.assertIsNotNone(pong['rtt'])
def test_count_domains(self):
# in the beginning, there should be nothing
domains = self.storage.count_domains(self.admin_context)
self.assertEqual(domains, 0)
# Create a single domain
self.create_domain()
# count 'em up
domains = self.storage.count_domains(self.admin_context)
# well, did we get 1?
self.assertEqual(domains, 1)
def test_count_records(self):
# in the beginning, there should be nothing
records = self.storage.count_records(self.admin_context)
self.assertEqual(records, 0)
# Create a single domain & record
_, domain = self.create_domain()
self.create_record(domain)
# we should have 1 record now
records = self.storage.count_domains(self.admin_context)
self.assertEqual(records, 1)
def test_count_tenants(self):
# in the beginning, there should be nothing
tenants = self.storage.count_tenants(self.admin_context)
self.assertEqual(tenants, 0)
# create 2 domains with 2 tenants
self.create_domain(fixture=0, values={'tenant_id': 1})
self.create_domain(fixture=1, values={'tenant_id': 2})
tenants = self.storage.count_tenants(self.admin_context)
self.assertEqual(tenants, 2)

View File

@ -66,6 +66,7 @@ setup(
[moniker.api.v1.extensions]
diagnostics = moniker.api.v1.extensions.diagnostics:blueprint
sync = moniker.api.v1.extensions.sync:blueprint
reports = moniker.api.v1.extensions.reports:blueprint
[moniker.storage]
sqlalchemy = moniker.storage.impl_sqlalchemy:SQLAlchemyStorage