diff --git a/doc/source/architecture.rst b/doc/source/architecture.rst index 41a559f4..669b5e3a 100644 --- a/doc/source/architecture.rst +++ b/doc/source/architecture.rst @@ -49,14 +49,14 @@ Following components are present in the Glance architecture: * **REST API** - Glance functionalities are exposed via REST. * **Database Abstraction Layer (DAL)** - an application programming interface -(API) that unifies the communication between Glance and databases. + (API) that unifies the communication between Glance and databases. * **Glance Domain Controller** - middleware that implements the main -Glance functionalities such as authorization, notifications, policies, -database connections. + Glance functionalities such as authorization, notifications, policies, + database connections. * **Glance Store** - used to organize interactions between Glance and various -data stores. + data stores. * **Registry Layer** - optional layer that is used to organise secure -communication between the domain and the DAL by using a separate service. + communication between the domain and the DAL by using a separate service. diff --git a/doc/source/authentication.rst b/doc/source/authentication.rst index efe9ec16..09c92283 100644 --- a/doc/source/authentication.rst +++ b/doc/source/authentication.rst @@ -40,6 +40,7 @@ information. In order to configure Glance to use Keystone, the the authentication token validation and retrieves actual user authentication information. It can be found in the Keystone distribution. + Configuring Glance API to use Keystone -------------------------------------- @@ -59,8 +60,11 @@ an example for ``authtoken``:: The actual values for these variables will need to be set depending on your situation. For more information, please refer to the Keystone -`documentation`_ on the ``auth_token`` middleware, but in short: -.. _documentation http://docs.openstack.org/developer/keystonemiddleware/middlewarearchitecture.html#configuration +`documentation`_ on the ``auth_token`` middleware. + +.. _`documentation`: http://docs.openstack.org/developer/keystonemiddleware/middlewarearchitecture.html#configuration + +In short: * The ``auth_url`` variable points to the Keystone service. This information is used by the middleware to actually query Keystone about @@ -83,6 +87,7 @@ with ``authtoken`` and ``context``:: [pipeline:glance-api] pipeline = versionnegotiation authtoken context apiv1app + Configuring Glance Registry to use Keystone ------------------------------------------- diff --git a/doc/source/configuring.rst b/doc/source/configuring.rst index 18e118eb..aec3a46e 100644 --- a/doc/source/configuring.rst +++ b/doc/source/configuring.rst @@ -1718,10 +1718,10 @@ deployment. Without HMAC key the profiling will not be triggered even profiling feature is enabled. **IMPORTANT NOTE**: previously HMAC keys (as well as enabled parameter) were -placed at /etc/glance/api-paste.ini and /etc/glance/registry-paste.ini files +placed at `/etc/glance/api-paste.ini` and `/etc/glance/registry-paste.ini` files for Glance API and Glance Registry services respectively. Starting with opsrofiler 0.3.1 release there is no need to set these arguments in the -*-paste.ini files. This functionality is still supported, although the +`*-paste.ini` files. This functionality is still supported, although the config values are having larger priority. The config value ``trace_sqlalchemy`` is used to determine whether fully enable diff --git a/doc/source/contributing/blueprints.rst b/doc/source/contributing/blueprints.rst index f6c09cf9..c6d4b0d6 100644 --- a/doc/source/contributing/blueprints.rst +++ b/doc/source/contributing/blueprints.rst @@ -55,14 +55,16 @@ The workflow for the life of a spec-lite in Launchpad is as follows: * File a bug with a small summary of what the request change is following the format below: -.. NOTE: add format -* The bug is triaged and tagged with the `spec-lite` tag. -* The bug is evaluated and marked as `Triaged` to announce approval or - to `Won't fix` to announce rejection or `Invalid` to request a full - spec. -* The bug is moved to `In Progress` once the code is up and ready to - review. -* The bug is moved to `Fix Committed` once the patch lands. + + .. NOTE: add format + + * The bug is triaged and tagged with the `spec-lite` tag. + * The bug is evaluated and marked as `Triaged` to announce approval or + to `Won't fix` to announce rejection or `Invalid` to request a full + spec. + * The bug is moved to `In Progress` once the code is up and ready to + review. + * The bug is moved to `Fix Committed` once the patch lands. In summary: @@ -80,9 +82,9 @@ In summary: The drivers team will be discussing the following bug reports during their IRC meeting: -* `New RFE's `_ -* `New RFE's `_ -* `New RFE's `_ +* `New Glance RFE's `_ +* `New Glance-Store RFE's `_ +* `New Glanceclient RFE's `_ Lite spec Submission Guidelines diff --git a/doc/source/glanceapi.rst b/doc/source/glanceapi.rst index 8b5523c6..f82e9ce7 100644 --- a/doc/source/glanceapi.rst +++ b/doc/source/glanceapi.rst @@ -362,9 +362,9 @@ The first call should be a ``POST`` to ``http://glance.example.com/v1/images``, which will result in a new image id being registered with a status of ``queued``:: - {"image": - {"status": "queued", - "id": "71c675ab-d94f-49cd-a114-e12490b328d9", + {'image': + {'status': 'queued', + 'id': '71c675ab-d94f-49cd-a114-e12490b328d9', ...} ...} diff --git a/glance/api/v1/images.py b/glance/api/v1/images.py index f9247631..4b60c875 100644 --- a/glance/api/v1/images.py +++ b/glance/api/v1/images.py @@ -332,16 +332,19 @@ class Controller(controller.BaseController): * size -- Size of image data in bytes :param req: The WSGI/Webob Request object - :retval The response body is a mapping of the following form:: + :returns: The response body is a mapping of the following form + + .. code-block:: json {'images': [ {'id': , 'name': , 'disk_format': , 'container_format': , - 'checksum': - 'size': }, ... - ]} + 'checksum': , + 'size': }, {...}] + } + """ self._enforce(req, 'get_images') params = self._get_query_params(req) @@ -357,24 +360,29 @@ class Controller(controller.BaseController): Returns detailed information for all available images :param req: The WSGI/Webob Request object - :retval The response body is a mapping of the following form:: + :returns: The response body is a mapping of the following form + + .. code-block:: json + + {'images': + [{ + 'id': , + 'name': , + 'size': , + 'disk_format': , + 'container_format': , + 'checksum': , + 'min_disk': , + 'min_ram': , + 'store': , + 'status': , + 'created_at': , + 'updated_at': , + 'deleted_at': |, + 'properties': {'distro': 'Ubuntu 10.04 LTS', {...}} + }, {...}] + } - {'images': [ - {'id': , - 'name': , - 'size': , - 'disk_format': , - 'container_format': , - 'checksum': , - 'min_disk': , - 'min_ram': , - 'store': , - 'status': , - 'created_at': , - 'updated_at': , - 'deleted_at': |, - 'properties': {'distro': 'Ubuntu 10.04 LTS', ...}}, ... - ]} """ if req.method == 'HEAD': msg = (_("This operation is currently not permitted on " @@ -405,7 +413,7 @@ class Controller(controller.BaseController): Extracts necessary query params from request. :param req: the WSGI Request object - :retval dict of parameters that can be used by registry client + :returns: dict of parameters that can be used by registry client """ params = {'filters': self._get_filters(req)} @@ -423,7 +431,7 @@ class Controller(controller.BaseController): Return a dictionary of query param filters from the request :param req: the Request object coming from the wsgi layer - :retval a dict of key/value filters + :returns: a dict of key/value filters """ query_filters = {} for param in req.params: @@ -443,7 +451,7 @@ class Controller(controller.BaseController): :param req: The WSGI/Webob Request object :param id: The opaque image identifier - :retval similar to 'show' method but without image_data + :returns: similar to 'show' method but without image_data :raises: HTTPNotFound if image metadata is not available to user """ @@ -634,7 +642,7 @@ class Controller(controller.BaseController): :param image_meta: Mapping of metadata about image :raises: HTTPConflict if image already exists - :retval The location where the image was stored + :returns: The location where the image was stored """ scheme = req.headers.get('x-image-meta-store', @@ -734,7 +742,7 @@ class Controller(controller.BaseController): :param req: The WSGI/Webob Request object :param image_meta: Mapping of metadata about image - :retval Mapping of updated image data + :returns: Mapping of updated image data """ location_data = self._upload(req, image_meta) image_id = image_meta['id'] @@ -938,7 +946,7 @@ class Controller(controller.BaseController): :param request: The WSGI/Webob Request object :param id: The opaque image identifier - :retval Returns the updated image information as a mapping + :returns: Returns the updated image information as a mapping """ self._enforce(req, 'modify_image') is_public = image_meta.get('is_public') diff --git a/glance/api/v1/members.py b/glance/api/v1/members.py index de4fa78a..2e9416cb 100644 --- a/glance/api/v1/members.py +++ b/glance/api/v1/members.py @@ -62,12 +62,15 @@ class Controller(controller.BaseController): :param req: the Request object coming from the wsgi layer :param image_id: The opaque image identifier - :retval The response body is a mapping of the following form:: + :returns: The response body is a mapping of the following form + + .. code-block:: json {'members': [ {'member_id': , 'can_share': , ...}, ... ]} + """ self._enforce(req, 'get_members') self._raise_404_if_image_deleted(req, image_id) @@ -128,13 +131,15 @@ class Controller(controller.BaseController): def update(self, req, image_id, id, body=None): """ Adds a membership to the image, or updates an existing one. - If a body is present, it is a dict with the following format:: + If a body is present, it is a dict with the following format - {"member": { - "can_share": [True|False] + .. code-block:: json + + {'member': { + 'can_share': [True|False] }} - If "can_share" is provided, the member's ability to share is + If `can_share` is provided, the member's ability to share is set accordingly. If it is not provided, existing memberships remain unchanged and new memberships default to False. """ @@ -169,12 +174,15 @@ class Controller(controller.BaseController): def update_all(self, req, image_id, body): """ Replaces the members of the image with those specified in the - body. The body is a dict with the following format:: + body. The body is a dict with the following format - {"memberships": [ - {"member_id": , - ["can_share": [True|False]]}, ... + .. code-block:: json + + {'memberships': [ + {'member_id': , + ['can_share': [True|False]]}, ... ]} + """ self._check_can_access_image_members(req.context) self._enforce(req, 'modify_member') @@ -206,12 +214,15 @@ class Controller(controller.BaseController): :param req: the Request object coming from the wsgi layer :param id: the opaque member identifier - :retval The response body is a mapping of the following form:: + :returns: The response body is a mapping of the following form + + .. code-block:: json {'shared_images': [ {'image_id': , 'can_share': , ...}, ... ]} + """ try: members = registry.get_member_images(req.context, id) diff --git a/glance/api/v2/image_members.py b/glance/api/v2/image_members.py index 7a18c8fe..4219949e 100644 --- a/glance/api/v2/image_members.py +++ b/glance/api/v2/image_members.py @@ -95,7 +95,9 @@ class ImageMembersController(object): :param req: the Request object coming from the wsgi layer :param image_id: the image identifier :param member_id: the member identifier - :retval The response body is a mapping of the following form:: + :returns: The response body is a mapping of the following form + + .. code-block:: json {'member_id': , 'image_id': , @@ -137,11 +139,13 @@ class ImageMembersController(object): :param req: the Request object coming from the wsgi layer :param image_id: the image identifier :param member_id: the member identifier - :retval The response body is a mapping of the following form:: + :returns: The response body is a mapping of the following form + + .. code-block:: json {'member_id': , 'image_id': , - 'status': + 'status': , 'created_at': .., 'updated_at': ..} @@ -170,15 +174,18 @@ class ImageMembersController(object): :param req: the Request object coming from the wsgi layer :param image_id: The image identifier - :retval The response body is a mapping of the following form:: + :returns: The response body is a mapping of the following form + + .. code-block:: json {'members': [ {'member_id': , 'image_id': , - 'status': + 'status': , 'created_at': .., 'updated_at': ..}, .. ]} + """ image = self._lookup_image(req, image_id) member_repo = self._get_member_repo(req, image) @@ -198,13 +205,16 @@ class ImageMembersController(object): :param req: the Request object coming from the wsgi layer :param image_id: The image identifier - :retval The response body is a mapping of the following form:: + :returns: The response body is a mapping of the following form + + .. code-block:: json {'member_id': , 'image_id': , 'status': 'created_at': .., 'updated_at': ..} + """ try: image = self._lookup_image(req, image_id) diff --git a/glance/async/flows/ovf_process.py b/glance/async/flows/ovf_process.py index e77498b4..920fa810 100644 --- a/glance/async/flows/ovf_process.py +++ b/glance/async/flows/ovf_process.py @@ -68,6 +68,7 @@ class _OVF_Process(task.Task): def _get_ova_iter_objects(self, uri): """Returns iterable object either for local file or uri + :param uri: uri (remote or local) to the ova package we want to iterate """ @@ -134,9 +135,10 @@ class OVAImageExtractor(object): Extracts a single disk image and OVF from OVA tar archive and calls OVF parser method. + :param ova: a file object containing the OVA file :returns: a tuple of extracted disk file object and dictionary of - properties parsed from the OVF file + properties parsed from the OVF file :raises: RuntimeError for malformed OVA and OVF files """ with tarfile.open(fileobj=ova) as tar_file: @@ -163,6 +165,7 @@ class OVAImageExtractor(object): The OVF file's qualified namespaces are removed from the included properties. + :param ovf: a file object containing the OVF file :returns: a tuple of disk filename and a properties dictionary :raises: RuntimeError for malformed OVF file diff --git a/glance/common/client.py b/glance/common/client.py index 73186f33..e35eaa5e 100644 --- a/glance/common/client.py +++ b/glance/common/client.py @@ -584,7 +584,7 @@ class BaseClient(object): :param actual_params: dict of keys to filter :param allowed_params: list of keys that 'actual_params' will be reduced to - :retval subset of 'params' dict + :returns: subset of 'params' dict """ try: # expect 'filters' param to be a dict here diff --git a/glance/common/rpc.py b/glance/common/rpc.py index ff9582f0..7d9d0606 100644 --- a/glance/common/rpc.py +++ b/glance/common/rpc.py @@ -86,6 +86,8 @@ class Controller(object): This is the base controller for RPC based APIs. Commands handled by this controller respect the following form: + .. code-block:: json + [{ 'command': 'method_name', 'kwargs': {...} @@ -94,8 +96,9 @@ class Controller(object): The controller is capable of processing more than one command per request and will always return a list of results. - :params raise_exc: Boolean that specifies whether to raise - exceptions instead of "serializing" them. + :param bool raise_exc: Specifies whether to raise + exceptions instead of "serializing" them. + """ def __init__(self, raise_exc=False): @@ -106,13 +109,14 @@ class Controller(object): """ Exports methods through the RPC Api. - :params resource: Resource's instance to register. - :params filtered: List of methods that *can* be registered. Read - as "Method must be in this list". - :params excluded: List of methods to exclude. - :params refiner: Callable to use as filter for methods. + :param resource: Resource's instance to register. + :param filtered: List of methods that *can* be registered. Read + as "Method must be in this list". + :param excluded: List of methods to exclude. + :param refiner: Callable to use as filter for methods. :raises TypeError: If refiner is not callable. + """ funcs = filter(lambda x: not x.startswith("_"), dir(resource)) @@ -216,13 +220,16 @@ class RPCClient(client.BaseClient): """ Execute multiple commands in a single request. - :params commands: List of commands to send. Commands - must respect the following form: + :param commands: List of commands to send. Commands + must respect the following form + + .. code-block:: json { 'command': 'method_name', 'kwargs': method_kwargs } + """ body = self._serializer.to_json(commands) response = super(RPCClient, self).do_request('POST', @@ -236,8 +243,8 @@ class RPCClient(client.BaseClient): the outgoing body and builds the command that will be sent. - :params method: The remote python method to call - :params kwargs: Dynamic parameters that will be + :param method: The remote python method to call + :param kwargs: Dynamic parameters that will be passed to the remote method. """ content = self.bulk_request([{'command': method, diff --git a/glance/db/sqlalchemy/migrate_repo/schema.py b/glance/db/sqlalchemy/migrate_repo/schema.py index b7d93e7b..9d40ea64 100644 --- a/glance/db/sqlalchemy/migrate_repo/schema.py +++ b/glance/db/sqlalchemy/migrate_repo/schema.py @@ -61,7 +61,7 @@ def from_migration_import(module_name, fromlist): :param module_name: name of migration module to import from (ex: 001_add_images_table) :param fromlist: list of items to import (ex: define_images_table) - :retval: module object + :returns: module object This bit of ugliness warrants an explanation: diff --git a/glance/image_cache/__init__.py b/glance/image_cache/__init__.py index 21e209c7..5b6dc1c0 100644 --- a/glance/image_cache/__init__.py +++ b/glance/image_cache/__init__.py @@ -284,7 +284,7 @@ class ImageCache(object): :param image_file: Iterator retrieving image chunks :param image_checksum: Checksum of image - :retval True if image file was cached, False otherwise + :returns: True if image file was cached, False otherwise """ if not self.driver.is_cacheable(image_id): return False @@ -301,7 +301,7 @@ class ImageCache(object): :param image_id: Image ID :param image_file: Image file to cache - :retval True if image file was cached, False otherwise + :returns: True if image file was cached, False otherwise """ CHUNKSIZE = 64 * units.Mi diff --git a/glance/location.py b/glance/location.py index 47c2a239..0472c45d 100644 --- a/glance/location.py +++ b/glance/location.py @@ -161,10 +161,12 @@ class ImageFactoryProxy(glance.domain.proxy.ImageFactory): class StoreLocations(collections.MutableSequence): """ - The proxy for store location property. It takes responsibility for: + The proxy for store location property. It takes responsibility for:: + 1. Location uri correctness checking when adding a new location. 2. Remove the image data from the store when a location is removed from an image. + """ def __init__(self, image_proxy, value): self.image_proxy = image_proxy diff --git a/glance/registry/api/v1/images.py b/glance/registry/api/v1/images.py index cc4380e1..4bc070ef 100644 --- a/glance/registry/api/v1/images.py +++ b/glance/registry/api/v1/images.py @@ -138,20 +138,25 @@ class Controller(object): """Return a basic filtered list of public, non-deleted images :param req: the Request object coming from the wsgi layer - :retval a mapping of the following form:: + :returns: a mapping of the following form + + .. code-block:: python dict(images=[image_list]) - Where image_list is a sequence of mappings:: + Where image_list is a sequence of mappings + + .. code-block:: json { - 'id': , - 'name': , - 'size': , - 'disk_format': , - 'container_format': , - 'checksum': + 'id': , + 'name': , + 'size': , + 'disk_format': , + 'container_format': , + 'checksum': } + """ params = self._get_query_params(req) images = self._get_images(req.context, **params) @@ -170,12 +175,29 @@ class Controller(object): """Return a filtered list of public, non-deleted images in detail :param req: the Request object coming from the wsgi layer - :retval a mapping of the following form:: + :returns: a mapping of the following form - dict(images=[image_list]) + .. code-block:: json + + {'images': + [{ + 'id': , + 'name': , + 'size': , + 'disk_format': , + 'container_format': , + 'checksum': , + 'min_disk': , + 'min_ram': , + 'store': , + 'status': , + 'created_at': , + 'updated_at': , + 'deleted_at': |, + 'properties': {'distro': 'Ubuntu 10.04 LTS', {...}} + }, {...}] + } - Where image_list is a sequence of mappings containing - all image model fields. """ params = self._get_query_params(req) @@ -188,7 +210,7 @@ class Controller(object): """Extract necessary query parameters from http request. :param req: the Request object coming from the wsgi layer - :retval dictionary of filters to apply to list of images + :returns: dictionary of filters to apply to list of images """ params = { 'filters': self._get_filters(req), @@ -217,7 +239,7 @@ class Controller(object): """Return a dictionary of query param filters from the request :param req: the Request object coming from the wsgi layer - :retval a dict of key/value filters + :returns: a dict of key/value filters """ filters = {} properties = {} @@ -360,8 +382,9 @@ class Controller(object): :param req: wsgi Request object :param id: The opaque internal identifier for the image - :retval Returns 200 if delete was successful, a fault if not. On - success, the body contains the deleted image information as a mapping. + :returns: 200 if delete was successful, a fault if not. On + success, the body contains the deleted image + information as a mapping. """ try: deleted_image = self.db_api.image_destroy(req.context, id) @@ -390,9 +413,9 @@ class Controller(object): :param req: wsgi Request object :param body: Dictionary of information about the image - :retval Returns the newly-created image information as a mapping, - which will include the newly-created image's internal id - in the 'id' field + :returns: The newly-created image information as a mapping, + which will include the newly-created image's internal id + in the 'id' field """ image_data = body['image'] @@ -441,7 +464,7 @@ class Controller(object): :param body: Dictionary of information about the image :param id: The opaque internal identifier for the image - :retval Returns the updated image information as a mapping, + :returns: Returns the updated image information as a mapping, """ image_data = body['image'] from_state = body.get('from_state', None) diff --git a/glance/registry/api/v1/members.py b/glance/registry/api/v1/members.py index 5c542dd5..eb987d5c 100644 --- a/glance/registry/api/v1/members.py +++ b/glance/registry/api/v1/members.py @@ -88,9 +88,9 @@ class Controller(object): Replaces the members of the image with those specified in the body. The body is a dict with the following format:: - {"memberships": [ - {"member_id": , - ["can_share": [True|False]]}, ... + {'memberships': [ + {'member_id': , + ['can_share': [True|False]]}, ... ]} """ self._check_can_access_image_members(req.context) @@ -205,11 +205,11 @@ class Controller(object): Adds a membership to the image, or updates an existing one. If a body is present, it is a dict with the following format:: - {"member": { - "can_share": [True|False] + {'member': { + 'can_share': [True|False] }} - If "can_share" is provided, the member's ability to share is + If `can_share` is provided, the member's ability to share is set accordingly. If it is not provided, existing memberships remain unchanged and new memberships default to False. """ diff --git a/glance/scrubber.py b/glance/scrubber.py index b3bc529c..67f4fb6c 100644 --- a/glance/scrubber.py +++ b/glance/scrubber.py @@ -113,7 +113,7 @@ class ScrubDBQueue(object): :param image_id: The opaque image identifier :param location: The opaque image location - :retval A boolean value to indicate success or not + :returns: A boolean value to indicate success or not """ loc_id = location.get('id') if loc_id: @@ -151,7 +151,9 @@ class ScrubDBQueue(object): def get_all_locations(self): """Returns a list of image id and location tuple from scrub queue. - :retval a list of image id, location id and uri tuple from scrub queue + :returns: a list of image id, location id and uri tuple from + scrub queue + """ ret = [] @@ -187,7 +189,7 @@ class ScrubDBQueue(object): :param image_id: The opaque image identifier - :retval a boolean value to inform including or not + :returns: a boolean value to inform including or not """ try: image = self.registry.get_image(image_id) diff --git a/glance/tests/functional/v1/test_api.py b/glance/tests/functional/v1/test_api.py index 941a5868..cb6bc33f 100644 --- a/glance/tests/functional/v1/test_api.py +++ b/glance/tests/functional/v1/test_api.py @@ -608,20 +608,21 @@ class TestApi(functional.FunctionalTest): def test_download_non_exists_image_raises_http_forbidden(self): """ - We test the following sequential series of actions: + We test the following sequential series of actions:: 0. POST /images with public image named Image1 - and no custom properties - - Verify 201 returned + and no custom properties + - Verify 201 returned 1. HEAD image - - Verify HTTP headers have correct information we just added + - Verify HTTP headers have correct information we just added 2. GET image - - Verify all information on image we just added is correct + - Verify all information on image we just added is correct 3. DELETE image1 - - Delete the newly added image + - Delete the newly added image 4. GET image - - Verify that 403 HTTPForbidden exception is raised prior to - 404 HTTPNotFound + - Verify that 403 HTTPForbidden exception is raised prior to + 404 HTTPNotFound + """ self.cleanup() self.start_servers(**self.__dict__.copy()) diff --git a/glance/tests/integration/legacy_functional/test_v1_api.py b/glance/tests/integration/legacy_functional/test_v1_api.py index 035cf811..696d4dd2 100644 --- a/glance/tests/integration/legacy_functional/test_v1_api.py +++ b/glance/tests/integration/legacy_functional/test_v1_api.py @@ -228,7 +228,7 @@ class TestApi(base.ApiTest): 0. GET /images - Verify no public images 1. POST /images with public image named Image1 with no location - attribute and no image data. + attribute and no image data. - Verify 201 returned 2. GET /images - Verify one public image diff --git a/glance/tests/unit/test_glare_plugin_loader.py b/glance/tests/unit/test_glare_plugin_loader.py index 3aac3120..752e8e45 100644 --- a/glance/tests/unit/test_glare_plugin_loader.py +++ b/glance/tests/unit/test_glare_plugin_loader.py @@ -138,9 +138,10 @@ class TestArtifactsLoader(utils.BaseTestCase): """ A test to show that plugin-load specific options in artifacts.conf are correctly processed: - * no plugins can be loaded if load_enabled = False - * if available_plugins list is given only plugins specified can be - be loaded + + * no plugins can be loaded if load_enabled = False + * if available_plugins list is given only plugins specified can be + be loaded """ self.config(load_enabled=False) self.assertRaises(exception.ArtifactLoadError,