This guide will walk you through how to add resources for a service.
Above all, names across this project conform to Python’s naming standards, as laid out in PEP 8.
The relevant details we need to know are as follows:
- Module names are lower case, and separated by underscores if more than one word. For example,
openstack.object_store
- Class names are capitalized, with no spacing, and each subsequent word is capitalized in a name. For example,
ServerMetadata
.- Attributes on classes, including methods, are lower case and separated by underscores. For example,
allow_list
orget_data
.
Services in the OpenStack SDK are named after their program name, not their code name. For example, the project often known as “Nova” is always called “compute” within this SDK.
This guide walks through creating service for an OpenStack program called
“Fake”. Following our guidelines, the code for its service would
live under the openstack.fake
namespace. What follows is the creation
of a Resource
class for the “Fake” service.
Resources are named after the server-side resource, which is set in the
base_path
attribute of the resource class. This guide creates a
resouce class for the /fake
server resource, so the resource module
is called fake.py
and the class is called Fake
.
openstack/fake/fake_service.py
1 2 3 4 5 6 7 8 9 10 11 12 13 | # Apache 2 header omitted for brevity
from openstack import service_filter
class FakeService(service_filter.ServiceFilter):
"""The fake service."""
valid_versions = [service_filter.ValidVersion('v2')]
def __init__(self, version=None):
"""Create a fake service."""
super(FakeService, self).__init__(service_type='fake', version=version)
|
openstack/fake/v2/fake.py
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 | # Apache 2 header omitted for brevity
from openstack.fake import fake_service
from openstack import resource
class Fake(resource.Resource):
resource_key = "resource"
resources_key = "resources"
base_path = "/fake"
service = fake_service.FakeService()
id_attribute = "name"
allow_create = True
allow_retrieve = True
allow_update = True
allow_delete = True
allow_list = True
allow_head = True
#: The transaction date and time.
timestamp = resource.prop("x-timestamp")
#: The name of this resource.
name = resource.prop("name")
#: The value of the resource. Also available in headers.
value = resource.prop("value", alias="x-resource-value")
#: Is this resource cool? If so, set it to True.
#: This is a multi-line comment about cool stuff.
cool = resource.prop("cool", type=bool)
|
fake.Fake
Attributes¶Each service’s resources inherit from Resource
,
so they can override any of the base attributes to fit the way their
particular resource operates.
resource_key
and resources_key
¶These attributes are set based on how your resource responds with data.
The default values for each of these are None
, which works fine
when your resource returns a JSON body that can be used directly without a
top-level key, such as {"name": "Ernie Banks", ...}"
.
However, our Fake
resource returns JSON bodies that have the details of
the resource one level deeper, such as
{"resources": {"name": "Ernie Banks", ...}, {...}}
. It does a similar
thing with single resources, putting them inside a dictionary keyed on
"resource"
.
By setting Fake.resource_key
on line 8, we tell the Resource.create
,
Resource.get
, and Resource.update
methods that we’re either sending
or receiving a resource that is in a dictionary with that key.
By setting Fake.resources_key
on line 9, we tell the Resource.list
method that we’re expecting to receive multiple resources inside a dictionary
with that key.
base_path
¶The base_path
is the URL we’re going to use to make requests for this
resource. In this case, line 10 sets base_path = "/fake"
, which also
corresponds to the name of our class, Fake
.
Most resources follow this basic formula. Some cases are more complex, where
the URL to make requests to has to contain some extra data. The volume service
has several resources which make either basic requests or detailed requests,
so they use base_path = "/volumes/%s(detailed)"
. Before a request is made,
if detailed = True
, they convert it to a string so the URL becomes
/volumes/detailed
. If it’s False
, they only send /volumes/
.
service
¶Line 11 is an instance of the service we’re implementing. Each resource ties itself to the service through this setting, so that the proper URL can be constructed.
In fake_service.py
, we specify the valid versions as well as what this
service is called in the service catalog. When a request is made for this
resource, the Session now knows how to construct the appropriate URL using
this FakeService
instance.
id_attribute
¶Line 12 specifies that this resource uses a different identifier than
the default of id
. While IDs are used internally, such as for creating
request URLs to interact with an individual resource, they are exposed for
consistency so users always have one place to find the resource’s identity.
The base Resource
disallows all types of requests
by default, requiring each resource to specify which requests they support.
On lines 14-19, our Fake
resource specifies that it’ll work with all
of the operations.
In order to have the following methods work, you must allow the corresponding
value by setting it to True
:
create |
allow_create |
delete |
allow_delete |
head |
allow_head |
list |
allow_list |
get |
allow_retrieve |
update |
allow_update |
An additional attribute to set is put_update
if your service uses PUT
requests in order to update a resource. By default, PATCH
requests are
used for Resource.update
.
The way resource classes communicate values between the user and the server
are prop
objects. These act similarly to Python’s
built-in property objects, but they share only the name - they’re not the same.
Properties are set based on the contents of a response body or headers.
Based on what your resource returns, you should set prop
s to map
those those values to ones on your Resource
object.
Line 22 sets a prop for timestamp
, which will cause the
Fake.timestamp
attribute to contain the value returned in an
X-Timestamp
header, such as from a Fake.head
request.
Line 24 sets a prop for name
, which is a value returned in a body, such
as from a Fake.get
request. Note from line 12 that name
is
specified its id
attribute, so when this resource
is populated from a response, Fake.name
and Fake.id
are the same
value.
Line 26 sets a prop which contains an alias. Fake.value
will be set
when a response body contains a value
, or when a header contains
X-Resource-Value
.
Line 28 specifies a type to be checked before sending the value in a request.
In this case, we can only set Fake.cool
to either True
or False
,
otherwise a TypeError will be raised if the value can’t be converted to the
expected type.
We use Sphinx’s autodoc
feature in order to build API documentation for
each resource we expose. The attributes we override from
Resource
don’t need to be documented, but any
prop
attributes must be. All you need to do is
add a comment above the line to document, with a colon following the
pound-sign.
Lines 21, 23, 25, and 27-28 are comments which will then appear in the API documentation. As shown in lines 27 & 28, these comments can span multiple lines.
Except where otherwise noted, this document is licensed under Creative Commons Attribution 3.0 License. See all OpenStack Legal Documents.