VYPR
High severityNVD Advisory· Published Mar 22, 2013· Updated Apr 29, 2026

CVE-2013-1838

CVE-2013-1838

Description

OpenStack Compute (Nova) Grizzly, Folsom (2012.2), and Essex (2012.1) does not properly implement a quota for fixed IPs, which allows remote authenticated users to cause a denial of service (resource exhaustion and failure to spawn new instances) via a large number of calls to the addFixedIp function.

Affected packages

Versions sourced from the GitHub Security Advisory.

PackageAffected versionsPatched versions
novaPyPI
< 12.0.0a012.0.0a0

Affected products

6
  • cpe:2.3:a:openstack:essex:2012.1:*:*:*:*:*:*:*
  • cpe:2.3:a:openstack:folsom:2012.2:*:*:*:*:*:*:*
  • cpe:2.3:a:openstack:grizzly:2012.2:*:*:*:*:*:*:*
  • cpe:2.3:o:canonical:ubuntu_linux:11.10:*:*:*:*:*:*:*+ 2 more
    • cpe:2.3:o:canonical:ubuntu_linux:11.10:*:*:*:*:*:*:*
    • cpe:2.3:o:canonical:ubuntu_linux:12.04:-:lts:*:*:*:*:*
    • cpe:2.3:o:canonical:ubuntu_linux:12.10:*:*:*:*:*:*:*

Patches

3
efaacdaee116

Add quotas for fixed ips.

https://github.com/openstack/novaMichael StillMar 14, 2013via ghsa
7 files changed · +67 6
  • nova/api/openstack/compute/contrib/quotas.py+3 2 modified
    @@ -30,8 +30,9 @@
     
     
     quota_resources = ['metadata_items', 'injected_file_content_bytes',
    -        'volumes', 'gigabytes', 'ram', 'floating_ips', 'instances',
    -        'injected_files', 'cores', 'security_groups', 'security_group_rules']
    +        'volumes', 'gigabytes', 'ram', 'floating_ips', 'fixed_ips',
    +        'instances', 'injected_files', 'cores', 'security_groups',
    +        'security_group_rules']
     
     
     class QuotaTemplate(xmlutil.TemplateBuilder):
    
  • nova/db/api.py+6 0 modified
    @@ -461,6 +461,12 @@ def fixed_ip_update(context, address, values):
         """Create a fixed ip from the values dictionary."""
         return IMPL.fixed_ip_update(context, address, values)
     
    +
    +def fixed_ip_count_by_project(context, project_id, session=None):
    +    """Count fixed ips used by project."""
    +    return IMPL.fixed_ip_count_by_project(context, project_id,
    +                                          session=session)
    +
     ####################
     
     
    
  • nova/db/sqlalchemy/api.py+21 0 modified
    @@ -1114,6 +1114,27 @@ def fixed_ip_update(context, address, values):
             fixed_ip_ref.save(session=session)
     
     
    +@require_context
    +def fixed_ip_count_by_project(context, project_id, session=None):
    +    authorize_project_context(context, project_id)
    +
    +    # NOTE(mikal): Yes I know this is horrible, but I couldn't
    +    # get a query using a join working, mainly because of a failure
    +    # to be able to express the where clause sensibly. Patches
    +    # welcome.
    +    session = get_session()
    +    with session.begin():
    +        instance_id_query = model_query(context, models.Instance.id,
    +                                        read_deleted="no", session=session).\
    +                               filter(models.Instance.project_id == \
    +                                      project_id)
    +        id_filter = models.FixedIp.instance_id.in_(instance_id_query)
    +        return model_query(context, models.FixedIp, read_deleted="no",
    +                           session=session).\
    +                           filter(id_filter).\
    +                           count()
    +
    +
     ###################
     
     
    
  • nova/network/manager.py+6 0 modified
    @@ -1152,6 +1152,12 @@ def _validate_instance_zone_for_dns_domain(self, context, instance_id):
     
         def allocate_fixed_ip(self, context, instance_id, network, **kwargs):
             """Gets a fixed ip from the pool."""
    +        LOG.debug("QUOTA: %s" % quota.allowed_fixed_ips(context, 1))
    +        if quota.allowed_fixed_ips(context, 1) < 1:
    +            LOG.warn(_('Quota exceeded for %s, tried to allocate address'),
    +                     context.project_id)
    +            raise exception.QuotaError(code='FixedAddressLimitExceeded')
    +
             # TODO(vish): when this is called by compute, we can associate compute
             #             with a network, or a cluster of computes with a network
             #             and use that network here with a method like
    
  • nova/quota.py+17 0 modified
    @@ -42,6 +42,10 @@
         cfg.IntOpt('quota_floating_ips',
                    default=10,
                    help='number of floating ips allowed per project'),
    +    cfg.IntOpt('quota_fixed_ips',
    +               default=10,
    +               help=('number of fixed ips allowed per project (this should be '
    +                     'at least the number of instances allowed)')),
         cfg.IntOpt('quota_metadata_items',
                    default=128,
                    help='number of metadata items allowed per instance'),
    @@ -74,6 +78,7 @@ def _get_default_quotas():
             'volumes': FLAGS.quota_volumes,
             'gigabytes': FLAGS.quota_gigabytes,
             'floating_ips': FLAGS.quota_floating_ips,
    +        'fixed_ips': FLAGS.quota_fixed_ips,
             'metadata_items': FLAGS.quota_metadata_items,
             'injected_files': FLAGS.quota_max_injected_files,
             'injected_file_content_bytes':
    @@ -173,6 +178,18 @@ def allowed_floating_ips(context, requested_floating_ips):
         return min(requested_floating_ips, allowed_floating_ips)
     
     
    +def allowed_fixed_ips(context, requested_fixed_ips):
    +    """Check quota and return min(requested, allowed) fixed ips."""
    +    project_id = context.project_id
    +    context = context.elevated()
    +    used_fixed_ips = db.fixed_ip_count_by_project(context, project_id)
    +    quota = get_project_quotas(context, project_id)
    +    allowed_fixed_ips = _get_request_allotment(requested_fixed_ips,
    +                                               used_fixed_ips,
    +                                               quota['fixed_ips'])
    +    return min(requested_fixed_ips, allowed_fixed_ips)
    +
    +
     def allowed_security_groups(context, requested_security_groups):
         """Check quota and return min(requested, allowed) security groups."""
         project_id = context.project_id
    
  • nova/tests/api/openstack/compute/contrib/test_quotas.py+13 4 modified
    @@ -27,8 +27,8 @@
     def quota_set(id):
         return {'quota_set': {'id': id, 'metadata_items': 128, 'volumes': 10,
                 'gigabytes': 1000, 'ram': 51200, 'floating_ips': 10,
    -            'instances': 10, 'injected_files': 5, 'cores': 20,
    -            'injected_file_content_bytes': 10240,
    +            'fixed_ips': 10, 'instances': 10, 'injected_files': 5,
    +            'cores': 20, 'injected_file_content_bytes': 10240,
                 'security_groups': 10, 'security_group_rules': 20}}
     
     
    @@ -50,6 +50,8 @@ def test_format_quota_set(self):
                 'ram': 51200,
                 'volumes': 10,
                 'floating_ips': 10,
    +            'fixed_ips': 10,
    +            'fixed_ips': 10,
                 'metadata_items': 128,
                 'gigabytes': 1000,
                 'injected_files': 5,
    @@ -69,6 +71,7 @@ def test_format_quota_set(self):
             self.assertEqual(qs['volumes'], 10)
             self.assertEqual(qs['gigabytes'], 1000)
             self.assertEqual(qs['floating_ips'], 10)
    +        self.assertEqual(qs['fixed_ips'], 10)
             self.assertEqual(qs['metadata_items'], 128)
             self.assertEqual(qs['injected_files'], 5)
             self.assertEqual(qs['injected_file_content_bytes'], 10240)
    @@ -89,6 +92,7 @@ def test_quotas_defaults(self):
                         'volumes': 10,
                         'gigabytes': 1000,
                         'floating_ips': 10,
    +                    'fixed_ips': 10,
                         'metadata_items': 128,
                         'injected_files': 5,
                         'injected_file_content_bytes': 10240,
    @@ -114,7 +118,8 @@ def test_quotas_update_as_admin(self):
             body = {'quota_set': {'instances': 50, 'cores': 50,
                                   'ram': 51200, 'volumes': 10,
                                   'gigabytes': 1000, 'floating_ips': 10,
    -                              'metadata_items': 128, 'injected_files': 5,
    +                              'fixed_ips': 10, 'metadata_items': 128,
    +                              'injected_files': 5,
                                   'injected_file_content_bytes': 10240,
                                   'security_groups': 10,
                                   'security_group_rules': 20}}
    @@ -129,7 +134,8 @@ def test_quotas_update_as_user(self):
             body = {'quota_set': {'instances': 50, 'cores': 50,
                                   'ram': 51200, 'volumes': 10,
                                   'gigabytes': 1000, 'floating_ips': 10,
    -                              'metadata_items': 128, 'injected_files': 5,
    +                              'fixed_ips': 10, 'metadata_items': 128,
    +                              'injected_files': 5,
                                   'injected_file_content_bytes': 10240,
                                   'security_groups': 10,
                                   'security_group_rules': 20}}
    @@ -154,6 +160,7 @@ def test_serializer(self):
                     gigabytes=40,
                     ram=50,
                     floating_ips=60,
    +                fixed_ips=10,
                     instances=70,
                     injected_files=80,
                     security_groups=10,
    @@ -179,6 +186,7 @@ def test_deserializer(self):
                     gigabytes='40',
                     ram='50',
                     floating_ips='60',
    +                fixed_ips='10',
                     instances='70',
                     injected_files='80',
                     security_groups='10',
    @@ -193,6 +201,7 @@ def test_deserializer(self):
                       '<gigabytes>40</gigabytes>'
                       '<ram>50</ram>'
                       '<floating_ips>60</floating_ips>'
    +                  '<fixed_ips>10</fixed_ips>'
                       '<instances>70</instances>'
                       '<injected_files>80</injected_files>'
                       '<security_groups>10</security_groups>'
    
  • nova/tests/network/test_manager.py+1 0 modified
    @@ -432,6 +432,7 @@ def test_allocate_fixed_ip(self):
             db.instance_get(mox.IgnoreArg(),
                             mox.IgnoreArg()).AndReturn({'security_groups':
                                                                  [{'id': 0}]})
    +
             db.fixed_ip_associate_pool(mox.IgnoreArg(),
                                        mox.IgnoreArg(),
                                        mox.IgnoreArg()).AndReturn('192.168.0.1')
    
