MAAS contains an RPC mechanism such that every process in the region is connected to every process in the cluster (strictly, every pserv process). It’s based on AMP, specifically Twisted’s implementation.
Start in the provisioningserver.rpc package. The first two files to look at are cluster.py and region.py. This contain the declarations of what commands are available on clusters and regions respectively.
A new command could be declared like so:
from twisted.protocols import amp
class EatCheez(amp.Command):
arguments = [
(b"name", amp.Unicode()),
(b"origin", amp.Unicode()),
]
response = [
(b"rating", amp.Integer()),
]
It’s also possible to map exceptions across the wire using an errors attribute; see the docs or code for more information.
Note that byte-strings are used for parameter names. Twisted gets quite fussy about this, so remember to do it.
To implement a new command on the cluster, see the class provisioningserver.rpc.clusterserver.Cluster. A method decorated with @cluster.EatCheez.responder is the implementation of the EatCheez command. There’s no trick to this, they’re just plain old functions. However:
To implement a new command on the region, see the class maasserver.rpc.regionserver.Region. It works the same.
There’s a convenient API in maasserver.rpc:
The clients returned are designed to be used in either the reactor thread or in another thread; when called from the latter, a crochet.EventualResult will be returned.
You need to get a handle to the rpc service that will have been started by twistd.
Probably the best way to do this is implement the behaviour you want as a new service, start it up via same mechanism as the rpc service (see provisioningserver.plugin, and pass over a reference.
Then call getClient(), and you will get a client for calling into a region process. You’re given a random client.
A utility function – gather() – helps here. An example:
from functools import partial
from maasserver.rpc import getAllClients
from maasserver.utils import async
from twisted.python.failure import Failure
# Wrap those calls you want to make into no-argument callables, but
# don't call them yet.
calls = [
partial(client, EatCheez)
for client in getAllClients()
]
# Use gather() to issue all the calls simultaneously and process the
# results as they come in. Note that responses can be failures too.
for response in async.gather(calls, timeout=10):
if isinstance(response, Failure):
pass # Do something sensible with this.
else:
celebrate_a_cheesy_victory(response)
Responses can be processed as soon as they come in. Any responses not received within timeout seconds will be discarded.
Event-loop support for the MAAS Region Controller.
This helps start up a background event loop (using Twisted, via crochet) to handle communications with Cluster Controllers, and any other tasks that are not tied to an HTTP reqoest.
The single instance of RegionEventLoop that’s all a process needs.
The MultiService which forms the root of this process’s service tree.
This is a convenient reference to loop.services.
RPC declarations for the region.
These are commands that a region controller ought to respond to.
Bases: twisted.protocols.amp.Command
Report boot images available on the invoking cluster controller.
Bases: twisted.protocols.amp.Command
Request the identity of the remote side, e.g. its UUID.
Bases: twisted.protocols.amp.AMP
The RPC protocol supported by a region controller.
This can be used on the client or server end of a connection; once a connection is established, AMP is symmetric.
Implementation of ReportBootImages.
RPC declarations for clusters.
These are commands that a cluster controller ought to respond to.
Bases: twisted.protocols.amp.Command
List the boot images available on this cluster controller.
Bases: twisted.protocols.amp.Command
Get a JSON Schema describing this cluster’s power types.
Bases: twisted.protocols.amp.Command
Request the identity of the remote side, e.g. its UUID.
Bases: twisted.protocols.amp.AMP, object
The RPC protocol supported by a cluster controller.
This can be used on the client or server end of a connection; once a connection is established, AMP is symmetric.
Implementation of ListBootImages.
Implementation of DescribePowerTypes.
Get a client with which to make RPCs to the specified cluster.
Raises: | NoConnectionsAvailable when there are no open connections to the specified cluster controller. |
---|
Issue calls into the reactor, passing results back to another thread.
Parameters: |
|
---|---|
Returns: | A UseOnceIterator of results. A result might be a failure, i.e. an instance of twisted.python.failure.Failure, or a valid result; it’s up to the caller to check. |