Creating a Proxy¶
This guide walks you through adding a proxy for a service. It builds on the
Creating a New Resource guide: where a Resource maps onto
a single server-side resource, a Proxy provides the
higher-level, user-facing interface for a whole service. A Proxy is what a
user reaches through a Connection - for example,
conn.compute is the compute service’s Proxy.
Layout¶
Each service implements a single Proxy class per API version, in a module
named _proxy.py:
openstack/<service>/v<N>/_proxy.py- the proxy, e.g.openstack/compute/v2/_proxy.py.openstack/<service>/v<N>/<resource>.py- one module per resource type that the proxy operates on, e.g.flavor.py,server.py.
The class is always named Proxy and subclasses
openstack.proxy.Proxy. It declares an api_version discriminator
and imports each resource module under an underscore-prefixed alias to avoid
namespace collisions:
from typing import ClassVar, Literal
from openstack.fake.v2 import fake as _fake
from openstack import proxy
class Proxy(proxy.Proxy):
api_version: ClassVar[Literal['2']] = '2'
Base Helpers¶
openstack.proxy.Proxy subclasses
keystoneauth1.adapter.Adapter - so a proxy is an authenticated HTTP
client - and adds a set of generic helpers that delegate to the corresponding
methods on your Resource classes. Each takes the
resource class as its first argument:
_createCreate a resource from keyword attributes.
_getFetch a single resource by ID or instance.
_listReturn a generator over resources, with pagination and query filtering.
_findLook a resource up by name or ID.
_updateCommit changes to a resource.
_deleteDelete a resource.
_headIssue a
HEADrequest for a resource._get_resourceNormalize an ID, dict, or existing instance into a
Resourceinstance. Used directly when a method needs an instance to call a resource action on.
Because these helpers do the work, most proxy methods are thin wrappers that select the right resource class and pass the caller’s arguments through.
Methods¶
Proxy methods follow a consistent naming and shape. A create, get, list, and
delete for a Fake resource look like this:
def create_fake(self, **attrs):
"""Create a new fake from attributes
:param attrs: Keyword arguments which will be used to create a
:class:`~openstack.fake.v2.fake.Fake`, comprised of the properties
on the Fake class.
:returns: The results of fake creation.
"""
return self._create(_fake.Fake, **attrs)
def get_fake(self, fake):
"""Get a single fake
:param fake: The value can be the ID of a fake or a
:class:`~openstack.fake.v2.fake.Fake` instance.
:returns: One :class:`~openstack.fake.v2.fake.Fake`
:raises: :class:`~openstack.exceptions.NotFoundException` when no
resource can be found.
"""
return self._get(_fake.Fake, fake)
def fakes(self, **query):
"""Return a generator of fakes
:param query: Optional query parameters to be sent to limit the
resources being returned.
:returns: A generator of fake objects.
"""
return self._list(_fake.Fake, **query)
def delete_fake(self, fake, ignore_missing=True):
"""Delete a fake
:param fake: The value can be either the ID of a fake or a
:class:`~openstack.fake.v2.fake.Fake` instance.
:param ignore_missing: When set to ``False``
:class:`~openstack.exceptions.NotFoundException` will be raised
when the fake does not exist. When set to ``True``, no exception
will be raised when attempting to delete a nonexistent fake.
:returns: ``None``
"""
self._delete(_fake.Fake, fake, ignore_missing=ignore_missing)
Note the conventions:
Methods that accept a resource take “either the ID … or a
instance”, and document it that way.Deletes and finds take an
ignore_missingargument, defaulting toTrue.List methods are named after the plural noun (
fakes) and return a generator; the other methods are named<verb>_<resource>.Docstrings use Sphinx field lists (
:param:,:returns:,:raises:) and cross-reference the concrete resource class with:class:`~...`so only the short name renders.
For actions that are not simple CRUD, use _get_resource to obtain an
instance and then call a method on the resource itself.
Wiring the Proxy to a Connection¶
A proxy is exposed on the Connection through a
ServiceDescription, as introduced in
the Creating a New Resource guide. The description maps each supported API version to
its proxy class:
from openstack.fake.v2 import _proxy
from openstack import service_description
class FakeService(service_description.ServiceDescription):
"""The fake service."""
supported_versions = {
'2': _proxy.Proxy,
}
Finally, the service must be registered so it becomes an attribute on every
Connection. The registry lives in the
generated file openstack/_services_mixin.py, where each service is
declared with the connection attribute name and its service_type:
fake = fake_service.FakeService(service_type='fake')
Do not edit openstack/_services_mixin.py by hand - regenerate it by running
tools/print-services.py. Once registered, conn.fake returns a lazily
constructed instance of your Proxy.