Add Neutron quota driver
The driver takes care of quota rebalancing for neutron resources. Added UTs for the same. Once this is merged, we will have nova, cinder & neutron drivers for quota rebalancing. Change-Id: I8f40deb60d50b69627902df9d8973989a8ebbd50
This commit is contained in:
parent
b2dc27f22c
commit
6539ffacdf
|
@ -35,6 +35,7 @@ neutron_quotas = [
|
|||
cfg.IntOpt('quota_subnet', default=10),
|
||||
cfg.IntOpt('quota_port', default=50),
|
||||
cfg.IntOpt('quota_security_group', default=10),
|
||||
cfg.IntOpt('quota_security_group_rule', default=100),
|
||||
cfg.IntOpt('quota_router', default=10),
|
||||
cfg.IntOpt('quota_floatingip', default=50)
|
||||
]
|
||||
|
|
|
@ -10,6 +10,8 @@
|
|||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
from collections import defaultdict
|
||||
|
||||
from oslo_log import log
|
||||
|
||||
from kingbird.common import exceptions
|
||||
|
@ -23,26 +25,74 @@ API_VERSION = '2.0'
|
|||
|
||||
class NeutronClient(base.DriverBase):
|
||||
'''Neutron V2 driver.'''
|
||||
def __init__(self, region, session):
|
||||
def __init__(self, region, disabled_quotas, session):
|
||||
try:
|
||||
self.neutron_client = client.Client(
|
||||
self.neutron = client.Client(
|
||||
API_VERSION, session=session,
|
||||
region_name=region)
|
||||
self.extension_list = self.neutron_client.list_extensions()
|
||||
self.extension_list = self.neutron.list_extensions()
|
||||
self.disabled_quotas = disabled_quotas
|
||||
self.no_network = True if 'floatingip' in self.disabled_quotas \
|
||||
else False
|
||||
self.is_sec_group_enabled = self.is_extension_supported(
|
||||
'security-group')
|
||||
except exceptions.ServiceUnavailable:
|
||||
raise
|
||||
|
||||
def get_resource_usages(self, project_id):
|
||||
'''Calcualte resources usage and return the dict'''
|
||||
return {}
|
||||
'''Calcualte resources usage and return the dict
|
||||
|
||||
:param: project_id
|
||||
:return: resource usage dict
|
||||
'''
|
||||
if not self.no_network:
|
||||
try:
|
||||
usages = defaultdict(dict)
|
||||
|
||||
opts = {'tenant_id': project_id}
|
||||
|
||||
networks = self.neutron.list_networks(**opts)['networks']
|
||||
subnets = self.neutron.list_subnets(**opts)['subnets']
|
||||
ports = self.neutron.list_ports(**opts)['ports']
|
||||
routers = self.neutron.list_routers(**opts)['routers']
|
||||
floatingips = self.neutron.list_floatingips(
|
||||
**opts)['floatingips']
|
||||
|
||||
usages['network'] = len(networks)
|
||||
usages['subnet'] = len(subnets)
|
||||
usages['port'] = len(ports)
|
||||
usages['router'] = len(routers)
|
||||
usages['floatingip'] = len(floatingips)
|
||||
|
||||
if self.is_sec_group_enabled:
|
||||
security_group_rules = \
|
||||
self.neutron.list_security_group_rules(
|
||||
**opts)['security_group_rules']
|
||||
security_groups = self.neutron.list_security_groups(
|
||||
**opts)['security_groups']
|
||||
usages['security_group_rule'] = len(security_group_rules)
|
||||
usages['security_group'] = len(security_groups)
|
||||
return usages
|
||||
|
||||
except exceptions.InternalError:
|
||||
raise
|
||||
|
||||
def update_quota_limits(self, project_id, new_quota):
|
||||
'''Update the limits'''
|
||||
pass
|
||||
try:
|
||||
if not self.no_network:
|
||||
return self.neutron.update_quota(project_id,
|
||||
{"quota": new_quota})
|
||||
except exceptions.InternalError:
|
||||
raise
|
||||
|
||||
def delete_quota_limits(self, project_id):
|
||||
'''Delete/Reset the limits'''
|
||||
pass
|
||||
try:
|
||||
if not self.no_network:
|
||||
return self.neutron.delete_quota(project_id)
|
||||
except exceptions.InternalError:
|
||||
raise
|
||||
|
||||
def is_extension_supported(self, extension):
|
||||
for current_extension in self.extension_list['extensions']:
|
||||
|
|
|
@ -47,6 +47,7 @@ class OpenStackDriver(object):
|
|||
else:
|
||||
self.keystone_client = KeystoneClient()
|
||||
OpenStackDriver.os_clients_dict['keystone'] = self.keystone_client
|
||||
self.disabled_quotas = self._get_disabled_quotas(region_name)
|
||||
if region_name in OpenStackDriver.os_clients_dict:
|
||||
LOG.info(_LI('Using cached OS client objects'))
|
||||
self.nova_client = OpenStackDriver.os_clients_dict[
|
||||
|
@ -59,9 +60,10 @@ class OpenStackDriver(object):
|
|||
# Create new objects and cache them
|
||||
LOG.info(_("Creating fresh OS Clients objects"))
|
||||
self.neutron_client = NeutronClient(region_name,
|
||||
self.disabled_quotas,
|
||||
self.keystone_client.session)
|
||||
self.disabled_quotas = self._get_disabled_quotas(region_name)
|
||||
self.nova_client = NovaClient(region_name, self.disabled_quotas,
|
||||
self.nova_client = NovaClient(region_name,
|
||||
self.disabled_quotas,
|
||||
self.keystone_client.session)
|
||||
self.cinder_client = CinderClient(region_name,
|
||||
self.disabled_quotas,
|
||||
|
@ -101,9 +103,8 @@ class OpenStackDriver(object):
|
|||
**limits_to_write['nova'])
|
||||
self.cinder_client.update_quota_limits(project_id,
|
||||
**limits_to_write['cinder'])
|
||||
# TODO(Ashish): Include other clients after nova is fixed
|
||||
# self.neutron_client.update_quota_limits(project_id,
|
||||
# **limits_to_write['neutron'])
|
||||
self.neutron_client.update_quota_limits(project_id,
|
||||
limits_to_write['neutron'])
|
||||
except (exceptions.ConnectionRefused, exceptions.NotAuthorized,
|
||||
exceptions.TimeOut):
|
||||
# Delete the cached objects for that region
|
||||
|
@ -132,22 +133,9 @@ class OpenStackDriver(object):
|
|||
if not self.keystone_client.is_service_enabled('network'):
|
||||
disabled_quotas.extend(consts.NEUTRON_QUOTA_FIELDS)
|
||||
else:
|
||||
# Remove the nova network quotas
|
||||
disabled_quotas.extend(['floating_ips', 'fixed_ips'])
|
||||
if self.neutron_client.is_extension_supported('security-group'):
|
||||
# If Neutron security group is supported, disable Nova quotas
|
||||
disabled_quotas.extend(['security_groups',
|
||||
'security_group_rules'])
|
||||
else:
|
||||
# If Nova security group is used, disable Neutron quotas
|
||||
disabled_quotas.extend(['security_group',
|
||||
'security_group_rule'])
|
||||
try:
|
||||
if not self.neutron_client.is_extension_supported('quotas'):
|
||||
disabled_quotas.extend(consts.NEUTRON_QUOTA_FIELDS)
|
||||
except Exception:
|
||||
LOG.error("There was an error checking if the Neutron "
|
||||
"quotas extension is enabled.")
|
||||
disabled_quotas.extend(['security_groups',
|
||||
'security_group_rules'])
|
||||
return disabled_quotas
|
||||
|
||||
def get_all_regions_for_project(self, project_id):
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
# Copyright (c) 2016 Ericsson AB
|
||||
# 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
|
||||
|
@ -18,9 +19,16 @@ from kingbird.tests import utils
|
|||
|
||||
|
||||
FAKE_EXTENSIONS = {
|
||||
'extensions': ['fake_extension1',
|
||||
'fake_extension2']
|
||||
'extensions': [{'name': 'fake_extension1', 'alias': 'fake_alias1'},
|
||||
{'name': 'fake_extension2', 'alias': 'fake_alias2'}]
|
||||
}
|
||||
FAKE_NETWORKS = {'networks': ['net1', 'net2']}
|
||||
FAKE_SUBNETS = {'subnets': ['subnet1', 'subnet2']}
|
||||
FAKE_PORTS = {'ports': ['port1', 'port2']}
|
||||
FAKE_ROUTERS = {'routers': ['router1', 'router2']}
|
||||
FAKE_FLOATINGIPS = {'floatingips': ['fp1', 'fp2']}
|
||||
FAKE_SEC_GRP_RULES = {'security_group_rules': ['sec_grp_rul1', 'sec_grp_rul2']}
|
||||
FAKE_SEC_GRP = {'security_groups': ['sec_grp1', 'sec_grp2']}
|
||||
|
||||
|
||||
class TestNeutronClient(base.KingbirdTestCase):
|
||||
|
@ -28,28 +36,79 @@ class TestNeutronClient(base.KingbirdTestCase):
|
|||
super(TestNeutronClient, self).setUp()
|
||||
self.ctx = utils.dummy_context()
|
||||
self.session = 'fake_session'
|
||||
self.project = 'fake_project'
|
||||
self.disabled_quota = ['floating_ips', 'volumes']
|
||||
|
||||
@mock.patch.object(neutron_v2, 'NeutronClient')
|
||||
@mock.patch.object(neutron_v2, 'client')
|
||||
def test_init(self, mock_neutron):
|
||||
mock_neutron().extension_list = FAKE_EXTENSIONS
|
||||
mock_neutron.Client().list_extensions.return_value = FAKE_EXTENSIONS
|
||||
neutron_client = neutron_v2.NeutronClient('fake_region',
|
||||
self.disabled_quota,
|
||||
self.session)
|
||||
self.assertIsNotNone(neutron_client.neutron)
|
||||
self.assertEqual(FAKE_EXTENSIONS,
|
||||
neutron_client.extension_list)
|
||||
|
||||
@mock.patch.object(neutron_v2, 'NeutronClient')
|
||||
@mock.patch.object(neutron_v2, 'client')
|
||||
def test_is_extension_supported(self, mock_neutron):
|
||||
mock_neutron.Client().list_extensions.return_value = FAKE_EXTENSIONS
|
||||
neutron_client = neutron_v2.NeutronClient('fake_region',
|
||||
self.disabled_quota,
|
||||
self.session)
|
||||
mock_neutron().is_extension_supported.return_value = True
|
||||
extension_enabled = neutron_client.is_extension_supported('quotas')
|
||||
self.assertEqual(extension_enabled, True)
|
||||
actual_value = neutron_client.is_extension_supported('fake_alias1')
|
||||
self.assertEqual(True, actual_value)
|
||||
|
||||
def test_get_resource_usages(self):
|
||||
pass
|
||||
@mock.patch.object(neutron_v2, 'client')
|
||||
def test_extension_not_supported(self, mock_neutron):
|
||||
mock_neutron.Client().list_extensions.return_value = FAKE_EXTENSIONS
|
||||
neutron_client = neutron_v2.NeutronClient('fake_region',
|
||||
self.disabled_quota,
|
||||
self.session)
|
||||
actual_value = neutron_client.is_extension_supported(
|
||||
'not_supported_alias')
|
||||
self.assertEqual(False, actual_value)
|
||||
|
||||
def test_update_quota_limits(self):
|
||||
pass
|
||||
@mock.patch.object(neutron_v2, 'client')
|
||||
def test_get_resource_usages(self, mock_neutron):
|
||||
mock_neutron.Client().list_networks.return_value = FAKE_NETWORKS
|
||||
mock_neutron.Client().list_subnets.return_value = FAKE_SUBNETS
|
||||
mock_neutron.Client().list_ports.return_value = FAKE_PORTS
|
||||
mock_neutron.Client().list_routers.return_value = FAKE_ROUTERS
|
||||
mock_neutron.Client().list_floatingips.return_value = FAKE_FLOATINGIPS
|
||||
mock_neutron.Client().list_security_group_rules.return_value = \
|
||||
FAKE_SEC_GRP_RULES
|
||||
mock_neutron.Client().list_security_groups.return_value = FAKE_SEC_GRP
|
||||
neutron_client = neutron_v2.NeutronClient('fake_region',
|
||||
self.disabled_quota,
|
||||
self.session)
|
||||
setattr(neutron_client, 'is_sec_group_enabled', True)
|
||||
total_neutron_usages = neutron_client.get_resource_usages(
|
||||
self.project)
|
||||
self.assertEqual(2, total_neutron_usages['port'])
|
||||
self.assertEqual(2, total_neutron_usages['router'])
|
||||
self.assertEqual(2, total_neutron_usages['security_group'])
|
||||
self.assertEqual(2, total_neutron_usages['security_group_rule'])
|
||||
self.assertEqual(2, total_neutron_usages['floatingip'])
|
||||
self.assertEqual(2, total_neutron_usages['network'])
|
||||
self.assertEqual(2, total_neutron_usages['subnet'])
|
||||
|
||||
def test_delete_quota_limits(self):
|
||||
pass
|
||||
@mock.patch.object(neutron_v2, 'client')
|
||||
def test_update_quota_limits(self, mock_neutron):
|
||||
neutron_client = neutron_v2.NeutronClient('fake_region',
|
||||
self.disabled_quota,
|
||||
self.session)
|
||||
new_quota = {'subnets': 4, 'ports': 3}
|
||||
setattr(neutron_client, 'no_network', False)
|
||||
neutron_client.update_quota_limits(self.project, new_quota)
|
||||
mock_neutron.Client().update_quota.assert_called_once_with(
|
||||
self.project, {"quota": new_quota})
|
||||
|
||||
@mock.patch.object(neutron_v2, 'client')
|
||||
def test_delete_quota_limits(self, mock_neutron):
|
||||
neutron_client = neutron_v2.NeutronClient('fake_region',
|
||||
self.disabled_quota,
|
||||
self.session)
|
||||
setattr(neutron_client, 'no_network', False)
|
||||
neutron_client.delete_quota_limits(self.project)
|
||||
mock_neutron.Client().delete_quota.assert_called_once_with(
|
||||
self.project)
|
||||
|
|
|
@ -86,12 +86,12 @@ class TestOpenStackDriver(base.KingbirdTestCase):
|
|||
).update_quota_limits.assert_called_once_with(project_id,
|
||||
instances=7, ram=1222,
|
||||
vcpus=10)
|
||||
# mock_network_client(
|
||||
# ).update_quota_limits.assert_called_once_with(project_id,
|
||||
# write_limits['neutron'])
|
||||
# mock_cinder_client(
|
||||
# ).update_quota_limits.assert_called_once_with(project_id,
|
||||
# write_limits['cinder'])
|
||||
mock_network_client(
|
||||
).update_quota_limits.assert_called_once_with(project_id,
|
||||
write_limits['neutron'])
|
||||
mock_cinder_client(
|
||||
).update_quota_limits.assert_called_once_with(project_id,
|
||||
**write_limits['cinder'])
|
||||
|
||||
@mock.patch.object(sdk, 'KeystoneClient')
|
||||
@mock.patch.object(sdk, 'NovaClient')
|
||||
|
|
Loading…
Reference in New Issue