9561484166f2

Add quotas for fixed ips.

https://github.com/openstack/novaMichael StillMar 12, 2013via ghsa
9 files changed · +157 42
  • nova/db/api.py+6 0 modified
    @@ -507,6 +507,12 @@ def fixed_ip_update(context, address, values):
         """Create a fixed ip from the values dictionary."""
         return IMPL.fixed_ip_update(context, address, values)
     
    +
    +def fixed_ip_count_by_project(context, project_id, session=None):
    +    """Count fixed ips used by project."""
    +    return IMPL.fixed_ip_count_by_project(context, project_id,
    +                                          session=session)
    +
     ####################
     
     
    
  • nova/db/sqlalchemy/api.py+21 0 modified
    @@ -1273,6 +1273,27 @@ def fixed_ip_update(context, address, values):
             fixed_ip_ref.save(session=session)
     
     
    +@require_context
    +def fixed_ip_count_by_project(context, project_id, session=None):
    +    authorize_project_context(context, project_id)
    +
    +    # NOTE(mikal): Yes I know this is horrible, but I couldn't
    +    # get a query using a join working, mainly because of a failure
    +    # to be able to express the where clause sensibly. Patches
    +    # welcome.
    +    session = get_session()
    +    with session.begin():
    +        instance_uuid_query = model_query(context, models.Instance.uuid,
    +                                          read_deleted="no", session=session).\
    +                                 filter(models.Instance.project_id == \
    +                                            project_id)
    +        uuid_filter = models.FixedIp.instance_uuid.in_(instance_uuid_query)
    +        return model_query(context, models.FixedIp, read_deleted="no",
    +                           session=session).\
    +                           filter(uuid_filter).\
    +                           count()
    +
    +
     ###################
     
     
    
  • nova/exception.py+4 0 modified
    @@ -998,6 +998,10 @@ class FloatingIpLimitExceeded(QuotaError):
         message = _("Maximum number of floating ips exceeded")
     
     
    +class FixedIpLimitExceeded(QuotaError):
    +    message = _("Maximum number of fixed ips exceeded")
    +
    +
     class MetadataLimitExceeded(QuotaError):
         message = _("Maximum number of metadata items exceeds %(allowed)d")
     
    
  • nova/network/manager.py+58 31 modified
    @@ -1294,37 +1294,53 @@ def allocate_fixed_ip(self, context, instance_id, network, **kwargs):
             address = None
             instance_ref = self.db.instance_get(context, instance_id)
     
    -        if network['cidr']:
    -            address = kwargs.get('address', None)
    -            if address:
    -                address = self.db.fixed_ip_associate(context,
    -                                                     address,
    -                                                     instance_ref['uuid'],
    -                                                     network['id'])
    -            else:
    -                address = self.db.fixed_ip_associate_pool(context.elevated(),
    -                                                          network['id'],
    -                                                          instance_ref['uuid'])
    -            self._do_trigger_security_group_members_refresh_for_instance(
    -                                                                   instance_id)
    -            get_vif = self.db.virtual_interface_get_by_instance_and_network
    -            vif = get_vif(context, instance_ref['uuid'], network['id'])
    -            values = {'allocated': True,
    -                      'virtual_interface_id': vif['id']}
    -            self.db.fixed_ip_update(context, address, values)
    -
    -        name = instance_ref['display_name']
    -
    -        if self._validate_instance_zone_for_dns_domain(context, instance_ref):
    -            uuid = instance_ref['uuid']
    -            self.instance_dns_manager.create_entry(name, address,
    -                                                   "A",
    -                                                   self.instance_dns_domain)
    -            self.instance_dns_manager.create_entry(uuid, address,
    -                                                   "A",
    -                                                   self.instance_dns_domain)
    -        self._setup_network_on_host(context, network)
    -        return address
    +        # Check the quota; can't put this in the API because we get
    +        # called into from other places
    +        try:
    +            reservations = QUOTAS.reserve(context, fixed_ips=1)
    +        except exception.OverQuota:
    +            pid = context.project_id
    +            LOG.warn(_("Quota exceeded for %(pid)s, tried to allocate "
    +                       "fixed IP") % locals())
    +            raise exception.FixedIpLimitExceeded()
    +
    +        try:
    +            if network['cidr']:
    +                address = kwargs.get('address', None)
    +                if address:
    +                    address = self.db.fixed_ip_associate(context,
    +                                                         address,
    +                                                         instance_ref['uuid'],
    +                                                         network['id'])
    +                else:
    +                    address = self.db.fixed_ip_associate_pool(
    +                        context.elevated(), network['id'],
    +                        instance_ref['uuid'])
    +                self._do_trigger_security_group_members_refresh_for_instance(
    +                    instance_id)
    +                get_vif = self.db.virtual_interface_get_by_instance_and_network
    +                vif = get_vif(context, instance_ref['uuid'], network['id'])
    +                values = {'allocated': True,
    +                          'virtual_interface_id': vif['id']}
    +                self.db.fixed_ip_update(context, address, values)
    +
    +            name = instance_ref['display_name']
    +
    +            if self._validate_instance_zone_for_dns_domain(context,
    +                                                           instance_ref):
    +                uuid = instance_ref['uuid']
    +                self.instance_dns_manager.create_entry(
    +                    name, address, "A", self.instance_dns_domain)
    +                self.instance_dns_manager.create_entry(
    +                    uuid, address, "A", self.instance_dns_domain)
    +            self._setup_network_on_host(context, network)
    +
    +            QUOTAS.commit(context, reservations)
    +            return address
    +
    +        except Exception:
    +            with excutils.save_and_reraise_exception():
    +                QUOTAS.rollback(context, reservations)
     
         def deallocate_fixed_ip(self, context, address, host=None, teardown=True):
             """Returns a fixed ip to the pool."""
    @@ -1334,6 +1350,13 @@ def deallocate_fixed_ip(self, context, address, host=None, teardown=True):
                     context.elevated(read_deleted='yes'),
                     fixed_ip_ref['instance_uuid'])
     
    +        try:
    +            reservations = QUOTAS.reserve(context, fixed_ips=-1)
    +        except Exception:
    +            reservations = None
    +            LOG.exception(_("Failed to update usages deallocating "
    +                            "fixed IP"))
    +
             self._do_trigger_security_group_members_refresh_for_instance(
                 instance['uuid'])
     
    @@ -1373,6 +1396,10 @@ def deallocate_fixed_ip(self, context, address, host=None, teardown=True):
                     #             callback will get called by nova-dhcpbridge.
                     self.driver.release_dhcp(dev, address, vif['address'])
     
    +        # Commit the reservations
    +        if reservations:
    +            QUOTAS.commit(context, reservations)
    +
         def lease_fixed_ip(self, context, address):
             """Called by dhcp-bridge when ip is leased."""
             LOG.debug(_('Leased IP |%(address)s|'), locals(), context=context)
    
  • nova/quota.py+10 0 modified
    @@ -50,6 +50,10 @@
         cfg.IntOpt('quota_floating_ips',
                    default=10,
                    help='number of floating ips allowed per project'),
    +    cfg.IntOpt('quota_fixed_ips',
    +               default=10,
    +               help=('number of fixed ips allowed per project (this should be '
    +                     'at least the number of instances allowed)')),
         cfg.IntOpt('quota_metadata_items',
                    default=128,
                    help='number of metadata items allowed per instance'),
    @@ -778,6 +782,11 @@ def _sync_floating_ips(context, project_id, session):
                 context, project_id, session=session))
     
     
    +def _sync_fixed_ips(context, project_id, session):
    +    return dict(fixed_ips=db.fixed_ip_count_by_project(
    +            context, project_id, session=session))
    +
    +
     def _sync_security_groups(context, project_id, session):
         return dict(security_groups=db.security_group_count_by_project(
                 context, project_id, session=session))
    @@ -794,6 +803,7 @@ def _sync_security_groups(context, project_id, session):
         ReservableResource('gigabytes', _sync_volumes, 'quota_gigabytes'),
         ReservableResource('floating_ips', _sync_floating_ips,
                            'quota_floating_ips'),
    +    ReservableResource('fixed_ips', _sync_fixed_ips, 'quota_fixed_ips'),
         AbsoluteResource('metadata_items', 'quota_metadata_items'),
         AbsoluteResource('injected_files', 'quota_injected_files'),
         AbsoluteResource('injected_file_content_bytes',
    
  • nova/tests/api/openstack/compute/contrib/test_quota_classes.py+9 5 modified
    @@ -25,10 +25,11 @@
     def quota_set(class_name):
         return {'quota_class_set': {'id': class_name, 'metadata_items': 128,
                 'volumes': 10, 'gigabytes': 1000, 'ram': 51200,
    -            'floating_ips': 10, 'instances': 10, 'injected_files': 5,
    -            'cores': 20, 'injected_file_content_bytes': 10240,
    -            'security_groups': 10, 'security_group_rules': 20,
    -            'key_pairs': 100, 'injected_file_path_bytes': 255}}
    +            'floating_ips': 10, 'fixed_ips': 10, 'instances': 10,
    +            'injected_files': 5, 'cores': 20,
    +            'injected_file_content_bytes': 10240, 'security_groups': 10,
    +            'security_group_rules': 20, 'key_pairs': 100,
    +            'injected_file_path_bytes': 255}}
     
     
     class QuotaClassSetsTest(test.TestCase):
    @@ -44,6 +45,7 @@ def test_format_quota_set(self):
                 'ram': 51200,
                 'volumes': 10,
                 'floating_ips': 10,
    +            'fixed_ips': 10,
                 'metadata_items': 128,
                 'gigabytes': 1000,
                 'injected_files': 5,
    @@ -91,7 +93,8 @@ def test_quotas_update_as_admin(self):
             body = {'quota_class_set': {'instances': 50, 'cores': 50,
                                         'ram': 51200, 'volumes': 10,
                                         'gigabytes': 1000, 'floating_ips': 10,
    -                                    'metadata_items': 128, 'injected_files': 5,
    +                                    'fixed_ips': 10, 'metadata_items': 128,
    +                                    'injected_files': 5,
                                         'injected_file_content_bytes': 10240,
                                         'injected_file_path_bytes': 255,
                                         'security_groups': 10,
    @@ -139,6 +142,7 @@ def test_serializer(self):
                     gigabytes=40,
                     ram=50,
                     floating_ips=60,
    +                fixed_ips=10,
                     instances=70,
                     injected_files=80,
                     security_groups=10,
    
  • nova/tests/api/openstack/compute/contrib/test_quotas.py+10 6 modified
    @@ -26,11 +26,12 @@
     
     def quota_set(id):
         return {'quota_set': {'id': id, 'metadata_items': 128, 'volumes': 10,
    -            'gigabytes': 1000, 'ram': 51200, 'floating_ips': 10,
    -            'instances': 10, 'injected_files': 5, 'cores': 20,
    -            'injected_file_content_bytes': 10240,
    -            'security_groups': 10, 'security_group_rules': 20,
    -            'key_pairs': 100, 'injected_file_path_bytes': 255}}
    +                          'gigabytes': 1000, 'ram': 51200, 'floating_ips': 10,
    +                          'fixed_ips': 10, 'instances': 10,
    +                          'injected_files': 5, 'cores': 20,
    +                          'injected_file_content_bytes': 10240,
    +                          'security_groups': 10, 'security_group_rules': 20,
    +                          'key_pairs': 100, 'injected_file_path_bytes': 255}}
     
     
     class QuotaSetsTest(test.TestCase):
    @@ -46,6 +47,7 @@ def test_format_quota_set(self):
                 'ram': 51200,
                 'volumes': 10,
                 'floating_ips': 10,
    +            'fixed_ips': 10,
                 'metadata_items': 128,
                 'gigabytes': 1000,
                 'injected_files': 5,
    @@ -88,6 +90,7 @@ def test_quotas_defaults(self):
                         'volumes': 10,
                         'gigabytes': 1000,
                         'floating_ips': 10,
    +                    'fixed_ips': 10,
                         'metadata_items': 128,
                         'injected_files': 5,
                         'injected_file_path_bytes': 255,
    @@ -120,7 +123,7 @@ def test_quotas_update_as_admin(self):
                                   'injected_file_path_bytes': 255,
                                   'security_groups': 10,
                                   'security_group_rules': 20,
    -                              'key_pairs': 100}}
    +                              'key_pairs': 100, 'fixed_ips': 10}}
     
             req = fakes.HTTPRequest.blank('/v2/fake4/os-quota-sets/update_me',
                                           use_admin_context=True)
    @@ -171,6 +174,7 @@ def test_serializer(self):
                     gigabytes=40,
                     ram=50,
                     floating_ips=60,
    +                fixed_ips=10,
                     instances=70,
                     injected_files=80,
                     security_groups=10,
    
  • nova/tests/network/test_manager.py+15 0 modified
    @@ -30,6 +30,7 @@
     from nova.openstack.common import log as logging
     from nova.openstack.common import rpc
     import nova.policy
    +from nova import quota
     from nova import test
     from nova.tests import fake_network
     from nova import utils
    @@ -278,6 +279,7 @@ def test_add_fixed_ip_instance_using_id_without_vpn(self):
             self.mox.StubOutWithMock(db,
                                   'virtual_interface_get_by_instance_and_network')
             self.mox.StubOutWithMock(db, 'fixed_ip_update')
    +        self.mox.StubOutWithMock(quota.QUOTAS, 'reserve')
     
             db.fixed_ip_update(mox.IgnoreArg(),
                                mox.IgnoreArg(),
    @@ -291,6 +293,10 @@ def test_add_fixed_ip_instance_using_id_without_vpn(self):
             db.instance_get(mox.IgnoreArg(),
                             mox.IgnoreArg()).AndReturn({'security_groups':
                                                                  [{'id': 0}]})
    +
    +        quota.QUOTAS.reserve(mox.IgnoreArg(),
    +                             fixed_ips=mox.IgnoreArg()).AndReturn(None)
    +
             db.fixed_ip_associate_pool(mox.IgnoreArg(),
                                        mox.IgnoreArg(),
                                        mox.IgnoreArg()).AndReturn('192.168.0.101')
    @@ -310,6 +316,7 @@ def test_add_fixed_ip_instance_using_uuid_without_vpn(self):
             self.mox.StubOutWithMock(db,
                                   'virtual_interface_get_by_instance_and_network')
             self.mox.StubOutWithMock(db, 'fixed_ip_update')
    +        self.mox.StubOutWithMock(quota.QUOTAS, 'reserve')
     
             db.fixed_ip_update(mox.IgnoreArg(),
                                mox.IgnoreArg(),
    @@ -323,6 +330,10 @@ def test_add_fixed_ip_instance_using_uuid_without_vpn(self):
             db.instance_get(mox.IgnoreArg(),
                             mox.IgnoreArg()).AndReturn({'security_groups':
                                                                  [{'id': 0}]})
    +
    +        quota.QUOTAS.reserve(mox.IgnoreArg(),
    +                             fixed_ips=mox.IgnoreArg()).AndReturn(None)
    +
             db.fixed_ip_associate_pool(mox.IgnoreArg(),
                                        mox.IgnoreArg(),
                                        mox.IgnoreArg()).AndReturn('192.168.0.101')
    @@ -376,6 +387,7 @@ def test_instance_dns(self):
             self.mox.StubOutWithMock(db,
                                   'virtual_interface_get_by_instance_and_network')
             self.mox.StubOutWithMock(db, 'fixed_ip_update')
    +        self.mox.StubOutWithMock(quota.QUOTAS, 'reserve')
     
             db.fixed_ip_update(mox.IgnoreArg(),
                                mox.IgnoreArg(),
    @@ -390,6 +402,9 @@ def test_instance_dns(self):
                             mox.IgnoreArg()).AndReturn({'security_groups':
                                                                  [{'id': 0}]})
     
    +        quota.QUOTAS.reserve(mox.IgnoreArg(),
    +                             fixed_ips=mox.IgnoreArg()).AndReturn(None)
    +
             db.fixed_ip_associate_pool(mox.IgnoreArg(),
                                        mox.IgnoreArg(),
                                        mox.IgnoreArg()).AndReturn(fixedip)
    
  • nova/tests/test_quota.py+24 0 modified
    @@ -723,6 +723,7 @@ def setUp(self):
                        quota_volumes=10,
                        quota_gigabytes=1000,
                        quota_floating_ips=10,
    +                   quota_fixed_ips=10,
                        quota_metadata_items=128,
                        quota_injected_files=5,
                        quota_injected_file_content_bytes=10 * 1024,
    @@ -755,6 +756,7 @@ def test_get_defaults(self):
                     volumes=10,
                     gigabytes=1000,
                     floating_ips=10,
    +                fixed_ips=10,
                     metadata_items=128,
                     injected_files=5,
                     injected_file_content_bytes=10 * 1024,
    @@ -791,6 +793,7 @@ def test_get_class_quotas(self):
                     volumes=10,
                     gigabytes=500,
                     floating_ips=10,
    +                fixed_ips=10,
                     metadata_items=64,
                     injected_files=5,
                     injected_file_content_bytes=5 * 1024,
    @@ -847,6 +850,7 @@ def fake_qugabp(context, project_id):
             self._stub_quota_class_get_all_by_name()
     
         def test_get_project_quotas(self):
    +        self.maxDiff = None
             self._stub_get_by_project()
             result = self.driver.get_project_quotas(
                 FakeContext('test_project', 'test_class'),
    @@ -888,6 +892,11 @@ def test_get_project_quotas(self):
                         in_use=2,
                         reserved=0,
                         ),
    +                fixed_ips=dict(
    +                    limit=10,
    +                    in_use=0,
    +                    reserved=0,
    +                    ),
                     metadata_items=dict(
                         limit=64,
                         in_use=0,
    @@ -926,6 +935,7 @@ def test_get_project_quotas(self):
                     ))
     
         def test_get_project_quotas_alt_context_no_class(self):
    +        self.maxDiff = None
             self._stub_get_by_project()
             result = self.driver.get_project_quotas(
                 FakeContext('other_project', 'other_class'),
    @@ -966,6 +976,11 @@ def test_get_project_quotas_alt_context_no_class(self):
                         in_use=2,
                         reserved=0,
                         ),
    +                fixed_ips=dict(
    +                    limit=10,
    +                    in_use=0,
    +                    reserved=0,
    +                    ),
                     metadata_items=dict(
                         limit=128,
                         in_use=0,
    @@ -1004,6 +1019,7 @@ def test_get_project_quotas_alt_context_no_class(self):
                     ))
     
         def test_get_project_quotas_alt_context_with_class(self):
    +        self.maxDiff = None
             self._stub_get_by_project()
             result = self.driver.get_project_quotas(
                 FakeContext('other_project', 'other_class'),
    @@ -1045,6 +1061,11 @@ def test_get_project_quotas_alt_context_with_class(self):
                         in_use=2,
                         reserved=0,
                         ),
    +                fixed_ips=dict(
    +                    limit=10,
    +                    in_use=0,
    +                    reserved=0,
    +                    ),
                     metadata_items=dict(
                         limit=64,
                         in_use=0,
    @@ -1145,6 +1166,9 @@ def test_get_project_quotas_no_usages(self):
                     floating_ips=dict(
                         limit=10,
                         ),
    +                fixed_ips=dict(
    +                    limit=10,
    +                    ),
                     metadata_items=dict(
                         limit=64,
                         ),
    
99429214d4dd

Add quotas for fixed ips.

https://github.com/openstack/novaMichael StillMar 4, 2013via ghsa
31 files changed · +198 54
  • doc/api_samples/os-quota-class-sets/quota-classes-show-get-resp.json+1 0 modified
    @@ -1,6 +1,7 @@
     {
         "quota_class_set": {
             "cores": 20,
    +        "fixed_ips": 10,
             "floating_ips": 10,
             "id": "test_class",
             "injected_file_content_bytes": 10240,
    
  • doc/api_samples/os-quota-class-sets/quota-classes-show-get-resp.xml+1 0 modified
    @@ -1,6 +1,7 @@
     <?xml version='1.0' encoding='UTF-8'?>
     <quota_class_set id="test_class">
       <cores>20</cores>
    +  <fixed_ips>10</fixed_ips>
       <floating_ips>10</floating_ips>
       <injected_file_content_bytes>10240</injected_file_content_bytes>
       <injected_file_path_bytes>255</injected_file_path_bytes>
    
  • doc/api_samples/os-quota-class-sets/quota-classes-update-post-resp.json+1 0 modified
    @@ -1,6 +1,7 @@
     {
         "quota_class_set": {
             "cores": 50,
    +        "fixed_ips": 10,
             "floating_ips": 10,
             "injected_file_content_bytes": 10240,
             "injected_file_path_bytes": 255,
    
  • doc/api_samples/os-quota-class-sets/quota-classes-update-post-resp.xml+1 0 modified
    @@ -1,6 +1,7 @@
     <?xml version='1.0' encoding='UTF-8'?>
     <quota_class_set>
       <cores>50</cores>
    +  <fixed_ips>10</fixed_ips>
       <floating_ips>10</floating_ips>
       <injected_file_content_bytes>10240</injected_file_content_bytes>
       <injected_file_path_bytes>255</injected_file_path_bytes>
    
  • doc/api_samples/os-quota-sets/quotas-show-defaults-get-resp.json+2 1 modified
    @@ -1,6 +1,7 @@
     {
         "quota_set": {
             "cores": 20,
    +        "fixed_ips": 10,
             "floating_ips": 10,
             "id": "fake_tenant",
             "injected_file_content_bytes": 10240,
    @@ -13,4 +14,4 @@
             "security_group_rules": 20,
             "security_groups": 10
         }
    -}
    +}
    \ No newline at end of file
    
  • doc/api_samples/os-quota-sets/quotas-show-defaults-get-resp.xml+2 1 modified
    @@ -1,6 +1,7 @@
     <?xml version='1.0' encoding='UTF-8'?>
     <quota_set id="fake_tenant">
       <cores>20</cores>
    +  <fixed_ips>10</fixed_ips>
       <floating_ips>10</floating_ips>
       <injected_file_content_bytes>10240</injected_file_content_bytes>
       <injected_file_path_bytes>255</injected_file_path_bytes>
    @@ -11,4 +12,4 @@
       <ram>51200</ram>
       <security_group_rules>20</security_group_rules>
       <security_groups>10</security_groups>
    -</quota_set>
    +</quota_set>
    \ No newline at end of file
    
  • doc/api_samples/os-quota-sets/quotas-show-get-resp.json+2 1 modified
    @@ -1,6 +1,7 @@
     {
         "quota_set": {
             "cores": 20,
    +        "fixed_ips": 10,
             "floating_ips": 10,
             "id": "fake_tenant",
             "injected_file_content_bytes": 10240,
    @@ -13,4 +14,4 @@
             "security_group_rules": 20,
             "security_groups": 10
         }
    -}
    +}
    \ No newline at end of file
    
  • doc/api_samples/os-quota-sets/quotas-show-get-resp.xml+2 1 modified
    @@ -1,6 +1,7 @@
     <?xml version='1.0' encoding='UTF-8'?>
     <quota_set id="fake_tenant">
       <cores>20</cores>
    +  <fixed_ips>10</fixed_ips>
       <floating_ips>10</floating_ips>
       <injected_file_content_bytes>10240</injected_file_content_bytes>
       <injected_file_path_bytes>255</injected_file_path_bytes>
    @@ -11,4 +12,4 @@
       <ram>51200</ram>
       <security_group_rules>20</security_group_rules>
       <security_groups>10</security_groups>
    -</quota_set>
    +</quota_set>
    \ No newline at end of file
    
  • doc/api_samples/os-quota-sets/quotas-update-post-resp.json+2 1 modified
    @@ -1,6 +1,7 @@
     {
         "quota_set": {
             "cores": 20,
    +        "fixed_ips": 10,
             "floating_ips": 10,
             "injected_file_content_bytes": 10240,
             "injected_file_path_bytes": 255,
    @@ -12,4 +13,4 @@
             "security_group_rules": 20,
             "security_groups": 45
         }
    -}
    +}
    \ No newline at end of file
    
  • doc/api_samples/os-quota-sets/quotas-update-post-resp.xml+2 1 modified
    @@ -1,6 +1,7 @@
     <?xml version='1.0' encoding='UTF-8'?>
     <quota_set>
       <cores>20</cores>
    +  <fixed_ips>10</fixed_ips>
       <floating_ips>10</floating_ips>
       <injected_file_content_bytes>10240</injected_file_content_bytes>
       <injected_file_path_bytes>255</injected_file_path_bytes>
    @@ -11,4 +12,4 @@
       <ram>51200</ram>
       <security_group_rules>20</security_group_rules>
       <security_groups>45</security_groups>
    -</quota_set>
    +</quota_set>
    \ No newline at end of file
    
  • nova/db/api.py+6 0 modified
    @@ -510,6 +510,12 @@ def fixed_ip_update(context, address, values):
         """Create a fixed ip from the values dictionary."""
         return IMPL.fixed_ip_update(context, address, values)
     
    +
    +def fixed_ip_count_by_project(context, project_id, session=None):
    +    """Count fixed ips used by project."""
    +    return IMPL.fixed_ip_count_by_project(context, project_id,
    +                                          session=session)
    +
     ####################
     
     
    
  • nova/db/sqlalchemy/api.py+12 0 modified
    @@ -1245,6 +1245,18 @@ def fixed_ip_update(context, address, values):
             fixed_ip_ref.save(session=session)
     
     
    +@require_context
    +def fixed_ip_count_by_project(context, project_id, session=None):
    +    nova.context.authorize_project_context(context, project_id)
    +    return model_query(context, models.FixedIp.id,
    +                       base_model=models.FixedIp, read_deleted="no",
    +                       session=session).\
    +                join((models.Instance,
    +                      models.Instance.uuid == models.FixedIp.instance_uuid)).\
    +                filter(models.Instance.uuid == project_id).\
    +                count()
    +
    +
     ###################
     
     
    
  • nova/exception.py+4 0 modified
    @@ -1008,6 +1008,10 @@ class FloatingIpLimitExceeded(QuotaError):
         message = _("Maximum number of floating ips exceeded")
     
     
    +class FixedIpLimitExceeded(QuotaError):
    +    message = _("Maximum number of fixed ips exceeded")
    +
    +
     class MetadataLimitExceeded(QuotaError):
         message = _("Maximum number of metadata items exceeds %(allowed)d")
     
    
  • nova/network/manager.py+62 33 modified
    @@ -69,11 +69,14 @@
     from nova.openstack.common import log as logging
     from nova.openstack.common import timeutils
     from nova.openstack.common import uuidutils
    +from nova import quota
     from nova import servicegroup
     from nova import utils
     
     LOG = logging.getLogger(__name__)
     
    +QUOTAS = quota.QUOTAS
    +
     
     network_opts = [
         cfg.StrOpt('flat_network_bridge',
    @@ -823,47 +826,69 @@ def allocate_fixed_ip(self, context, instance_id, network, **kwargs):
             #             network_get_by_compute_host
             address = None
     
    -        if network['cidr']:
    -            address = kwargs.get('address', None)
    -            if address:
    -                address = self.db.fixed_ip_associate(context,
    -                                                     address,
    -                                                     instance_id,
    -                                                     network['id'])
    -            else:
    -                address = self.db.fixed_ip_associate_pool(context.elevated(),
    -                                                          network['id'],
    -                                                          instance_id)
    -            self._do_trigger_security_group_members_refresh_for_instance(
    -                                                                   instance_id)
    -            self._do_trigger_security_group_handler(
    -                'instance_add_security_group', instance_id)
    -            get_vif = self.db.virtual_interface_get_by_instance_and_network
    -            vif = get_vif(context, instance_id, network['id'])
    -            values = {'allocated': True,
    -                      'virtual_interface_id': vif['id']}
    -            self.db.fixed_ip_update(context, address, values)
    +        # Check the quota; can't put this in the API because we get
    +        # called into from other places
    +        try:
    +            reservations = QUOTAS.reserve(context, fixed_ips=1)
    +        except exception.OverQuota:
    +            pid = context.project_id
    +            LOG.warn(_("Quota exceeded for %(pid)s, tried to allocate "
    +                       "fixed IP") % locals())
    +            raise exception.FixedIpLimitExceeded()
     
    -        # NOTE(vish) This db query could be removed if we pass az and name
    -        #            (or the whole instance object).
    -        instance = self.db.instance_get_by_uuid(context, instance_id)
    -        name = instance['display_name']
    +        try:
    +            if network['cidr']:
    +                address = kwargs.get('address', None)
    +                if address:
    +                    address = self.db.fixed_ip_associate(context,
    +                                                         address,
    +                                                         instance_id,
    +                                                         network['id'])
    +                else:
    +                    address = self.db.fixed_ip_associate_pool(
    +                        context.elevated(), network['id'], instance_id)
    +                self._do_trigger_security_group_members_refresh_for_instance(
    +                    instance_id)
    +                self._do_trigger_security_group_handler(
    +                    'instance_add_security_group', instance_id)
    +                get_vif = self.db.virtual_interface_get_by_instance_and_network
    +                vif = get_vif(context, instance_id, network['id'])
    +                values = {'allocated': True,
    +                          'virtual_interface_id': vif['id']}
    +                self.db.fixed_ip_update(context, address, values)
    +
    +            # NOTE(vish) This db query could be removed if we pass az and name
    +            #            (or the whole instance object).
    +            instance = self.db.instance_get_by_uuid(context, instance_id)
    +            name = instance['display_name']
     
    -        if self._validate_instance_zone_for_dns_domain(context, instance):
    -            self.instance_dns_manager.create_entry(name, address,
    -                                                   "A",
    -                                                   self.instance_dns_domain)
    -            self.instance_dns_manager.create_entry(instance_id, address,
    -                                                   "A",
    -                                                   self.instance_dns_domain)
    -        self._setup_network_on_host(context, network)
    -        return address
    +            if self._validate_instance_zone_for_dns_domain(context, instance):
    +                self.instance_dns_manager.create_entry(
    +                    name, address, "A", self.instance_dns_domain)
    +                self.instance_dns_manager.create_entry(
    +                    instance_id, address, "A", self.instance_dns_domain)
    +            self._setup_network_on_host(context, network)
    +
    +            QUOTAS.commit(context, reservations)
    +            return address
    +
    +        except Exception:
    +            with excutils.save_and_reraise_exception():
    +                QUOTAS.rollback(context, reservations)
     
         def deallocate_fixed_ip(self, context, address, host=None, teardown=True):
             """Returns a fixed ip to the pool."""
             fixed_ip_ref = self.db.fixed_ip_get_by_address(context, address)
             instance_uuid = fixed_ip_ref['instance_uuid']
             vif_id = fixed_ip_ref['virtual_interface_id']
    +
    +        try:
    +            reservations = QUOTAS.reserve(context, fixed_ips=-1)
    +        except Exception:
    +            reservations = None
    +            LOG.exception(_("Failed to update usages deallocating "
    +                            "fixed IP"))
    +
             self._do_trigger_security_group_members_refresh_for_instance(
                 instance_uuid)
             self._do_trigger_security_group_handler(
    @@ -912,6 +937,10 @@ def deallocate_fixed_ip(self, context, address, host=None, teardown=True):
     
                 self._teardown_network_on_host(context, network)
     
    +        # Commit the reservations
    +        if reservations:
    +            QUOTAS.commit(context, reservations)
    +
         def lease_fixed_ip(self, context, address):
             """Called by dhcp-bridge when ip is leased."""
             LOG.debug(_('Leased IP |%(address)s|'), locals(), context=context)
    
  • nova/quota.py+10 0 modified
    @@ -43,6 +43,10 @@
         cfg.IntOpt('quota_floating_ips',
                    default=10,
                    help='number of floating ips allowed per project'),
    +    cfg.IntOpt('quota_fixed_ips',
    +               default=10,
    +               help=('number of fixed ips allowed per project (this should be '
    +                     'at least the number of instances allowed)')),
         cfg.IntOpt('quota_metadata_items',
                    default=128,
                    help='number of metadata items allowed per instance'),
    @@ -1062,6 +1066,11 @@ def _sync_floating_ips(context, project_id, session):
                 context, project_id, session=session))
     
     
    +def _sync_fixed_ips(context, project_id, session):
    +    return dict(fixed_ips=db.fixed_ip_count_by_project(
    +            context, project_id, session=session))
    +
    +
     def _sync_security_groups(context, project_id, session):
         return dict(security_groups=db.security_group_count_by_project(
                 context, project_id, session=session))
    @@ -1076,6 +1085,7 @@ def _sync_security_groups(context, project_id, session):
         ReservableResource('ram', _sync_instances, 'quota_ram'),
         ReservableResource('floating_ips', _sync_floating_ips,
                            'quota_floating_ips'),
    +    ReservableResource('fixed_ips', _sync_fixed_ips, 'quota_fixed_ips'),
         AbsoluteResource('metadata_items', 'quota_metadata_items'),
         AbsoluteResource('injected_files', 'quota_injected_files'),
         AbsoluteResource('injected_file_content_bytes',
    
  • nova/tests/api/openstack/compute/contrib/test_quota_classes.py+16 7 modified
    @@ -24,11 +24,13 @@
     
     def quota_set(class_name):
         return {'quota_class_set': {'id': class_name, 'metadata_items': 128,
    -            'ram': 51200, 'floating_ips': 10, 'instances': 10,
    -            'injected_files': 5, 'cores': 20,
    -            'injected_file_content_bytes': 10240, 'security_groups': 10,
    -            'security_group_rules': 20, 'key_pairs': 100,
    -            'injected_file_path_bytes': 255}}
    +                                'ram': 51200, 'floating_ips': 10,
    +                                'fixed_ips': 10, 'instances': 10,
    +                                'injected_files': 5, 'cores': 20,
    +                                'injected_file_content_bytes': 10240,
    +                                'security_groups': 10,
    +                                'security_group_rules': 20, 'key_pairs': 100,
    +                                'injected_file_path_bytes': 255}}
     
     
     class QuotaClassSetsTest(test.TestCase):
    @@ -43,6 +45,7 @@ def test_format_quota_set(self):
                 'cores': 20,
                 'ram': 51200,
                 'floating_ips': 10,
    +            'fixed_ips': 10,
                 'metadata_items': 128,
                 'injected_files': 5,
                 'injected_file_path_bytes': 255,
    @@ -61,6 +64,7 @@ def test_format_quota_set(self):
             self.assertEqual(qs['cores'], 20)
             self.assertEqual(qs['ram'], 51200)
             self.assertEqual(qs['floating_ips'], 10)
    +        self.assertEqual(qs['fixed_ips'], 10)
             self.assertEqual(qs['metadata_items'], 128)
             self.assertEqual(qs['injected_files'], 5)
             self.assertEqual(qs['injected_file_path_bytes'], 255)
    @@ -86,7 +90,8 @@ def test_quotas_show_as_unauthorized_user(self):
         def test_quotas_update_as_admin(self):
             body = {'quota_class_set': {'instances': 50, 'cores': 50,
                                         'ram': 51200, 'floating_ips': 10,
    -                                    'metadata_items': 128, 'injected_files': 5,
    +                                    'fixed_ips': 10, 'metadata_items': 128,
    +                                    'injected_files': 5,
                                         'injected_file_content_bytes': 10240,
                                         'injected_file_path_bytes': 255,
                                         'security_groups': 10,
    @@ -103,7 +108,8 @@ def test_quotas_update_as_admin(self):
         def test_quotas_update_as_user(self):
             body = {'quota_class_set': {'instances': 50, 'cores': 50,
                                         'ram': 51200, 'floating_ips': 10,
    -                                    'metadata_items': 128, 'injected_files': 5,
    +                                    'fixed_ips': 10, 'metadata_items': 128,
    +                                    'injected_files': 5,
                                         'injected_file_content_bytes': 10240,
                                         'security_groups': 10,
                                         'security_group_rules': 20,
    @@ -130,6 +136,7 @@ def test_serializer(self):
                     injected_file_content_bytes=20,
                     ram=50,
                     floating_ips=60,
    +                fixed_ips=10,
                     instances=70,
                     injected_files=80,
                     security_groups=10,
    @@ -154,6 +161,7 @@ def test_deserializer(self):
                     injected_file_content_bytes='20',
                     ram='50',
                     floating_ips='60',
    +                fixed_ips='10',
                     instances='70',
                     injected_files='80',
                     security_groups='10',
    @@ -167,6 +175,7 @@ def test_deserializer(self):
                       '</injected_file_content_bytes>'
                       '<ram>50</ram>'
                       '<floating_ips>60</floating_ips>'
    +                  '<fixed_ips>10</fixed_ips>'
                       '<instances>70</instances>'
                       '<injected_files>80</injected_files>'
                       '<cores>90</cores>'
    
  • nova/tests/api/openstack/compute/contrib/test_quotas.py+19 8 modified
    @@ -26,7 +26,7 @@
     
     def quota_set(id):
         return {'quota_set': {'id': id, 'metadata_items': 128,
    -            'ram': 51200, 'floating_ips': 10,
    +            'ram': 51200, 'floating_ips': 10, 'fixed_ips': 10,
                 'instances': 10, 'injected_files': 5, 'cores': 20,
                 'injected_file_content_bytes': 10240,
                 'security_groups': 10, 'security_group_rules': 20,
    @@ -45,6 +45,7 @@ def test_format_quota_set(self):
                 'cores': 20,
                 'ram': 51200,
                 'floating_ips': 10,
    +            'fixed_ips': 10,
                 'metadata_items': 128,
                 'injected_files': 5,
                 'injected_file_path_bytes': 255,
    @@ -61,6 +62,7 @@ def test_format_quota_set(self):
             self.assertEqual(qs['cores'], 20)
             self.assertEqual(qs['ram'], 51200)
             self.assertEqual(qs['floating_ips'], 10)
    +        self.assertEqual(qs['fixed_ips'], 10)
             self.assertEqual(qs['metadata_items'], 128)
             self.assertEqual(qs['injected_files'], 5)
             self.assertEqual(qs['injected_file_path_bytes'], 255)
    @@ -81,6 +83,7 @@ def test_quotas_defaults(self):
                         'cores': 20,
                         'ram': 51200,
                         'floating_ips': 10,
    +                    'fixed_ips': 10,
                         'metadata_items': 128,
                         'injected_files': 5,
                         'injected_file_path_bytes': 255,
    @@ -106,12 +109,13 @@ def test_quotas_show_as_unauthorized_user(self):
         def test_quotas_update_as_admin(self):
             body = {'quota_set': {'instances': 50, 'cores': 50,
                                   'ram': 51200, 'floating_ips': 10,
    -                              'metadata_items': 128, 'injected_files': 5,
    +                              'fixed_ips': 10, 'metadata_items': 128,
    +                              'injected_files': 5,
                                   'injected_file_content_bytes': 10240,
                                   'injected_file_path_bytes': 255,
                                   'security_groups': 10,
                                   'security_group_rules': 20,
    -                              'key_pairs': 100}}
    +                              'key_pairs': 100, 'fixed_ips': 10}}
     
             req = fakes.HTTPRequest.blank('/v2/fake4/os-quota-sets/update_me',
                                           use_admin_context=True)
    @@ -122,7 +126,8 @@ def test_quotas_update_as_admin(self):
         def test_quotas_update_as_user(self):
             body = {'quota_set': {'instances': 50, 'cores': 50,
                                   'ram': 51200, 'floating_ips': 10,
    -                              'metadata_items': 128, 'injected_files': 5,
    +                              'fixed_ips': 10, 'metadata_items': 128,
    +                              'injected_files': 5,
                                   'injected_file_content_bytes': 10240,
                                   'security_groups': 10,
                                   'security_group_rules': 20,
    @@ -134,7 +139,7 @@ def test_quotas_update_as_user(self):
     
         def test_quotas_update_invalid_limit(self):
             body = {'quota_set': {'instances': -2, 'cores': -2,
    -                              'ram': -2, 'floating_ips': -2,
    +                              'ram': -2, 'floating_ips': -2, 'fixed_ips': -2,
                                   'metadata_items': -2, 'injected_files': -2,
                                   'injected_file_content_bytes': -2}}
     
    @@ -147,7 +152,8 @@ def test_quotas_update_invalid_value(self):
             expected_resp = {'quota_set': {
                                   'instances': 50, 'cores': 50,
                                   'ram': 51200, 'floating_ips': 10,
    -                              'metadata_items': 128, 'injected_files': 5,
    +                              'fixed_ips': 10, 'metadata_items': 128,
    +                              'injected_files': 5,
                                   'injected_file_content_bytes': 10240,
                                   'injected_file_path_bytes': 255,
                                   'security_groups': 10,
    @@ -157,7 +163,8 @@ def test_quotas_update_invalid_value(self):
             # when PUT JSON format with empty string for quota
             body = {'quota_set': {'instances': 50, 'cores': 50,
                                   'ram': '', 'floating_ips': 10,
    -                              'metadata_items': 128, 'injected_files': 5,
    +                              'fixed_ips': 10, 'metadata_items': 128,
    +                              'injected_files': 5,
                                   'injected_file_content_bytes': 10240,
                                   'injected_file_path_bytes': 255,
                                   'security_groups': 10,
    @@ -171,7 +178,8 @@ def test_quotas_update_invalid_value(self):
             # when PUT XML format with empty string for quota
             body = {'quota_set': {'instances': 50, 'cores': 50,
                                   'ram': {}, 'floating_ips': 10,
    -                              'metadata_items': 128, 'injected_files': 5,
    +                              'fixed_ips': 10, 'metadata_items': 128,
    +                              'injected_files': 5,
                                   'injected_file_content_bytes': 10240,
                                   'injected_file_path_bytes': 255,
                                   'security_groups': 10,
    @@ -197,6 +205,7 @@ def test_serializer(self):
                     injected_file_content_bytes=20,
                     ram=50,
                     floating_ips=60,
    +                fixed_ips=10,
                     instances=70,
                     injected_files=80,
                     security_groups=10,
    @@ -220,6 +229,7 @@ def test_deserializer(self):
                     injected_file_content_bytes='20',
                     ram='50',
                     floating_ips='60',
    +                fixed_ips='10',
                     instances='70',
                     injected_files='80',
                     security_groups='10',
    @@ -233,6 +243,7 @@ def test_deserializer(self):
                       '</injected_file_content_bytes>'
                       '<ram>50</ram>'
                       '<floating_ips>60</floating_ips>'
    +                  '<fixed_ips>10</fixed_ips>'
                       '<instances>70</instances>'
                       '<injected_files>80</injected_files>'
                       '<security_groups>10</security_groups>'
    
  • nova/tests/integrated/api_samples/os-quota-class-sets/quota-classes-show-get-resp.json.tpl+1 0 modified
    @@ -2,6 +2,7 @@
         "quota_class_set": {
             "cores": 20,
             "floating_ips": 10,
    +	"fixed_ips": 10,
             "id": "%(set_id)s",
             "injected_file_content_bytes": 10240,
             "injected_file_path_bytes": 255,
    
  • nova/tests/integrated/api_samples/os-quota-class-sets/quota-classes-show-get-resp.xml.tpl+1 0 modified
    @@ -2,6 +2,7 @@
     <quota_class_set id="%(set_id)s">
       <cores>20</cores>
       <floating_ips>10</floating_ips>
    +  <fixed_ips>10</fixed_ips>
       <injected_file_content_bytes>10240</injected_file_content_bytes>
       <injected_file_path_bytes>255</injected_file_path_bytes>
       <injected_files>5</injected_files>
    
  • nova/tests/integrated/api_samples/os-quota-class-sets/quota-classes-update-post-req.json.tpl+1 0 modified
    @@ -4,6 +4,7 @@
             "cores": 50,
             "ram": 51200,
             "floating_ips": 10,
    +        "fixed_ips": 10,
             "metadata_items": 128,
             "injected_files": 5,
             "injected_file_content_bytes": 10240,
    
  • nova/tests/integrated/api_samples/os-quota-class-sets/quota-classes-update-post-req.xml.tpl+1 0 modified
    @@ -2,6 +2,7 @@
     <quota_class_set>
       <cores>50</cores>
       <floating_ips>10</floating_ips>
    +  <fixed_ips>10</fixed_ips>
       <injected_file_content_bytes>10240</injected_file_content_bytes>
       <injected_file_path_bytes>255</injected_file_path_bytes>
       <injected_files>5</injected_files>
    
  • nova/tests/integrated/api_samples/os-quota-class-sets/quota-classes-update-post-resp.json.tpl+1 0 modified
    @@ -2,6 +2,7 @@
         "quota_class_set": {
             "cores": 50,
             "floating_ips": 10,
    +        "fixed_ips": 10,
             "injected_file_content_bytes": 10240,
             "injected_file_path_bytes": 255,
             "injected_files": 5,
    
  • nova/tests/integrated/api_samples/os-quota-class-sets/quota-classes-update-post-resp.xml.tpl+1 0 modified
    @@ -2,6 +2,7 @@
     <quota_class_set>
       <cores>50</cores>
       <floating_ips>10</floating_ips>
    +  <fixed_ips>10</fixed_ips>
       <injected_file_content_bytes>10240</injected_file_content_bytes>
       <injected_file_path_bytes>255</injected_file_path_bytes>
       <injected_files>5</injected_files>
    
  • nova/tests/integrated/api_samples/os-quota-sets/quotas-show-defaults-get-resp.json.tpl+1 0 modified
    @@ -2,6 +2,7 @@
         "quota_set": {
             "cores": 20,
             "floating_ips": 10,
    +        "fixed_ips": 10,
             "id": "fake_tenant",
             "injected_file_content_bytes": 10240,
             "injected_file_path_bytes": 255,
    
  • nova/tests/integrated/api_samples/os-quota-sets/quotas-show-defaults-get-resp.xml.tpl+1 0 modified
    @@ -2,6 +2,7 @@
     <quota_set id="fake_tenant">
       <cores>20</cores>
       <floating_ips>10</floating_ips>
    +  <fixed_ips>10</fixed_ips>
       <injected_file_content_bytes>10240</injected_file_content_bytes>
       <injected_file_path_bytes>255</injected_file_path_bytes>
       <injected_files>5</injected_files>
    
  • nova/tests/integrated/api_samples/os-quota-sets/quotas-show-get-resp.json.tpl+1 0 modified
    @@ -2,6 +2,7 @@
         "quota_set": {
             "cores": 20,
             "floating_ips": 10,
    +        "fixed_ips": 10,
             "id": "fake_tenant",
             "injected_file_content_bytes": 10240,
             "injected_file_path_bytes": 255,
    
  • nova/tests/integrated/api_samples/os-quota-sets/quotas-show-get-resp.xml.tpl+1 0 modified
    @@ -2,6 +2,7 @@
     <quota_set id="fake_tenant">
       <cores>20</cores>
       <floating_ips>10</floating_ips>
    +  <fixed_ips>10</fixed_ips>
       <injected_file_content_bytes>10240</injected_file_content_bytes>
       <injected_file_path_bytes>255</injected_file_path_bytes>
       <injected_files>5</injected_files>
    
  • nova/tests/integrated/api_samples/os-quota-sets/quotas-update-post-resp.json.tpl+1 0 modified
    @@ -2,6 +2,7 @@
         "quota_set": {
             "cores": 20,
             "floating_ips": 10,
    +        "fixed_ips": 10,
             "injected_file_content_bytes": 10240,
             "injected_file_path_bytes": 255,
             "injected_files": 5,
    
  • nova/tests/integrated/api_samples/os-quota-sets/quotas-update-post-resp.xml.tpl+1 0 modified
    @@ -2,6 +2,7 @@
     <quota_set>
       <cores>20</cores>
       <floating_ips>10</floating_ips>
    +  <fixed_ips>10</fixed_ips>
       <injected_file_content_bytes>10240</injected_file_content_bytes>
       <injected_file_path_bytes>255</injected_file_path_bytes>
       <injected_files>5</injected_files>
    
  • nova/tests/network/test_manager.py+17 0 modified
    @@ -34,6 +34,7 @@
     from nova.openstack.common import log as logging
     from nova.openstack.common import rpc
     from nova.openstack.common.rpc import common as rpc_common
    +from nova import quota
     from nova import test
     from nova.tests import fake_ldap
     from nova.tests import fake_network
    @@ -286,6 +287,7 @@ def test_add_fixed_ip_instance_using_id_without_vpn(self):
             self.mox.StubOutWithMock(db,
                                   'virtual_interface_get_by_instance_and_network')
             self.mox.StubOutWithMock(db, 'fixed_ip_update')
    +        self.mox.StubOutWithMock(quota.QUOTAS, 'reserve')
             self.mox.StubOutWithMock(db, 'instance_get_by_uuid')
             self.mox.StubOutWithMock(self.network, 'get_instance_nw_info')
     
    @@ -306,6 +308,10 @@ def test_add_fixed_ip_instance_using_id_without_vpn(self):
             db.fixed_ip_update(mox.IgnoreArg(),
                                mox.IgnoreArg(),
                                mox.IgnoreArg())
    +
    +        quota.QUOTAS.reserve(mox.IgnoreArg(),
    +                             fixed_ips=mox.IgnoreArg()).AndReturn(None)
    +
             db.instance_get_by_uuid(self.context,
                             mox.IgnoreArg()).AndReturn({'display_name': HOST})
     
    @@ -327,6 +333,7 @@ def test_add_fixed_ip_instance_using_uuid_without_vpn(self):
             self.mox.StubOutWithMock(db,
                                   'virtual_interface_get_by_instance_and_network')
             self.mox.StubOutWithMock(db, 'fixed_ip_update')
    +        self.mox.StubOutWithMock(quota.QUOTAS, 'reserve')
             self.mox.StubOutWithMock(db, 'instance_get_by_uuid')
             self.mox.StubOutWithMock(self.network, 'get_instance_nw_info')
     
    @@ -347,6 +354,10 @@ def test_add_fixed_ip_instance_using_uuid_without_vpn(self):
             db.fixed_ip_update(mox.IgnoreArg(),
                                mox.IgnoreArg(),
                                mox.IgnoreArg())
    +
    +        quota.QUOTAS.reserve(mox.IgnoreArg(),
    +                             fixed_ips=mox.IgnoreArg()).AndReturn(None)
    +
             db.instance_get_by_uuid(self.context,
                             mox.IgnoreArg()).AndReturn({'display_name': HOST})
     
    @@ -414,6 +425,7 @@ def test_instance_dns(self):
             self.mox.StubOutWithMock(db, 'fixed_ip_update')
             self.mox.StubOutWithMock(db, 'instance_get_by_uuid')
             self.mox.StubOutWithMock(self.network, 'get_instance_nw_info')
    +        self.mox.StubOutWithMock(quota.QUOTAS, 'reserve')
     
             db.fixed_ip_associate_pool(mox.IgnoreArg(),
                                        mox.IgnoreArg(),
    @@ -432,6 +444,10 @@ def test_instance_dns(self):
             db.fixed_ip_update(mox.IgnoreArg(),
                                mox.IgnoreArg(),
                                mox.IgnoreArg())
    +
    +        quota.QUOTAS.reserve(mox.IgnoreArg(),
    +                             fixed_ips=mox.IgnoreArg()).AndReturn(None)
    +
             db.instance_get_by_uuid(self.context,
                             mox.IgnoreArg()).AndReturn({'display_name': HOST})
     
    @@ -531,6 +547,7 @@ def test_allocate_fixed_ip(self):
             db.instance_get_by_uuid(mox.IgnoreArg(),
                             mox.IgnoreArg()).AndReturn({'security_groups':
                                                                  [{'id': 0}]})
    +
             db.fixed_ip_associate_pool(mox.IgnoreArg(),
                                        mox.IgnoreArg(),
                                        mox.IgnoreArg()).AndReturn('192.168.0.1')
    
  • nova/tests/test_quota.py+24 0 modified
    @@ -719,6 +719,7 @@ def setUp(self):
                        quota_cores=20,
                        quota_ram=50 * 1024,
                        quota_floating_ips=10,
    +                   quota_fixed_ips=10,
                        quota_metadata_items=128,
                        quota_injected_files=5,
                        quota_injected_file_content_bytes=10 * 1024,
    @@ -745,6 +746,7 @@ def test_get_defaults(self):
                     cores=20,
                     ram=50 * 1024,
                     floating_ips=10,
    +                fixed_ips=10,
                     metadata_items=128,
                     injected_files=5,
                     injected_file_content_bytes=10 * 1024,
    @@ -778,6 +780,7 @@ def test_get_class_quotas(self):
                     cores=20,
                     ram=25 * 1024,
                     floating_ips=10,
    +                fixed_ips=10,
                     metadata_items=64,
                     injected_files=5,
                     injected_file_content_bytes=5 * 1024,
    @@ -830,6 +833,7 @@ def fake_qugabp(context, project_id):
             self._stub_quota_class_get_all_by_name()
     
         def test_get_project_quotas(self):
    +        self.maxDiff = None
             self._stub_get_by_project()
             result = self.driver.get_project_quotas(
                 FakeContext('test_project', 'test_class'),
    @@ -861,6 +865,11 @@ def test_get_project_quotas(self):
                         in_use=2,
                         reserved=0,
                         ),
    +                fixed_ips=dict(
    +                    limit=10,
    +                    in_use=0,
    +                    reserved=0,
    +                    ),
                     metadata_items=dict(
                         limit=64,
                         in_use=0,
    @@ -899,6 +908,7 @@ def test_get_project_quotas(self):
                     ))
     
         def test_get_project_quotas_alt_context_no_class(self):
    +        self.maxDiff = None
             self._stub_get_by_project()
             result = self.driver.get_project_quotas(
                 FakeContext('other_project', 'other_class'),
    @@ -929,6 +939,11 @@ def test_get_project_quotas_alt_context_no_class(self):
                         in_use=2,
                         reserved=0,
                         ),
    +                fixed_ips=dict(
    +                    limit=10,
    +                    in_use=0,
    +                    reserved=0,
    +                    ),
                     metadata_items=dict(
                         limit=128,
                         in_use=0,
    @@ -967,6 +982,7 @@ def test_get_project_quotas_alt_context_no_class(self):
                     ))
     
         def test_get_project_quotas_alt_context_with_class(self):
    +        self.maxDiff = None
             self._stub_get_by_project()
             result = self.driver.get_project_quotas(
                 FakeContext('other_project', 'other_class'),
    @@ -998,6 +1014,11 @@ def test_get_project_quotas_alt_context_with_class(self):
                         in_use=2,
                         reserved=0,
                         ),
    +                fixed_ips=dict(
    +                    limit=10,
    +                    in_use=0,
    +                    reserved=0,
    +                    ),
                     metadata_items=dict(
                         limit=64,
                         in_use=0,
    @@ -1087,6 +1108,9 @@ def test_get_project_quotas_no_usages(self):
                     floating_ips=dict(
                         limit=10,
                         ),
    +                fixed_ips=dict(
    +                    limit=10,
    +                    ),
                     metadata_items=dict(
                         limit=64,
                         ),
    

Vulnerability mechanics

Generated by null/stub on May 9, 2026. Inputs: CWE entries + fix-commit diffs from this CVE's patches. Citations validated against bundle.

References

23

News mentions

0

No linked articles in our index yet.