-
Notifications
You must be signed in to change notification settings - Fork 9
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
This plugin allows to create raid, logical volume store and logical volumes on the Kalray DPU. It also allows the deletion of a volume. Currently restrications are due to the Kalray DPU. Next generation of the DPU will allow more volumes and won't be restricted on volume name. See the REAMDE for details. Signed-off-by: Guillaume <[email protected]>
- Loading branch information
Showing
5 changed files
with
398 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,152 @@ | ||
#!/usr/bin/python3 | ||
"""XAPI plugin to manage Kalray DPU.""" | ||
|
||
import json | ||
import XenAPIPlugin # pylint: disable=import-error | ||
|
||
from kalray.acs.spdk.rpc.client import HTTPJSONRPCClient, JSONRPCException # pylint: disable=import-error | ||
from xcpngutils import error_wrapped | ||
|
||
class KalrayCmd: | ||
"""Describe a command to be ran on the Kalray DPU.""" | ||
|
||
def __init__(self, rpc_name: str, updates: dict): | ||
self.server = 'localhost' | ||
self.port = 8080 | ||
self.username = None | ||
self.password = None | ||
self.timeout = 60.0 | ||
self.rpc_name = rpc_name | ||
self.rpc_params = {} # will be updated using add_rpc_params | ||
|
||
for k, v in updates.items(): | ||
if hasattr(self, k): | ||
setattr(self, k, v) | ||
|
||
# Check that username & password are well set | ||
if self.username is None: | ||
raise XenAPIPlugin.Failure("-1", ["'username' is required"]) | ||
if self.password is None: | ||
raise XenAPIPlugin.Failure("-1", ["'password' is required"]) | ||
|
||
def add_rpc_params(self, key, value): | ||
"""Adds a parameter that will be passed to the RPC.""" | ||
self.rpc_params[key] = value | ||
|
||
def call_rpc(self): | ||
"""Do the RPC call.""" | ||
try: | ||
client = HTTPJSONRPCClient( | ||
self.server, | ||
self.port, | ||
self.timeout, | ||
self.username, | ||
self.password, | ||
log_level="ERROR") | ||
message = client.call(self.rpc_name, self.rpc_params) | ||
except JSONRPCException as exc: | ||
raise XenAPIPlugin.Failure("-1", [exc.message]) | ||
|
||
return json.dumps(message) | ||
|
||
@error_wrapped | ||
def get_devices(_session, args): | ||
"""Get the list of devices available on the Kalray DPU.""" | ||
kc = KalrayCmd("bdev_get_bdevs", args) | ||
return kc.call_rpc() | ||
|
||
@error_wrapped | ||
def get_raids(_session, args): | ||
"""Get the list of raids available on the Kalray DPU.""" | ||
kc = KalrayCmd("bdev_raid_get_bdevs", args) | ||
kc.add_rpc_params("category", "all") | ||
return kc.call_rpc() | ||
|
||
@error_wrapped | ||
def get_lvs(_session, args): | ||
"""Get the list of logical volume stores available on the Kalray DPU.""" | ||
kc = KalrayCmd("bdev_lvol_get_lvstores", args) | ||
return kc.call_rpc() | ||
|
||
@error_wrapped | ||
def raid_create(_session, args): | ||
"""Create a raid.""" | ||
kc = KalrayCmd("bdev_raid_create", args) | ||
try: | ||
raid_name = args["raid_name"] | ||
raid_level = args["raid_level"] | ||
base_bdevs = args["base_bdevs"].split(',') | ||
except KeyError as msg: | ||
raise XenAPIPlugin.Failure("-1", [f"Key {msg} is missing"]) | ||
|
||
# Check supported raids | ||
if raid_level not in ["raid0", "raid1", "raid10"]: | ||
raise XenAPIPlugin.Failure("-1", ["Only raid0, raid1 and raid10 are supported"]) | ||
|
||
kc.add_rpc_params("name", raid_name) | ||
kc.add_rpc_params("raid_level", raid_level) | ||
kc.add_rpc_params("base_bdevs", base_bdevs) | ||
kc.add_rpc_params("strip_size_kb", 128) | ||
kc.add_rpc_params("persist", True) | ||
kc.add_rpc_params("split_dp", True) | ||
return kc.call_rpc() | ||
|
||
@error_wrapped | ||
def lvs_create(_session, args): | ||
"""Create a logical volume store.""" | ||
kc = KalrayCmd("bdev_lvol_create_lvstore", args) | ||
try: | ||
lvs_name = args["lvs_name"] | ||
bdev_name = args["bdev_name"] | ||
except KeyError as msg: | ||
raise XenAPIPlugin.Failure("-1", [f"Key {msg} is missing"]) | ||
|
||
kc.add_rpc_params("lvs_name", lvs_name) | ||
kc.add_rpc_params("bdev_name", bdev_name) | ||
|
||
return kc.call_rpc() | ||
|
||
@error_wrapped | ||
def lvol_create(_session, args): | ||
"""Create a new lvol on the Kalray DPU.""" | ||
kc = KalrayCmd("bdev_lvol_create", args) | ||
|
||
try: | ||
lvol_name = args["lvol_name"] | ||
lvol_size = int(args["lvol_size_in_bytes"]) | ||
lvs_name = args["lvs_name"] | ||
except KeyError as msg: | ||
raise XenAPIPlugin.Failure("-1", [f"Key {msg} is missing"]) | ||
except ValueError as msg: | ||
raise XenAPIPlugin.Failure("-1", [f"Wrong size: {msg}"]) | ||
|
||
kc.add_rpc_params("lvol_name", lvol_name) | ||
# size is deprecated but Kalray DPU uses an old version of SPDK that | ||
# does not provide the new 'size_in_mib' parameter. | ||
kc.add_rpc_params("size", lvol_size) | ||
kc.add_rpc_params("lvs_name", lvs_name) | ||
return kc.call_rpc() | ||
|
||
@error_wrapped | ||
def lvol_delete(_session, args): | ||
"""Delete the lvol passed as parameter on the Kalray DPU if exists.""" | ||
kc = KalrayCmd("bdev_lvol_delete", args) | ||
|
||
try: | ||
lvol_name = args["lvol_name"] | ||
except KeyError as msg: | ||
raise XenAPIPlugin.Failure("-1", [f"Key {msg} is missing"]) | ||
|
||
kc.add_rpc_params("name", lvol_name) | ||
return kc.call_rpc() | ||
|
||
if __name__ == "__main__": | ||
XenAPIPlugin.dispatch({ | ||
"get_devices": get_devices, | ||
"get_raids": get_raids, | ||
"get_lvs": get_lvs, | ||
"raid_create": raid_create, | ||
"lvs_create": lvs_create, | ||
"lvol_create": lvol_create, | ||
"lvol_delete": lvol_delete, | ||
}) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,55 @@ | ||
class HTTPJSONRPCClient(object): | ||
def __init__(self, addr, port=None, timeout=60.0, user='admin', password='admin', **kwargs): | ||
pass | ||
|
||
def call(self, method, params=None): | ||
"""We will juste check that parameters are ok.""" | ||
parameters = { | ||
"bdev_get_bdevs": { | ||
"required": [], | ||
"optional": ['name', 'timeout'], | ||
}, | ||
"bdev_raid_get_bdevs": { | ||
"required": ['category'], | ||
"optional": [], | ||
}, | ||
"bdev_lvol_get_lvstores": { | ||
"required": [], | ||
"optional": ['uuid', 'lvs_name'], | ||
}, | ||
"bdev_raid_create": { | ||
"required": ['name', 'strip_size_kb', 'raid_level', 'base_bdevs'], | ||
"optional": ['persist', 'split_dp'], | ||
}, | ||
"bdev_lvol_create_lvstore": { | ||
"required": ['bdev_name', 'lvs_name'], | ||
"optional": ['cluster_sz', 'clear_method', 'num_md_pages_per_cluster_ratio'], | ||
}, | ||
"bdev_lvol_create": { | ||
"required": ['lvol_name'], | ||
"optional": ['size', 'size_in_mib', 'thin_provision', 'uuid', 'lvs_name', 'clear_method'], | ||
}, | ||
"bdev_lvol_delete": { | ||
"required": ['name'], | ||
"optional": [], | ||
}, | ||
} | ||
|
||
# Check that method is mocked | ||
try: | ||
p = parameters[method] | ||
except KeyError: | ||
assert False, f"{method} is not mocked" | ||
|
||
# Check that required parameters are given | ||
for k in p['required']: | ||
assert k in params, f"Required parameter '{k}' is missing for {method}" | ||
|
||
# Check that params passed to method are valid | ||
for k in params: | ||
assert k in p['required'] or k in p['optional'], f"Invalid parameter '{k}' for {method}" | ||
|
||
|
||
class JSONRPCException(BaseException): | ||
def __init__(self, message): | ||
assert False, "Mock me!" |
Oops, something went wrong.