Add cinder quota driver

The driver takes care of quota rebalancing. Currently the limits
considered by the driver are volumes, snapshots, backups, gigabytes.

Change-Id: I4b638088dd5331e1c51d14b97ebbcba173217fca
This commit is contained in:
Dimitri Mazmanov 2016-05-04 13:14:31 +02:00
parent fe721d02f4
commit 30dde56e6a
3 changed files with 112 additions and 18 deletions

View File

@ -10,6 +10,7 @@
# License for the specific language governing permissions and limitations
# under the License.
from collections import defaultdict
from oslo_log import log
from cinderclient import client
@ -23,21 +24,53 @@ API_VERSION = '2'
class CinderClient(base.DriverBase):
'''Cinder V2 driver.'''
def __init__(self, region, session):
def __init__(self, region, disabled_quotas, session):
try:
self.cinder_client = client.Client(API_VERSION, session=session,
region_name=region)
self.cinder = client.Client(API_VERSION,
session=session,
region_name=region)
self.no_volumes = True if 'volumes' in disabled_quotas else False
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
def update_quota_limits(self, project_id, new_quota):
:param: project_id
:return: resource usage dict
'''
if not self.no_volumes:
try:
usages = defaultdict(dict)
opts = {'all_tenants': 1, 'project_id': project_id}
volumes = self.cinder.volumes.list(search_opts=opts)
snapshots = self.cinder.volume_snapshots.list(search_opts=opts)
backups = self.cinder.backups.list(search_opts=opts)
usages['gigabytes'] = sum([int(v.size) for v in volumes])
usages['volumes'] = len(volumes)
usages['snapshots'] = len(snapshots)
usages['backups'] = len(backups)
return usages
except exceptions.InternalError:
raise
def update_quota_limits(self, project_id, **new_quota):
'''Update the limits'''
pass
try:
if not self.no_volumes:
return self.cinder.quotas.update(project_id, **new_quota)
except exceptions.InternalError:
raise
def delete_quota_limits(self, project_id):
'''Delete/Reset the limits'''
pass
try:
if not self.no_volumes:
return self.cinder.quotas.delete(project_id)
except exceptions.InternalError:
raise

View File

@ -58,13 +58,13 @@ class OpenStackDriver(object):
else:
# Create new objects and cache them
LOG.info(_("Creating fresh OS Clients objects"))
self.cinder_client = CinderClient(region_name,
self.keystone_client.session)
self.neutron_client = NeutronClient(region_name,
self.keystone_client.session)
self.disabled_quotas = self._get_disabled_quotas(region_name)
self.nova_client = NovaClient(region_name, self.disabled_quotas,
self.keystone_client.session)
self.cinder_client = CinderClient(region_name,
self.keystone_client.session)
OpenStackDriver.os_clients_dict[
region_name] = collections.defaultdict(dict)
OpenStackDriver.os_clients_dict[region_name][
@ -124,6 +124,9 @@ class OpenStackDriver(object):
def _get_disabled_quotas(self, region):
disabled_quotas = []
if not self.keystone_client.is_service_enabled('volume') or \
self.keystone_client.is_service_enabled('volumev2'):
disabled_quotas.extend(consts.CINDER_QUOTA_FIELDS)
# Neutron
if not self.keystone_client.is_service_enabled('network'):
disabled_quotas.extend(consts.NEUTRON_QUOTA_FIELDS)

View File

@ -11,29 +11,87 @@
# under the License.
import cinderclient
import mock
from kingbird.drivers.openstack import cinder_v2
from kingbird.tests import base
from kingbird.tests import utils
class Volume(object):
def __init__(self, id, size):
self.id = id
self.size = size
class VolumeSnapshot(object):
def __init__(self, volume_id):
self.volume_id = volume_id
class VolumeBackup(object):
def __init__(self, volume_id):
self.volume_id = volume_id
volumes = [Volume("9fc1c259-1d66-470f-8525-313696d1ad46", 20),
Volume("7f505069-ad68-48c3-a09f-16d7014ec707", 15)]
snapshots = [VolumeSnapshot(volumes[0].id)]
backups = [VolumeBackup(volumes[0].id),
VolumeBackup(volumes[0].id),
VolumeBackup(volumes[1].id)]
DISABLED_QUOTAS = ["floating_ips", "fixed_ips", "security_groups"]
class TestCinderClient(base.KingbirdTestCase):
def setUp(self):
super(TestCinderClient, self).setUp()
self.ctx = utils.dummy_context()
self.session = 'fake_session'
self.project = 'fake_project'
def test_init(self):
ci_client = cinder_v2.CinderClient('fake_region', self.session)
ci_client = cinder_v2.CinderClient('fake_region', DISABLED_QUOTAS,
self.session)
self.assertIsNotNone(ci_client)
self.assertIsInstance(ci_client.cinder_client,
self.assertIsInstance(ci_client.cinder,
cinderclient.v2.client.Client)
def test_get_resource_usages(self):
pass
@mock.patch.object(cinder_v2, 'client')
def test_get_resource_usages(self, mock_cinderclient):
mock_cinderclient.Client().volumes.list.return_value = volumes
mock_cinderclient.Client().volume_snapshots.list.return_value = \
snapshots
mock_cinderclient.Client().backups.list.return_value = \
backups
cinder = cinder_v2.CinderClient('fake_region', DISABLED_QUOTAS,
self.session)
total_cinder_usage = cinder.get_resource_usages(self.project)
self.assertEqual(2, total_cinder_usage['volumes'])
self.assertEqual(1, total_cinder_usage['snapshots'])
self.assertEqual(35, total_cinder_usage['gigabytes'])
self.assertEqual(3, total_cinder_usage['backups'])
def test_update_quota_limits(self):
pass
@mock.patch.object(cinder_v2, 'client')
def test_update_quota_limits(self, mock_cinderclient):
c_client = cinder_v2.CinderClient('fake_region', DISABLED_QUOTAS,
self.session)
new_quota = {'volumes': 4, 'snapshots': 3}
c_client.update_quota_limits(self.project, **new_quota)
def test_delete_quota_limits(self):
pass
mock_cinderclient.Client().quotas.update.assert_called_once_with(
self.project, **new_quota)
@mock.patch.object(cinder_v2, 'client')
def test_delete_quota_limits(self, mock_cinderclient):
c_client = cinder_v2.CinderClient('fake_region', DISABLED_QUOTAS,
self.session)
new_quota = {'volumes': 4, 'snapshots': 3}
c_client.update_quota_limits(self.project, **new_quota)
c_client.delete_quota_limits(self.project)
mock_cinderclient.Client().quotas.delete.assert_called_once_with(
self.project)