You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@aurora.apache.org by mc...@apache.org on 2014/02/06 20:43:37 UTC
git commit: Add a "quota" noun for querying role quotas.
Updated Branches:
refs/heads/master 1545e7f56 -> d2dc5715b
Add a "quota" noun for querying role quotas.
Reviewed in https://reviews.apache.org/r/17332/
Project: http://git-wip-us.apache.org/repos/asf/incubator-aurora/repo
Commit: http://git-wip-us.apache.org/repos/asf/incubator-aurora/commit/d2dc5715
Tree: http://git-wip-us.apache.org/repos/asf/incubator-aurora/tree/d2dc5715
Diff: http://git-wip-us.apache.org/repos/asf/incubator-aurora/diff/d2dc5715
Branch: refs/heads/master
Commit: d2dc5715b551c81c02dfe87645010c4db70fd2eb
Parents: 1545e7f
Author: Mark Chu-Carroll <mc...@twopensource.com>
Authored: Thu Feb 6 14:39:40 2014 -0500
Committer: Mark Chu-Carroll <mc...@twitter.com>
Committed: Thu Feb 6 14:39:40 2014 -0500
----------------------------------------------------------------------
src/main/python/apache/aurora/client/cli/BUILD | 10 +-
.../python/apache/aurora/client/cli/__init__.py | 2 +
.../python/apache/aurora/client/cli/options.py | 16 +++
.../python/apache/aurora/client/cli/quota.py | 108 +++++++++++++++++++
src/test/python/apache/aurora/client/cli/BUILD | 14 ++-
.../apache/aurora/client/cli/test_quota.py | 90 ++++++++++++++++
.../python/apache/aurora/client/cli/util.py | 14 +++
7 files changed, 247 insertions(+), 7 deletions(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/incubator-aurora/blob/d2dc5715/src/main/python/apache/aurora/client/cli/BUILD
----------------------------------------------------------------------
diff --git a/src/main/python/apache/aurora/client/cli/BUILD b/src/main/python/apache/aurora/client/cli/BUILD
index 1019e00..63954d6 100644
--- a/src/main/python/apache/aurora/client/cli/BUILD
+++ b/src/main/python/apache/aurora/client/cli/BUILD
@@ -16,15 +16,13 @@
python_binary(
name='client',
- entry_point = 'apache.aurora.cli:main',
- dependencies = [
- pants(':cli'),
- ],
-)
+ entry_point = 'apache.aurora.client.cli:main',
+ dependencies = [ pants(':cli') ],
+ )
python_library(
name='cli',
- sources = [ '__init__.py', 'context.py', 'jobs.py', 'options.py' ],
+ sources = globs('*.py'),
dependencies = [
pants('3rdparty/python:argparse'),
pants('3rdparty/python:twitter.common.python'),
http://git-wip-us.apache.org/repos/asf/incubator-aurora/blob/d2dc5715/src/main/python/apache/aurora/client/cli/__init__.py
----------------------------------------------------------------------
diff --git a/src/main/python/apache/aurora/client/cli/__init__.py b/src/main/python/apache/aurora/client/cli/__init__.py
index 20ecbcf..14a6676 100644
--- a/src/main/python/apache/aurora/client/cli/__init__.py
+++ b/src/main/python/apache/aurora/client/cli/__init__.py
@@ -223,6 +223,8 @@ class AuroraCommandLine(CommandLine):
def register_nouns(self):
from .jobs import Job
self.register_noun(Job())
+ from .quota import Quota
+ self.register_noun(Quota())
def main():
http://git-wip-us.apache.org/repos/asf/incubator-aurora/blob/d2dc5715/src/main/python/apache/aurora/client/cli/options.py
----------------------------------------------------------------------
diff --git a/src/main/python/apache/aurora/client/cli/options.py b/src/main/python/apache/aurora/client/cli/options.py
index b45af32..5d6eba2 100644
--- a/src/main/python/apache/aurora/client/cli/options.py
+++ b/src/main/python/apache/aurora/client/cli/options.py
@@ -18,6 +18,15 @@ from apache.aurora.client.cli import CommandOption
from apache.aurora.common.aurora_job_key import AuroraJobKey
+def parse_qualified_role(rolestr):
+ if rolestr is None:
+ raise ValueError('Role argument cannot be empty!')
+ role_parts = rolestr.split('/')
+ if len(role_parts) != 2:
+ raise ValueError('Role argument must be a CLUSTER/NAME pair')
+ return role_parts
+
+
def parse_instances(instances):
"""Parse lists of instances or instance ranges into a set().
Examples:
@@ -83,6 +92,13 @@ JSON_WRITE_OPTION = CommandOption('--write_json', default=False, dest='write_jso
help='Generate command output in JSON format')
+ROLE_ARGUMENT = CommandOption('role', type=parse_qualified_role,
+ help='Rolename to retrieve information about, in CLUSTER/NAME format')
+
+
WATCH_OPTION = CommandOption('--watch_secs', type=int, default=30,
help='Minimum number of seconds a shard must remain in RUNNING state before considered a '
'success.')
+
+
+
http://git-wip-us.apache.org/repos/asf/incubator-aurora/blob/d2dc5715/src/main/python/apache/aurora/client/cli/quota.py
----------------------------------------------------------------------
diff --git a/src/main/python/apache/aurora/client/cli/quota.py b/src/main/python/apache/aurora/client/cli/quota.py
new file mode 100644
index 0000000..a7bcfbe
--- /dev/null
+++ b/src/main/python/apache/aurora/client/cli/quota.py
@@ -0,0 +1,108 @@
+#
+# Copyright 2013 Apache Software Foundation
+#
+# 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 __future__ import print_function
+
+from apache.aurora.client.cli import (
+ EXIT_INVALID_PARAMETER,
+ EXIT_OK,
+ Noun,
+ Verb,
+)
+from apache.aurora.client.cli.context import AuroraCommandContext
+from apache.aurora.client.cli.options import (
+ JSON_WRITE_OPTION,
+ ROLE_ARGUMENT,
+)
+
+from gen.apache.aurora.ttypes import ResponseCode
+
+from thrift.protocol import TJSONProtocol
+from thrift.TSerialization import serialize
+
+
+class GetQuotaCmd(Verb):
+ @property
+ def name(self):
+ return 'get'
+
+ @property
+ def help(self):
+ return """Usage: aurora quota get cluster/role
+
+ Prints information about quotas for a role
+ """
+
+ def setup_options_parser(self, parser):
+ self.add_option(parser, JSON_WRITE_OPTION)
+ self.add_option(parser, ROLE_ARGUMENT)
+
+ def render_quota(self, write_json, quota_resp):
+ def get_quota_json(quota):
+ result = {}
+ result['cpu'] = quota.numCpus
+ result['ram'] = float(quota.ramMb) / 1024
+ result['disk'] = float(quota.diskMb) / 1024
+ return result
+
+ def get_quota_str(quota):
+ result = []
+ result.append(' CPU: %s' % quota.numCpus)
+ result.append(' RAM: %f GB' % (float(quota.ramMb) / 1024))
+ result.append(' Disk: %f GB' % (float(quota.diskMb) / 1024))
+ return result
+
+ if write_json:
+ return serialize(quota_resp.result.getQuotaResult,
+ protocol_factory=TJSONProtocol.TSimpleJSONProtocolFactory())
+ else:
+ result = ['Allocated:']
+ result += get_quota_str(quota_resp.result.getQuotaResult.quota)
+ if quota_resp.result.getQuotaResult.consumed:
+ result.append('Consumed:')
+ result += get_quota_str(quota_resp.result.getQuotaResult.consumed)
+ return '\n'.join(result)
+
+ def execute(self, context):
+ (cluster, role) = context.options.role
+ api = context.get_api(cluster)
+ resp = api.get_quota(role)
+ if resp.responseCode == ResponseCode.ERROR:
+ raise context.CommandError(EXIT_INVALID_PARAMETER, 'Role %s not found' % role)
+ elif resp.responseCode == ResponseCode.INVALID_REQUEST:
+ raise context.CommandError(EXIT_COMMAND_FAILURE, 'Error retrieving quota for role %s' % role)
+ elif resp.responseCode == ResponseCode.AUTH_FAILED:
+ raise context.CommandError(EXIT_COMMAND_FAILURE, 'Invalid authorization to retrieve quota for role %s' % role)
+ context.print_out(self.render_quota(context.options.write_json, resp))
+ return EXIT_OK
+
+
+class Quota(Noun):
+ @property
+ def name(self):
+ return 'quota'
+
+ @property
+ def help(self):
+ return "Work with quota settings for an Apache Aurora cluster"
+
+ @classmethod
+ def create_context(cls):
+ return AuroraCommandContext()
+
+ def __init__(self):
+ super(Quota, self).__init__()
+ self.register_verb(GetQuotaCmd())
http://git-wip-us.apache.org/repos/asf/incubator-aurora/blob/d2dc5715/src/test/python/apache/aurora/client/cli/BUILD
----------------------------------------------------------------------
diff --git a/src/test/python/apache/aurora/client/cli/BUILD b/src/test/python/apache/aurora/client/cli/BUILD
index 5ba08ad..51cc380 100644
--- a/src/test/python/apache/aurora/client/cli/BUILD
+++ b/src/test/python/apache/aurora/client/cli/BUILD
@@ -16,7 +16,7 @@
python_test_suite(
name = 'all',
- dependencies = [ pants(':job') ]
+ dependencies = [ pants(':job'), pants(':quota') ]
)
python_library(
@@ -47,3 +47,15 @@ python_tests(
pants('src/test/python/apache/aurora/client/commands:util')
]
)
+
+python_tests(
+ name = 'quota',
+ sources = [ 'test_quota.py' ],
+ dependencies = [
+ pants(':util'),
+ pants('3rdparty/python:mock'),
+ pants('3rdparty/python:twitter.common.contextutil'),
+ pants('src/main/python/apache/aurora/client/cli'),
+ pants('src/test/python/apache/aurora/client/commands:util')
+ ]
+)
http://git-wip-us.apache.org/repos/asf/incubator-aurora/blob/d2dc5715/src/test/python/apache/aurora/client/cli/test_quota.py
----------------------------------------------------------------------
diff --git a/src/test/python/apache/aurora/client/cli/test_quota.py b/src/test/python/apache/aurora/client/cli/test_quota.py
new file mode 100644
index 0000000..d582fce
--- /dev/null
+++ b/src/test/python/apache/aurora/client/cli/test_quota.py
@@ -0,0 +1,90 @@
+#
+# Copyright 2013 Apache Software Foundation
+#
+# 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 contextlib
+
+from apache.aurora.client.cli import AuroraCommandLine
+from apache.aurora.client.cli.util import AuroraClientCommandTest, FakeAuroraCommandContext
+
+from gen.apache.aurora.ttypes import (
+ GetQuotaResult,
+ Quota,
+)
+
+from mock import patch
+
+
+class TestGetQuotaCommand(AuroraClientCommandTest):
+ @classmethod
+ def setup_mock_quota_call_no_consumed(cls, mock_context):
+ api = mock_context.get_api('west')
+ response = cls.create_simple_success_response()
+ response.result.getQuotaResult = GetQuotaResult()
+ response.result.getQuotaResult.quota = Quota()
+ response.result.getQuotaResult.quota.numCpus = 5
+ response.result.getQuotaResult.quota.ramMb = 20480
+ response.result.getQuotaResult.quota.diskMb = 40960
+ response.result.getQuotaResult.consumed = None
+ api.get_quota.return_value = response
+
+ @classmethod
+ def setup_mock_quota_call_with_consumed(cls, mock_context):
+ api = mock_context.get_api('west')
+ response = cls.create_simple_success_response()
+ response.result.getQuotaResult = GetQuotaResult()
+ response.result.getQuotaResult.quota = Quota()
+ response.result.getQuotaResult.quota.numCpus = 5
+ response.result.getQuotaResult.quota.ramMb = 20480
+ response.result.getQuotaResult.quota.diskMb = 40960
+ response.result.getQuotaResult.consumed = Quota()
+ response.result.getQuotaResult.consumed.numCpus = 1
+ response.result.getQuotaResult.consumed.ramMb = 1024
+ response.result.getQuotaResult.consumed.diskMb = 2048
+ api.get_quota.return_value = response
+
+ def test_get_quota_no_consumed(self):
+ mock_context = FakeAuroraCommandContext()
+ self.setup_mock_quota_call_no_consumed(mock_context)
+ with contextlib.nested(
+ patch('apache.aurora.client.cli.quota.Quota.create_context', return_value=mock_context),
+ patch('apache.aurora.client.factory.CLUSTERS', new=self.TEST_CLUSTERS)):
+ cmd = AuroraCommandLine()
+ cmd.execute(['quota', 'get', 'west/bozo'])
+ out = '\n'.join(mock_context.get_out())
+ assert out == "Allocated:\n CPU: 5\n RAM: 20.000000 GB\n Disk: 40.000000 GB"
+
+ def test_get_quota_with_consumed(self):
+ mock_context = FakeAuroraCommandContext()
+ self.setup_mock_quota_call_with_consumed(mock_context)
+ with contextlib.nested(
+ patch('apache.aurora.client.cli.quota.Quota.create_context', return_value=mock_context),
+ patch('apache.aurora.client.factory.CLUSTERS', new=self.TEST_CLUSTERS)):
+ cmd = AuroraCommandLine()
+ cmd.execute(['quota', 'get', 'west/bozo'])
+ out = '\n'.join(mock_context.get_out())
+ assert out == ("Allocated:\n CPU: 5\n RAM: 20.000000 GB\n Disk: 40.000000 GB\n"
+ "Consumed:\n CPU: 1\n RAM: 1.000000 GB\n Disk: 2.000000 GB")
+
+ def test_get_quota_with_consumed_json(self):
+ mock_context = FakeAuroraCommandContext()
+ self.setup_mock_quota_call_no_consumed(mock_context)
+ with contextlib.nested(
+ patch('apache.aurora.client.cli.quota.Quota.create_context', return_value=mock_context),
+ patch('apache.aurora.client.factory.CLUSTERS', new=self.TEST_CLUSTERS)):
+ cmd = AuroraCommandLine()
+ cmd.execute(['quota', 'get', '--write_json', 'west/bozo'])
+ out = '\n'.join(mock_context.get_out())
+ assert out == '{"quota":{"numCpus":5,"ramMb":20480,"diskMb":40960}}'
http://git-wip-us.apache.org/repos/asf/incubator-aurora/blob/d2dc5715/src/test/python/apache/aurora/client/cli/util.py
----------------------------------------------------------------------
diff --git a/src/test/python/apache/aurora/client/cli/util.py b/src/test/python/apache/aurora/client/cli/util.py
index cbd544d..db65eee 100644
--- a/src/test/python/apache/aurora/client/cli/util.py
+++ b/src/test/python/apache/aurora/client/cli/util.py
@@ -38,6 +38,8 @@ class FakeAuroraCommandContext(AuroraCommandContext):
self.fake_api = self.create_mock_api()
self.task_status = []
self.showed_urls = []
+ self.out = []
+ self.err = []
def get_api(self, cluster):
return self.fake_api
@@ -59,6 +61,18 @@ class FakeAuroraCommandContext(AuroraCommandContext):
mock_api.scheduler_proxy = mock_scheduler_proxy
return mock_api
+ def print_out(self, str):
+ self.out.append(str)
+
+ def print_err(self, str):
+ self.err.append(str)
+
+ def get_out(self):
+ return self.out
+
+ def get_err(self):
+ return self.err
+
def open_page(self, url):
self.showed_urls.append(url)