Script 'mail_helper' called by obssrc Hello community, here is the log from the commit of package python-linstor for openSUSE:Factory checked in at 2021-01-26 14:46:54 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Comparing /work/SRC/openSUSE:Factory/python-linstor (Old) and /work/SRC/openSUSE:Factory/.python-linstor.new.28504 (New) ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Package is "python-linstor" Tue Jan 26 14:46:54 2021 rev:3 rq:866724 version:1.6.0 Changes: -------- --- /work/SRC/openSUSE:Factory/python-linstor/python-linstor.changes 2020-09-22 21:15:14.036142149 +0200 +++ /work/SRC/openSUSE:Factory/.python-linstor.new.28504/python-linstor.changes 2021-01-26 14:50:29.527738321 +0100 @@ -1,0 +2,11 @@ +Tue Jan 26 06:07:49 UTC 2021 - nick wang <nw...@suse.com> + +- bsc#1181397, update to 1.6.0 + * Added node_restore() method + * Added addition_place_count params for auto_place + * Node response object now has flags() + * added SizeCalc.unit_to_str method + * Added methods for error-report deletion + * Node create case insensitive for types + +------------------------------------------------------------------- Old: ---- python-linstor-1.3.0.tar.gz New: ---- python-linstor-1.6.0.tar.gz ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Other differences: ------------------ ++++++ python-linstor.spec ++++++ --- /var/tmp/diff_new_pack.63Hfsh/_old 2021-01-26 14:50:30.091739090 +0100 +++ /var/tmp/diff_new_pack.63Hfsh/_new 2021-01-26 14:50:30.095739096 +0100 @@ -1,7 +1,7 @@ # # spec file for package python-linstor # -# Copyright (c) 2020 SUSE LLC +# Copyright (c) 2021 SUSE LLC # # All modifications and additions to the file contributed by third parties # remain the property of their copyright owners, unless otherwise agreed @@ -18,7 +18,7 @@ %{?!python_module:%define python_module() python-%{**} python3-%{**}} Name: python-linstor -Version: 1.3.0 +Version: 1.6.0 Release: 0 Summary: Python API for Linstor License: GPL-3.0-only ++++++ _service ++++++ --- /var/tmp/diff_new_pack.63Hfsh/_old 2021-01-26 14:50:30.119739128 +0100 +++ /var/tmp/diff_new_pack.63Hfsh/_new 2021-01-26 14:50:30.119739128 +0100 @@ -5,9 +5,9 @@ <param name="filename">linstor-api-py</param> <!-- build service using release python-linstor atm. - <param name="version">1.3.0</param> + <param name="version">1.6.0</param> --> - <param name="versionformat">1.3.0+git.%h</param> + <param name="versionformat">1.6.0+git.%h</param> <param name="revision">master</param> </service> ++++++ python-linstor-1.3.0.tar.gz -> python-linstor-1.6.0.tar.gz ++++++ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/python-linstor-1.3.0/PKG-INFO new/python-linstor-1.6.0/PKG-INFO --- old/python-linstor-1.3.0/PKG-INFO 2020-08-17 15:41:04.873492000 +0200 +++ new/python-linstor-1.6.0/PKG-INFO 2020-12-21 10:17:32.351524600 +0100 @@ -1,6 +1,6 @@ Metadata-Version: 1.2 Name: python-linstor -Version: 1.3.0 +Version: 1.6.0 Summary: Linstor python api Home-page: https://www.linbit.com Author: Robert Altnoeder <robert.altnoe...@linbit.com>, Roland Kammerer <roland.kamme...@linbit.com>, Rene Peinthor <rene.peint...@linbit.com> diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/python-linstor-1.3.0/linstor/consts_githash.py new/python-linstor-1.6.0/linstor/consts_githash.py --- old/python-linstor-1.3.0/linstor/consts_githash.py 2020-08-17 15:41:04.000000000 +0200 +++ new/python-linstor-1.6.0/linstor/consts_githash.py 2020-12-21 10:17:32.000000000 +0100 @@ -1 +1 @@ -GITHASH = 'GIT-hash: 334da86189285f4f32c169d8ed639bf81a41d793' +GITHASH = 'GIT-hash: df9121a4e86502351bd7373446dc0f9233ab3bb5' diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/python-linstor-1.3.0/linstor/linstorapi.py new/python-linstor-1.6.0/linstor/linstorapi.py --- old/python-linstor-1.3.0/linstor/linstorapi.py 2020-08-17 15:29:10.000000000 +0200 +++ new/python-linstor-1.6.0/linstor/linstorapi.py 2020-12-21 10:17:30.000000000 +0100 @@ -11,6 +11,7 @@ import base64 import re import shutil +import xml.etree.ElementTree as ET from datetime import datetime from distutils.version import StrictVersion @@ -24,6 +25,7 @@ from linstor.responses import RESTMessageResponse, SnapshotResponse, ControllerProperties, ResourceConnection from linstor.responses import StoragePoolDefinitionResponse, MaxVolumeSizeResponse, ControllerVersion from linstor.responses import ResourceGroupResponse, VolumeGroupResponse, PhysicalStorageList, SnapshotShippingResponse +from linstor.responses import SpaceReport from linstor.size_calc import SizeCalc try: @@ -153,6 +155,7 @@ apiconsts.API_LST_KVS: KeyValueStoresResponse, apiconsts.API_VERSION: ControllerVersion, apiconsts.API_LST_PHYS_STOR: PhysicalStorageList, + apiconsts.API_RPT_SPC: SpaceReport, API_SINGLE_NODE_REQ: ResourceConnection } @@ -351,7 +354,11 @@ headers=headers ) except socket.error as err: - raise LinstorNetworkError("Unable to connect to {hp}: {err}".format(hp=self._ctrl_host, err=err)) + if self._keep_alive and reconnect: + self.connect() + return self._rest_request_base(apicall, method, path, body, reconnect=False) + else: + raise LinstorNetworkError("Unable to send request to {hp}: {err}".format(hp=self._ctrl_host, err=err)) try: return self._rest_conn.getresponse() @@ -373,14 +380,25 @@ def _handle_response_error(self, response, method, path): error_data_raw = self._decode_response_data(response) if error_data_raw: - try: - error_data = json.loads(error_data_raw) - except ValueError as ve: - raise LinstorError( - "Unable to parse REST json data: " + str(ve) + "\n" - "Request-Uri: " + path - ) - return [ApiCallResponse(x) for x in error_data] + if response.getheader("Content-Type", "text").startswith('application/json'): + try: + error_data = json.loads(error_data_raw) + except ValueError as ve: + raise LinstorError( + "Unable to parse REST json data: " + str(ve) + "\n" + "Request-Uri: " + path + ) + return [ApiCallResponse(x) for x in error_data] + else: + # try to get an error message from html + root = ET.fromstring(error_data_raw) + # get head error message + error_msg = "Request failed." + for child in root.find("body"): + if "header" in child.attrib.get("class"): + error_msg = child.text + break + raise LinstorError("HTTP-Status({s})/{err}".format(s=response.status, err=error_msg)) raise LinstorError("REST api call method '{m}' to resource '{p}' returned status {s} with no data." .format(m=method, p=path, s=response.status)) @@ -700,17 +718,19 @@ :return: A list containing ApiCallResponses from the controller. :rtype: list[ApiCallResponse] """ - if node_type not in self._node_types: + node_type_lower = node_type.lower() + if node_type_lower not in [nt.lower() for nt in self._node_types]: raise LinstorError( "Unknown node type '{nt}'. Known types are: {kt}".format(nt=node_type, kt=", ".join(self._node_types)) ) if port is None: - if com_type == apiconsts.VAL_NETCOM_TYPE_PLAIN: + com_lower = com_type.lower() + if com_lower == apiconsts.VAL_NETCOM_TYPE_PLAIN.lower(): port = apiconsts.DFLT_CTRL_PORT_PLAIN \ - if node_type == apiconsts.VAL_NODE_TYPE_CTRL else apiconsts.DFLT_STLT_PORT_PLAIN - elif com_type == apiconsts.VAL_NETCOM_TYPE_SSL: - if node_type == apiconsts.VAL_NODE_TYPE_STLT: + if node_type_lower == apiconsts.VAL_NODE_TYPE_CTRL.lower() else apiconsts.DFLT_STLT_PORT_PLAIN + elif com_lower == apiconsts.VAL_NETCOM_TYPE_SSL.lower(): + if node_type_lower == apiconsts.VAL_NODE_TYPE_STLT.lower(): port = apiconsts.DFLT_STLT_PORT_SSL else: port = apiconsts.DFLT_CTRL_PORT_SSL @@ -795,6 +815,16 @@ replies += self._rest_request(apiconsts.API_NODE_RECONNECT, "PUT", "/v1/nodes/" + node_name + "/reconnect") return replies + def node_restore(self, node_name): + """ + Restores an evicted node. + + :param str node_name: Node name to restore + :return: A list containing ApiCallResponses from the controller. + :rtype: list[ApiCallResponse] + """ + return self._rest_request(apiconsts.API_NODE_RESTORE, "PUT", "/v1/nodes/" + node_name + "/restore") + def netinterface_create(self, node_name, interface_name, ip, port=None, com_type=None, is_active=False): """ Create a netinterface for a given node. @@ -1267,7 +1297,9 @@ replicas_on_different=replicas_on_different, diskless_on_remaining=diskless_on_remaining, layer_list=layer_list, - provider_list=provider_list + provider_list=provider_list, + additional_place_count=0, + diskless_type=None ) return self._rest_request( @@ -1322,6 +1354,7 @@ self._set_select_filter_body( body, place_count=place_count, + additional_place_count=None, # rsc_grps will never ask for "additional" resources storage_pool=storage_pool, do_not_place_with=do_not_place_with, do_not_place_with_regex=do_not_place_with_regex, @@ -1329,7 +1362,8 @@ replicas_on_different=replicas_on_different, diskless_on_remaining=diskless_on_remaining, layer_list=layer_list, - provider_list=provider_list + provider_list=provider_list, + diskless_type=None # rsc_grps will never ask to place diskless resources ) if property_dict: @@ -1938,12 +1972,14 @@ replicas_on_different, diskless_on_remaining, layer_list, - provider_list + provider_list, + additional_place_count, + diskless_type ): """ :param dict[Any] body: - :param int place_count: + :param Optional[int] place_count: :param Optional[List[str]] storage_pool: :param Optional[List[str]] do_not_place_with: :param Optional[str] do_not_place_with_regex: @@ -1952,6 +1988,7 @@ :param Optional[bool] diskless_on_remaining: :param Optional[List[str]] layer_list: :param Optional[List[str]] provider_list: + :param Optional[int] additional_place_count: :return: """ if "select_filter" not in body: @@ -1960,6 +1997,12 @@ if place_count is not None: body["select_filter"]["place_count"] = place_count + if additional_place_count is not None: + body["select_filter"]["additional_place_count"] = additional_place_count + + if diskless_type: + body["select_filter"]["diskless_type"] = diskless_type + if diskless_on_remaining is not None: body["select_filter"]["diskless_on_remaining"] = diskless_on_remaining @@ -2000,13 +2043,16 @@ diskless_on_remaining=False, async_msg=False, layer_list=None, - provider_list=None + provider_list=None, + additional_place_count=None, + diskless_type=None ): """ Auto places(deploys) a resource to the amount of place_count. :param str rsc_name: Name of the resource definition to deploy - :param int place_count: Number of placements, on how many different nodes + :param optional[int] place_count: Number of placements to reach, on how many different nodes. + either place_count or additional_place_count must be present :param list[str] storage_pool: List of storage pools to use :param list[str] do_not_place_with: Do not place with resource names in this list :param str do_not_place_with_regex: A regex string that rules out resources @@ -2016,6 +2062,8 @@ :param bool async_msg: True to return without waiting for the action to complete on the satellites :param list[str] layer_list: Define layers for the resource :param list[str] provider_list: Filter provider kinds + :param optional[int] additional_place_count: Number of additional placements. + either place_count or additional_place_count must be present :return: A list containing ApiCallResponses from the controller. :rtype: list[ApiCallResponse] """ @@ -2036,7 +2084,9 @@ replicas_on_different=replicas_on_different, diskless_on_remaining=diskless_on_remaining, layer_list=layer_list, - provider_list=provider_list + provider_list=provider_list, + additional_place_count=additional_place_count, + diskless_type=diskless_type ) if layer_list: @@ -2894,6 +2944,48 @@ return result + def error_report_delete( + self, + nodes=None, + since=None, + to=None, + exception=None, + version=None, + ids=None): + """ + Deletes error-reports on the linstor cluster, filtered by the given parameters + + :param list[str] nodes: Only delete error-reports from this nodes, if None or empty all + :param datetime since: Start datetime from when to delete + :param datetime to: Until datetime to delete + :param str exception: Delete error reports matching this exception string + :param str version: Delete error reports matching this version string + :param list[str] ids: Error report ids to delete + :rtype: list[ApiCallResponse] + """ + self._require_version("1.4.0", msg="Error report delete API not supported by server") + + body = {} + + if nodes: + body["nodes"] = nodes + + if since: + body["since"] = int(time.mktime(since.timetuple()) * 1000) + if to: + body["to"] = int(time.mktime(to.timetuple()) * 1000) + + if exception: + body["exception"] = exception + + if version: + body["version"] = version + + if ids: + body["ids"] = ids + + return self._rest_request(apiconsts.API_DEL_ERROR_REPORTS, "PATCH", "/v1/error-reports", body) + def keyvaluestore_modify(self, instance_name, property_dict=None, delete_props=None): """ Modify the properties of a given key value store instance. @@ -3079,6 +3171,22 @@ to_file=to_file ) + def space_reporting_query(self): + """ + Acquire the hashed space reporting string for commercial customers. + + :return: Space reporting object from controller + :rtype: list[SpaceReport] + """ + self._require_version("1.5.0", msg="Space reporting API not supported by server") + + path = "/v1/space-report" + return self._rest_request( + apiconsts.API_RPT_SPC, + "GET", + path + ) + def stats(self): """ Returns a printable string containing network statistics. diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/python-linstor-1.3.0/linstor/responses.py new/python-linstor-1.6.0/linstor/responses.py --- old/python-linstor-1.3.0/linstor/responses.py 2020-08-17 15:29:10.000000000 +0200 +++ new/python-linstor-1.6.0/linstor/responses.py 2020-12-21 10:17:30.000000000 +0100 @@ -334,6 +334,10 @@ return self._rest_data.get("props", {}) @property + def flags(self): + return self._rest_data.get("flags", []) + + @property def data_v0(self): d = dict(self._rest_data) d["props"] = [{"key": x, "value": self.props[x]} for x in self.props] @@ -2055,3 +2059,12 @@ @property def physical_devices(self): return [PhysicalDevice(x) for x in self._rest_data] + + +class SpaceReport(RESTMessageResponse): + def __init__(self, data): + super(SpaceReport, self).__init__(data) + + @property + def report(self): + return self._rest_data["reportText"] diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/python-linstor-1.3.0/linstor/size_calc.py new/python-linstor-1.6.0/linstor/size_calc.py --- old/python-linstor-1.3.0/linstor/size_calc.py 2020-08-17 15:29:10.000000000 +0200 +++ new/python-linstor-1.6.0/linstor/size_calc.py 2020-12-21 10:17:30.000000000 +0100 @@ -58,6 +58,14 @@ UNITS_LIST_STR = ', '.join([unit_str for unit_str, _ in UNITS_MAP.values()]) @classmethod + def unit_to_str(cls, unit): + for u_key in cls.UNITS_MAP: + u = cls.UNITS_MAP[u_key] + if u[1] == unit: + return u_key + return '' + + @classmethod def parse_unit(cls, value): """ Parses the given value as a computer size + unit. diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/python-linstor-1.3.0/linstor/version.py new/python-linstor-1.6.0/linstor/version.py --- old/python-linstor-1.3.0/linstor/version.py 2020-08-17 15:29:10.000000000 +0200 +++ new/python-linstor-1.6.0/linstor/version.py 2020-12-21 10:17:30.000000000 +0100 @@ -1 +1 @@ -VERSION = "1.3.0" +VERSION = "1.6.0" diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/python-linstor-1.3.0/linstor-common/consts.json new/python-linstor-1.6.0/linstor-common/consts.json --- old/python-linstor-1.3.0/linstor-common/consts.json 2020-08-17 15:29:11.000000000 +0200 +++ new/python-linstor-1.6.0/linstor-common/consts.json 2020-12-21 10:17:31.000000000 +0100 @@ -359,6 +359,11 @@ "type": "BOR" }, { + "name": "FAIL_NODE_HAS_USED_RSC", + "value": [232, "MASK_ERROR"], + "type": "BOR" + }, + { "blockcomment": "Codes 300-399: dependency not found failures" }, { @@ -850,6 +855,11 @@ "blockcomment": "Other failures" }, { + "name": "FAIL_ONLY_ONE_ACT_RSC_PER_SHARED_STOR_POOL_ALLOWED", + "value": [981, "MASK_ERROR"], + "type": "BOR" + }, + { "name": "FAIL_CRYPT_INIT", "value": [982, "MASK_ERROR"], "type": "BOR" @@ -1023,6 +1033,11 @@ "type": "BOR" }, { + "name": "WARN_NODE_EVICTED", + "value": [1017, "MASK_WARN"], + "type": "BOR" + }, + { "name": "WARN_NOT_FOUND", "value": [3000, "MASK_WARN"], "type": "BOR" @@ -1076,6 +1091,11 @@ "type": "BOR" }, { + "name": "INFO_RSC_ALREADY_EXISTS", + "value": [10008, "MASK_INFO"], + "type": "BOR" + }, + { "blockcomment": "Special codes" }, { @@ -1480,6 +1500,11 @@ "type": "string" }, { + "name": "API_NODE_RESTORE", + "value": "NodeRestore", + "type": "string" + }, + { "blockcomment": "List object APIs" }, { @@ -1538,6 +1563,16 @@ "type": "string" }, { + "name": "API_DEL_ERROR_REPORT", + "value": "DelErrorReport", + "type": "string" + }, + { + "name": "API_DEL_ERROR_REPORTS", + "value": "DelErrorReports", + "type": "string" + }, + { "name": "API_REQ_SOS_REPORT", "value": "ReqSosReport", "type": "string" @@ -1572,12 +1607,17 @@ "value": "LstPhysicalStorage", "type": "string" }, - { + { "name": "API_LST_SNAPSHOT_SHIPPINGS", "value": "LstSnapShips", "type": "string" }, { + "name": "API_LST_PROPS_INFO", + "value": "LstPropsInfo", + "type": "string" + }, + { "blockcomment": "Query APIs" }, { @@ -1657,12 +1697,7 @@ "type": "string" }, { - "blockcomment": "Object property keys" - }, - { - "name": "KEY_KVS", - "value": "Kvs", - "type": "string" + "blockcomment": "DRBD property keys" }, { "name": "KEY_UUID", @@ -1670,148 +1705,112 @@ "type": "string" }, { - "name": "KEY_NODE", - "value": "Node", - "type": "string" - }, - { - "name": "KEY_RSC_DFN", - "value": "RscDfn", - "type": "string" - }, - { - "name": "KEY_RSC_GRP", - "value": "RscGrp", - "type": "string" - }, - { - "name": "KEY_VLM_GRP", - "value": "VlmGrp", - "type": "string" - }, - { - "name": "KEY_STOR_POOL_DFN", - "value": "StorPoolDfn", - "type": "string" - }, - { - "name": "KEY_1ST_NODE", - "value": "FirstNode", - "type": "string" - }, - { - "name": "KEY_2ND_NODE", - "value": "SecondNode", + "name": "KEY_DRBD_CURRENT_GI", + "value": "DrbdCurrentGi", "type": "string" }, { - "name": "KEY_SNAPSHOT", - "value": "Snapshot", + "name": "KEY_DMSTATS", + "value": "DMStats", "type": "string" }, { - "name": "KEY_NODE_NAME", - "value": "NodeName", + "name": "KEY_DRBD_AUTO_QUORUM", + "value": "auto-quorum", "type": "string" }, { - "name": "KEY_NODE_TYPE", - "value": "NodeType", + "name": "KEY_DRBD_AUTO_ADD_QUORUM_TIEBREAKER", + "value": "auto-add-quorum-tiebreaker", "type": "string" }, { - "name": "KEY_NODE_FLAGS", - "value": "NodeFlags", + "name": "KEY_MINOR_NR_AUTO_RANGE", + "value": "MinorNrAutoRange", "type": "string" }, { - "name": "KEY_NODE_ID", - "value": "NodeId", + "name": "KEY_DRBD_AUTO_DISKFUL", + "value": "auto-diskful", "type": "string" }, { - "name": "KEY_1ST_NODE_NAME", - "value": "FirstNodeName", + "name": "KEY_DRBD_AUTO_DISKFUL_ALLOW_CLEANUP", + "value": "auto-diskful-allow-cleanup", "type": "string" }, { - "name": "KEY_2ND_NODE_NAME", - "value": "SecondNodeName", - "type": "string" + "blockcomment": "Node property keys" }, { - "name": "KEY_RSC_NAME", - "value": "RscName", + "name": "KEY_NODE", + "value": "Node", "type": "string" }, { - "name": "KEY_STOR_POOL_NAME", - "value": "StorPoolName", + "name": "KEY_1ST_NODE", + "value": "FirstNode", "type": "string" }, { - "name": "KEY_STOR_POOL_DRBD_META_NAME", - "value": "StorPoolNameDrbdMeta", + "name": "KEY_2ND_NODE", + "value": "SecondNode", "type": "string" }, { - "name": "KEY_STOR_POOL_DRBD_META_TYPE", - "value": "DrbdMetaType", + "name": "KEY_CUR_STLT_CONN_NAME", + "value": "CurStltConnName", "type": "string" }, { - "name": "KEY_NET_IF_NAME", - "value": "NetIfName", - "type": "string" + "blockcomment": "Resource property keys" }, { - "name": "KEY_SNAPSHOT_NAME", - "value": "SnapshotName", + "name": "KEY_RSC_DFN", + "value": "RscDfn", "type": "string" }, { - "name": "KEY_VLM_NR", - "value": "VlmNr", + "name": "KEY_RSC_GRP", + "value": "RscGrp", "type": "string" }, { - "name": "KEY_VLM_SIZE", - "value": "VlmSize", + "name": "KEY_TCP_PORT_AUTO_RANGE", + "value": "TcpPortAutoRange", "type": "string" }, { - "name": "KEY_MINOR_NR", - "value": "MinorNr", + "name": "KEY_PEER_SLOTS_NEW_RESOURCE", + "value": "PeerSlotsNewResource", "type": "string" }, { - "name": "KEY_PEER_COUNT", - "value": "PeerCount", + "name": "KEY_PEER_SLOTS", + "value": "PeerSlots", "type": "string" }, { - "name": "KEY_AL_SIZE", - "value": "AlSize", + "name": "KEY_RSC_ROLLBACK_TARGET", + "value": "RollbackTarget", "type": "string" }, { - "name": "KEY_AL_STRIPES", - "value": "AlStripes", + "name": "KEY_RSC_MIGRATE_FROM", + "value": "MigrateFrom", "type": "string" }, { - "name": "KEY_ID", - "value": "ID", - "type": "string" + "blockcomment": "Volume property keys" }, { - "name": "KEY_ROLE", - "value": "Role", + "name": "KEY_VLM_GRP", + "value": "VlmGrp", "type": "string" }, { - "name": "KEY_MISSING_NAMESPC", - "value": "MissingNameSpace", + "name": "KEY_VLM_NR", + "value": "VlmNr", "type": "string" }, { @@ -1825,28 +1824,27 @@ "type": "string" }, { - "name": "KEY_DRBD_CURRENT_GI", - "value": "DrbdCurrentGi", - "type": "string" + "blockcomment": "ldap property keys" }, { - "name": "KEY_DRBD_BITMAP_GI", - "value": "DrbdBitmapGi", + "name": "KEY_SEARCH_DOMAIN", + "value": "SearchDomain", "type": "string" }, { - "name": "KEY_DRBD_HISTORY_1_GI", - "value": "DrbdHistory1Gi", - "type": "string" + "blockcomment": "nvme property keys" }, { - "name": "KEY_DRBD_HISTORY_2_GI", - "value": "DrbdHistory2Gi", + "name": "KEY_TR_TYPE", + "value": "TRType", "type": "string" }, { - "name": "KEY_DMSTATS", - "value": "DMStats", + "blockcomment": "Snapshot property keys" + }, + { + "name": "KEY_SNAPSHOT", + "value": "Snapshot", "type": "string" }, { @@ -1855,21 +1853,22 @@ "type": "string" }, { - "name": "KEY_DRBD_AUTO_QUORUM", - "value": "auto-quorum", - "type": "string" + "blockcomment": "Network Interface property keys" }, { - "name": "KEY_DRBD_AUTO_ADD_QUORUM_TIEBREAKER", - "value": "auto-add-quorum-tiebreaker", + "name": "KEY_PORT", + "value": "Port", "type": "string" }, { - "name": "KEY_DRBD_AUTO_DISKFUL", - "value": "auto-diskful", + "name": "KEY_DISABLE_HTTP_METRICS", + "value": "disable-http-metrics", "type": "string" }, { + "blockcomment": "Writecache property keys" + }, + { "name": "KEY_WRITECACHE_BLOCKSIZE", "value": "Blocksize", "type": "string" @@ -1925,6 +1924,9 @@ "type": "string" }, { + "blockcomment": "Cache property keys" + }, + { "name": "KEY_CACHE_OPERATING_MODE", "value": "OpMode", "type": "string" @@ -1960,6 +1962,14 @@ "type": "string" }, { + "name": "KEY_UPDATE_CACHE_INTERVAL", + "value": "UpdateCacheInterval", + "type": "string" + }, + { + "blockcomment": "Autoplace property keys" + }, + { "name": "KEY_AUTOPLACE_STRAT_WEIGHT_MAX_FREESPACE", "value": "MaxFreeSpace", "type": "string" @@ -1990,6 +2000,37 @@ "type": "string" }, { + "name": "KEY_SITE", + "value": "Site", + "type": "string" + }, + { + "blockcomment": "Auto-Evict property keys" + }, + { + "name": "KEY_AUTO_EVICT_MIN_REPLICA_COUNT", + "value": "AutoEvictMinReplicaCount", + "type": "string" + }, + { + "name": "KEY_AUTO_EVICT_AFTER_TIME", + "value": "AutoEvictAfterTime", + "type": "string" + }, + { + "name": "KEY_AUTO_EVICT_MAX_DISCONNECTED_NODES", + "value": "AutoEvictMaxDisconnectedNodes", + "type": "string" + }, + { + "name": "KEY_AUTO_EVICT_ALLOW_EVICTION", + "value": "AutoEvictAllowEviction", + "type": "string" + }, + { + "blockcomment": "Snapshot shipping property keys" + }, + { "name": "KEY_SNAPSHOT_SHIPPING_PREFIX", "value": "SnapshotShippingPrefix", "type": "string" @@ -2025,6 +2066,11 @@ "type": "string" }, { + "name": "KEY_TCP_PORT_RANGE", + "value": "TcpPortRange", + "type": "string" + }, + { "blockcomment": "Property namespaces" }, { @@ -2058,6 +2104,11 @@ "type": "string" }, { + "name": "NAMESPC_NODE", + "value": "Node", + "type": "string" + }, + { "name": "NAMESPC_STORAGE_DRIVER", "value": "StorDriver", "type": "string" @@ -2186,6 +2237,21 @@ "blockcomment": "Storage pool property keys" }, { + "name": "KEY_STOR_POOL_DFN", + "value": "StorPoolDfn", + "type": "string" + }, + { + "name": "KEY_STOR_POOL_NAME", + "value": "StorPoolName", + "type": "string" + }, + { + "name": "KEY_STOR_POOL_DRBD_META_NAME", + "value": "StorPoolNameDrbdMeta", + "type": "string" + }, + { "name": "KEY_STOR_POOL_VOLUME_GROUP", "value": "LvmVg", "type": "string" @@ -2296,6 +2362,16 @@ "type": "string" }, { + "name": "KEY_OF_TARGET_PORT_AUTO_RANGE", + "value": "OpenflexTargetPortAutoRange", + "type": "string" + }, + { + "name": "KEY_PREF_NIC", + "value": "PrefNic", + "type": "string" + }, + { "blockcomment": "Storage pool traits keys" }, { @@ -2346,164 +2422,6 @@ "type": "string" }, { - "blockcomment": "Property keys" - }, - { - "name": "KEY_PORT_NR", - "value": "PortNr", - "type": "string" - }, - { - "name": "KEY_IP_ADDR", - "value": "IPAddr", - "type": "string" - }, - { - "name": "KEY_NETCOM_TYPE", - "value": "NetComType", - "type": "string" - }, - { - "name": "KEY_NETIF_NAME", - "value": "NetIfName", - "type": "string" - }, - { - "name": "KEY_NETIF_TYPE", - "value": "NetIfType", - "type": "string" - }, - { - "name": "KEY_NETCOM_ENABLED", - "value": "NetComEnabled", - "type": "string" - }, - { - "name": "KEY_KEYSTORE", - "value": "Keystore", - "type": "string" - }, - { - "name": "KEY_TRUSTSTORE", - "value": "Truststore", - "type": "string" - }, - { - "name": "KEY_KEY_PWD", - "value": "KeyPwd", - "type": "string" - }, - { - "name": "KEY_KEYSTORE_PWD", - "value": "KeystorePwd", - "type": "string" - }, - { - "name": "KEY_TRUSTSTORE_PWD", - "value": "TruststorePwd", - "type": "string" - }, - { - "name": "KEY_SSL_PROTO", - "value": "SslProto", - "type": "string" - }, - { - "name": "KEY_TCP_PORT_AUTO_RANGE", - "value": "TcpPortAutoRange", - "type": "string" - }, - { - "name": "KEY_MINOR_NR_AUTO_RANGE", - "value": "MinorNrAutoRange", - "type": "string" - }, - { - "name": "KEY_GLOBAL_SEQ_API_CALLS", - "value": "GlobSeqApiCalls", - "type": "string" - }, - { - "name": "KEY_OF_TARGET_PORT_AUTO_RANGE", - "value": "OpenflexTargetPortAutoRange", - "type": "string" - }, - { - "name": "KEY_CUR_STLT_CONN_NAME", - "value": "CurStltConnName", - "type": "string" - }, - { - "name": "KEY_PEER_SLOTS_NEW_RESOURCE", - "value": "PeerSlotsNewResource", - "type": "string" - }, - { - "name": "KEY_PEER_SLOTS", - "value": "PeerSlots", - "type": "string" - }, - { - "name": "KEY_ALLOW_LARGER_VOLUME_SIZE", - "value": "AllowLargerVolumeSize", - "type": "string" - }, - { - "name": "KEY_RSC_ROLLBACK_TARGET", - "value": "RollbackTarget", - "type": "string" - }, - { - "name": "KEY_RSC_MIGRATE_FROM", - "value": "MigrateFrom", - "type": "string" - }, - { - "name": "KEY_BIND_ADDR", - "value": "bindAddress", - "type": "string" - }, - { - "name": "KEY_BIND_PORT", - "value": "port", - "type": "string" - }, - { - "name": "KEY_ENABLED", - "value": "enabled", - "type": "string" - }, - { - "name": "KEY_TR_TYPE", - "value": "TRType", - "type": "string" - }, - { - "name": "KEY_PORT", - "value": "Port", - "type": "string" - }, - { - "name": "KEY_PREF_NIC", - "value": "PrefNic", - "type": "string" - }, - { - "name": "KEY_SEARCH_DOMAIN", - "value": "SearchDomain", - "type": "string" - }, - { - "name": "KEY_SITE", - "value": "Site", - "type": "string" - }, - { - "name": "KEY_DISABLE_HTTP_METRICS", - "value": "disable-http-metrics", - "type": "string" - }, - { "blockcomment": "File system property keys" }, { @@ -2540,6 +2458,16 @@ "type": "string" }, { + "name": "KEY_SYS_FS_BLKIO_THROTTLE_READ_IOPS", + "value": "blkio_throttle_read_iops", + "type": "string" + }, + { + "name": "KEY_SYS_FS_BLKIO_THROTTLE_WRITE_IOPS", + "value": "blkio_throttle_write_iops", + "type": "string" + }, + { "blockcomment": "Property values" }, { @@ -2778,6 +2706,11 @@ "type": "string" }, { + "name": "FLAG_EVICTED", + "value": "EVICTED", + "type": "string" + }, + { "name": "FLAG_DELETE", "value": "DELETE", "type": "string" diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/python-linstor-1.3.0/linstor-common/gendrbdoptions.py new/python-linstor-1.6.0/linstor-common/gendrbdoptions.py --- old/python-linstor-1.3.0/linstor-common/gendrbdoptions.py 2020-08-17 15:29:11.000000000 +0200 +++ new/python-linstor-1.6.0/linstor-common/gendrbdoptions.py 2020-12-21 10:17:31.000000000 +0100 @@ -17,6 +17,15 @@ 'peer-device-options': "DrbdOptions/PeerDevice" } +# map a CategoryNamespace to a .res file section +_ResfileSections = { + 'DrbdOptions/Net': 'net', + 'DrbdOptions/Disk': 'disk', + 'DrbdOptions/Resource': 'options', + 'DrbdOptions/PeerDevice': 'disk', + 'DrbdOptions/Handlers': 'handlers' +} + _ObjectCategories = { "controller": ['disk-options', 'resource-options', 'new-peer', 'peer-device-options'], "resource-definition": ['disk-options', 'resource-options', 'new-peer', 'peer-device-options'], @@ -48,10 +57,12 @@ if option_name in properties: raise RuntimeError("drbd option name already in use: " + option_name) + cmd_namespace = 'DrbdOptions/Handlers' properties[option_name] = { 'internal': True, - 'key': 'DrbdOptions/Handlers/' + option_name, + 'key': cmd_namespace + '/' + option_name, 'drbd_option_name': option_name, + 'drbd_res_file_section': _ResfileSections[cmd_namespace], 'type': 'string' } @@ -125,7 +136,8 @@ prop = { 'internal': True, 'key': cmd_namespace + '/' + option_name, - 'drbd_option_name': option_name + 'drbd_option_name': option_name, + 'drbd_res_file_section': _ResfileSections[cmd_namespace] } if option_type == 'string': diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/python-linstor-1.3.0/linstor-common/genproperties.py new/python-linstor-1.6.0/linstor-common/genproperties.py --- old/python-linstor-1.3.0/linstor-common/genproperties.py 2020-08-17 15:29:11.000000000 +0200 +++ new/python-linstor-1.6.0/linstor-common/genproperties.py 2020-12-21 10:17:31.000000000 +0100 @@ -176,6 +176,8 @@ _print(4, '.values(') _print(5, '"%s"' % ('",\n' + _indent(5) + '"').join(sorted(propVal))) _print(4, ')') + elif propKey == "default": + _print(4, '.dflt("%s")' % (propVal)) else: _print(4, '.%s("%s")' % (propKey, propVal)) _print(4, '.build()') @@ -211,7 +213,7 @@ def _relevant_for_java(propKey): - return propKey not in ['default', 'drbd_option_name', 'unit_prefix', 'unit'] + return propKey not in ['drbd_option_name', 'unit_prefix', 'drbd_res_file_section'] def _as_java_rule_name(name): diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/python-linstor-1.3.0/linstor-common/properties.json new/python-linstor-1.6.0/linstor-common/properties.json --- old/python-linstor-1.3.0/linstor-common/properties.json 2020-08-17 15:29:11.000000000 +0200 +++ new/python-linstor-1.6.0/linstor-common/properties.json 2020-12-21 10:17:31.000000000 +0100 @@ -119,18 +119,6 @@ "value": "^|.internal|[a-zA-Z0-9_][a-zA-Z0-9_-]{2,47}$", "info": "Linstor storage pool name to use for external metadata." }, - "storage_pool_drbd_meta_type": { - "key": [ - "KEY_STOR_POOL_DRBD_META_TYPE" - ], - "type": "symbol", - "default": "internal", - "values": [ - "external", - "internal" - ], - "info": "Where to place DRBD meta data" - }, "storage_pool_openflex_api_host": { "key": [ "NAMESPC_STORAGE_DRIVER", @@ -236,13 +224,6 @@ "value": "[0-9]+-[0-9]+", "info": "Range for auto-allocation of volume minor numbers" }, - "global_seq_api_calls": { - "key": [ - "KEY_GLOBAL_SEQ_API_CALLS" - ], - "type": "boolean_true_false", - "info": "Globally sequentialize all ctrl -> stlt api calls" - }, "peer_slots_new_resource": { "key": [ "KEY_PEER_SLOTS_NEW_RESOURCE" @@ -262,13 +243,6 @@ "info": "DRBD peer slots present on resource", "internal": true }, - "allow_larger_volume_size": { - "key": [ - "KEY_ALLOW_LARGER_VOLUME_SIZE" - ], - "type": "boolean_true_false", - "info": "Do not fail when the underlying volume is larger than expected" - }, "dm_stats": { "key": [ "NAMESPC_STORAGE_DRIVER", @@ -521,35 +495,6 @@ "value": "[a-fA-F0-9]{16}", "info": "Initial DRBD generation id, if initial sync is skipped, this ID will be set" }, - "rest_listen_addr": { - "internal": false, - "key": [ - "NAMESPC_REST", - "KEY_BIND_ADDR" - ], - "type": "string", - "info": "Bind address of the REST API" - }, - "rest_listen_port": { - "internal": false, - "key": [ - "NAMESPC_REST", - "KEY_BIND_PORT" - ], - "type": "range", - "min": 1, - "max": 65535, - "info": "TCP Port of the REST API" - }, - "rest_enabled": { - "internal": false, - "key": [ - "NAMESPC_REST", - "KEY_ENABLED" - ], - "info": "Bool if REST API should be enabled", - "type": "boolean_true_false" - }, "rest_disable_http_metrics": { "internal": false, "key": [ @@ -595,7 +540,7 @@ "info": "Preferred network interface to use" }, "fs_type": { - "internal": true, + "internal": false, "key": [ "NAMESPC_FILESYSTEM", "KEY_FS_TYPE" @@ -634,6 +579,24 @@ "info": "Sets the /sys/fs/cgroup/blkio/blkio.throttle.write_bps_device", "type": "long" }, + "sys_fs_blkio_throttle_read_iops": { + "internal": false, + "key": [ + "NAMESPC_SYS_FS", + "KEY_SYS_FS_BLKIO_THROTTLE_READ_IOPS" + ], + "info": "Sets the /sys/fs/cgroup/blkio/blkio.throttle.read_iops_device", + "type": "long" + }, + "sys_fs_blkio_throttle_write_iops": { + "internal": false, + "key": [ + "NAMESPC_SYS_FS", + "KEY_SYS_FS_BLKIO_THROTTLE_WRITE_IOPS" + ], + "info": "Sets the /sys/fs/cgroup/blkio/blkio.throttle.write_iops_device", + "type": "long" + }, "search_domain": { "internal": false, "key": ["KEY_SEARCH_DOMAIN"], @@ -646,6 +609,48 @@ "info": "Wait timeout for an external command in milliseconds", "type": "long" }, + "auto_evict_after_time": { + "internal": false, + "key": [ + "NAMESPC_DRBD_OPTIONS", + "KEY_AUTO_EVICT_AFTER_TIME" + ], + "info": "Time a node can be offline before it is declared EVICTED in minutes", + "type": "long", + "default": "1 hour" + }, + "auto_evict_max_disconnected_nodes": { + "internal": false, + "key": [ + "NAMESPC_DRBD_OPTIONS", + "KEY_AUTO_EVICT_MAX_DISCONNECTED_NODES" + ], + "info": "Percentage(0-100) of nodes that can disconnect at the same time without the controller stopping the max_offline_time timer", + "type": "range", + "min": 0, + "max": 100, + "default": 34 + }, + "auto_evict_min_replica_count": { + "internal": false, + "key": [ + "NAMESPC_DRBD_OPTIONS", + "KEY_AUTO_EVICT_MIN_REPLICA_COUNT" + ], + "info": "The minimum amount of replicas that should be present for a resource at all times.", + "type": "long", + "default": "The place count set for the resource group" + }, + "auto_evict_allow_eviction": { + "internal": false, + "key": [ + "NAMESPC_DRBD_OPTIONS", + "KEY_AUTO_EVICT_ALLOW_EVICTION" + ], + "info": "If set to true on a node, it cannot be evicted anymore", + "type": "boolean_true_false", + "default": "true" + }, "drbd_auto_quorum": { "internal": false, "key": [ @@ -678,6 +683,15 @@ "info": "Makes a resource diskful if it was continously diskless primary for X minutes", "type": "long" }, + "drbd_auto_diskful_allow_cleanup": { + "internal": false, + "key": [ + "NAMESPC_DRBD_OPTIONS", + "KEY_DRBD_AUTO_DISKFUL_ALLOW_CLEANUP" + ], + "info": "Allows this resource to be cleaned up after toggle-disk + resync is finished", + "type": "boolean_true_false" + }, "writecache_pool_name": { "key": [ "NAMESPC_WRITECACHE", @@ -696,15 +710,6 @@ "value": "^100%|[0-9]{1,2}([.][0-9]*)?%|[1-9][0-9]{2,}$", "info": "Size of the writecache in % (0-100) or KiB otherwise" }, - "writecache_block_size": { - "key": [ - "NAMESPC_WRITECACHE", - "KEY_WRITECACHE_BLOCKSIZE" - ], - "type": "long", - "info": "4096 is recommended; the maximum block size is the page size", - "default": "4096" - }, "writecache_opt_start_sector": { "key": [ @@ -1006,6 +1011,25 @@ ], "type": "string", "info": "Node name of the snapshot shipping source" + }, + "snapshot_shipping_port_range": + { + "key": [ + "NAMESPC_SNAPSHOT_SHIPPING", + "KEY_TCP_PORT_RANGE" + ], + "type": "regex", + "value": "[0-9]+-[0-9]+", + "info": "Range for allocation of snapshot shipping TCP ports" + }, + "update_cache_interval": + { + "key": [ + "KEY_UPDATE_CACHE_INTERVAL" + ], + "type": "long", + "info": "Interval for space cache background updates in seconds", + "default": "180" } }, "objects": { @@ -1014,25 +1038,27 @@ "minor_nr_range", "peer_slots_new_resource", "dm_stats", - "global_seq_api_calls", - "rest_listen_addr", - "rest_listen_port", - "rest_enabled", "rest_disable_http_metrics", + "auto_evict_after_time", + "auto_evict_max_disconnected_nodes", + "auto_evict_min_replica_count", + "auto_evict_allow_eviction", "nvme_tr_type", "nvme_port", "nvme_preferred_nic", "stordriver_wait_timeout_after_create", "sys_fs_blkio_throttle_read", "sys_fs_blkio_throttle_write", + "sys_fs_blkio_throttle_read_iops", + "sys_fs_blkio_throttle_write_iops", "search_domain", "ext_cmd_wait_to", "drbd_auto_quorum", "drbd_auto_add_quorum_tie_breaker", "drbd_auto_diskful", + "drbd_auto_diskful_allow_cleanup", "writecache_pool_name", "writecache_size", - "writecache_block_size", "writecache_opt_start_sector", "writecache_opt_high_watermark", "writecache_opt_low_watermark", @@ -1064,18 +1090,22 @@ "autoplacer_pre_select_script_timeout", "autoplacer_max_throughput", "proxy_auto_enable", - "site" + "site", + "update_cache_interval", + "snapshot_shipping_port_range" ], "node": [ "preferred_nic", "storage_pool_name", "storage_pool_drbd_meta_name", - "storage_pool_drbd_meta_type", "sys_fs_blkio_throttle_read", "sys_fs_blkio_throttle_write", + "sys_fs_blkio_throttle_read_iops", + "sys_fs_blkio_throttle_write_iops", + "auto_evict_after_time", + "auto_evict_allow_eviction", "writecache_pool_name", "writecache_size", - "writecache_block_size", "writecache_opt_start_sector", "writecache_opt_high_watermark", "writecache_opt_low_watermark", @@ -1104,12 +1134,14 @@ "autoplacer_max_throughput", "proxy_auto_enable", "site", - "drbd_auto_diskful" + "drbd_auto_diskful_allow_cleanup" ], "storagepool-definition": [ "max_oversubscription_ratio", "sys_fs_blkio_throttle_read", - "sys_fs_blkio_throttle_write" + "sys_fs_blkio_throttle_write", + "sys_fs_blkio_throttle_read_iops", + "sys_fs_blkio_throttle_write_iops" ], "storagepool": [ "stordriver_pool_name", @@ -1126,6 +1158,8 @@ "stordriver_wait_timeout_after_create", "sys_fs_blkio_throttle_read", "sys_fs_blkio_throttle_write", + "sys_fs_blkio_throttle_read_iops", + "sys_fs_blkio_throttle_write_iops", "storage_pool_openflex_api_host", "storage_pool_openflex_api_port", "storage_pool_openflex_user_name", @@ -1138,9 +1172,9 @@ "resource-definition": [ "storage_pool_name", "storage_pool_drbd_meta_name", - "storage_pool_drbd_meta_type", "peer_slots_new_resource", "proxy_compression_type", + "auto_evict_min_replica_count", "fs_type", "fs_mkfsparameters", "nvme_tr_type", @@ -1150,12 +1184,14 @@ "stordriver_zfs_create_options", "sys_fs_blkio_throttle_read", "sys_fs_blkio_throttle_write", + "sys_fs_blkio_throttle_read_iops", + "sys_fs_blkio_throttle_write_iops", "drbd_auto_quorum", "drbd_auto_add_quorum_tie_breaker", "drbd_auto_diskful", + "drbd_auto_diskful_allow_cleanup", "writecache_pool_name", "writecache_size", - "writecache_block_size", "writecache_opt_start_sector", "writecache_opt_high_watermark", "writecache_opt_low_watermark", @@ -1182,7 +1218,6 @@ "resource": [ "storage_pool_name", "storage_pool_drbd_meta_name", - "storage_pool_drbd_meta_type", "preferred_nic", "peer_slots", "fs_type", @@ -1190,14 +1225,14 @@ "nvme_preferred_nic", "sys_fs_blkio_throttle_read", "sys_fs_blkio_throttle_write", - "drbd_auto_diskful" + "sys_fs_blkio_throttle_read_iops", + "sys_fs_blkio_throttle_write_iops", + "drbd_auto_diskful_allow_cleanup" ], "volume-definition": [ "storage_pool_name", "storage_pool_drbd_meta_name", - "storage_pool_drbd_meta_type", "override_vlm_id", - "allow_larger_volume_size", "fs_type", "fs_mkfsparameters", "stordriver_lvcreate_type", @@ -1206,9 +1241,10 @@ "drbd_current_gi", "sys_fs_blkio_throttle_read", "sys_fs_blkio_throttle_write", + "sys_fs_blkio_throttle_read_iops", + "sys_fs_blkio_throttle_write_iops", "writecache_pool_name", "writecache_size", - "writecache_block_size", "writecache_opt_start_sector", "writecache_opt_high_watermark", "writecache_opt_low_watermark", @@ -1227,7 +1263,9 @@ ], "volume": [ "sys_fs_blkio_throttle_read", - "sys_fs_blkio_throttle_write" + "sys_fs_blkio_throttle_write", + "sys_fs_blkio_throttle_read_iops", + "sys_fs_blkio_throttle_write_iops" ], "drbd-proxy": [ "proxy_memlimit", diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/python-linstor-1.3.0/python_linstor.egg-info/PKG-INFO new/python-linstor-1.6.0/python_linstor.egg-info/PKG-INFO --- old/python-linstor-1.3.0/python_linstor.egg-info/PKG-INFO 2020-08-17 15:41:04.000000000 +0200 +++ new/python-linstor-1.6.0/python_linstor.egg-info/PKG-INFO 2020-12-21 10:17:32.000000000 +0100 @@ -1,6 +1,6 @@ Metadata-Version: 1.2 Name: python-linstor -Version: 1.3.0 +Version: 1.6.0 Summary: Linstor python api Home-page: https://www.linbit.com Author: Robert Altnoeder <robert.altnoe...@linbit.com>, Roland Kammerer <roland.kamme...@linbit.com>, Rene Peinthor <rene.peint...@linbit.com>