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:
Ashish Singh 2016-05-17 14:03:53 +05:30
parent b2dc27f22c
commit 6539ffacdf
5 changed files with 145 additions and 47 deletions

View File

@ -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)
]

View File

@ -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']:

View File

@ -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):

View File

@ -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)

View File

@ -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')