Skip to content

Commit 772033d

Browse files
kathapphilippthun
andauthored
Enhance /v3/organizations/:guid/usage_summary (#3513)
* Enhance /v3/organizations/:guid/usage_summary It would be nice to get org usage info for (nearly) all resources that are restricted by an org quota plan, beside memory in mb and started instances, which are already implemented. In the course of the change running_and_pending_tasks_count was refactored from nested selects to joins. Joins are generally more efficient than nested selects, especially when dealing with large datasets. Co-authored-by: Philipp Thun <philipp.thun@sap.com>
1 parent 7935829 commit 772033d

7 files changed

Lines changed: 145 additions & 7 deletions

File tree

app/models/runtime/organization.rb

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -274,6 +274,14 @@ def members
274274
User.where(id: Role.where(organization_id: id).distinct.select(:user_id))
275275
end
276276

277+
def running_and_pending_tasks_count
278+
VCAP::CloudController::TaskModel.dataset.where(state: [TaskModel::PENDING_STATE, TaskModel::RUNNING_STATE]).
279+
join(:apps, guid: :app_guid).
280+
join(:spaces, guid: :space_guid).
281+
where(spaces__organization_id: id).
282+
count
283+
end
284+
277285
private
278286

279287
def validate_default_isolation_segment
@@ -334,9 +342,5 @@ def running_task_log_rate_limit
334342
def started_app_log_rate_limit
335343
processes_dataset.where(state: ProcessModel::STARTED).sum(Sequel.*(:log_rate_limit, :instances)) || 0
336344
end
337-
338-
def running_and_pending_tasks_count
339-
tasks_dataset.where(state: [TaskModel::PENDING_STATE, TaskModel::RUNNING_STATE]).count
340-
end
341345
end
342346
end

app/presenters/v3/organization_usage_summary_presenter.rb

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,10 +3,17 @@
33
module VCAP::CloudController::Presenters::V3
44
class OrganizationUsageSummaryPresenter < BasePresenter
55
def to_hash
6+
org_usage = VCAP::CloudController::OrganizationQuotaUsage.new(org)
67
{
78
usage_summary: {
89
started_instances: VCAP::CloudController::OrganizationInstanceUsageCalculator.get_instance_usage(org),
9-
memory_in_mb: org.memory_used
10+
memory_in_mb: org.memory_used,
11+
routes: org_usage.routes,
12+
service_instances: org_usage.service_instances,
13+
reserved_ports: org_usage.reserved_route_ports,
14+
domains: org_usage.private_domains,
15+
per_app_tasks: org_usage.app_tasks,
16+
service_keys: org_usage.service_keys
1017
},
1118
links: build_links
1219
}

docs/v3/source/includes/api_resources/_organizations.erb

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -219,7 +219,13 @@
219219
{
220220
"usage_summary": {
221221
"started_instances": 3,
222-
"memory_in_mb": 50
222+
"memory_in_mb": 50,
223+
"routes": 4,
224+
"service_instances": 2,
225+
"reserved_ports": 1,
226+
"domains": 4,
227+
"per_app_tasks": 2,
228+
"service_keys": 1
223229
},
224230
"links": {
225231
"self": {

lib/cloud_controller.rb

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -79,6 +79,7 @@ module VCAP::CloudController; end
7979
require 'cloud_controller/controller_factory'
8080
require 'cloud_controller/egress_network_rules_presenter'
8181
require 'cloud_controller/organization_instance_usage_calculator'
82+
require 'cloud_controller/organization_quota_usage'
8283
require 'cloud_controller/url_secret_obfuscator'
8384

8485
require 'cloud_controller/legacy_api/legacy_api_base'
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
module VCAP::CloudController
2+
class OrganizationQuotaUsage
3+
def initialize(organization)
4+
@organization = organization
5+
end
6+
7+
def routes
8+
OrganizationRoutes.new(@organization).count
9+
end
10+
11+
def service_instances
12+
@organization.managed_service_instances_dataset.count
13+
end
14+
15+
def private_domains
16+
@organization.owned_private_domains_dataset.count
17+
end
18+
19+
def service_keys
20+
VCAP::CloudController::ServiceKey.dataset.join(:service_instances, id: :service_instance_id).
21+
join(:spaces, id: :space_id).
22+
where(spaces__organization_id: @organization.id).
23+
count
24+
end
25+
26+
def reserved_route_ports
27+
OrganizationReservedRoutePorts.new(@organization).count
28+
end
29+
30+
def app_tasks
31+
@organization.running_and_pending_tasks_count
32+
end
33+
end
34+
end

spec/request/organizations_spec.rb

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -994,7 +994,13 @@ module VCAP::CloudController
994994
{
995995
usage_summary: {
996996
started_instances: 5,
997-
memory_in_mb: 705 # (tasks: 200 * 2) + (processes: 101 + 2 * 102)
997+
memory_in_mb: 705, # (tasks: 200 * 2) + (processes: 101 + 2 * 102)
998+
routes: 0,
999+
service_instances: 0,
1000+
reserved_ports: 0,
1001+
domains: 0,
1002+
per_app_tasks: 0,
1003+
service_keys: 0
9981004
},
9991005
links: {
10001006
self: { href: %r{#{Regexp.escape(link_prefix)}/v3/organizations/#{org.guid}/usage_summary} },
Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,80 @@
1+
require 'spec_helper'
2+
3+
module VCAP::CloudController
4+
RSpec.describe OrganizationQuotaUsage do
5+
let(:org) { Organization.make }
6+
let(:space1) { Space.make(organization: org) }
7+
let(:space2) { Space.make }
8+
let(:space3) { Space.make(organization: org) }
9+
10+
subject(:org_usage) { OrganizationQuotaUsage.new(org) }
11+
12+
describe '#routes' do
13+
before do
14+
[space1, space1, space2, space3].each { |s| Route.make(space: s) }
15+
end
16+
17+
it 'returns the number of routes in all spaces under the org' do
18+
expect(org_usage.routes).to eq(3)
19+
end
20+
end
21+
22+
describe '#service_instances' do
23+
before do
24+
[space1, space1, space2, space3].each { |s| ManagedServiceInstance.make(space: s) }
25+
UserProvidedServiceInstance.make(space: space1)
26+
end
27+
28+
it 'returns the number of service instances in all spaces under the org' do
29+
expect(org_usage.service_instances).to eq(3)
30+
end
31+
end
32+
33+
describe '#private_domains' do
34+
before do
35+
[space1, space1, space2, space3].each { |s| Domain.make(owning_organization: s.organization) }
36+
end
37+
38+
it 'returns the number of private domains in all spaces under the org' do
39+
expect(org_usage.private_domains).to eq(3)
40+
end
41+
end
42+
43+
describe '#service_keys' do
44+
before do
45+
[space1, space1, space2, space3].each { |s| ServiceKey.make(service_instance: ServiceInstance.make(space: s)) }
46+
end
47+
48+
it 'returns the number of service keys in all spaces under the org' do
49+
expect(org_usage.service_keys).to eq(3)
50+
end
51+
end
52+
53+
describe '#reserved_route_ports' do
54+
before do
55+
reservable_ports = [1234, 2, 2345, 3]
56+
router_group = instance_double(RoutingApi::RouterGroup, type: 'tcp', reservable_ports: reservable_ports)
57+
routing_api_client = instance_double(RoutingApi::Client, router_group: router_group, enabled?: true)
58+
allow(CloudController::DependencyLocator.instance).to receive(:routing_api_client).and_return(routing_api_client)
59+
domain = SharedDomain.make(router_group_guid: 'some-router-group')
60+
[space1, space1, space2, space3].each_with_index { |s, i| Route.make(space: s, domain: domain, host: '', port: reservable_ports[i]) }
61+
end
62+
63+
it 'returns the number of reserved route ports in all spaces under the org' do
64+
expect(org_usage.reserved_route_ports).to eq(3)
65+
end
66+
end
67+
68+
describe '#app_tasks' do
69+
before do
70+
[space1, space1, space2, space3].each { |s| TaskModel.make(app: AppModel.make(space: s), state: TaskModel::RUNNING_STATE) }
71+
TaskModel.make(app: AppModel.make(space: space1), state: TaskModel::PENDING_STATE)
72+
TaskModel.make(app: AppModel.make(space: space1), state: TaskModel::CANCELING_STATE)
73+
end
74+
75+
it 'returns the number of app tasks in all spaces under the org' do
76+
expect(org_usage.app_tasks).to eq(4)
77+
end
78+
end
79+
end
80+
end

0 commit comments

Comments
 (0)