Add quotas and quota classes
Change-Id: I6ecdb63e633f145614b1a0d57712352dace7acf2 Implements: blueprint quota-support
This commit is contained in:
@@ -70,6 +70,12 @@ openstack.container.v1 =
|
|||||||
appcontainer_rebuild = zunclient.osc.v1.containers:RebuildContainer
|
appcontainer_rebuild = zunclient.osc.v1.containers:RebuildContainer
|
||||||
appcontainer_action_list = zunclient.osc.v1.containers:ActionList
|
appcontainer_action_list = zunclient.osc.v1.containers:ActionList
|
||||||
appcontainer_action_show = zunclient.osc.v1.containers:ActionShow
|
appcontainer_action_show = zunclient.osc.v1.containers:ActionShow
|
||||||
|
appcontainer_quota_get = zunclient.osc.v1.quotas:GetQuota
|
||||||
|
appcontainer_quota_default = zunclient.osc.v1.quotas:GetDefaultQuota
|
||||||
|
appcontainer_quota_delete = zunclient.osc.v1.quotas:DeleteQuota
|
||||||
|
appcontainer_quota_update = zunclient.osc.v1.quotas:UpdateQuota
|
||||||
|
appcontainer_quota_class_update = zunclient.osc.v1.quotas:UpdateQuotaClass
|
||||||
|
appcontainer_quota_class_get = zunclient.osc.v1.quotas:GetQuotaClass
|
||||||
|
|
||||||
[build_sphinx]
|
[build_sphinx]
|
||||||
source-dir = doc/source
|
source-dir = doc/source
|
||||||
|
@@ -31,7 +31,7 @@ if not LOG.handlers:
|
|||||||
HEADER_NAME = "OpenStack-API-Version"
|
HEADER_NAME = "OpenStack-API-Version"
|
||||||
SERVICE_TYPE = "container"
|
SERVICE_TYPE = "container"
|
||||||
MIN_API_VERSION = '1.1'
|
MIN_API_VERSION = '1.1'
|
||||||
MAX_API_VERSION = '1.25'
|
MAX_API_VERSION = '1.26'
|
||||||
DEFAULT_API_VERSION = '1.latest'
|
DEFAULT_API_VERSION = '1.latest'
|
||||||
|
|
||||||
_SUBSTITUTIONS = {}
|
_SUBSTITUTIONS = {}
|
||||||
|
@@ -230,18 +230,21 @@ def keys_and_vals_to_strs(dictionary):
|
|||||||
return dict((to_str(k), to_str(v)) for k, v in dictionary.items())
|
return dict((to_str(k), to_str(v)) for k, v in dictionary.items())
|
||||||
|
|
||||||
|
|
||||||
def print_dict(dct, dict_property="Property", wrap=0):
|
def print_dict(dct, dict_property="Property", wrap=0, value_fields=None):
|
||||||
"""Print a `dict` as a table of two columns.
|
"""Print a `dict` as a table of two columns.
|
||||||
|
|
||||||
:param dct: `dict` to print
|
:param dct: `dict` to print
|
||||||
:param dict_property: name of the first column
|
:param dict_property: name of the first column
|
||||||
:param wrap: wrapping for the second column
|
:param wrap: wrapping for the second column
|
||||||
|
:param value_fields: attributes that correspond to columns, in order
|
||||||
"""
|
"""
|
||||||
pt = prettytable.PrettyTable([dict_property, 'Value'])
|
pt = prettytable.PrettyTable([dict_property, 'Value'])
|
||||||
|
if value_fields:
|
||||||
|
pt = prettytable.PrettyTable([dict_property] + list(value_fields))
|
||||||
pt.align = 'l'
|
pt.align = 'l'
|
||||||
for k, v in dct.items():
|
for k, v in dct.items():
|
||||||
# convert dict to str to check length
|
# convert dict to str to check length
|
||||||
if isinstance(v, dict):
|
if isinstance(v, dict) and not value_fields:
|
||||||
v = six.text_type(keys_and_vals_to_strs(v))
|
v = six.text_type(keys_and_vals_to_strs(v))
|
||||||
if wrap > 0:
|
if wrap > 0:
|
||||||
v = textwrap.fill(six.text_type(v), wrap)
|
v = textwrap.fill(six.text_type(v), wrap)
|
||||||
@@ -255,6 +258,9 @@ def print_dict(dct, dict_property="Property", wrap=0):
|
|||||||
for line in lines:
|
for line in lines:
|
||||||
pt.add_row([col1, line])
|
pt.add_row([col1, line])
|
||||||
col1 = ''
|
col1 = ''
|
||||||
|
elif isinstance(v, dict):
|
||||||
|
vals = [v[field] for field in v if field in value_fields]
|
||||||
|
pt.add_row([k] + vals)
|
||||||
elif isinstance(v, list):
|
elif isinstance(v, list):
|
||||||
val = str([str(i) for i in v])
|
val = str([str(i) for i in v])
|
||||||
pt.add_row([k, val])
|
pt.add_row([k, val])
|
||||||
|
91
zunclient/osc/v1/quota_classes.py
Normal file
91
zunclient/osc/v1/quota_classes.py
Normal file
@@ -0,0 +1,91 @@
|
|||||||
|
# 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 osc_lib.command import command
|
||||||
|
from osc_lib import utils
|
||||||
|
from oslo_log import log as logging
|
||||||
|
|
||||||
|
|
||||||
|
def _quota_class_columns(quota_class):
|
||||||
|
return quota_class.__dict__.keys()
|
||||||
|
|
||||||
|
|
||||||
|
def _get_client(obj, parsed_args):
|
||||||
|
obj.log.debug("take_action(%s)" % parsed_args)
|
||||||
|
return obj.app.client_manager.container
|
||||||
|
|
||||||
|
|
||||||
|
class UpdateQuotaClass(command.ShowOne):
|
||||||
|
"""Update the quotas for a quota class"""
|
||||||
|
|
||||||
|
log = logging.getLogger(__name__ + ".UpdateQuotaClass")
|
||||||
|
|
||||||
|
def get_parser(self, prog_name):
|
||||||
|
parser = super(UpdateQuotaClass, self).get_parser(prog_name)
|
||||||
|
parser.add_argument(
|
||||||
|
'--containers',
|
||||||
|
metavar='<containers>',
|
||||||
|
help='The number of containers allowed per project')
|
||||||
|
parser.add_argument(
|
||||||
|
'--memory',
|
||||||
|
metavar='<memory>',
|
||||||
|
help='The number of megabytes of container RAM '
|
||||||
|
'allowed per project')
|
||||||
|
parser.add_argument(
|
||||||
|
'--cpu',
|
||||||
|
metavar='<cpu>',
|
||||||
|
help='The number of container cores or vCPUs '
|
||||||
|
'allowed per project')
|
||||||
|
parser.add_argument(
|
||||||
|
'--disk',
|
||||||
|
metavar='<disk>',
|
||||||
|
help='The number of gigabytes of container Disk '
|
||||||
|
'allowed per project')
|
||||||
|
parser.add_argument(
|
||||||
|
'quota_class_name',
|
||||||
|
metavar='<quota_class_name>',
|
||||||
|
help='The name of quota class')
|
||||||
|
return parser
|
||||||
|
|
||||||
|
def take_action(self, parsed_args):
|
||||||
|
client = _get_client(self, parsed_args)
|
||||||
|
opts = {}
|
||||||
|
opts['containers'] = parsed_args.containers
|
||||||
|
opts['memory'] = parsed_args.memory
|
||||||
|
opts['cpu'] = parsed_args.cpu
|
||||||
|
opts['disk'] = parsed_args.disk
|
||||||
|
quota_class_name = parsed_args.quota_class_name
|
||||||
|
quota_class = client.quota_classes.update(
|
||||||
|
quota_class_name, **opts)
|
||||||
|
columns = _quota_class_columns(quota_class)
|
||||||
|
return columns, utils.get_item_properties(quota_class, columns)
|
||||||
|
|
||||||
|
|
||||||
|
class GetQuotaClass(command.ShowOne):
|
||||||
|
"""List the quotas for a quota class"""
|
||||||
|
|
||||||
|
log = logging.getLogger(__name__ + '.GetQuotaClass')
|
||||||
|
|
||||||
|
def get_parser(self, prog_name):
|
||||||
|
parser = super(GetQuotaClass, self).get_parser(prog_name)
|
||||||
|
parser.add_argument(
|
||||||
|
'quota_class_name',
|
||||||
|
metavar='<quota_class_name>',
|
||||||
|
help='The name of quota class')
|
||||||
|
return parser
|
||||||
|
|
||||||
|
def take_action(self, parsed_args):
|
||||||
|
client = _get_client(self, parsed_args)
|
||||||
|
quota_class_name = parsed_args.quota_class_name
|
||||||
|
quota_class = client.quota_class.get(quota_class_name)
|
||||||
|
columns = _quota_class_columns(quota_class_name)
|
||||||
|
return columns, utils.get_item_properties(quota_class, columns)
|
113
zunclient/osc/v1/quotas.py
Normal file
113
zunclient/osc/v1/quotas.py
Normal file
@@ -0,0 +1,113 @@
|
|||||||
|
# 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 osc_lib.command import command
|
||||||
|
from osc_lib import utils
|
||||||
|
from oslo_log import log as logging
|
||||||
|
|
||||||
|
from zunclient.i18n import _
|
||||||
|
|
||||||
|
|
||||||
|
def _quota_columns(quota):
|
||||||
|
return quota._info.keys()
|
||||||
|
|
||||||
|
|
||||||
|
def _get_client(obj, parsed_args):
|
||||||
|
obj.log.debug("take_action(%s)" % parsed_args)
|
||||||
|
return obj.app.client_manager.container
|
||||||
|
|
||||||
|
|
||||||
|
class UpdateQuota(command.ShowOne):
|
||||||
|
"""Update the quotas of the project"""
|
||||||
|
|
||||||
|
log = logging.getLogger(__name__ + ".UpdateQuota")
|
||||||
|
|
||||||
|
def get_parser(self, prog_name):
|
||||||
|
parser = super(UpdateQuota, self).get_parser(prog_name)
|
||||||
|
parser.add_argument(
|
||||||
|
'--containers',
|
||||||
|
metavar='<containers>',
|
||||||
|
help='The number of containers allowed per project')
|
||||||
|
parser.add_argument(
|
||||||
|
'--memory',
|
||||||
|
metavar='<memory>',
|
||||||
|
help='The number of megabytes of container RAM '
|
||||||
|
'allowed per project')
|
||||||
|
parser.add_argument(
|
||||||
|
'--cpu',
|
||||||
|
metavar='<cpu>',
|
||||||
|
help='The number of container cores or vCPUs '
|
||||||
|
'allowed per project')
|
||||||
|
parser.add_argument(
|
||||||
|
'--disk',
|
||||||
|
metavar='<disk>',
|
||||||
|
help='The number of gigabytes of container Disk '
|
||||||
|
'allowed per project')
|
||||||
|
return parser
|
||||||
|
|
||||||
|
def take_action(self, parsed_args):
|
||||||
|
client = _get_client(self, parsed_args)
|
||||||
|
opts = {}
|
||||||
|
opts['containers'] = parsed_args.containers
|
||||||
|
opts['memory'] = parsed_args.memory
|
||||||
|
opts['cpu'] = parsed_args.cpu
|
||||||
|
opts['disk'] = parsed_args.disk
|
||||||
|
quota = client.quotas.update(**opts)
|
||||||
|
columns = _quota_columns(quota)
|
||||||
|
return columns, utils.get_item_properties(quota, columns)
|
||||||
|
|
||||||
|
|
||||||
|
class GetQuota(command.ShowOne):
|
||||||
|
"""Get quota of the project"""
|
||||||
|
|
||||||
|
log = logging.getLogger(__name__ + '.GetQuota')
|
||||||
|
|
||||||
|
def get_parser(self, prog_name):
|
||||||
|
parser = super(GetQuota, self).get_parser(prog_name)
|
||||||
|
parser.add_argument(
|
||||||
|
'--usages',
|
||||||
|
action='store_true',
|
||||||
|
help='Whether show quota usage statistic or not')
|
||||||
|
return parser
|
||||||
|
|
||||||
|
def take_action(self, parsed_args):
|
||||||
|
client = _get_client(self, parsed_args)
|
||||||
|
quota = client.quotas.get(usages=parsed_args.usages)
|
||||||
|
columns = _quota_columns(quota)
|
||||||
|
return columns, utils.get_item_properties(quota, columns)
|
||||||
|
|
||||||
|
|
||||||
|
class GetDefaultQuota(command.ShowOne):
|
||||||
|
"""Get default quota of the project"""
|
||||||
|
|
||||||
|
log = logging.getLogger(__name__ + '.GetDefeaultQuota')
|
||||||
|
|
||||||
|
def take_action(self, parsed_args):
|
||||||
|
client = _get_client(self, parsed_args)
|
||||||
|
default_quota = client.quotas.defaults()
|
||||||
|
columns = _quota_columns(default_quota)
|
||||||
|
return columns, utils.get_item_properties(
|
||||||
|
default_quota, columns)
|
||||||
|
|
||||||
|
|
||||||
|
class DeleteQuota(command.Command):
|
||||||
|
"""Delete quota of the project"""
|
||||||
|
|
||||||
|
log = logging.getLogger(__name__ + '.DeleteQuota')
|
||||||
|
|
||||||
|
def take_action(self, parsed_args):
|
||||||
|
client = _get_client(self, parsed_args)
|
||||||
|
try:
|
||||||
|
client.quotas.delete()
|
||||||
|
print(_('Request to delete quotas has been accepted.'))
|
||||||
|
except Exception as e:
|
||||||
|
print("Delete for quotas failed: %(e)s" % {'e': e})
|
@@ -249,32 +249,32 @@ class ShellTest(utils.TestCase):
|
|||||||
project_domain_id='', project_domain_name='',
|
project_domain_id='', project_domain_name='',
|
||||||
user_domain_id='', user_domain_name='', profile=None,
|
user_domain_id='', user_domain_name='', profile=None,
|
||||||
endpoint_override=None, insecure=False, cacert=None,
|
endpoint_override=None, insecure=False, cacert=None,
|
||||||
version=api_versions.APIVersion('1.25'))
|
version=api_versions.APIVersion('1.26'))
|
||||||
|
|
||||||
def test_main_option_region(self):
|
def test_main_option_region(self):
|
||||||
self.make_env()
|
self.make_env()
|
||||||
self._test_main_region(
|
self._test_main_region(
|
||||||
'--zun-api-version 1.25 '
|
'--zun-api-version 1.26 '
|
||||||
'--os-region-name=myregion service-list', 'myregion')
|
'--os-region-name=myregion service-list', 'myregion')
|
||||||
|
|
||||||
def test_main_env_region(self):
|
def test_main_env_region(self):
|
||||||
fake_env = dict(utils.FAKE_ENV, OS_REGION_NAME='myregion')
|
fake_env = dict(utils.FAKE_ENV, OS_REGION_NAME='myregion')
|
||||||
self.make_env(fake_env=fake_env)
|
self.make_env(fake_env=fake_env)
|
||||||
self._test_main_region(
|
self._test_main_region(
|
||||||
'--zun-api-version 1.25 '
|
'--zun-api-version 1.26 '
|
||||||
'service-list', 'myregion')
|
'service-list', 'myregion')
|
||||||
|
|
||||||
def test_main_no_region(self):
|
def test_main_no_region(self):
|
||||||
self.make_env()
|
self.make_env()
|
||||||
self._test_main_region(
|
self._test_main_region(
|
||||||
'--zun-api-version 1.25 '
|
'--zun-api-version 1.26 '
|
||||||
'service-list', None)
|
'service-list', None)
|
||||||
|
|
||||||
@mock.patch('zunclient.client.Client')
|
@mock.patch('zunclient.client.Client')
|
||||||
def test_main_endpoint_public(self, mock_client):
|
def test_main_endpoint_public(self, mock_client):
|
||||||
self.make_env()
|
self.make_env()
|
||||||
self.shell(
|
self.shell(
|
||||||
'--zun-api-version 1.25 '
|
'--zun-api-version 1.26 '
|
||||||
'--endpoint-type publicURL service-list')
|
'--endpoint-type publicURL service-list')
|
||||||
mock_client.assert_called_once_with(
|
mock_client.assert_called_once_with(
|
||||||
username='username', password='password',
|
username='username', password='password',
|
||||||
@@ -284,13 +284,13 @@ class ShellTest(utils.TestCase):
|
|||||||
project_domain_id='', project_domain_name='',
|
project_domain_id='', project_domain_name='',
|
||||||
user_domain_id='', user_domain_name='', profile=None,
|
user_domain_id='', user_domain_name='', profile=None,
|
||||||
endpoint_override=None, insecure=False, cacert=None,
|
endpoint_override=None, insecure=False, cacert=None,
|
||||||
version=api_versions.APIVersion('1.25'))
|
version=api_versions.APIVersion('1.26'))
|
||||||
|
|
||||||
@mock.patch('zunclient.client.Client')
|
@mock.patch('zunclient.client.Client')
|
||||||
def test_main_endpoint_internal(self, mock_client):
|
def test_main_endpoint_internal(self, mock_client):
|
||||||
self.make_env()
|
self.make_env()
|
||||||
self.shell(
|
self.shell(
|
||||||
'--zun-api-version 1.25 '
|
'--zun-api-version 1.26 '
|
||||||
'--endpoint-type internalURL service-list')
|
'--endpoint-type internalURL service-list')
|
||||||
mock_client.assert_called_once_with(
|
mock_client.assert_called_once_with(
|
||||||
username='username', password='password',
|
username='username', password='password',
|
||||||
@@ -300,7 +300,7 @@ class ShellTest(utils.TestCase):
|
|||||||
project_domain_id='', project_domain_name='',
|
project_domain_id='', project_domain_name='',
|
||||||
user_domain_id='', user_domain_name='', profile=None,
|
user_domain_id='', user_domain_name='', profile=None,
|
||||||
endpoint_override=None, insecure=False, cacert=None,
|
endpoint_override=None, insecure=False, cacert=None,
|
||||||
version=api_versions.APIVersion('1.25'))
|
version=api_versions.APIVersion('1.26'))
|
||||||
|
|
||||||
|
|
||||||
class ShellTestKeystoneV3(ShellTest):
|
class ShellTestKeystoneV3(ShellTest):
|
||||||
@@ -323,7 +323,7 @@ class ShellTestKeystoneV3(ShellTest):
|
|||||||
def test_main_endpoint_public(self, mock_client):
|
def test_main_endpoint_public(self, mock_client):
|
||||||
self.make_env(fake_env=FAKE_ENV4)
|
self.make_env(fake_env=FAKE_ENV4)
|
||||||
self.shell(
|
self.shell(
|
||||||
'--zun-api-version 1.25 '
|
'--zun-api-version 1.26 '
|
||||||
'--endpoint-type publicURL service-list')
|
'--endpoint-type publicURL service-list')
|
||||||
mock_client.assert_called_once_with(
|
mock_client.assert_called_once_with(
|
||||||
username='username', password='password',
|
username='username', password='password',
|
||||||
@@ -334,4 +334,4 @@ class ShellTestKeystoneV3(ShellTest):
|
|||||||
user_domain_id='', user_domain_name='Default',
|
user_domain_id='', user_domain_name='Default',
|
||||||
endpoint_override=None, insecure=False, profile=None,
|
endpoint_override=None, insecure=False, profile=None,
|
||||||
cacert=None,
|
cacert=None,
|
||||||
version=api_versions.APIVersion('1.25'))
|
version=api_versions.APIVersion('1.26'))
|
||||||
|
91
zunclient/tests/unit/v1/test_quotas.py
Normal file
91
zunclient/tests/unit/v1/test_quotas.py
Normal file
@@ -0,0 +1,91 @@
|
|||||||
|
# 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 testtools
|
||||||
|
|
||||||
|
from zunclient.tests.unit import utils
|
||||||
|
from zunclient.v1 import quotas
|
||||||
|
|
||||||
|
|
||||||
|
DEFAULT_QUOTAS = {
|
||||||
|
'containers': '40',
|
||||||
|
'memory': '51200',
|
||||||
|
'cpu': '20',
|
||||||
|
'disk': '100'
|
||||||
|
}
|
||||||
|
|
||||||
|
MODIFIED_QUOTAS = {
|
||||||
|
'containers': '50',
|
||||||
|
'memory': '51200',
|
||||||
|
'cpu': '20',
|
||||||
|
'disk': '100'
|
||||||
|
}
|
||||||
|
|
||||||
|
MODIFIED_USAGE_QUOTAS = {
|
||||||
|
'containers': {
|
||||||
|
'limit': '50',
|
||||||
|
'in_use': '30'
|
||||||
|
},
|
||||||
|
'memory': {},
|
||||||
|
'cpu': {},
|
||||||
|
'disk': {}
|
||||||
|
}
|
||||||
|
|
||||||
|
fake_responses = {
|
||||||
|
'/v1/quotas':
|
||||||
|
{
|
||||||
|
'GET': (
|
||||||
|
{},
|
||||||
|
MODIFIED_QUOTAS
|
||||||
|
),
|
||||||
|
'PUT': (
|
||||||
|
{},
|
||||||
|
MODIFIED_QUOTAS
|
||||||
|
),
|
||||||
|
'DELETE': (
|
||||||
|
{},
|
||||||
|
None
|
||||||
|
)
|
||||||
|
},
|
||||||
|
'/v1/quotas/defaults':
|
||||||
|
{
|
||||||
|
'GET': (
|
||||||
|
{},
|
||||||
|
DEFAULT_QUOTAS
|
||||||
|
)
|
||||||
|
},
|
||||||
|
'/v1/quotas?usages=True':
|
||||||
|
{
|
||||||
|
'GET': (
|
||||||
|
{},
|
||||||
|
MODIFIED_USAGE_QUOTAS
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
class QuotaManagerTest(testtools.TestCase):
|
||||||
|
|
||||||
|
def setUp(self):
|
||||||
|
super(QuotaManagerTest, self).setUp()
|
||||||
|
self.api = utils.FakeAPI(fake_responses)
|
||||||
|
self.mgr = quotas.QuotaManager(self.api)
|
||||||
|
|
||||||
|
def test_quotas_get_defaults(self):
|
||||||
|
quotas = self.mgr.defaults()
|
||||||
|
expect = [
|
||||||
|
('GET', '/v1/quotas/defaults', {}, None)
|
||||||
|
]
|
||||||
|
self.assertEqual(expect, self.api.calls)
|
||||||
|
self.assertEqual(quotas.containers, DEFAULT_QUOTAS['containers'])
|
||||||
|
self.assertEqual(quotas.memory, DEFAULT_QUOTAS['memory'])
|
||||||
|
self.assertEqual(quotas.cpu, DEFAULT_QUOTAS['cpu'])
|
||||||
|
self.assertEqual(quotas.disk, DEFAULT_QUOTAS['disk'])
|
@@ -23,6 +23,8 @@ from zunclient.v1 import capsules
|
|||||||
from zunclient.v1 import containers
|
from zunclient.v1 import containers
|
||||||
from zunclient.v1 import hosts
|
from zunclient.v1 import hosts
|
||||||
from zunclient.v1 import images
|
from zunclient.v1 import images
|
||||||
|
from zunclient.v1 import quota_classes
|
||||||
|
from zunclient.v1 import quotas
|
||||||
from zunclient.v1 import services
|
from zunclient.v1 import services
|
||||||
from zunclient.v1 import versions
|
from zunclient.v1 import versions
|
||||||
|
|
||||||
@@ -132,6 +134,8 @@ class Client(object):
|
|||||||
self.capsules = capsules.CapsuleManager(self.http_client)
|
self.capsules = capsules.CapsuleManager(self.http_client)
|
||||||
self.availability_zones = az.AvailabilityZoneManager(self.http_client)
|
self.availability_zones = az.AvailabilityZoneManager(self.http_client)
|
||||||
self.actions = actions.ActionManager(self.http_client)
|
self.actions = actions.ActionManager(self.http_client)
|
||||||
|
self.quotas = quotas.QuotaManager(self.http_client)
|
||||||
|
self.quota_classes = quota_classes.QuotaClassManager(self.http_client)
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def api_version(self):
|
def api_version(self):
|
||||||
|
43
zunclient/v1/quota_classes.py
Normal file
43
zunclient/v1/quota_classes.py
Normal file
@@ -0,0 +1,43 @@
|
|||||||
|
# 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 zunclient.common import base
|
||||||
|
|
||||||
|
|
||||||
|
class QuotaClass(base.Resource):
|
||||||
|
def __repr__(self):
|
||||||
|
return "<QuotaClass %s>" % self._info
|
||||||
|
|
||||||
|
|
||||||
|
class QuotaClassManager(base.Manager):
|
||||||
|
resource_class = QuotaClass
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def _path(quota_class_name):
|
||||||
|
return '/v1/quota_classes/{}' . format(quota_class_name)
|
||||||
|
|
||||||
|
def get(self, quota_class_name):
|
||||||
|
return self._list(self._path(quota_class_name))[0]
|
||||||
|
|
||||||
|
def update(self, quota_class_name, containers=None,
|
||||||
|
memory=None, cpu=None, disk=None):
|
||||||
|
resources = {}
|
||||||
|
if cpu is not None:
|
||||||
|
resources['cpu'] = cpu
|
||||||
|
if memory is not None:
|
||||||
|
resources['memory'] = memory
|
||||||
|
if containers is not None:
|
||||||
|
resources['containers'] = containers
|
||||||
|
if disk is not None:
|
||||||
|
resources['disk'] = disk
|
||||||
|
return self._update(self._path(quota_class_name),
|
||||||
|
resources, method='PUT')
|
56
zunclient/v1/quota_classes_shell.py
Normal file
56
zunclient/v1/quota_classes_shell.py
Normal file
@@ -0,0 +1,56 @@
|
|||||||
|
# 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 zunclient.common import cliutils as utils
|
||||||
|
|
||||||
|
|
||||||
|
@utils.arg(
|
||||||
|
'--containers',
|
||||||
|
metavar='<containers>',
|
||||||
|
type=int,
|
||||||
|
help='The number of containers allowed per project')
|
||||||
|
@utils.arg(
|
||||||
|
'--cpu',
|
||||||
|
metavar='<cpu>',
|
||||||
|
type=int,
|
||||||
|
help='The number of container cores or vCPUs allowed per project')
|
||||||
|
@utils.arg(
|
||||||
|
'--memory',
|
||||||
|
metavar='<memory>',
|
||||||
|
type=int,
|
||||||
|
help='The number of megabytes of container RAM allowed per project')
|
||||||
|
@utils.arg(
|
||||||
|
'--disk',
|
||||||
|
metavar='<disk>',
|
||||||
|
type=int,
|
||||||
|
help='The number of gigabytes of container Disk allowed per project')
|
||||||
|
@utils.arg(
|
||||||
|
'quota_class_name',
|
||||||
|
metavar='<quota_class_name>',
|
||||||
|
help='The name of quota class')
|
||||||
|
def do_quota_class_update(cs, args):
|
||||||
|
"""Print an updated quotas for a quota class"""
|
||||||
|
utils.print_dict(cs.quota_classes.update(
|
||||||
|
args.quota_class_name,
|
||||||
|
containers=args.containers,
|
||||||
|
memory=args.memory,
|
||||||
|
cpu=args.cpu,
|
||||||
|
disk=args.disk)._info)
|
||||||
|
|
||||||
|
|
||||||
|
@utils.arg(
|
||||||
|
'quota_class_name',
|
||||||
|
metavar='<quota_class_name>',
|
||||||
|
help='The name of quota class')
|
||||||
|
def do_quota_class_get(cs, args):
|
||||||
|
"""Print a quotas for a quota class"""
|
||||||
|
utils.print_dict(cs.quota_classes.get(args.quota_class_name)._info)
|
50
zunclient/v1/quotas.py
Normal file
50
zunclient/v1/quotas.py
Normal file
@@ -0,0 +1,50 @@
|
|||||||
|
# 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 zunclient.common import base
|
||||||
|
|
||||||
|
|
||||||
|
class Quota(base.Resource):
|
||||||
|
def __repr__(self):
|
||||||
|
return "<Quota %s>" % self._info
|
||||||
|
|
||||||
|
|
||||||
|
class QuotaManager(base.Manager):
|
||||||
|
resource_class = Quota
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def _path():
|
||||||
|
return '/v1/quotas'
|
||||||
|
|
||||||
|
def get(self, **kwargs):
|
||||||
|
if not kwargs.get('usages'):
|
||||||
|
kwargs = {}
|
||||||
|
return self._list(self._path(), qparams=kwargs)[0]
|
||||||
|
|
||||||
|
def update(self, containers=None, memory=None,
|
||||||
|
cpu=None, disk=None):
|
||||||
|
resources = {}
|
||||||
|
if cpu is not None:
|
||||||
|
resources['cpu'] = cpu
|
||||||
|
if memory is not None:
|
||||||
|
resources['memory'] = memory
|
||||||
|
if containers is not None:
|
||||||
|
resources['containers'] = containers
|
||||||
|
if disk is not None:
|
||||||
|
resources['disk'] = disk
|
||||||
|
return self._update(self._path(), resources, method='PUT')
|
||||||
|
|
||||||
|
def defaults(self):
|
||||||
|
return self._list(self._path() + '/defaults')[0]
|
||||||
|
|
||||||
|
def delete(self):
|
||||||
|
return self._delete(self._path())
|
65
zunclient/v1/quotas_shell.py
Normal file
65
zunclient/v1/quotas_shell.py
Normal file
@@ -0,0 +1,65 @@
|
|||||||
|
# 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 zunclient.common import cliutils as utils
|
||||||
|
|
||||||
|
|
||||||
|
@utils.arg(
|
||||||
|
'--containers',
|
||||||
|
metavar='<containers>',
|
||||||
|
type=int,
|
||||||
|
help='The number of containers allowed per project')
|
||||||
|
@utils.arg(
|
||||||
|
'--cpu',
|
||||||
|
metavar='<cpu>',
|
||||||
|
type=int,
|
||||||
|
help='The number of container cores or vCPUs allowed per project')
|
||||||
|
@utils.arg(
|
||||||
|
'--memory',
|
||||||
|
metavar='<memory>',
|
||||||
|
type=int,
|
||||||
|
help='The number of megabytes of container RAM allowed per project')
|
||||||
|
@utils.arg(
|
||||||
|
'--disk',
|
||||||
|
metavar='<disk>',
|
||||||
|
type=int,
|
||||||
|
help='The number of gigabytes of container Disk allowed per project')
|
||||||
|
def do_quota_update(cs, args):
|
||||||
|
"""Print an updated quotas for a project"""
|
||||||
|
utils.print_dict(cs.quotas.update(containers=args.containers,
|
||||||
|
memory=args.memory,
|
||||||
|
cpu=args.cpu,
|
||||||
|
disk=args.disk)._info)
|
||||||
|
|
||||||
|
|
||||||
|
@utils.arg(
|
||||||
|
'--usages',
|
||||||
|
default=False,
|
||||||
|
action='store_true',
|
||||||
|
help='Whether show quota usage statistic or not')
|
||||||
|
def do_quota_get(cs, args):
|
||||||
|
"""Print a quotas for a project with usages (optional)"""
|
||||||
|
if args.usages:
|
||||||
|
utils.print_dict(cs.quotas.get(usages=args.usages)._info,
|
||||||
|
value_fields=('limit', 'in_use'))
|
||||||
|
else:
|
||||||
|
utils.print_dict(cs.quotas.get(usages=args.usages)._info)
|
||||||
|
|
||||||
|
|
||||||
|
def do_quota_defaults(cs, args):
|
||||||
|
"""Print a default quotas for a project"""
|
||||||
|
utils.print_dict(cs.quotas.defaults()._info)
|
||||||
|
|
||||||
|
|
||||||
|
def do_quota_delete(cs, args):
|
||||||
|
"""Delete quotas for a project"""
|
||||||
|
cs.quotas.delete()
|
@@ -19,6 +19,8 @@ from zunclient.v1 import capsules_shell
|
|||||||
from zunclient.v1 import containers_shell
|
from zunclient.v1 import containers_shell
|
||||||
from zunclient.v1 import hosts_shell
|
from zunclient.v1 import hosts_shell
|
||||||
from zunclient.v1 import images_shell
|
from zunclient.v1 import images_shell
|
||||||
|
from zunclient.v1 import quota_classes_shell
|
||||||
|
from zunclient.v1 import quotas_shell
|
||||||
from zunclient.v1 import services_shell
|
from zunclient.v1 import services_shell
|
||||||
from zunclient.v1 import versions_shell
|
from zunclient.v1 import versions_shell
|
||||||
|
|
||||||
@@ -31,4 +33,6 @@ COMMAND_MODULES = [
|
|||||||
versions_shell,
|
versions_shell,
|
||||||
capsules_shell,
|
capsules_shell,
|
||||||
actions_shell,
|
actions_shell,
|
||||||
|
quotas_shell,
|
||||||
|
quota_classes_shell,
|
||||||
]
|
]
|
||||||
|
Reference in New Issue
Block a user