Merge "docs: Remove errant indentation, mark up literals"

This commit is contained in:
Zuul 2022-09-23 16:23:58 +00:00 committed by Gerrit Code Review
commit 23a2c980e2

View File

@ -43,7 +43,7 @@ limits are currently not enforced on RPC interfaces listening on the AMQP
bus.
Plugin and ML2 drivers are not supposed to enforce quotas for resources they
manage. However, the subnet_allocation [1]_ extension is an exception and will
manage. However, the ``subnet_allocation`` [1]_ extension is an exception and will
be discussed below.
The quota management and enforcement mechanisms discussed here apply to every
@ -55,14 +55,14 @@ High Level View
There are two main components in the Neutron quota system:
* The Quota API extensions.
* The Quota Engine.
* The Quota API extensions.
* The Quota Engine.
Both components rely on a quota driver. The neutron codebase currently defines
three quota drivers:
* neutron.db.quota.driver.DbQuotaDriver
* neutron.db.quota.driver_nolock.DbQuotaNoLockDriver (default)
* ``neutron.db.quota.driver.DbQuotaDriver``
* ``neutron.db.quota.driver_nolock.DbQuotaNoLockDriver`` (default)
The ``DbQuotaNoLockDriver`` is the default quota driver, defined in the
configuration option ``quota_driver``.
@ -107,23 +107,23 @@ delete operations are implemented by the usual index, show, update and
delete methods. These method simply call into the quota driver for either
fetching project quotas or updating them.
The _update_attributes method is called only once in the controller lifetime.
The ``_update_attributes`` method is called only once in the controller lifetime.
This method dynamically updates Neutron's resource attribute map [4]_ so that
an attribute is added for every resource managed by the quota engine.
Request authorisation is performed in this controller, and only 'admin' users
are allowed to modify quotas for projects. As the neutron policy engine is not
used, it is not possible to configure which users should be allowed to manage
quotas using policy.yaml.
quotas using ``policy.yaml``.
The driver operations dealing with quota management are:
* delete_tenant_quota, which simply removes all entries from the 'quotas'
table for a given project identifier;
* update_quota_limit, which adds or updates an entry in the 'quotas' project
for a given project identifier and a given resource name;
* _get_quotas, which fetches limits for a set of resource and a given project
identifier
* _get_all_quotas, which behaves like _get_quotas, but for all projects.
* ``delete_tenant_quota``, which simply removes all entries from the 'quotas'
table for a given project identifier;
* ``update_quota_limit``, which adds or updates an entry in the 'quotas' project
for a given project identifier and a given resource name;
* ``_get_quotas``, which fetches limits for a set of resource and a given project
identifier
* ``_get_all_quotas``, which behaves like ``_get_quotas``, but for all projects.
Resource Usage Info
@ -131,29 +131,29 @@ Resource Usage Info
Neutron has two ways of tracking resource usage info:
* CountableResource, where resource usage is calculated every time quotas
limits are enforced by counting rows in the resource table or resources
tables and reservations for that resource.
* TrackedResource, depends on the selected driver:
* ``CountableResource``, where resource usage is calculated every time quotas
limits are enforced by counting rows in the resource table or resources
tables and reservations for that resource.
* ``TrackedResource``, depends on the selected driver:
* DbQuotaDriver: the resource usage relies on a specific table tracking
usage data, and performs explicitly counting only when the data in this
table are not in sync with actual used and reserved resources.
* DbQuotaNoLockDriver: the resource usage is counted directly from the
database table associated to the resource. In this new driver,
CountableResource and TrackedResource could look similar but
TrackedResource depends on one single database model (table) and the
resource count is done directly on this table only.
* ``DbQuotaDriver``: the resource usage relies on a specific table tracking
usage data, and performs explicitly counting only when the data in this
table are not in sync with actual used and reserved resources.
* ``DbQuotaNoLockDriver``: the resource usage is counted directly from the
database table associated to the resource. In this new driver,
``CountableResource`` and ``TrackedResource`` could look similar but
``TrackedResource`` depends on one single database model (table) and the
resource count is done directly on this table only.
Another difference between CountableResource and TrackedResource is that the
former invokes a plugin method to count resources. CountableResource should be
Another difference between ``CountableResource`` and ``TrackedResource`` is that the
former invokes a plugin method to count resources. ``CountableResource`` should be
therefore employed for plugins which do not leverage the Neutron database.
The actual class that the Neutron quota engine will use is determined by the
track_quota_usage variable in the quota configuration section. If True,
TrackedResource instances will be created, otherwise the quota engine will
use CountableResource instances.
Resource creation is performed by the create_resource_instance factory method
in the neutron.quota.resource module.
``track_quota_usage`` variable in the quota configuration section. If ``True``,
``TrackedResource`` instances will be created, otherwise the quota engine will
use ``CountableResource`` instances.
Resource creation is performed by the ``create_resource_instance`` factory method
in the ``neutron.quota.resource`` module.
DbQuotaDriver description
-------------------------
@ -161,10 +161,10 @@ DbQuotaDriver description
From a performance perspective, having a table tracking resource usage
has some advantages, albeit not fundamental. Indeed the time required for
executing queries to explicitly count objects will increase with the number of
records in the table. On the other hand, using TrackedResource will fetch a
records in the table. On the other hand, using ``TrackedResource`` will fetch a
single record, but has the drawback of having to execute an UPDATE statement
once the operation is completed.
Nevertheless, CountableResource instances do not simply perform a SELECT query
Nevertheless, ``CountableResource`` instances do not simply perform a SELECT query
on the relevant table for a resource, but invoke a plugin method, which might
execute several statements and sometimes even interacts with the backend
before returning.
@ -179,7 +179,7 @@ While a RESTful API request is the most common one, resources can be created
by RPC handlers listing on the AMQP bus, such as those which create DHCP
ports, or by plugin operations, such as those which create router ports.
To this aim, TrackedResource instances are initialised with a reference to
To this aim, ``TrackedResource`` instances are initialised with a reference to
the model class for the resource for which they track usage data. During
object initialisation, SqlAlchemy event handlers are installed for this class.
The event handler is executed after a record is inserted or deleted.
@ -189,10 +189,10 @@ it will be synchronised counting resource usage from the database.
Even if this solution has some drawbacks, listed in the 'exceptions and
caveats' section, it is more reliable than solutions such as:
* Updating the usage counters with the new 'correct' value every time an
operation completes.
* Having a periodic task synchronising quota usage data with actual data in
the Neutron DB.
* Updating the usage counters with the new 'correct' value every time an
operation completes.
* Having a periodic task synchronising quota usage data with actual data in
the Neutron DB.
DbQuotaNoLockDriver description
@ -201,7 +201,7 @@ DbQuotaNoLockDriver description
The strategy of this quota driver is the opposite to ``DbQuotaDriver``.
Instead of tracking the usage quota of each resource in a specific table,
this driver retrieves the used resources directly form the database.
Each TrackedResource is linked to a database table that stores the tracked
Each ``TrackedResource`` is linked to a database table that stores the tracked
resources. This driver claims that a trivial query on the resource table,
filtering by project ID, is faster than attending to the DB events and tracking
the quota usage in an independent table.
@ -210,13 +210,13 @@ This driver relays on the database engine transactionality isolation. Each
time a new resource is requested, the quota driver opens a database transaction
to:
* Clean up the expired reservations. The amount of expired reservations is
always limited because of the short timeout set (2 minutes).
* Retrieve the used resources for a specific project. This query retrieves
only the "project_id" column of the resource to avoid backref requests; that
limits the scope of the query and speeds up it.
* Retrieve the reserved resources, created by other concurrent operations.
* If there is enough quota, create a new reservation register.
* Clean up the expired reservations. The amount of expired reservations is
always limited because of the short timeout set (2 minutes).
* Retrieve the used resources for a specific project. This query retrieves
only the "project_id" column of the resource to avoid backref requests; that
limits the scope of the query and speeds up it.
* Retrieve the reserved resources, created by other concurrent operations.
* If there is enough quota, create a new reservation register.
Those operations, executed in the same transaction, are fast enough to avoid
another concurrent resource reservation, exceeding the available quota. At the
@ -227,33 +227,33 @@ the chances of overcommiting resources over the quota limits are low. Neutron
does not enforce quota in such way that a quota limit violation could never
occur [5]_.
Regardless of whether CountableResource or TrackedResource is used, the quota
engine always invokes its count() method to retrieve resource usage.
Regardless of whether ``CountableResource`` or ``TrackedResource`` is used, the quota
engine always invokes its ``count()`` method to retrieve resource usage.
Therefore, from the perspective of the Quota engine there is absolutely no
difference between CountableResource and TrackedResource.
difference between ``CountableResource`` and ``TrackedResource``.
Quota Enforcement in DbQuotaDriver
----------------------------------
Before dispatching a request to the plugin, the Neutron 'base' controller [6]_
attempts to make a reservation for requested resource(s).
Reservations are made by calling the make_reservation method in
neutron.quota.QuotaEngine.
Reservations are made by calling the ``make_reservation`` method in
``neutron.quota.QuotaEngine``.
The process of making a reservation is fairly straightforward:
* Get current resource usages. This is achieved by invoking the count method
on every requested resource, and then retrieving the amount of reserved
resources.
* Fetch current quota limits for requested resources, by invoking the
_get_project_quotas method.
* Fetch expired reservations for selected resources. This amount will be
subtracted from resource usage. As in most cases there won't be any
expired reservation, this approach actually requires less DB operations than
doing a sum of non-expired, reserved resources for each request.
* For each resource calculate its headroom, and verify the requested
amount of resource is less than the headroom.
* If the above is true for all resource, the reservation is saved in the DB,
otherwise an OverQuotaLimit exception is raised.
* Get current resource usages. This is achieved by invoking the count method
on every requested resource, and then retrieving the amount of reserved
resources.
* Fetch current quota limits for requested resources, by invoking the
``_get_project_quotas`` method.
* Fetch expired reservations for selected resources. This amount will be
subtracted from resource usage. As in most cases there won't be any
expired reservation, this approach actually requires less DB operations than
doing a sum of non-expired, reserved resources for each request.
* For each resource calculate its headroom, and verify the requested
amount of resource is less than the headroom.
* If the above is true for all resource, the reservation is saved in the DB,
otherwise an ``OverQuotaLimit`` exception is raised.
The quota engine is able to make a reservation for multiple resources.
However, it is worth noting that because of the current structure of the
@ -266,11 +266,11 @@ In order to ensure correct operations, a row-level lock is acquired in
the transaction which creates the reservation. The lock is acquired when
reading usage data. In case of write-set certification failures,
which can occur in active/active clusters such as MySQL galera, the decorator
neutron_lib.db.api.retry_db_errors will retry the transaction if a DBDeadLock
``neutron_lib.db.api.retry_db_errors`` will retry the transaction if a DBDeadLock
exception is raised.
While non-locking approaches are possible, it has been found out that, since
a non-locking algorithms increases the chances of collision, the cost of
handling a DBDeadlock is still lower than the cost of retrying the operation
handling a ``DBDeadlock`` is still lower than the cost of retrying the operation
when a collision is detected. A study in this direction was conducted for
IP allocation operations, but the same principles apply here as well [7]_.
Nevertheless, moving away for DB-level locks is something that must happen
@ -280,11 +280,12 @@ Committing and cancelling a reservation is as simple as deleting the
reservation itself. When a reservation is committed, the resources which
were committed are now stored in the database, so the reservation itself
should be deleted. The Neutron quota engine simply removes the record when
cancelling a reservation (ie: the request failed to complete), and also
marks quota usage info as dirty when the reservation is committed (ie:
cancelling a reservation (i.e. the request failed to complete), and also
marks quota usage info as dirty when the reservation is committed (i.e.
the request completed correctly).
Reservations are committed or cancelled by respectively calling the
commit_reservation and cancel_reservation methods in neutron.quota.QuotaEngine.
``commit_reservation`` and ``cancel_reservation`` methods in
``neutron.quota.QuotaEngine``.
Reservations are not perennial. Eternal reservation would eventually exhaust
projects' quotas because they would never be removed when an API worker crashes
@ -314,16 +315,17 @@ argument must be a resource name, and the value of the argument must be
a DB model class. For example:
::
@resource_registry.tracked_resources(network=models_v2.Network,
@resource_registry.tracked_resources(network=models_v2.Network,
port=models_v2.Port,
subnet=models_v2.Subnet,
subnetpool=models_v2.SubnetPool)
Will ensure network, port, subnet and subnetpool resources are tracked.
In theory, it is possible to use this decorator multiple times, and not
exclusively to __init__ methods. However, this would eventually lead to
exclusively to ``__init__`` methods. However, this would eventually lead to
code readability and maintainability problems, so developers are strongly
encourage to apply this decorator exclusively to the plugin's __init__
encourage to apply this decorator exclusively to the plugin's ``__init__``
method (or any other method which is called by the plugin only once
during its initialization).
@ -350,41 +352,41 @@ Exceptions and Caveats
Please be aware of the following limitations of the quota enforcement engine:
* Subnet allocation from subnet pools, in particularly shared pools, is also
subject to quota limit checks. However this checks are not enforced by the
quota engine, but trough a mechanism implemented in the
neutron.ipam.subnetalloc module. This is because the Quota engine is not
able to satisfy the requirements for quotas on subnet allocation.
* The quota engine also provides a limit_check routine which enforces quota
checks without creating reservations. This way of doing quota enforcement
is extremely unreliable and superseded by the reservation mechanism. It
has not been removed to ensure off-tree plugins and extensions which leverage
are not broken.
* SqlAlchemy events might not be the most reliable way for detecting changes
in resource usage. Since the event mechanism monitors the data model class,
it is paramount for a correct quota enforcement, that resources are always
created and deleted using object relational mappings. For instance, deleting
a resource with a query.delete call, will not trigger the event. SQLAlchemy
events should be considered as a temporary measure adopted as Neutron lacks
persistent API objects.
* As CountableResource instance do not track usage data, when making a
reservation no write-intent lock is acquired. Therefore the quota engine
with CountableResource is not concurrency-safe.
* The mechanism for specifying for which resources enable usage tracking
relies on the fact that the plugin is loaded before quota-limited resources
are registered. For this reason it is not possible to validate whether a
resource actually exists or not when enabling tracking for it. Developers
should pay particular attention into ensuring resource names are correctly
specified.
* The code assumes usage trackers are a trusted source of truth: if they
report a usage counter and the dirty bit is not set, that counter is
correct. If it's dirty than surely that counter is out of sync.
This is not very robust, as there might be issues upon restart when toggling
the use_tracked_resources configuration variable, as stale counters might be
trusted upon for making reservations. Also, the same situation might occur
if a server crashes after the API operation is completed but before the
reservation is committed, as the actual resource usage is changed but
the corresponding usage tracker is not marked as dirty.
* Subnet allocation from subnet pools, in particularly shared pools, is also
subject to quota limit checks. However this checks are not enforced by the
quota engine, but trough a mechanism implemented in the
``neutron.ipam.subnetalloc`` module. This is because the quota engine is not
able to satisfy the requirements for quotas on subnet allocation.
* The quota engine also provides a ``limit_check`` routine which enforces quota
checks without creating reservations. This way of doing quota enforcement
is extremely unreliable and superseded by the reservation mechanism. It
has not been removed to ensure off-tree plugins and extensions which leverage
are not broken.
* SqlAlchemy events might not be the most reliable way for detecting changes
in resource usage. Since the event mechanism monitors the data model class,
it is paramount for a correct quota enforcement, that resources are always
created and deleted using object relational mappings. For instance, deleting
a resource with a ``query.delete`` call will not trigger the event. SQLAlchemy
events should be considered as a temporary measure adopted as Neutron lacks
persistent API objects.
* As ``CountableResource`` instance do not track usage data, when making a
reservation no write-intent lock is acquired. Therefore the quota engine
with ``CountableResource`` is not concurrency-safe.
* The mechanism for specifying for which resources enable usage tracking
relies on the fact that the plugin is loaded before quota-limited resources
are registered. For this reason it is not possible to validate whether a
resource actually exists or not when enabling tracking for it. Developers
should pay particular attention into ensuring resource names are correctly
specified.
* The code assumes usage trackers are a trusted source of truth: if they
report a usage counter and the dirty bit is not set, that counter is
correct. If it's dirty than surely that counter is out of sync.
This is not very robust, as there might be issues upon restart when toggling
the use_tracked_resources configuration variable, as stale counters might be
trusted upon for making reservations. Also, the same situation might occur
if a server crashes after the API operation is completed but before the
reservation is committed, as the actual resource usage is changed but
the corresponding usage tracker is not marked as dirty.
References
----------