module: | ave-broker |
---|
The broker is used to allocate resources: Handsets, workspaces, GPS simulators, programmable relays and more. It uses some simple rules for how to treat the client that performs the allocations, depending on what it tries to do. Knowing how the rules work is essential when working with AVE.
If the allocation attempt raises an exception, check its type. If the type is Busy, that means the wanted equipment exists but is allocated to someone else. Exit your program with status vcsjob.BUSY and let the scheduler that ran your program decide when to try it again:
import sys import vcsjob from ave.broker import Broker from ave.broker.exceptions import Busy broker = Broker() try: handset = broker.get({'type':'handset', 'pretty':'yuga'}) except Busy: # tell the scheduler that the job should be retried sometime later: sys.exit(vcsjob.BUSY)Client can allocate multiple resources in one call, and if the client needs some resources configured to work together, in a ‘stack’, the client should enclose these resources in a parentheses when invoking the ‘get’ method:
# ensure workspace is allocated on the same host as the equipment, # no need configure workspace in the stack, for workspace, this is a # default behavior. from ave.broker import Broker broker = Broker() h, w = broker.get(({'type':'handset'}, {'type':'workspace'})) # in this case, the client intends to allocate a handset and a relay which # were configured to work together in a 'stack'. from ave.broker import Broker broker = Broker() h, r, w = broker.get(({'type':'handset'}, {'type':'relay'},{'type':'workspace'})) #client allocates two handsets, but these two handsets may be located in different #Host PC, from ave.broker import Broker broker = Broker() h1, h2 = broker.get(({'type':'handset'},), ({'type':'handset'},)) #client allocate two handsets, and these two handsets were configured #to work together in a 'stack' from ave.broker import Broker broker = Broker() h1, h2 = broker.get({'type':'handset'}, {'type':'handset'}) # or h1, h2 = broker.get(({'type':'handset'}, {'type':'handset'})) # in this case, client intends to allocate two handsets and one relay, and # one of the handsets should work together with the relay. from ave.broker import Broker broker = Broker() h1, r, h2 = broker.get(({'type':'handset'}, {'type':'relay'}), {'type':'handset'})If the client disconnects from the broker, the broker will free all of the client’s resources, terminate its session and make the equipment available again. This means that the client does not need to yield resources. Simply terminate the program.
Allocating a handset and a workspace separately can cause them to end up on different hosts. Interaction between them will then not work:
# handset.push() may raise an exception about the source file not existing # in the file system, because the workspace downloaded it on a different # host: broker = Broker() handset = broker.get({'type':'handset'}) workspace = broker.get({'type':'workspace'}) path = workspace.download_git(...) handset.push(path+'/some_file', '/system/data')The broker disconnects your client if an allocation fails for any reason. The rationale is that a misbehaving client should not be able to loop on the allocation request because that may lead to deadlocks between two clients that fight for the same set of resources:
import time from ave.broker import Broker from ave.broker.exceptions import Busy broker = Broker() # THE FOLLOWING WILL NOT WORK. the broker disconnects the client on the # first failed allocation attempt, after which broker.get() will raise a # different exception, telling you that the client's session has been # terminated. while True: try: h1 = broker.get({'type':'handset', 'pretty':'yuga'}) h2 = broker.get({'type':'handset', 'pretty':'togari'}) break except Busy: time.sleep(1) # retryThe client must keep a reference to its Broker instance to prevent the broker from disconnecting the client. Here is a common pitfall that will not work:
from ave.broker import Broker def allocate(): broker = Broker() return broker.get({'type':'handset', 'pretty':'yuga'}) # broker object is garbage collected here handset = allocate() handset.ls('/') # will raise ConnectionRefused
Allocate one or more resources based on profiles.
Parameters: | profiles – One or more dict objects that encode resource profiles. All profiles must have the "type" field set. |
---|---|
Returns: | The same number of resource objects as there were profiles in the request. |
Raises: |
|
Example allocations:
b = Broker()
# any android handset on the GTE network together with a workspace:
h,w = b.get(
{'type':'handset', 'platform':'android', 'gsm.operator':'GTE'},
{'type':'workspace'}
)
# a Hayabusa handset running FirefoxOS of a specific label, together
# with a programmable relay that has been wired to cut the USB and
# battery connections of the handset, plus a workspace:
h,r,w = b.get(
{
'type':'handset',
'platform':'firefox',
'sw.label':'ICS-BLUE-FF-130516-1131'
},
{'type':'relay', 'circuits':['usb.pc.vcc', 'handset.battery']},
{'type':'workspace'}
)
If the broker cannot satisfy the request and a forwarding rule is set to point to another broker on the network, then the request will be passed on and tried there instead. This chain can be arbitrarily long.
Connecting as administrator gives the client new capabilities and removes some. The administrator role is supposed to be used by web frontends and the like, not by regular programs.
Parameters: | authkey – An authentication string that must match the value of the "admin" field in .ave/config/authkeys.json. |
---|
Raises ConnectionClosed: | |
---|---|
The client is not allowed to allocate resources while logged in as administrator. |
Returns: | A list of profiles representing the combined allocations of all clients. |
---|
Returns: | A list of profiles representing the collateral of the combined allocations of all clients. |
---|
Collateral is any equipment that is part of a stack that the client has taken some other resource from, but not the equipment itself. It is an internal book keeping mechanism that secures that different clients cannot accidentally get resources that will interfere with each other when manipulated.
If the broker has a sharing rule, this call will force it to start sharing its equipment over the network.
Stop sharing equipment with another broker.
List all brokers that share equipment with this broker.
Parameters: | address – A (host, port) tuple. |
---|
Causes the broker to disconnect another broker that shares equipment with it. The call has no effect if no such broker is connected. Note that sharing brokers are by default configured to try to reconnect when they loose the connection. This call is only useful for diagnostics.
Causes the broker to disconnect all brokers that share equipment with it. Note that sharing brokers are by default configured to try to reconnect when they loose the connection. This call is only useful for diagnostics.
Returns a list of equipment that matches the profile. The equipment does not have to be available for allocation.
Returns a list of equipment that matches the profile and is available for allocation.
Returns a list of the broker’s configured equipment stacks. Stacking determines what equipment can be allocated together. (Workspaces can always be allocated together with equipment and is not listed in the stacks.)
On systems that use Upstart, the broker and other AVE services will be started by init on system boot. On other systems, the user has to start the broker manually:
ave-broker --start
The broker can be restarted without affecting clients and their sessions:
ave-broker --restart
Stopping the broker terminates all open sessions and disconnects all clients:
ave-broker --stop
The files are located under $home/.ave/config, where the value of $home is read from /etc/ave/user.
Changes to the files take effect when the broker is restarted:
# change the config files
ave-broker --restart
Note
Some changes require a full stop and start of the broker: Port numbers and changes to authentication keys:
ave-broker --stop
# change the config files
ave-broker --start
The smallest valid configuration is just the empty dictionary:
{}
A broker that is configured to forward failed allocations to another broker uses the remote rule (always use port 4000 unless you know what you are doing):
{
"remote": {
"host" : "hostname",
"port" : 4000,
"policy": "forward"
}
}
A broker that is configured to share its equipment with another broker also uses the remote rule, but with slightly different settings. The authkey field must match the share value of the remote broker’s authkeys.json file:
{
"remote": {
"host" : "hostname",
"port" : 4000,
"policy" : "share",
"authkey": "sharing key of the other broker"
}
}
A broker that is configured to handle stacked equipment can add the stacks field. Each entry must be a list of profiles that contain the type field and a field that uniquely identifies an instance of that kind of equipment. In the following example, handsets (identified by serial number) are stacked against relays (identified by symbolic ID’s):
{
"stacks":[
[{"type":"relay","uid":"a"}, {"type":"handset","serial":"CB5A1JYXRQ"}],
[{"type":"relay","uid":"b"}, {"type":"handset","serial":"CB511VD8AF"}],
[{"type":"relay","uid":"c"}, {"type":"handset","serial":"CB5A1M7CTP"}]
]
}
A broker configuration can contain both remote and stacks rules.
Note
Although JSON is great for comfortable configuration handling, it is a format with some limitations:
Certain adiministrative functions in the broker are protected by authentication keys to prevent accidental usage of functionality that are not meant to be used by regular clients. These are keys are generated automatically when AVE is installed but can be changed to more memorable values:
{
"admin": "Some random stuff... 1234!",
"share": "please stick to printable characters, ok?"
}
In the API documentation above, functions that require use of the admin key have been put under their own section.