Linstor Python API Introduction¶
Linstor python api is a python library wrapping all tcp client communication between a python client and the controller REST-API.
Overview¶
Important classes¶
The few most important classes the python api currently uses are:
Main class that has all methods for manipulating Linstor objects. Method names are structured in “object”_”action” e.g.: node_create, resource_list, volume_dfn_delete
Wrapper class arount ~.Linstor that supports connections to multiple controllers. It will try to connect to the first controller in the list.
The usual message reply from the controller for actions.
There are 2 error classes that will or can be thrown from a Linstor
object.
LinstorError
Common error class, has a message and possible child errors.
LinstorNetworkError
Linstor error indicating a network/connection error.
Code Samples Using the High-Level ResourceGroup API¶
In this section we describe methods that are typically used by plugin developers. Using resource groups is the prefered way.
Create a resource N-times redundant¶
A code sample on how to create a resource “foo”, with a size of 20MiB. This first creates (or reuses) a resource group named threeSSD, that uses a fast SSD pool and places resources 3-times redundant. Usually that code is executed in a “create” call in a plugin.
import linstor
ssd_grp = linstor.ResourceGroup('threeSSD', uri='linstor://192.168.0.42') # by default uri is localhost
ssd_grp.redundancy = 3 # only if used for the first time
ssd_grp.storage_pool = 'myssdpool' # only if used for the first time
foo = ssd_grp.create_resource('foo', ['20 MiB'])
Code Samples Using the High-Level Resource API¶
In this section we describe methods that are typically used by plugin developers after a resource is created from a resource group, or if a resource already exists.
Resizing an existing resource/volume¶
import linstor
foo = linstor.Resource('foo') # or from a .create_resource() of a resource group
foo.volumes[0].size = linstor.Volume('30 MiB')
# resize again
foo.volumes[0].size += 10 * 1024 * 1024
Create a diskless assignment if there isn’t already an assignment¶
This is useful in hyper-converged setups where a local diskless assignment should be created, but only if there is not already an assignment with a disk.
import linstor
foo = linstor.Resource('foo')
foo.activate('bravo')
Remove diskless assignment (only if diskless)¶
This is usually called in a plugin in a “close” call, where then a diskless assignment should be deleted. Deletion in such cases is limited to diskless assignments as the redundancy should not be decreased
import linstor
foo = linstor.Resource('foo')
foo.deactivate('bravo')
Setting the assignment state of a resource¶
This assigns the resource if it isn’t assigned yet and convertes if necessary.
import linstor
foo = linstor.Resource('foo')
foo.placement.storage_pool = 'drbdpool'
foo.diskful('alpha') # whatever it was it is now diskful
foo.diskless('alpha') # converted to diskless
foo.delete('alpha')
foo.diskless('alpha') # created diskless
Setting and unsetting dual primary¶
import linstor
foo = linstor.Resource('foo')
foo.allow_two_primaries = True
# do some live migration
foo.allow_two_primaries = False
Various query and list operations¶
import linstor
foo = linstor.Resource('foo')
for diskless_node in foo.diskless_nodes():
print(diskless_node)
print(foo.is_diskful('alpha'))
print(foo.is_assigned('bravo'))
print(foo.volumes[0].backing_disk)
print(foo.volumes[0].device_path)
Code Samples Using the High-Level Key Value Store API¶
In this section we describe methods that are typically used by plugin developers.
Create a resource N-times redundant¶
Create or attach to the KV “foo” and manipulate keys in different name spaces.
import linstor
kv = linstor.KV('myKV', namespace='/foo/bar/')
kv['key'] = 'val'
list(kv.items()) -> [('key', 'val')]
kv.namespace = '/'
list(kv.items()) -> [('/foo/bar/key', 'val')]
kv['foo/baz/key'] = 'valbaz'
kv.namespace = '/foo/bar'
list(kv.items()) -> [('key', 'val')] # keys in /foo/baz not visible
Code Samples using the Low-Level API¶
List nodes¶
A code sample on how to get the current node list from the Controller.
import linstor
with linstor.Linstor("linstor://localhost") as lin: # may raise exception
node_list_reply = lin.node_list() # API calls will always return a list
assert node_list_reply, "Empty return list"
node_list = node_list_reply[0] # NodeListResponse
print(node_list)
This code sample will print out the current known node list of the controller. The returned node_list is a NodeListResponse class, a wrapper over a REST-API message, All rest-messages are declared in the responses module.
Create a node¶
A slightly different connect approach without enter and exit methods, but basically the same routine.
import linstor
lin = linstor.Linstor("linstor://localhost")
lin.connect()
node_create_replies = lin.node_create(
node_name="alpha",
ip="10.0.0.20",
node_type=linstor.consts.VAL_NODE_TYPE_STLT
)
if linstor.all_api_responses_success(node_create_replies):
print('SUCCESS', node_create_replies)
else:
print('NO SUCCESS', node_create_replies)
lin.disconnect()
This code snippet connects to the localhost controller and create a satellite node “alpha” with the ip “10.0.0.20”.
Create a resource on 2 nodes¶
Here is a example on how to create a resource “rsc” on 2 nodes (alpha, bravo), both nodes are already added to the controller with correctly setup default storage pools.
import linstor
def check_api_response(api_response): # check apicallresponses and print messages
for apiresp in api_response:
print(apiresp)
return linstor.Linstor.all_api_responses_success(api_response)
with linstor.Linstor("linstor://localhost") as lin:
res_dfn_replies = lin.resource_dfn_create(name="rsc")
assert check_api_response(res_dfn_replies)
vlm_dfn_replies = lin.volume_dfn_create(rsc_name="rsc", size=10240) # size is in KiB
assert check_api_response(vlm_dfn_replies)
rsc_create_replies = lin.resource_create(rsc_name="rsc", node_name="alpha")
assert check_api_response(rsc_create_replies)
rsc_create_replies = lin.resource_create(rsc_name="rsc", node_name="bravo")
assert check_api_response(rsc_create_replies)