diff --git a/ceph/python-cephclient/centos/build_srpm.data b/ceph/python-cephclient/centos/build_srpm.data new file mode 100644 index 00000000..e6b7ea23 --- /dev/null +++ b/ceph/python-cephclient/centos/build_srpm.data @@ -0,0 +1,2 @@ +SRC_DIR="python-cephclient" +TIS_PATCH_VER=0 diff --git a/ceph/python-cephclient/centos/python-cephclient.spec b/ceph/python-cephclient/centos/python-cephclient.spec new file mode 100644 index 00000000..0d531b6d --- /dev/null +++ b/ceph/python-cephclient/centos/python-cephclient.spec @@ -0,0 +1,65 @@ +Summary: Handle Ceph API calls and provide status updates via alarms +Name: python-cephclient +Version: 13.2.2.0 +Release: %{tis_patch_ver}%{?_tis_dist} +License: Apache-2.0 +Group: base +Packager: Wind River +URL: https://github.com/openstack/stx-integ/tree/master/ceph/python-cephclient/python-cephclient' +Source0: %{name}-%{version}.tar.gz + +BuildArch: noarch + +BuildRequires: python +BuildRequires: python2-pip +BuildRequires: python2-wheel + +Requires: python +Requires: python-ipaddress +Requires: python2-six +Requires: python2-requests + +Provides: python-cephclient + +%description +A client library in Python for Ceph Mgr RESTful plugin providing REST API +access to the cluster over an SSL-secured connection. Python API is compatible +with the old Python Ceph client at +https://github.com/dmsimard/python-cephclient that no longer works in Ceph +mimic because Ceph REST API component was removed. + +%define debug_package %{nil} + +%prep +%autosetup -p 1 -n %{name}-%{version} + +rm -rf .pytest_cache +rm -rf python_cephclient.egg-info +rm -f requirements.txt + +%build +%{__python} setup.py build +%py2_build_wheel + +%install +%{__python2} setup.py install --skip-build --root %{buildroot} +mkdir -p $RPM_BUILD_ROOT/wheels +install -m 644 dist/*.whl $RPM_BUILD_ROOT/wheels/ + +%clean +rm -rf $RPM_BUILD_ROOT + +%files +%defattr(-,root,root,-) +%license LICENSE +%{python2_sitelib}/cephclient +%{python2_sitelib}/*.egg-info + +%package wheels +Summary: %{name} wheels + +%description wheels +Contains python wheels for %{name} + +%files wheels +/wheels/* diff --git a/ceph/python-cephclient/python-cephclient/.gitignore b/ceph/python-cephclient/python-cephclient/.gitignore new file mode 100644 index 00000000..2e436dc5 --- /dev/null +++ b/ceph/python-cephclient/python-cephclient/.gitignore @@ -0,0 +1,2 @@ +.pytest_cache +*.egg-info diff --git a/ceph/python-cephclient/python-cephclient/LICENSE b/ceph/python-cephclient/python-cephclient/LICENSE new file mode 100644 index 00000000..d6e28015 --- /dev/null +++ b/ceph/python-cephclient/python-cephclient/LICENSE @@ -0,0 +1,202 @@ + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright 2019 Wind River Systems, Inc. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/ceph/python-cephclient/python-cephclient/cephclient/__init__.py b/ceph/python-cephclient/python-cephclient/cephclient/__init__.py new file mode 100644 index 00000000..0c941312 --- /dev/null +++ b/ceph/python-cephclient/python-cephclient/cephclient/__init__.py @@ -0,0 +1,5 @@ +# +# Copyright (c) 2019 Wind River Systems, Inc. +# +# SPDX-License-Identifier: Apache-2.0 +# diff --git a/ceph/python-cephclient/python-cephclient/cephclient/client.py b/ceph/python-cephclient/python-cephclient/cephclient/client.py new file mode 100644 index 00000000..331e6536 --- /dev/null +++ b/ceph/python-cephclient/python-cephclient/cephclient/client.py @@ -0,0 +1,5790 @@ +# +# Copyright (c) 2019 Wind River Systems, Inc. +# +# SPDX-License-Identifier: Apache-2.0 +# + +import ipaddress +import json +import logging +import re +import requests +import six +import subprocess +import time + +from cephclient.exception import CephMonRestfulListKeysError +from cephclient.exception import CephMonRestfulJsonError +from cephclient.exception import CephMonRestfulMissingUserCredentials +from cephclient.exception import CephMgrDumpError +from cephclient.exception import CephMgrJsonError +from cephclient.exception import CephMgrMissingRestfulService +from cephclient.exception import CephClientFormatNotSupported +from cephclient.exception import CephClientResponseFormatNotImplemented +from cephclient.exception import CephClientInvalidChoice +from cephclient.exception import CephClientTypeError +from cephclient.exception import CephClientValueOutOfBounds +from cephclient.exception import CephClientInvalidPgid +from cephclient.exception import CephClientInvalidIPAddr +from cephclient.exception import CephClientInvalidOsdIdValue +from cephclient.exception import CephClientNoSuchUser +from cephclient.exception import CephClientIncorrectPassword + + +CEPH_MON_RESTFUL_USER = 'admin' +CEPH_MON_RESTFUL_SERVICE = 'restful' +CEPH_CLIENT_RETRY_COUNT = 2 +CEPH_CLIENT_RETRY_TIMEOUT_SEC = 5 +CEPH_CLI_TIMEOUT_SEC = 5 +API_SUPPORTED_RESPONSE_FORMATS = [ + 'text', 'json', 'xml', 'binary' +] + +LOG = logging.getLogger('ceph_client') +LOG.setLevel(logging.DEBUG) +ch = logging.StreamHandler() +ch.setLevel(logging.DEBUG) +ch.setFormatter(logging.Formatter( + '%(asctime)s %(levelname)s %(name)s %(message)s')) +LOG.addHandler(ch) + + +class CephClient(object): + + def __init__(self, + username=CEPH_MON_RESTFUL_USER, + password=None, + retry_count=CEPH_CLIENT_RETRY_COUNT, + retry_timeout=CEPH_CLIENT_RETRY_TIMEOUT_SEC): + self.username = username + self.password = password + self.check_certificate = True + self.service_url = None + # TODO: fix certificates + self._disable_certificate_checks() + self.session = None + self.retry_count = retry_count + self.retry_timeout = retry_timeout + + def _refresh_session(self): + self.session = requests.Session() + self.session.auth = (self.username, self.password) + + def _disable_certificate_checks(self): + self.check_certificate = False + requests.packages.urllib3.disable_warnings() + LOG.warning('skip checking server certificate') + + def _get_password(self): + try: + output = subprocess.check_output( + ('ceph restful list-keys ' + '--connect-timeout {}').format( + CEPH_CLI_TIMEOUT_SEC), + shell=True) + except subprocess.CalledProcessError as e: + raise CephMonRestfulListKeysError(str(e)) + try: + keys = json.loads(output) + except (KeyError, ValueError): + raise CephMonRestfulJsonError(output) + try: + self.password = keys[self.username] + except KeyError: + raise CephMonRestfulMissingUserCredentials(self.username) + + def _get_service_url(self): + try: + output = subprocess.check_output( + ('ceph mgr dump ' + '--connect-timeout {}').format( + CEPH_CLI_TIMEOUT_SEC), + shell=True) + except subprocess.CalledProcessError as e: + raise CephMgrDumpError(str(e)) + try: + status = json.loads(output) + except (KeyError, ValueError): + raise CephMgrJsonError(output) + try: + self.service_url = status["services"][CEPH_MON_RESTFUL_SERVICE] + except (KeyError, TypeError): + raise CephMgrMissingRestfulService( + status.get('services', '')) + + def _make_text_result(self, prefix, result): + if result.get('has_failed'): + assert(len(result['failed']) == 1) + response = requests.Response() + response.status_code = requests.codes.internal_server_error + response.reason = result['failed'][0]['outs'].rstrip() + return response, response.reason + else: + assert(len(result['finished']) == 1) + response = requests.Response() + response.status_code = requests.codes.ok + response.reason = "OK" + return response, result['finished'][0]['outb'].rstrip() + + def _apply_json_result_workarounds(self, prefix, outb): + if prefix == 'osd crush tree': + # ceph mgr strangely adds a pair of square brackets at the end + while outb.endswith('][]'): + LOG.info("Trim 'osd crush tree' json response") + outb = outb[:-2] + return outb + + def _make_json_result(self, prefix, result): + if result.get('has_failed'): + assert(len(result['failed']) == 1) + response = requests.Response() + response.status_code = requests.codes.internal_server_error + response.reason = result['failed'][0]['outs'] + return response, dict( + status=result['failed'][0]['outs'], + output=result['failed'][0]['outb']) + else: + assert(len(result['finished']) == 1) + outb = result['finished'][0]['outb'] + outb = self._apply_json_result_workarounds(prefix, outb) + response = requests.Response() + response.status_code = requests.codes.ok + response.reason = "OK" + try: + return response, dict( + status=result['finished'][0]['outs'], + output=json.loads(outb or 'null')) + except (ValueError, TypeError): + raise CephMgrJsonError(outb) + + def _request(self, prefix, *args, **kwargs): + if not self.password: + self._get_password() + if not self.service_url: + self._get_service_url() + if not self.session: + self._refresh_session() + format = kwargs.get('body', 'json').lower() + if format not in API_SUPPORTED_RESPONSE_FORMATS: + raise CephClientFormatNotSupported( + prefix=prefix, format=format) + del kwargs['body'] + req_json = dict(kwargs) + req_json['format'] = format + assert('prefix' not in kwargs) + req_json['prefix'] = prefix + if 'timeout' in req_json: + timeout = req_json['timeout'] + del req_json['timeout'] + else: + timeout = None + LOG.info('Request params: url={}, json={}'.format( + self.service_url + 'request?wait=1', req_json)) + credit = self.retry_count + 1 + while credit > 0: + credit -= 1 + try: + result = self.session.post( + self.service_url + 'request?wait=1', + json=req_json, + verify=self.check_certificate, + timeout=timeout).json() + LOG.info('Result: {}'.format(result)) + if 'is_finished' not in result: + assert('message' in result) + if 'auth: No such user' in result['message']: + raise CephClientNoSuchUser(user=self.username) + elif 'auth: Incorrect password' in result['message']: + raise CephClientIncorrectPassword( + user=self.username) + break + except CephClientIncorrectPassword: + if not credit: + raise + LOG.warning('Incorrect password for user \'{}\'. ' + 'Fetch user password via list-keys ' + 'and retry.'.format(self.username)) + if self.retry_timeout > 0: + time.sleep(self.retry_timeout) + self._get_password() + self._refresh_session() + except (requests.ConnectionError, + requests.Timeout, + requests.HTTPError) as e: + if not credit: + raise IOError(str(e)) + LOG.warning( + 'Request error: {}. ' + 'Refresh restful service URL and retry'.format(e)) + if self.retry_timeout > 0: + time.sleep(self.retry_timeout) + self._get_service_url() + self._refresh_session() + if format == 'json': + return self._make_json_result(prefix, result) + elif format == 'text': + return self._make_text_result(prefix, result) + else: + raise CephClientResponseFormatNotImplemented( + format=format, reason=result["finished"][0]["outb"]) + + def pg_stat(self, body='json', timeout=None): + """show placement group status.""" + return self._request('pg stat', body=body, timeout=timeout) + + def pg_getmap(self, body='json', timeout=None): + """get binary pg map to -o/stdout""" + return self._request('pg getmap', body=body, timeout=timeout) + + PG_DUMP_DUMPCONTENTS_VALUES = \ + ['all', 'summary', 'sum', 'delta', 'pools', + 'osds', 'pgs', 'pgs_brief'] + + def pg_dump(self, dumpcontents=None, body='json', timeout=None): + """show human-readable versions of pg map (only 'all' valid with plain)""" + kwargs = dict(body=body, timeout=timeout) + if dumpcontents is not None: + if not isinstance(dumpcontents, six.string_types): + raise CephClientTypeError( + name='dumpcontents', + actual=type(dumpcontents), + expected=six.string_types) + supported = CephClient.PG_DUMP_DUMPCONTENTS_VALUES + if dumpcontents not in supported: + raise CephClientInvalidChoice( + function='pg_dump', + option='dumpcontents', + value=dumpcontents, + supported=', '.join(supported)) + if not isinstance(dumpcontents, list): + dumpcontents = [dumpcontents] + kwargs['dumpcontents'] = dumpcontents + return self._request('pg dump', **kwargs) + + PG_DUMP_JSON_DUMPCONTENTS_VALUES = [ + 'all', 'summary', 'sum', 'pools', 'osds', 'pgs'] + + def pg_dump_json(self, dumpcontents=None, body='json', timeout=None): + """show human-readable version of pg map in json only""" + kwargs = dict(body=body, timeout=timeout) + if dumpcontents is not None: + if not isinstance(dumpcontents, six.string_types): + raise CephClientTypeError( + name='dumpcontents', + actual=type(dumpcontents), + expected=six.string_types) + supported = CephClient.PG_DUMP_JSON_DUMPCONTENTS_VALUES + if dumpcontents not in supported: + raise CephClientInvalidChoice( + function='pg_dump_json', + option='dumpcontents', + value=dumpcontents, + supported=', '.join(supported)) + if not isinstance(dumpcontents, list): + dumpcontents = [dumpcontents] + kwargs['dumpcontents'] = dumpcontents + return self._request('pg dump_json', **kwargs) + + def pg_dump_pools_json(self, body='json', timeout=None): + """show pg pools info in json only""" + return self._request('pg dump_pools_json', + body=body, timeout=timeout) + + def pg_ls_by_pool(self, poolstr, states=None, + body='json', timeout=None): + """list pg with pool = [poolname]""" + kwargs = dict(body=body, timeout=timeout) + if not isinstance(poolstr, six.string_types): + raise CephClientTypeError( + name='poolstr', + actual=type(poolstr), + expected=six.string_types) + + kwargs['poolstr'] = poolstr + if states is not None: + if isinstance(states, list): + for item in states: + if not isinstance(item, six.string_types): + raise CephClientTypeError( + name='states', + actual=item, + expected='list of strings') + else: + if not isinstance(states, six.string_types): + raise CephClientTypeError( + name='states', + actual=type(states), + expected=six.string_types) + if not isinstance(states, list): + states = [states] + kwargs['states'] = states + return self._request('pg ls-by-pool', **kwargs) + + def pg_ls_by_primary(self, osd, pool=None, + states=None, body='json', timeout=None): + """list pg with primary = [osd]""" + kwargs = dict(body=body, timeout=timeout) + if isinstance(osd, six.integer_types): + pass + elif isinstance(osd, six.string_types): + osd = osd.lower() + prefix = 'osd.' + if not osd.startswith(prefix): + raise CephClientInvalidOsdIdValue(osdid=osd) + osd = int(osd[len(prefix):]) + else: + raise CephClientTypeError( + name='osd', + actual=type(osd), + expected='int or string') + + kwargs['osd'] = osd + if pool is not None: + if not isinstance(pool, six.integer_types): + raise CephClientTypeError( + name='pool', + actual=type(pool), + expected=int) + kwargs['pool'] = pool + if states is not None: + if isinstance(states, list): + for item in states: + if not isinstance(item, six.string_types): + raise CephClientTypeError( + name='states', + actual=item, + expected='list of strings') + else: + if not isinstance(states, six.string_types): + raise CephClientTypeError( + name='states', + actual=type(states), + expected=six.string_types) + if not isinstance(states, list): + states = [states] + kwargs['states'] = states + return self._request('pg ls-by-primary', **kwargs) + + def pg_ls_by_osd(self, osd, pool=None, states=None, + body='json', timeout=None): + """list pg on osd [osd]""" + kwargs = dict(body=body, timeout=timeout) + if isinstance(osd, six.integer_types): + pass + elif isinstance(osd, six.string_types): + osd = osd.lower() + prefix = 'osd.' + if not osd.startswith(prefix): + raise CephClientInvalidOsdIdValue(osdid=osd) + osd = int(osd[len(prefix):]) + else: + raise CephClientTypeError( + name='osd', + actual=type(osd), + expected='int or string') + + kwargs['osd'] = osd + if pool is not None: + if not isinstance(pool, six.integer_types): + raise CephClientTypeError( + name='pool', + actual=type(pool), + expected=int) + kwargs['pool'] = pool + if states is not None: + if isinstance(states, list): + for item in states: + if not isinstance(item, six.string_types): + raise CephClientTypeError( + name='states', + actual=item, + expected='list of strings') + else: + if not isinstance(states, six.string_types): + raise CephClientTypeError( + name='states', + actual=type(states), + expected=six.string_types) + if not isinstance(states, list): + states = [states] + kwargs['states'] = states + return self._request('pg ls-by-osd', **kwargs) + + def pg_ls(self, pool=None, states=None, body='json', timeout=None): + """list pg with specific pool, osd, state""" + kwargs = dict(body=body, timeout=timeout) + if pool is not None: + if not isinstance(pool, six.integer_types): + raise CephClientTypeError( + name='pool', + actual=type(pool), + expected=int) + kwargs['pool'] = pool + if states is not None: + if isinstance(states, list): + for item in states: + if not isinstance(item, six.string_types): + raise CephClientTypeError( + name='states', + actual=item, + expected='list of strings') + else: + if not isinstance(states, six.string_types): + raise CephClientTypeError( + name='states', + actual=type(states), + expected=six.string_types) + if not isinstance(states, list): + states = [states] + kwargs['states'] = states + return self._request('pg ls', **kwargs) + + PG_DUMP_STUCK_STUCKOPS_VALUES = \ + ['inactive', 'unclean', 'stale', 'undersized', + 'degraded'] + + def pg_dump_stuck(self, stuckops=None, threshold=None, + body='json', timeout=None): + """show information about stuck pgs""" + kwargs = dict(body=body, timeout=timeout) + if stuckops is not None: + if not isinstance(stuckops, six.string_types): + raise CephClientTypeError( + name='stuckops', + actual=type(stuckops), + expected=six.string_types) + supported = CephClient.PG_DUMP_STUCK_STUCKOPS_VALUES + if stuckops not in supported: + raise CephClientInvalidChoice( + function='pg_dump_stuck', + option='stuckops', + value=stuckops, + supported=', '.join(supported)) + if not isinstance(stuckops, list): + stuckops = [stuckops] + kwargs['stuckops'] = stuckops + if threshold is not None: + if not isinstance(threshold, six.integer_types): + raise CephClientTypeError( + name='threshold', + actual=type(threshold), + expected=int) + kwargs['threshold'] = threshold + return self._request('pg dump_stuck', **kwargs) + + PG_DEBUG_DEBUGOP_VALUES = \ + ['unfound_objects_exist', 'degraded_pgs_exist'] + + def pg_debug(self, debugop, body='json', timeout=None): + """show debug info about pgs""" + kwargs = dict(body=body, timeout=timeout) + if not isinstance(debugop, six.string_types): + raise CephClientTypeError( + name='debugop', + actual=type(debugop), + expected=six.string_types) + supported = CephClient.PG_DEBUG_DEBUGOP_VALUES + if debugop not in supported: + raise CephClientInvalidChoice( + function='pg_debug', + option='debugop', + value=debugop, + supported=', '.join(supported)) + + kwargs['debugop'] = debugop + return self._request('pg debug', **kwargs) + + def pg_scrub(self, pgid, body='json', timeout=None): + """start scrub on """ + kwargs = dict(body=body, timeout=timeout) + if not isinstance(pgid, six.string_types): + raise CephClientTypeError( + name='pgid', + actual=type(pgid), + expected=six.string_types) + if not re.match(r'[0-9]+\.[0-9a-fA-F]+', pgid): + raise CephClientInvalidPgid( + name='pgid', + actual=pgid) + + kwargs['pgid'] = pgid + return self._request('pg scrub', **kwargs) + + def pg_deep_scrub(self, pgid, body='json', timeout=None): + """start deep-scrub on """ + kwargs = dict(body=body, timeout=timeout) + if not isinstance(pgid, six.string_types): + raise CephClientTypeError( + name='pgid', + actual=type(pgid), + expected=six.string_types) + if not re.match(r'[0-9]+\.[0-9a-fA-F]+', pgid): + raise CephClientInvalidPgid( + name='pgid', + actual=pgid) + + kwargs['pgid'] = pgid + return self._request('pg deep-scrub', **kwargs) + + def pg_repair(self, pgid, body='json', timeout=None): + """start repair on """ + kwargs = dict(body=body, timeout=timeout) + if not isinstance(pgid, six.string_types): + raise CephClientTypeError( + name='pgid', + actual=type(pgid), + expected=six.string_types) + if not re.match(r'[0-9]+\.[0-9a-fA-F]+', pgid): + raise CephClientInvalidPgid( + name='pgid', + actual=pgid) + + kwargs['pgid'] = pgid + return self._request('pg repair', **kwargs) + + def pg_force_recovery(self, pgid, body='json', timeout=None): + """force recovery of first""" + kwargs = dict(body=body, timeout=timeout) + if not isinstance(pgid, six.string_types): + raise CephClientTypeError( + name='pgid', + actual=type(pgid), + expected=six.string_types) + if not re.match(r'[0-9]+\.[0-9a-fA-F]+', pgid): + raise CephClientInvalidPgid( + name='pgid', + actual=pgid) + + if not isinstance(pgid, list): + pgid = [pgid] + kwargs['pgid'] = pgid + return self._request('pg force-recovery', **kwargs) + + def pg_force_backfill(self, pgid, body='json', timeout=None): + """force backfill of first""" + kwargs = dict(body=body, timeout=timeout) + if not isinstance(pgid, six.string_types): + raise CephClientTypeError( + name='pgid', + actual=type(pgid), + expected=six.string_types) + if not re.match(r'[0-9]+\.[0-9a-fA-F]+', pgid): + raise CephClientInvalidPgid( + name='pgid', + actual=pgid) + + if not isinstance(pgid, list): + pgid = [pgid] + kwargs['pgid'] = pgid + return self._request('pg force-backfill', **kwargs) + + def pg_cancel_force_recovery(self, pgid, body='json', timeout=None): + """restore normal recovery priority of """ + kwargs = dict(body=body, timeout=timeout) + if not isinstance(pgid, six.string_types): + raise CephClientTypeError( + name='pgid', + actual=type(pgid), + expected=six.string_types) + if not re.match(r'[0-9]+\.[0-9a-fA-F]+', pgid): + raise CephClientInvalidPgid( + name='pgid', + actual=pgid) + + if not isinstance(pgid, list): + pgid = [pgid] + kwargs['pgid'] = pgid + return self._request('pg cancel-force-recovery', **kwargs) + + def pg_cancel_force_backfill(self, pgid, body='json', timeout=None): + """restore normal backfill priority of """ + kwargs = dict(body=body, timeout=timeout) + if not isinstance(pgid, six.string_types): + raise CephClientTypeError( + name='pgid', + actual=type(pgid), + expected=six.string_types) + if not re.match(r'[0-9]+\.[0-9a-fA-F]+', pgid): + raise CephClientInvalidPgid( + name='pgid', + actual=pgid) + + if not isinstance(pgid, list): + pgid = [pgid] + kwargs['pgid'] = pgid + return self._request('pg cancel-force-backfill', **kwargs) + + def osd_perf(self, body='json', timeout=None): + """print dump of OSD perf summary stats""" + return self._request('osd perf', body=body, timeout=timeout) + + OSD_DF_OUTPUT_METHOD_VALUES = ['plain', 'tree'] + + def osd_df(self, output_method=None, body='json', timeout=None): + """show OSD utilization""" + kwargs = dict(body=body, timeout=timeout) + if output_method is not None: + if not isinstance(output_method, six.string_types): + raise CephClientTypeError( + name='output_method', + actual=type(output_method), + expected=six.string_types) + supported = CephClient.OSD_DF_OUTPUT_METHOD_VALUES + if output_method not in supported: + raise CephClientInvalidChoice( + function='osd_df', + option='output_method', + value=output_method, + supported=', '.join(supported)) + kwargs['output_method'] = output_method + return self._request('osd df', **kwargs) + + def osd_blocked_by(self, body='json', timeout=None): + """print histogram of which OSDs are blocking their peers""" + return self._request('osd blocked-by', body=body, timeout=timeout) + + def osd_pool_stats(self, pool_name=None, body='json', timeout=None): + """obtain stats from all pools, or from specified pool""" + kwargs = dict(body=body, timeout=timeout) + if pool_name is not None: + if not isinstance(pool_name, six.string_types): + raise CephClientTypeError( + name='pool_name', + actual=type(pool_name), + expected=six.string_types) + kwargs['pool_name'] = pool_name + return self._request('osd pool stats', **kwargs) + + OSD_REWEIGHT_BY_UTILIZATION_NO_INCREASING_VALUES = ['--no-increasing'] + + def osd_reweight_by_utilization( + self, oload=None, max_change=None, max_osds=None, + no_increasing=None, body='json', timeout=None): + """reweight OSDs by utilization [overload-percentage-for-consideration,default 120] """ + kwargs = dict(body=body, timeout=timeout) + if oload is not None: + if not isinstance(oload, six.integer_types): + raise CephClientTypeError( + name='oload', + actual=type(oload), + expected=int) + kwargs['oload'] = oload + if max_change is not None: + if not isinstance(max_change, (six.integer_types, float)): + raise CephClientTypeError( + name='max_change', + actual=type(max_change), + expected=int) + kwargs['max_change'] = max_change + if max_osds is not None: + if not isinstance(max_osds, six.integer_types): + raise CephClientTypeError( + name='max_osds', + actual=type(max_osds), + expected=int) + kwargs['max_osds'] = max_osds + if no_increasing is not None: + if not isinstance(no_increasing, six.string_types): + raise CephClientTypeError( + name='no_increasing', + actual=type(no_increasing), + expected=six.string_types) + supported = CephClient.OSD_REWEIGHT_BY_UTILIZATION_NO_INCREASING_VALUES # noqa E501 + if no_increasing not in supported: + raise CephClientInvalidChoice( + function='osd_reweight_by_utilization', + option='no_increasing', + value=no_increasing, + supported=', '.join(supported)) + kwargs['no_increasing'] = no_increasing + return self._request('osd reweight-by-utilization', **kwargs) + + OSD_TEST_REWEIGHT_BY_UTILIZATION_NO_INCREASING_VALUES = [ + '--no-increasing'] + + def osd_test_reweight_by_utilization( + self, oload=None, max_change=None, max_osds=None, + no_increasing=None, body='json', timeout=None): + """dry run of reweight OSDs by utilization [overload-percentage-for-consideration, default 120] """ + kwargs = dict(body=body, timeout=timeout) + if oload is not None: + if not isinstance(oload, six.integer_types): + raise CephClientTypeError( + name='oload', + actual=type(oload), + expected=int) + kwargs['oload'] = oload + if max_change is not None: + if not isinstance(max_change, (six.integer_types, float)): + raise CephClientTypeError( + name='max_change', + actual=type(max_change), + expected=int) + kwargs['max_change'] = max_change + if max_osds is not None: + if not isinstance(max_osds, six.integer_types): + raise CephClientTypeError( + name='max_osds', + actual=type(max_osds), + expected=int) + kwargs['max_osds'] = max_osds + if no_increasing is not None: + if not isinstance(no_increasing, six.string_types): + raise CephClientTypeError( + name='no_increasing', + actual=type(no_increasing), + expected=six.string_types) + supported = CephClient.OSD_TEST_REWEIGHT_BY_UTILIZATION_NO_INCREASING_VALUES # noqa E501 + if no_increasing not in supported: + raise CephClientInvalidChoice( + function='osd_test_reweight_by_utilization', + option='no_increasing', + value=no_increasing, + supported=', '.join(supported)) + kwargs['no_increasing'] = no_increasing + return self._request('osd test-reweight-by-utilization', **kwargs) + + def osd_reweight_by_pg( + self, oload=None, max_change=None, max_osds=None, pools=None, + body='json', timeout=None): + """reweight OSDs by PG distribution [overload-percentage-for-consideration, default 120] """ + kwargs = dict(body=body, timeout=timeout) + if oload is not None: + if not isinstance(oload, six.integer_types): + raise CephClientTypeError( + name='oload', + actual=type(oload), + expected=int) + kwargs['oload'] = oload + if max_change is not None: + if not isinstance(max_change, (six.integer_types, float)): + raise CephClientTypeError( + name='max_change', + actual=type(max_change), + expected=int) + kwargs['max_change'] = max_change + if max_osds is not None: + if not isinstance(max_osds, six.integer_types): + raise CephClientTypeError( + name='max_osds', + actual=type(max_osds), + expected=int) + kwargs['max_osds'] = max_osds + if pools is not None: + if isinstance(pools, list): + for item in pools: + if not isinstance(item, six.string_types): + raise CephClientTypeError( + name='pools', + actual=item, + expected='list of strings') + else: + if not isinstance(pools, six.string_types): + raise CephClientTypeError( + name='pools', + actual=type(pools), + expected=six.string_types) + if not isinstance(pools, list): + pools = [pools] + kwargs['pools'] = pools + return self._request('osd reweight-by-pg', **kwargs) + + def osd_test_reweight_by_pg( + self, oload=None, max_change=None, max_osds=None, pools=None, + body='json', timeout=None): + """dry run of reweight OSDs by PG distribution [overload-percentage-for-consideration, default 120] """ + kwargs = dict(body=body, timeout=timeout) + if oload is not None: + if not isinstance(oload, six.integer_types): + raise CephClientTypeError( + name='oload', + actual=type(oload), + expected=int) + kwargs['oload'] = oload + if max_change is not None: + if not isinstance(max_change, (six.integer_types, float)): + raise CephClientTypeError( + name='max_change', + actual=type(max_change), + expected=int) + kwargs['max_change'] = max_change + if max_osds is not None: + if not isinstance(max_osds, six.integer_types): + raise CephClientTypeError( + name='max_osds', + actual=type(max_osds), + expected=int) + kwargs['max_osds'] = max_osds + if pools is not None: + if isinstance(pools, list): + for item in pools: + if not isinstance(item, six.string_types): + raise CephClientTypeError( + name='pools', + actual=item, + expected='list of strings') + else: + if not isinstance(pools, six.string_types): + raise CephClientTypeError( + name='pools', + actual=type(pools), + expected=six.string_types) + if not isinstance(pools, list): + pools = [pools] + kwargs['pools'] = pools + return self._request('osd test-reweight-by-pg', **kwargs) + + def osd_safe_to_destroy(self, ids, body='json', timeout=None): + """check whether osd(s) can be safely destroyed without reducing datadurability """ + kwargs = dict(body=body, timeout=timeout) + if isinstance(ids, list): + for item in ids: + if not isinstance(item, six.string_types): + raise CephClientTypeError( + name='ids', + actual=item, + expected='list of strings') + else: + if not isinstance(ids, six.string_types): + raise CephClientTypeError( + name='ids', + actual=type(ids), + expected=six.string_types) + + if not isinstance(ids, list): + ids = [ids] + kwargs['ids'] = ids + return self._request('osd safe-to-destroy', **kwargs) + + def osd_ok_to_stop(self, ids, body='json', timeout=None): + """check whether osd(s) can be safely stopped without reducing immediatedata availability """ + kwargs = dict(body=body, timeout=timeout) + if isinstance(ids, list): + for item in ids: + if not isinstance(item, six.string_types): + raise CephClientTypeError( + name='ids', + actual=item, + expected='list of strings') + else: + if not isinstance(ids, six.string_types): + raise CephClientTypeError( + name='ids', + actual=type(ids), + expected=six.string_types) + + if not isinstance(ids, list): + ids = [ids] + kwargs['ids'] = ids + return self._request('osd ok-to-stop', **kwargs) + + def osd_scrub(self, who, body='json', timeout=None): + """initiate scrub on osd , or use to scrub all""" + kwargs = dict(body=body, timeout=timeout) + if not isinstance(who, six.string_types): + raise CephClientTypeError( + name='who', + actual=type(who), + expected=six.string_types) + + kwargs['who'] = who + return self._request('osd scrub', **kwargs) + + def osd_deep_scrub(self, who, body='json', timeout=None): + """initiate deep scrub on osd , or use to deep scrub all""" + kwargs = dict(body=body, timeout=timeout) + if not isinstance(who, six.string_types): + raise CephClientTypeError( + name='who', + actual=type(who), + expected=six.string_types) + + kwargs['who'] = who + return self._request('osd deep-scrub', **kwargs) + + def osd_repair(self, who, body='json', timeout=None): + """initiate repair on osd , or use to repair all""" + kwargs = dict(body=body, timeout=timeout) + if not isinstance(who, six.string_types): + raise CephClientTypeError( + name='who', + actual=type(who), + expected=six.string_types) + + kwargs['who'] = who + return self._request('osd repair', **kwargs) + + def service_dump(self, body='json', timeout=None): + """dump service map""" + return self._request('service dump', body=body, timeout=timeout) + + def service_status(self, body='json', timeout=None): + """dump service state""" + return self._request('service status', body=body, timeout=timeout) + + def config_show(self, who, key, body='json', timeout=None): + """Show running configuration""" + kwargs = dict(body=body, timeout=timeout) + if not isinstance(who, six.string_types): + raise CephClientTypeError( + name='who', + actual=type(who), + expected=six.string_types) + + kwargs['who'] = who + if not isinstance(key, six.string_types): + raise CephClientTypeError( + name='key', + actual=type(key), + expected=six.string_types) + + kwargs['key'] = key + return self._request('config show', **kwargs) + + def config_show_with_defaults(self, who, body='json', timeout=None): + """Show running configuration (including compiled-in defaults)""" + kwargs = dict(body=body, timeout=timeout) + if not isinstance(who, six.string_types): + raise CephClientTypeError( + name='who', + actual=type(who), + expected=six.string_types) + + kwargs['who'] = who + return self._request('config show-with-defaults', **kwargs) + + def pg_map(self, pgid, body='json', timeout=None): + """show mapping of pg to osds""" + kwargs = dict(body=body, timeout=timeout) + if not isinstance(pgid, six.string_types): + raise CephClientTypeError( + name='pgid', + actual=type(pgid), + expected=six.string_types) + if not re.match(r'[0-9]+\.[0-9a-fA-F]+', pgid): + raise CephClientInvalidPgid( + name='pgid', + actual=pgid) + + kwargs['pgid'] = pgid + return self._request('pg map', **kwargs) + + def osd_last_stat_seq(self, _id, body='json', timeout=None): + """get the last pg stats sequence number reported for this osd""" + kwargs = dict(body=body, timeout=timeout) + if isinstance(_id, six.integer_types): + pass + elif isinstance(_id, six.string_types): + _id = _id.lower() + prefix = 'osd.' + if not _id.startswith(prefix): + raise CephClientInvalidOsdIdValue(osdid=_id) + _id = int(_id[len(prefix):]) + else: + raise CephClientTypeError( + name='_id', + actual=type(_id), + expected='int or string') + + kwargs['id'] = _id + return self._request('osd last-stat-seq', **kwargs) + + def auth_export(self, entity=None, body='json', timeout=None): + """write keyring for requested entity, or master keyring if none given""" + kwargs = dict(body=body, timeout=timeout) + if entity is not None: + if not isinstance(entity, six.string_types): + raise CephClientTypeError( + name='entity', + actual=type(entity), + expected=six.string_types) + kwargs['entity'] = entity + return self._request('auth export', **kwargs) + + def auth_get(self, entity, body='json', timeout=None): + """write keyring file with requested key""" + kwargs = dict(body=body, timeout=timeout) + if not isinstance(entity, six.string_types): + raise CephClientTypeError( + name='entity', + actual=type(entity), + expected=six.string_types) + + kwargs['entity'] = entity + return self._request('auth get', **kwargs) + + def auth_get_key(self, entity, body='json', timeout=None): + """display requested key""" + kwargs = dict(body=body, timeout=timeout) + if not isinstance(entity, six.string_types): + raise CephClientTypeError( + name='entity', + actual=type(entity), + expected=six.string_types) + + kwargs['entity'] = entity + return self._request('auth get-key', **kwargs) + + def auth_print_key(self, entity, body='json', timeout=None): + """display requested key""" + kwargs = dict(body=body, timeout=timeout) + if not isinstance(entity, six.string_types): + raise CephClientTypeError( + name='entity', + actual=type(entity), + expected=six.string_types) + + kwargs['entity'] = entity + return self._request('auth print-key', **kwargs) + + def auth_list(self, body='json', timeout=None): + """list authentication state""" + return self._request('auth list', body=body, timeout=timeout) + + def auth_ls(self, body='json', timeout=None): + """list authentication state""" + return self._request('auth ls', body=body, timeout=timeout) + + def auth_import(self, body='json', timeout=None): + """auth import: read keyring file from -i """ + return self._request('auth import', body=body, timeout=timeout) + + def auth_add(self, entity, caps=None, body='json', timeout=None): + """add auth info for from input file, or random key if no inputis given, and/or any caps specified in the command """ + kwargs = dict(body=body, timeout=timeout) + if not isinstance(entity, six.string_types): + raise CephClientTypeError( + name='entity', + actual=type(entity), + expected=six.string_types) + + kwargs['entity'] = entity + if caps is not None: + if isinstance(caps, list): + for item in caps: + if not isinstance(item, six.string_types): + raise CephClientTypeError( + name='caps', + actual=item, + expected='list of strings') + else: + if not isinstance(caps, six.string_types): + raise CephClientTypeError( + name='caps', + actual=type(caps), + expected=six.string_types) + if not isinstance(caps, list): + caps = [caps] + kwargs['caps'] = caps + return self._request('auth add', **kwargs) + + def auth_get_or_create_key( + self, entity, caps=None, body='json', timeout=None): + """get, or add, key for from system/caps pairs specified in thecommand. If key already exists, any given caps must match the existing caps for that key. """ + kwargs = dict(body=body, timeout=timeout) + if not isinstance(entity, six.string_types): + raise CephClientTypeError( + name='entity', + actual=type(entity), + expected=six.string_types) + + kwargs['entity'] = entity + if caps is not None: + if isinstance(caps, list): + for item in caps: + if not isinstance(item, six.string_types): + raise CephClientTypeError( + name='caps', + actual=item, + expected='list of strings') + else: + if not isinstance(caps, six.string_types): + raise CephClientTypeError( + name='caps', + actual=type(caps), + expected=six.string_types) + if not isinstance(caps, list): + caps = [caps] + kwargs['caps'] = caps + return self._request('auth get-or-create-key', **kwargs) + + def auth_get_or_create(self, entity, caps=None, + body='json', timeout=None): + """add auth info for from input file, or random key if no inputgiven, and/or any caps specified in the command """ + kwargs = dict(body=body, timeout=timeout) + if not isinstance(entity, six.string_types): + raise CephClientTypeError( + name='entity', + actual=type(entity), + expected=six.string_types) + + kwargs['entity'] = entity + if caps is not None: + if isinstance(caps, list): + for item in caps: + if not isinstance(item, six.string_types): + raise CephClientTypeError( + name='caps', + actual=item, + expected='list of strings') + else: + if not isinstance(caps, six.string_types): + raise CephClientTypeError( + name='caps', + actual=type(caps), + expected=six.string_types) + if not isinstance(caps, list): + caps = [caps] + kwargs['caps'] = caps + return self._request('auth get-or-create', **kwargs) + + def fs_authorize(self, filesystem, entity, caps, + body='json', timeout=None): + """add auth for to access file system based onfollowing directory and permissions pairs """ + kwargs = dict(body=body, timeout=timeout) + if not isinstance(filesystem, six.string_types): + raise CephClientTypeError( + name='filesystem', + actual=type(filesystem), + expected=six.string_types) + + kwargs['filesystem'] = filesystem + if not isinstance(entity, six.string_types): + raise CephClientTypeError( + name='entity', + actual=type(entity), + expected=six.string_types) + + kwargs['entity'] = entity + if isinstance(caps, list): + for item in caps: + if not isinstance(item, six.string_types): + raise CephClientTypeError( + name='caps', + actual=item, + expected='list of strings') + else: + if not isinstance(caps, six.string_types): + raise CephClientTypeError( + name='caps', + actual=type(caps), + expected=six.string_types) + + if not isinstance(caps, list): + caps = [caps] + kwargs['caps'] = caps + return self._request('fs authorize', **kwargs) + + def auth_caps(self, entity, caps, body='json', timeout=None): + """update caps for from caps specified in the command""" + kwargs = dict(body=body, timeout=timeout) + if not isinstance(entity, six.string_types): + raise CephClientTypeError( + name='entity', + actual=type(entity), + expected=six.string_types) + + kwargs['entity'] = entity + if isinstance(caps, list): + for item in caps: + if not isinstance(item, six.string_types): + raise CephClientTypeError( + name='caps', + actual=item, + expected='list of strings') + else: + if not isinstance(caps, six.string_types): + raise CephClientTypeError( + name='caps', + actual=type(caps), + expected=six.string_types) + + if not isinstance(caps, list): + caps = [caps] + kwargs['caps'] = caps + return self._request('auth caps', **kwargs) + + def auth_del(self, entity, body='json', timeout=None): + """delete all caps for """ + kwargs = dict(body=body, timeout=timeout) + if not isinstance(entity, six.string_types): + raise CephClientTypeError( + name='entity', + actual=type(entity), + expected=six.string_types) + + kwargs['entity'] = entity + return self._request('auth del', **kwargs) + + def auth_rm(self, entity, body='json', timeout=None): + """remove all caps for """ + kwargs = dict(body=body, timeout=timeout) + if not isinstance(entity, six.string_types): + raise CephClientTypeError( + name='entity', + actual=type(entity), + expected=six.string_types) + + kwargs['entity'] = entity + return self._request('auth rm', **kwargs) + + def compact(self, body='json', timeout=None): + """cause compaction of monitor's leveldb/rocksdb storage""" + return self._request('compact', body=body, timeout=timeout) + + def scrub(self, body='json', timeout=None): + """scrub the monitor stores""" + return self._request('scrub', body=body, timeout=timeout) + + def fsid(self, body='json', timeout=None): + """show cluster FSID/UUID""" + return self._request('fsid', body=body, timeout=timeout) + + def log(self, logtext, body='json', timeout=None): + """log supplied text to the monitor log""" + kwargs = dict(body=body, timeout=timeout) + if isinstance(logtext, list): + for item in logtext: + if not isinstance(item, six.string_types): + raise CephClientTypeError( + name='logtext', + actual=item, + expected='list of strings') + else: + if not isinstance(logtext, six.string_types): + raise CephClientTypeError( + name='logtext', + actual=type(logtext), + expected=six.string_types) + + if not isinstance(logtext, list): + logtext = [logtext] + kwargs['logtext'] = logtext + return self._request('log', **kwargs) + + LOG_LAST_LEVEL_VALUES = ['debug', 'info', 'sec', 'warn', 'error'] + + LOG_LAST_CHANNEL_VALUES = ['*', 'cluster', 'audit'] + + def log_last( + self, num=None, level=None, channel=None, body='json', + timeout=None): + """print last few lines of the cluster log""" + kwargs = dict(body=body, timeout=timeout) + if num is not None: + if not isinstance(num, six.integer_types): + raise CephClientTypeError( + name='num', + actual=type(num), + expected=int) + if num < 1: + raise CephClientValueOutOfBounds( + name='num', + actual=num, + min=1, + max='unlimited') + kwargs['num'] = num + if level is not None: + if not isinstance(level, six.string_types): + raise CephClientTypeError( + name='level', + actual=type(level), + expected=six.string_types) + supported = CephClient.LOG_LAST_LEVEL_VALUES + if level not in supported: + raise CephClientInvalidChoice( + function='log_last', + option='level', + value=level, + supported=', '.join(supported)) + kwargs['level'] = level + if channel is not None: + if not isinstance(channel, six.string_types): + raise CephClientTypeError( + name='channel', + actual=type(channel), + expected=six.string_types) + supported = CephClient.LOG_LAST_CHANNEL_VALUES + if channel not in supported: + raise CephClientInvalidChoice( + function='log_last', + option='channel', + value=channel, + supported=', '.join(supported)) + kwargs['channel'] = channel + return self._request('log last', **kwargs) + + def injectargs(self, injected_args, body='json', timeout=None): + """inject config arguments into monitor""" + kwargs = dict(body=body, timeout=timeout) + if isinstance(injected_args, list): + for item in injected_args: + if not isinstance(item, six.string_types): + raise CephClientTypeError( + name='injected_args', + actual=item, + expected='list of strings') + else: + if not isinstance(injected_args, six.string_types): + raise CephClientTypeError( + name='injected_args', + actual=type(injected_args), + expected=six.string_types) + + if not isinstance(injected_args, list): + injected_args = [injected_args] + kwargs['injected_args'] = injected_args + return self._request('injectargs', **kwargs) + + def status(self, body='json', timeout=None): + """show cluster status""" + return self._request('status', body=body, timeout=timeout) + + HEALTH_DETAIL_VALUES = ['detail'] + + def health(self, detail=None, body='json', timeout=None): + """show cluster health""" + kwargs = dict(body=body, timeout=timeout) + if detail is not None: + if not isinstance(detail, six.string_types): + raise CephClientTypeError( + name='detail', + actual=type(detail), + expected=six.string_types) + supported = CephClient.HEALTH_DETAIL_VALUES + if detail not in supported: + raise CephClientInvalidChoice( + function='health', + option='detail', + value=detail, + supported=', '.join(supported)) + kwargs['detail'] = detail + return self._request('health', **kwargs) + + def time_sync_status(self, body='json', timeout=None): + """show time sync status""" + return self._request('time-sync-status', + body=body, timeout=timeout) + + DF_DETAIL_VALUES = ['detail'] + + def df(self, detail=None, body='json', timeout=None): + """show cluster free space stats""" + kwargs = dict(body=body, timeout=timeout) + if detail is not None: + if not isinstance(detail, six.string_types): + raise CephClientTypeError( + name='detail', + actual=type(detail), + expected=six.string_types) + supported = CephClient.DF_DETAIL_VALUES + if detail not in supported: + raise CephClientInvalidChoice( + function='df', + option='detail', + value=detail, + supported=', '.join(supported)) + kwargs['detail'] = detail + return self._request('df', **kwargs) + + def report(self, tags=None, body='json', timeout=None): + """report full status of cluster, optional title tag strings""" + kwargs = dict(body=body, timeout=timeout) + if tags is not None: + if isinstance(tags, list): + for item in tags: + if not isinstance(item, six.string_types): + raise CephClientTypeError( + name='tags', + actual=item, + expected='list of strings') + else: + if not isinstance(tags, six.string_types): + raise CephClientTypeError( + name='tags', + actual=type(tags), + expected=six.string_types) + if not isinstance(tags, list): + tags = [tags] + kwargs['tags'] = tags + return self._request('report', **kwargs) + + def features(self, body='json', timeout=None): + """report of connected features""" + return self._request('features', body=body, timeout=timeout) + + def quorum_status(self, body='json', timeout=None): + """report status of monitor quorum""" + return self._request('quorum_status', body=body, timeout=timeout) + + def mon_status(self, body='json', timeout=None): + """report status of monitors""" + return self._request('mon_status', body=body, timeout=timeout) + + SYNC_FORCE_VALIDATE1_VALUES = ['--yes-i-really-mean-it'] + + SYNC_FORCE_VALIDATE2_VALUES = ['--i-know-what-i-am-doing'] + + def sync_force( + self, validate1=None, validate2=None, body='json', + timeout=None): + """force sync of and clear monitor store""" + kwargs = dict(body=body, timeout=timeout) + if validate1 is not None: + if not isinstance(validate1, six.string_types): + raise CephClientTypeError( + name='validate1', + actual=type(validate1), + expected=six.string_types) + supported = CephClient.SYNC_FORCE_VALIDATE1_VALUES + if validate1 not in supported: + raise CephClientInvalidChoice( + function='sync_force', + option='validate1', + value=validate1, + supported=', '.join(supported)) + kwargs['validate1'] = validate1 + if validate2 is not None: + if not isinstance(validate2, six.string_types): + raise CephClientTypeError( + name='validate2', + actual=type(validate2), + expected=six.string_types) + supported = CephClient.SYNC_FORCE_VALIDATE2_VALUES + if validate2 not in supported: + raise CephClientInvalidChoice( + function='sync_force', + option='validate2', + value=validate2, + supported=', '.join(supported)) + kwargs['validate2'] = validate2 + return self._request('sync force', **kwargs) + + HEAP_HEAPCMD_VALUES = \ + ['dump', 'start_profiler', 'stop_profiler', + 'release', 'stats'] + + def heap(self, heapcmd, body='json', timeout=None): + """show heap usage info (available only if compiled with tcmalloc)""" + kwargs = dict(body=body, timeout=timeout) + if not isinstance(heapcmd, six.string_types): + raise CephClientTypeError( + name='heapcmd', + actual=type(heapcmd), + expected=six.string_types) + supported = CephClient.HEAP_HEAPCMD_VALUES + if heapcmd not in supported: + raise CephClientInvalidChoice( + function='heap', + option='heapcmd', + value=heapcmd, + supported=', '.join(supported)) + + kwargs['heapcmd'] = heapcmd + return self._request('heap', **kwargs) + + QUORUM_QUORUMCMD_VALUES = ['enter', 'exit'] + + def quorum(self, quorumcmd, body='json', timeout=None): + """enter or exit quorum""" + kwargs = dict(body=body, timeout=timeout) + if not isinstance(quorumcmd, six.string_types): + raise CephClientTypeError( + name='quorumcmd', + actual=type(quorumcmd), + expected=six.string_types) + supported = CephClient.QUORUM_QUORUMCMD_VALUES + if quorumcmd not in supported: + raise CephClientInvalidChoice( + function='quorum', + option='quorumcmd', + value=quorumcmd, + supported=', '.join(supported)) + + kwargs['quorumcmd'] = quorumcmd + return self._request('quorum', **kwargs) + + def tell(self, target, args, body='json', timeout=None): + """send a command to a specific daemon""" + kwargs = dict(body=body, timeout=timeout) + + kwargs['target'] = target + if isinstance(args, list): + for item in args: + if not isinstance(item, six.string_types): + raise CephClientTypeError( + name='args', + actual=item, + expected='list of strings') + else: + if not isinstance(args, six.string_types): + raise CephClientTypeError( + name='args', + actual=type(args), + expected=six.string_types) + + if not isinstance(args, list): + args = [args] + kwargs['args'] = args + return self._request('tell', **kwargs) + + def version(self, body='json', timeout=None): + """show mon daemon version""" + return self._request('version', body=body, timeout=timeout) + + NODE_LS__TYPE_VALUES = ['all', 'osd', 'mon', 'mds', 'mgr'] + + def node_ls(self, _type=None, body='json', timeout=None): + """list all nodes in cluster [type]""" + kwargs = dict(body=body, timeout=timeout) + if _type is not None: + if not isinstance(_type, six.string_types): + raise CephClientTypeError( + name='_type', + actual=type(_type), + expected=six.string_types) + supported = CephClient.NODE_LS__TYPE_VALUES + if _type not in supported: + raise CephClientInvalidChoice( + function='node_ls', + option='_type', + value=_type, + supported=', '.join(supported)) + kwargs['type'] = _type + return self._request('node ls', **kwargs) + + def mon_compact(self, body='json', timeout=None): + """cause compaction of monitor's leveldb/rocksdb storage""" + return self._request('mon compact', body=body, timeout=timeout) + + def mon_scrub(self, body='json', timeout=None): + """scrub the monitor stores""" + return self._request('mon scrub', body=body, timeout=timeout) + + MON_SYNC_FORCE_VALIDATE1_VALUES = ['--yes-i-really-mean-it'] + + MON_SYNC_FORCE_VALIDATE2_VALUES = ['--i-know-what-i-am-doing'] + + def mon_sync_force( + self, validate1=None, validate2=None, body='json', + timeout=None): + """force sync of and clear monitor store""" + kwargs = dict(body=body, timeout=timeout) + if validate1 is not None: + if not isinstance(validate1, six.string_types): + raise CephClientTypeError( + name='validate1', + actual=type(validate1), + expected=six.string_types) + supported = CephClient.MON_SYNC_FORCE_VALIDATE1_VALUES + if validate1 not in supported: + raise CephClientInvalidChoice( + function='mon_sync_force', + option='validate1', + value=validate1, + supported=', '.join(supported)) + kwargs['validate1'] = validate1 + if validate2 is not None: + if not isinstance(validate2, six.string_types): + raise CephClientTypeError( + name='validate2', + actual=type(validate2), + expected=six.string_types) + supported = CephClient.MON_SYNC_FORCE_VALIDATE2_VALUES + if validate2 not in supported: + raise CephClientInvalidChoice( + function='mon_sync_force', + option='validate2', + value=validate2, + supported=', '.join(supported)) + kwargs['validate2'] = validate2 + return self._request('mon sync force', **kwargs) + + def mon_metadata(self, _id=None, body='json', timeout=None): + """fetch metadata for mon """ + kwargs = dict(body=body, timeout=timeout) + if _id is not None: + if not isinstance(_id, six.string_types): + raise CephClientTypeError( + name='_id', + actual=type(_id), + expected=six.string_types) + kwargs['id'] = _id + return self._request('mon metadata', **kwargs) + + def mon_count_metadata(self, _property, body='json', timeout=None): + """count mons by metadata field property""" + kwargs = dict(body=body, timeout=timeout) + if not isinstance(_property, six.string_types): + raise CephClientTypeError( + name='_property', + actual=type(_property), + expected=six.string_types) + + kwargs['property'] = _property + return self._request('mon count-metadata', **kwargs) + + def mon_versions(self, body='json', timeout=None): + """check running versions of monitors""" + return self._request('mon versions', body=body, timeout=timeout) + + def versions(self, body='json', timeout=None): + """check running versions of ceph daemons""" + return self._request('versions', body=body, timeout=timeout) + + def mds_stat(self, body='json', timeout=None): + """show MDS status""" + return self._request('mds stat', body=body, timeout=timeout) + + def mds_dump(self, epoch=None, body='json', timeout=None): + """dump legacy MDS cluster info, optionally from epoch""" + kwargs = dict(body=body, timeout=timeout) + if epoch is not None: + if not isinstance(epoch, six.integer_types): + raise CephClientTypeError( + name='epoch', + actual=type(epoch), + expected=int) + if epoch < 0: + raise CephClientValueOutOfBounds( + name='epoch', + actual=epoch, + min=0, + max='unlimited') + kwargs['epoch'] = epoch + return self._request('mds dump', **kwargs) + + def fs_dump(self, epoch=None, body='json', timeout=None): + """dump all CephFS status, optionally from epoch""" + kwargs = dict(body=body, timeout=timeout) + if epoch is not None: + if not isinstance(epoch, six.integer_types): + raise CephClientTypeError( + name='epoch', + actual=type(epoch), + expected=int) + if epoch < 0: + raise CephClientValueOutOfBounds( + name='epoch', + actual=epoch, + min=0, + max='unlimited') + kwargs['epoch'] = epoch + return self._request('fs dump', **kwargs) + + def mds_getmap(self, epoch=None, body='json', timeout=None): + """get MDS map, optionally from epoch""" + kwargs = dict(body=body, timeout=timeout) + if epoch is not None: + if not isinstance(epoch, six.integer_types): + raise CephClientTypeError( + name='epoch', + actual=type(epoch), + expected=int) + if epoch < 0: + raise CephClientValueOutOfBounds( + name='epoch', + actual=epoch, + min=0, + max='unlimited') + kwargs['epoch'] = epoch + return self._request('mds getmap', **kwargs) + + def mds_metadata(self, who=None, body='json', timeout=None): + """fetch metadata for mds """ + kwargs = dict(body=body, timeout=timeout) + if who is not None: + if not isinstance(who, six.string_types): + raise CephClientTypeError( + name='who', + actual=type(who), + expected=six.string_types) + kwargs['who'] = who + return self._request('mds metadata', **kwargs) + + def mds_count_metadata(self, _property, body='json', timeout=None): + """count MDSs by metadata field property""" + kwargs = dict(body=body, timeout=timeout) + if not isinstance(_property, six.string_types): + raise CephClientTypeError( + name='_property', + actual=type(_property), + expected=six.string_types) + + kwargs['property'] = _property + return self._request('mds count-metadata', **kwargs) + + def mds_versions(self, body='json', timeout=None): + """check running versions of MDSs""" + return self._request('mds versions', body=body, timeout=timeout) + + def mds_tell(self, who, args, body='json', timeout=None): + """send command to particular mds""" + kwargs = dict(body=body, timeout=timeout) + if not isinstance(who, six.string_types): + raise CephClientTypeError( + name='who', + actual=type(who), + expected=six.string_types) + + kwargs['who'] = who + if isinstance(args, list): + for item in args: + if not isinstance(item, six.string_types): + raise CephClientTypeError( + name='args', + actual=item, + expected='list of strings') + else: + if not isinstance(args, six.string_types): + raise CephClientTypeError( + name='args', + actual=type(args), + expected=six.string_types) + + if not isinstance(args, list): + args = [args] + kwargs['args'] = args + return self._request('mds tell', **kwargs) + + def mds_compat_show(self, body='json', timeout=None): + """show mds compatibility settings""" + return self._request('mds compat show', body=body, timeout=timeout) + + def mds_stop(self, role, body='json', timeout=None): + """stop mds""" + kwargs = dict(body=body, timeout=timeout) + if not isinstance(role, six.string_types): + raise CephClientTypeError( + name='role', + actual=type(role), + expected=six.string_types) + + kwargs['role'] = role + return self._request('mds stop', **kwargs) + + def mds_deactivate(self, role, body='json', timeout=None): + """clean up specified MDS rank (use with `set max_mds` to shrink cluster)""" + kwargs = dict(body=body, timeout=timeout) + if not isinstance(role, six.string_types): + raise CephClientTypeError( + name='role', + actual=type(role), + expected=six.string_types) + + kwargs['role'] = role + return self._request('mds deactivate', **kwargs) + + def mds_set_max_mds(self, maxmds, body='json', timeout=None): + """set max MDS index""" + kwargs = dict(body=body, timeout=timeout) + if not isinstance(maxmds, six.integer_types): + raise CephClientTypeError( + name='maxmds', + actual=type(maxmds), + expected=int) + if maxmds < 0: + raise CephClientValueOutOfBounds( + name='maxmds', + actual=maxmds, + min=0, + max='unlimited') + + kwargs['maxmds'] = maxmds + return self._request('mds set_max_mds', **kwargs) + + MDS_SET_VAR_VALUES = \ + ['max_mds', 'max_file_size', 'inline_data', + 'allow_new_snaps', 'allow_multimds', + 'allow_multimds_snaps', 'allow_dirfrags'] + + def mds_set(self, var, val, confirm=None, body='json', timeout=None): + """set mds parameter to """ + kwargs = dict(body=body, timeout=timeout) + if not isinstance(var, six.string_types): + raise CephClientTypeError( + name='var', + actual=type(var), + expected=six.string_types) + supported = CephClient.MDS_SET_VAR_VALUES + if var not in supported: + raise CephClientInvalidChoice( + function='mds_set', + option='var', + value=var, + supported=', '.join(supported)) + + kwargs['var'] = var + if not isinstance(val, six.string_types): + raise CephClientTypeError( + name='val', + actual=type(val), + expected=six.string_types) + + kwargs['val'] = val + if confirm is not None: + if not isinstance(confirm, six.string_types): + raise CephClientTypeError( + name='confirm', + actual=type(confirm), + expected=six.string_types) + kwargs['confirm'] = confirm + return self._request('mds set', **kwargs) + + def mds_set_state(self, gid, state, body='json', timeout=None): + """set mds state of to """ + kwargs = dict(body=body, timeout=timeout) + if not isinstance(gid, six.integer_types): + raise CephClientTypeError( + name='gid', + actual=type(gid), + expected=int) + if gid < 0: + raise CephClientValueOutOfBounds( + name='gid', + actual=gid, + min=0, + max='unlimited') + + kwargs['gid'] = gid + if not isinstance(state, six.integer_types): + raise CephClientTypeError( + name='state', + actual=type(state), + expected=int) + if state < 0 or state > 20: + raise CephClientValueOutOfBounds( + name='state', + actual=state, + min=0, + max=20) + + kwargs['state'] = state + return self._request('mds set_state', **kwargs) + + def mds_fail(self, role_or_gid, body='json', timeout=None): + """Mark MDS failed: trigger a failover if a standby is available""" + kwargs = dict(body=body, timeout=timeout) + if not isinstance(role_or_gid, six.string_types): + raise CephClientTypeError( + name='role_or_gid', + actual=type(role_or_gid), + expected=six.string_types) + + kwargs['role_or_gid'] = role_or_gid + return self._request('mds fail', **kwargs) + + def mds_repaired(self, role, body='json', timeout=None): + """mark a damaged MDS rank as no longer damaged""" + kwargs = dict(body=body, timeout=timeout) + if not isinstance(role, six.string_types): + raise CephClientTypeError( + name='role', + actual=type(role), + expected=six.string_types) + + kwargs['role'] = role + return self._request('mds repaired', **kwargs) + + def mds_rm(self, gid, body='json', timeout=None): + """remove nonactive mds""" + kwargs = dict(body=body, timeout=timeout) + if not isinstance(gid, six.integer_types): + raise CephClientTypeError( + name='gid', + actual=type(gid), + expected=int) + if gid < 0: + raise CephClientValueOutOfBounds( + name='gid', + actual=gid, + min=0, + max='unlimited') + + kwargs['gid'] = gid + return self._request('mds rm', **kwargs) + + def mds_rmfailed(self, role, confirm=None, body='json', timeout=None): + """remove failed mds""" + kwargs = dict(body=body, timeout=timeout) + if not isinstance(role, six.string_types): + raise CephClientTypeError( + name='role', + actual=type(role), + expected=six.string_types) + + kwargs['role'] = role + if confirm is not None: + if not isinstance(confirm, six.string_types): + raise CephClientTypeError( + name='confirm', + actual=type(confirm), + expected=six.string_types) + kwargs['confirm'] = confirm + return self._request('mds rmfailed', **kwargs) + + def mds_cluster_down(self, body='json', timeout=None): + """take MDS cluster down""" + return self._request('mds cluster_down', + body=body, timeout=timeout) + + def mds_cluster_up(self, body='json', timeout=None): + """bring MDS cluster up""" + return self._request('mds cluster_up', body=body, timeout=timeout) + + def mds_compat_rm_compat(self, feature, body='json', timeout=None): + """remove compatible feature""" + kwargs = dict(body=body, timeout=timeout) + if not isinstance(feature, six.integer_types): + raise CephClientTypeError( + name='feature', + actual=type(feature), + expected=int) + if feature < 0: + raise CephClientValueOutOfBounds( + name='feature', + actual=feature, + min=0, + max='unlimited') + + kwargs['feature'] = feature + return self._request('mds compat rm_compat', **kwargs) + + def mds_compat_rm_incompat(self, feature, body='json', timeout=None): + """remove incompatible feature""" + kwargs = dict(body=body, timeout=timeout) + if not isinstance(feature, six.integer_types): + raise CephClientTypeError( + name='feature', + actual=type(feature), + expected=int) + if feature < 0: + raise CephClientValueOutOfBounds( + name='feature', + actual=feature, + min=0, + max='unlimited') + + kwargs['feature'] = feature + return self._request('mds compat rm_incompat', **kwargs) + + def mds_add_data_pool(self, pool, body='json', timeout=None): + """add data pool """ + kwargs = dict(body=body, timeout=timeout) + if not isinstance(pool, six.string_types): + raise CephClientTypeError( + name='pool', + actual=type(pool), + expected=six.string_types) + + kwargs['pool'] = pool + return self._request('mds add_data_pool', **kwargs) + + def mds_rm_data_pool(self, pool, body='json', timeout=None): + """remove data pool """ + kwargs = dict(body=body, timeout=timeout) + if not isinstance(pool, six.string_types): + raise CephClientTypeError( + name='pool', + actual=type(pool), + expected=six.string_types) + + kwargs['pool'] = pool + return self._request('mds rm_data_pool', **kwargs) + + def mds_remove_data_pool(self, pool, body='json', timeout=None): + """remove data pool """ + kwargs = dict(body=body, timeout=timeout) + if not isinstance(pool, six.string_types): + raise CephClientTypeError( + name='pool', + actual=type(pool), + expected=six.string_types) + + kwargs['pool'] = pool + return self._request('mds remove_data_pool', **kwargs) + + MDS_NEWFS_SURE_VALUES = ['--yes-i-really-mean-it'] + + def mds_newfs(self, metadata, data, sure=None, + body='json', timeout=None): + """make new filesystem using pools and """ + kwargs = dict(body=body, timeout=timeout) + if not isinstance(metadata, six.integer_types): + raise CephClientTypeError( + name='metadata', + actual=type(metadata), + expected=int) + if metadata < 0: + raise CephClientValueOutOfBounds( + name='metadata', + actual=metadata, + min=0, + max='unlimited') + + kwargs['metadata'] = metadata + if not isinstance(data, six.integer_types): + raise CephClientTypeError( + name='data', + actual=type(data), + expected=int) + if data < 0: + raise CephClientValueOutOfBounds( + name='data', + actual=data, + min=0, + max='unlimited') + + kwargs['data'] = data + if sure is not None: + if not isinstance(sure, six.string_types): + raise CephClientTypeError( + name='sure', + actual=type(sure), + expected=six.string_types) + supported = CephClient.MDS_NEWFS_SURE_VALUES + if sure not in supported: + raise CephClientInvalidChoice( + function='mds_newfs', + option='sure', + value=sure, + supported=', '.join(supported)) + kwargs['sure'] = sure + return self._request('mds newfs', **kwargs) + + FS_NEW_FORCE_VALUES = ['--force'] + + FS_NEW_SURE_VALUES = ['--allow-dangerous-metadata-overlay'] + + def fs_new(self, fs_name, metadata, data, force=None, + sure=None, body='json', timeout=None): + """make new filesystem using named pools and """ + kwargs = dict(body=body, timeout=timeout) + if not isinstance(fs_name, six.string_types): + raise CephClientTypeError( + name='fs_name', + actual=type(fs_name), + expected=six.string_types) + + kwargs['fs_name'] = fs_name + if not isinstance(metadata, six.string_types): + raise CephClientTypeError( + name='metadata', + actual=type(metadata), + expected=six.string_types) + + kwargs['metadata'] = metadata + if not isinstance(data, six.string_types): + raise CephClientTypeError( + name='data', + actual=type(data), + expected=six.string_types) + + kwargs['data'] = data + if force is not None: + if not isinstance(force, six.string_types): + raise CephClientTypeError( + name='force', + actual=type(force), + expected=six.string_types) + supported = CephClient.FS_NEW_FORCE_VALUES + if force not in supported: + raise CephClientInvalidChoice( + function='fs_new', + option='force', + value=force, + supported=', '.join(supported)) + kwargs['force'] = force + if sure is not None: + if not isinstance(sure, six.string_types): + raise CephClientTypeError( + name='sure', + actual=type(sure), + expected=six.string_types) + supported = CephClient.FS_NEW_SURE_VALUES + if sure not in supported: + raise CephClientInvalidChoice( + function='fs_new', + option='sure', + value=sure, + supported=', '.join(supported)) + kwargs['sure'] = sure + return self._request('fs new', **kwargs) + + FS_RM_SURE_VALUES = ['--yes-i-really-mean-it'] + + def fs_rm(self, fs_name, sure=None, body='json', timeout=None): + """disable the named filesystem""" + kwargs = dict(body=body, timeout=timeout) + if not isinstance(fs_name, six.string_types): + raise CephClientTypeError( + name='fs_name', + actual=type(fs_name), + expected=six.string_types) + + kwargs['fs_name'] = fs_name + if sure is not None: + if not isinstance(sure, six.string_types): + raise CephClientTypeError( + name='sure', + actual=type(sure), + expected=six.string_types) + supported = CephClient.FS_RM_SURE_VALUES + if sure not in supported: + raise CephClientInvalidChoice( + function='fs_rm', + option='sure', + value=sure, + supported=', '.join(supported)) + kwargs['sure'] = sure + return self._request('fs rm', **kwargs) + + FS_RESET_SURE_VALUES = ['--yes-i-really-mean-it'] + + def fs_reset(self, fs_name, sure=None, body='json', timeout=None): + """disaster recovery only: reset to a single-MDS map""" + kwargs = dict(body=body, timeout=timeout) + if not isinstance(fs_name, six.string_types): + raise CephClientTypeError( + name='fs_name', + actual=type(fs_name), + expected=six.string_types) + + kwargs['fs_name'] = fs_name + if sure is not None: + if not isinstance(sure, six.string_types): + raise CephClientTypeError( + name='sure', + actual=type(sure), + expected=six.string_types) + supported = CephClient.FS_RESET_SURE_VALUES + if sure not in supported: + raise CephClientInvalidChoice( + function='fs_reset', + option='sure', + value=sure, + supported=', '.join(supported)) + kwargs['sure'] = sure + return self._request('fs reset', **kwargs) + + def fs_ls(self, body='json', timeout=None): + """list filesystems""" + return self._request('fs ls', body=body, timeout=timeout) + + def fs_get(self, fs_name, body='json', timeout=None): + """get info about one filesystem""" + kwargs = dict(body=body, timeout=timeout) + if not isinstance(fs_name, six.string_types): + raise CephClientTypeError( + name='fs_name', + actual=type(fs_name), + expected=six.string_types) + + kwargs['fs_name'] = fs_name + return self._request('fs get', **kwargs) + + FS_SET_VAR_VALUES = \ + ['max_mds', 'max_file_size', 'allow_new_snaps', + 'inline_data', 'cluster_down', + 'allow_dirfrags', 'balancer', + 'standby_count_wanted', 'session_timeout', + 'session_autoclose', 'down', 'joinable', + 'min_compat_client'] + + def fs_set(self, fs_name, var, val, confirm=None, + body='json', timeout=None): + """set fs parameter to """ + kwargs = dict(body=body, timeout=timeout) + if not isinstance(fs_name, six.string_types): + raise CephClientTypeError( + name='fs_name', + actual=type(fs_name), + expected=six.string_types) + + kwargs['fs_name'] = fs_name + if not isinstance(var, six.string_types): + raise CephClientTypeError( + name='var', + actual=type(var), + expected=six.string_types) + supported = CephClient.FS_SET_VAR_VALUES + if var not in supported: + raise CephClientInvalidChoice( + function='fs_set', + option='var', + value=var, + supported=', '.join(supported)) + + kwargs['var'] = var + if not isinstance(val, six.string_types): + raise CephClientTypeError( + name='val', + actual=type(val), + expected=six.string_types) + + kwargs['val'] = val + if confirm is not None: + if not isinstance(confirm, six.string_types): + raise CephClientTypeError( + name='confirm', + actual=type(confirm), + expected=six.string_types) + kwargs['confirm'] = confirm + return self._request('fs set', **kwargs) + + FS_FLAG_SET_FLAG_NAME_VALUES = ['enable_multiple'] + + FS_FLAG_SET_CONFIRM_VALUES = ['--yes-i-really-mean-it'] + + def fs_flag_set(self, flag_name, val, confirm=None, + body='json', timeout=None): + """Set a global CephFS flag""" + kwargs = dict(body=body, timeout=timeout) + if not isinstance(flag_name, six.string_types): + raise CephClientTypeError( + name='flag_name', + actual=type(flag_name), + expected=six.string_types) + supported = CephClient.FS_FLAG_SET_FLAG_NAME_VALUES + if flag_name not in supported: + raise CephClientInvalidChoice( + function='fs_flag_set', + option='flag_name', + value=flag_name, + supported=', '.join(supported)) + + kwargs['flag_name'] = flag_name + if not isinstance(val, six.string_types): + raise CephClientTypeError( + name='val', + actual=type(val), + expected=six.string_types) + + kwargs['val'] = val + if confirm is not None: + if not isinstance(confirm, six.string_types): + raise CephClientTypeError( + name='confirm', + actual=type(confirm), + expected=six.string_types) + supported = CephClient.FS_FLAG_SET_CONFIRM_VALUES + if confirm not in supported: + raise CephClientInvalidChoice( + function='fs_flag_set', + option='confirm', + value=confirm, + supported=', '.join(supported)) + kwargs['confirm'] = confirm + return self._request('fs flag set', **kwargs) + + def fs_add_data_pool(self, fs_name, pool, body='json', timeout=None): + """add data pool """ + kwargs = dict(body=body, timeout=timeout) + if not isinstance(fs_name, six.string_types): + raise CephClientTypeError( + name='fs_name', + actual=type(fs_name), + expected=six.string_types) + + kwargs['fs_name'] = fs_name + if not isinstance(pool, six.string_types): + raise CephClientTypeError( + name='pool', + actual=type(pool), + expected=six.string_types) + + kwargs['pool'] = pool + return self._request('fs add_data_pool', **kwargs) + + def fs_rm_data_pool(self, fs_name, pool, body='json', timeout=None): + """remove data pool """ + kwargs = dict(body=body, timeout=timeout) + if not isinstance(fs_name, six.string_types): + raise CephClientTypeError( + name='fs_name', + actual=type(fs_name), + expected=six.string_types) + + kwargs['fs_name'] = fs_name + if not isinstance(pool, six.string_types): + raise CephClientTypeError( + name='pool', + actual=type(pool), + expected=six.string_types) + + kwargs['pool'] = pool + return self._request('fs rm_data_pool', **kwargs) + + def fs_set_default(self, fs_name, body='json', timeout=None): + """set the default to the named filesystem""" + kwargs = dict(body=body, timeout=timeout) + if not isinstance(fs_name, six.string_types): + raise CephClientTypeError( + name='fs_name', + actual=type(fs_name), + expected=six.string_types) + + kwargs['fs_name'] = fs_name + return self._request('fs set_default', **kwargs) + + def mon_dump(self, epoch=None, body='json', timeout=None): + """dump formatted monmap (optionally from epoch)""" + kwargs = dict(body=body, timeout=timeout) + if epoch is not None: + if not isinstance(epoch, six.integer_types): + raise CephClientTypeError( + name='epoch', + actual=type(epoch), + expected=int) + if epoch < 0: + raise CephClientValueOutOfBounds( + name='epoch', + actual=epoch, + min=0, + max='unlimited') + kwargs['epoch'] = epoch + return self._request('mon dump', **kwargs) + + def mon_stat(self, body='json', timeout=None): + """summarize monitor status""" + return self._request('mon stat', body=body, timeout=timeout) + + def mon_getmap(self, epoch=None, body='json', timeout=None): + """get monmap""" + kwargs = dict(body=body, timeout=timeout) + if epoch is not None: + if not isinstance(epoch, six.integer_types): + raise CephClientTypeError( + name='epoch', + actual=type(epoch), + expected=int) + if epoch < 0: + raise CephClientValueOutOfBounds( + name='epoch', + actual=epoch, + min=0, + max='unlimited') + kwargs['epoch'] = epoch + return self._request('mon getmap', **kwargs) + + def mon_add(self, name, addr, body='json', timeout=None): + """add new monitor named at """ + kwargs = dict(body=body, timeout=timeout) + if not isinstance(name, six.string_types): + raise CephClientTypeError( + name='name', + actual=type(name), + expected=six.string_types) + + kwargs['name'] = name + try: + ipaddress.ip_address(addr) + except ValueError: + raise CephClientInvalidIPAddr( + name='addr', + actual=addr) + + kwargs['addr'] = addr + return self._request('mon add', **kwargs) + + def mon_rm(self, name, body='json', timeout=None): + """remove monitor named """ + kwargs = dict(body=body, timeout=timeout) + if not isinstance(name, six.string_types): + raise CephClientTypeError( + name='name', + actual=type(name), + expected=six.string_types) + + kwargs['name'] = name + return self._request('mon rm', **kwargs) + + def mon_remove(self, name, body='json', timeout=None): + """remove monitor named """ + kwargs = dict(body=body, timeout=timeout) + if not isinstance(name, six.string_types): + raise CephClientTypeError( + name='name', + actual=type(name), + expected=six.string_types) + + kwargs['name'] = name + return self._request('mon remove', **kwargs) + + MON_FEATURE_LS_WITH_VALUE_VALUES = ['--with-value'] + + def mon_feature_ls(self, with_value=None, body='json', timeout=None): + """list available mon map features to be set/unset""" + kwargs = dict(body=body, timeout=timeout) + if with_value is not None: + if not isinstance(with_value, six.string_types): + raise CephClientTypeError( + name='with_value', + actual=type(with_value), + expected=six.string_types) + supported = CephClient.MON_FEATURE_LS_WITH_VALUE_VALUES + if with_value not in supported: + raise CephClientInvalidChoice( + function='mon_feature_ls', + option='with_value', + value=with_value, + supported=', '.join(supported)) + kwargs['with_value'] = with_value + return self._request('mon feature ls', **kwargs) + + MON_FEATURE_SET_SURE_VALUES = ['--yes-i-really-mean-it'] + + def mon_feature_set(self, feature_name, sure=None, + body='json', timeout=None): + """set provided feature on mon map""" + kwargs = dict(body=body, timeout=timeout) + if not isinstance(feature_name, six.string_types): + raise CephClientTypeError( + name='feature_name', + actual=type(feature_name), + expected=six.string_types) + + kwargs['feature_name'] = feature_name + if sure is not None: + if not isinstance(sure, six.string_types): + raise CephClientTypeError( + name='sure', + actual=type(sure), + expected=six.string_types) + supported = CephClient.MON_FEATURE_SET_SURE_VALUES + if sure not in supported: + raise CephClientInvalidChoice( + function='mon_feature_set', + option='sure', + value=sure, + supported=', '.join(supported)) + kwargs['sure'] = sure + return self._request('mon feature set', **kwargs) + + def osd_stat(self, body='json', timeout=None): + """print summary of OSD map""" + return self._request('osd stat', body=body, timeout=timeout) + + def osd_dump(self, epoch=None, body='json', timeout=None): + """print summary of OSD map""" + kwargs = dict(body=body, timeout=timeout) + if epoch is not None: + if not isinstance(epoch, six.integer_types): + raise CephClientTypeError( + name='epoch', + actual=type(epoch), + expected=int) + if epoch < 0: + raise CephClientValueOutOfBounds( + name='epoch', + actual=epoch, + min=0, + max='unlimited') + kwargs['epoch'] = epoch + return self._request('osd dump', **kwargs) + + OSD_TREE_STATES_VALUES = ['up', 'down', 'in', 'out', 'destroyed'] + + def osd_tree(self, epoch=None, states=None, body='json', timeout=None): + """print OSD tree""" + kwargs = dict(body=body, timeout=timeout) + if epoch is not None: + if not isinstance(epoch, six.integer_types): + raise CephClientTypeError( + name='epoch', + actual=type(epoch), + expected=int) + if epoch < 0: + raise CephClientValueOutOfBounds( + name='epoch', + actual=epoch, + min=0, + max='unlimited') + kwargs['epoch'] = epoch + if states is not None: + if not isinstance(states, six.string_types): + raise CephClientTypeError( + name='states', + actual=type(states), + expected=six.string_types) + supported = CephClient.OSD_TREE_STATES_VALUES + if states not in supported: + raise CephClientInvalidChoice( + function='osd_tree', + option='states', + value=states, + supported=', '.join(supported)) + if not isinstance(states, list): + states = [states] + kwargs['states'] = states + return self._request('osd tree', **kwargs) + + OSD_TREE_FROM_STATES_VALUES = [ + 'up', 'down', 'in', 'out', 'destroyed'] + + def osd_tree_from( + self, bucket, epoch=None, states=None, body='json', + timeout=None): + """print OSD tree in bucket""" + kwargs = dict(body=body, timeout=timeout) + if not isinstance(bucket, six.string_types): + raise CephClientTypeError( + name='bucket', + actual=type(bucket), + expected=six.string_types) + + kwargs['bucket'] = bucket + if epoch is not None: + if not isinstance(epoch, six.integer_types): + raise CephClientTypeError( + name='epoch', + actual=type(epoch), + expected=int) + if epoch < 0: + raise CephClientValueOutOfBounds( + name='epoch', + actual=epoch, + min=0, + max='unlimited') + kwargs['epoch'] = epoch + if states is not None: + if not isinstance(states, six.string_types): + raise CephClientTypeError( + name='states', + actual=type(states), + expected=six.string_types) + supported = CephClient.OSD_TREE_FROM_STATES_VALUES + if states not in supported: + raise CephClientInvalidChoice( + function='osd_tree_from', + option='states', + value=states, + supported=', '.join(supported)) + if not isinstance(states, list): + states = [states] + kwargs['states'] = states + return self._request('osd tree-from', **kwargs) + + def osd_ls(self, epoch=None, body='json', timeout=None): + """show all OSD ids""" + kwargs = dict(body=body, timeout=timeout) + if epoch is not None: + if not isinstance(epoch, six.integer_types): + raise CephClientTypeError( + name='epoch', + actual=type(epoch), + expected=int) + if epoch < 0: + raise CephClientValueOutOfBounds( + name='epoch', + actual=epoch, + min=0, + max='unlimited') + kwargs['epoch'] = epoch + return self._request('osd ls', **kwargs) + + def osd_getmap(self, epoch=None, body='json', timeout=None): + """get OSD map""" + kwargs = dict(body=body, timeout=timeout) + if epoch is not None: + if not isinstance(epoch, six.integer_types): + raise CephClientTypeError( + name='epoch', + actual=type(epoch), + expected=int) + if epoch < 0: + raise CephClientValueOutOfBounds( + name='epoch', + actual=epoch, + min=0, + max='unlimited') + kwargs['epoch'] = epoch + return self._request('osd getmap', **kwargs) + + def osd_getcrushmap(self, epoch=None, body='json', timeout=None): + """get CRUSH map""" + kwargs = dict(body=body, timeout=timeout) + if epoch is not None: + if not isinstance(epoch, six.integer_types): + raise CephClientTypeError( + name='epoch', + actual=type(epoch), + expected=int) + if epoch < 0: + raise CephClientValueOutOfBounds( + name='epoch', + actual=epoch, + min=0, + max='unlimited') + kwargs['epoch'] = epoch + return self._request('osd getcrushmap', **kwargs) + + def osd_getmaxosd(self, body='json', timeout=None): + """show largest OSD id""" + return self._request('osd getmaxosd', body=body, timeout=timeout) + + def osd_ls_tree(self, name, epoch=None, body='json', timeout=None): + """show OSD ids under bucket in the CRUSH map""" + kwargs = dict(body=body, timeout=timeout) + if not isinstance(name, six.string_types): + raise CephClientTypeError( + name='name', + actual=type(name), + expected=six.string_types) + + kwargs['name'] = name + if epoch is not None: + if not isinstance(epoch, six.integer_types): + raise CephClientTypeError( + name='epoch', + actual=type(epoch), + expected=int) + if epoch < 0: + raise CephClientValueOutOfBounds( + name='epoch', + actual=epoch, + min=0, + max='unlimited') + kwargs['epoch'] = epoch + return self._request('osd ls-tree', **kwargs) + + def osd_find(self, _id, body='json', timeout=None): + """find osd in the CRUSH map and show its location""" + kwargs = dict(body=body, timeout=timeout) + if isinstance(_id, six.integer_types): + pass + elif isinstance(_id, six.string_types): + _id = _id.lower() + prefix = 'osd.' + if not _id.startswith(prefix): + raise CephClientInvalidOsdIdValue(osdid=_id) + _id = int(_id[len(prefix):]) + else: + raise CephClientTypeError( + name='_id', + actual=type(_id), + expected='int or string') + + kwargs['id'] = _id + return self._request('osd find', **kwargs) + + def osd_metadata(self, _id=None, body='json', timeout=None): + """fetch metadata for osd {id} (default all)""" + kwargs = dict(body=body, timeout=timeout) + if _id is not None: + if isinstance(_id, six.integer_types): + pass + elif isinstance(_id, six.string_types): + _id = _id.lower() + prefix = 'osd.' + if not _id.startswith(prefix): + raise CephClientInvalidOsdIdValue(osdid=_id) + _id = int(_id[len(prefix):]) + else: + raise CephClientTypeError( + name='_id', + actual=type(_id), + expected='int or string') + kwargs['id'] = _id + return self._request('osd metadata', **kwargs) + + def osd_count_metadata(self, _property, body='json', timeout=None): + """count OSDs by metadata field property""" + kwargs = dict(body=body, timeout=timeout) + if not isinstance(_property, six.string_types): + raise CephClientTypeError( + name='_property', + actual=type(_property), + expected=six.string_types) + + kwargs['property'] = _property + return self._request('osd count-metadata', **kwargs) + + def osd_versions(self, body='json', timeout=None): + """check running versions of OSDs""" + return self._request('osd versions', body=body, timeout=timeout) + + def osd_map(self, pool, _object, nspace=None, + body='json', timeout=None): + """find pg for in with [namespace]""" + kwargs = dict(body=body, timeout=timeout) + if not isinstance(pool, six.string_types): + raise CephClientTypeError( + name='pool', + actual=type(pool), + expected=six.string_types) + + kwargs['pool'] = pool + + kwargs['object'] = _object + if nspace is not None: + if not isinstance(nspace, six.string_types): + raise CephClientTypeError( + name='nspace', + actual=type(nspace), + expected=six.string_types) + kwargs['nspace'] = nspace + return self._request('osd map', **kwargs) + + def osd_lspools(self, auid=None, body='json', timeout=None): + """list pools""" + kwargs = dict(body=body, timeout=timeout) + if auid is not None: + if not isinstance(auid, six.integer_types): + raise CephClientTypeError( + name='auid', + actual=type(auid), + expected=int) + kwargs['auid'] = auid + return self._request('osd lspools', **kwargs) + + def osd_crush_rule_list(self, body='json', timeout=None): + """list crush rules""" + return self._request('osd crush rule list', + body=body, timeout=timeout) + + def osd_crush_rule_ls(self, body='json', timeout=None): + """list crush rules""" + return self._request('osd crush rule ls', + body=body, timeout=timeout) + + def osd_crush_rule_ls_by_class( + self, _class, body='json', timeout=None): + """list all crush rules that reference the same """ + kwargs = dict(body=body, timeout=timeout) + if not isinstance(_class, six.string_types): + raise CephClientTypeError( + name='_class', + actual=type(_class), + expected=six.string_types) + + kwargs['class'] = _class + return self._request('osd crush rule ls-by-class', **kwargs) + + def osd_crush_rule_dump(self, name=None, body='json', timeout=None): + """dump crush rule (default all)""" + kwargs = dict(body=body, timeout=timeout) + if name is not None: + if not isinstance(name, six.string_types): + raise CephClientTypeError( + name='name', + actual=type(name), + expected=six.string_types) + kwargs['name'] = name + return self._request('osd crush rule dump', **kwargs) + + def osd_crush_dump(self, body='json', timeout=None): + """dump crush map""" + return self._request('osd crush dump', body=body, timeout=timeout) + + def osd_crush_add_bucket( + self, name, _type, args=None, body='json', timeout=None): + """add no-parent (probably root) crush bucket of type tolocation """ + kwargs = dict(body=body, timeout=timeout) + if not isinstance(name, six.string_types): + raise CephClientTypeError( + name='name', + actual=type(name), + expected=six.string_types) + + kwargs['name'] = name + if not isinstance(_type, six.string_types): + raise CephClientTypeError( + name='_type', + actual=type(_type), + expected=six.string_types) + + kwargs['type'] = _type + if args is not None: + if isinstance(args, list): + for item in args: + if not isinstance(item, six.string_types): + raise CephClientTypeError( + name='args', + actual=item, + expected='list of strings') + else: + if not isinstance(args, six.string_types): + raise CephClientTypeError( + name='args', + actual=type(args), + expected=six.string_types) + if not isinstance(args, list): + args = [args] + kwargs['args'] = args + return self._request('osd crush add-bucket', **kwargs) + + def osd_crush_rename_bucket( + self, srcname, dstname, body='json', timeout=None): + """rename bucket to """ + kwargs = dict(body=body, timeout=timeout) + if not isinstance(srcname, six.string_types): + raise CephClientTypeError( + name='srcname', + actual=type(srcname), + expected=six.string_types) + + kwargs['srcname'] = srcname + if not isinstance(dstname, six.string_types): + raise CephClientTypeError( + name='dstname', + actual=type(dstname), + expected=six.string_types) + + kwargs['dstname'] = dstname + return self._request('osd crush rename-bucket', **kwargs) + + def osd_crush_set(self, _id, weight, args, body='json', timeout=None): + """update crushmap position and weight for to withlocation """ + kwargs = dict(body=body, timeout=timeout) + if isinstance(_id, six.integer_types): + pass + elif isinstance(_id, six.string_types): + _id = _id.lower() + prefix = 'osd.' + if not _id.startswith(prefix): + raise CephClientInvalidOsdIdValue(osdid=_id) + _id = int(_id[len(prefix):]) + else: + raise CephClientTypeError( + name='_id', + actual=type(_id), + expected='int or string') + + kwargs['id'] = _id + if not isinstance(weight, (six.integer_types, float)): + raise CephClientTypeError( + name='weight', + actual=type(weight), + expected=int) + if weight < 0.0: + raise CephClientValueOutOfBounds( + name='weight', + actual=weight, + min=0.0, + max='unlimited') + + kwargs['weight'] = weight + if isinstance(args, list): + for item in args: + if not isinstance(item, six.string_types): + raise CephClientTypeError( + name='args', + actual=item, + expected='list of strings') + else: + if not isinstance(args, six.string_types): + raise CephClientTypeError( + name='args', + actual=type(args), + expected=six.string_types) + + if not isinstance(args, list): + args = [args] + kwargs['args'] = args + return self._request('osd crush set', **kwargs) + + def osd_crush_add(self, _id, weight, args, body='json', timeout=None): + """add or update crushmap position and weight for with and location """ + kwargs = dict(body=body, timeout=timeout) + if isinstance(_id, six.integer_types): + pass + elif isinstance(_id, six.string_types): + _id = _id.lower() + prefix = 'osd.' + if not _id.startswith(prefix): + raise CephClientInvalidOsdIdValue(osdid=_id) + _id = int(_id[len(prefix):]) + else: + raise CephClientTypeError( + name='_id', + actual=type(_id), + expected='int or string') + + kwargs['id'] = _id + if not isinstance(weight, (six.integer_types, float)): + raise CephClientTypeError( + name='weight', + actual=type(weight), + expected=int) + if weight < 0.0: + raise CephClientValueOutOfBounds( + name='weight', + actual=weight, + min=0.0, + max='unlimited') + + kwargs['weight'] = weight + if isinstance(args, list): + for item in args: + if not isinstance(item, six.string_types): + raise CephClientTypeError( + name='args', + actual=item, + expected='list of strings') + else: + if not isinstance(args, six.string_types): + raise CephClientTypeError( + name='args', + actual=type(args), + expected=six.string_types) + + if not isinstance(args, list): + args = [args] + kwargs['args'] = args + return self._request('osd crush add', **kwargs) + + def osd_crush_set_all_straw_buckets_to_straw2( + self, body='json', timeout=None): + """convert all CRUSH current straw buckets to use the straw2 algorithm""" + return self._request( + 'osd crush set-all-straw-buckets-to-straw2', body=body, + timeout=timeout) + + def osd_crush_set_device_class( + self, _class, ids, body='json', timeout=None): + """set the of the osd(s) [...],or use to setall. """ + kwargs = dict(body=body, timeout=timeout) + if not isinstance(_class, six.string_types): + raise CephClientTypeError( + name='_class', + actual=type(_class), + expected=six.string_types) + + kwargs['class'] = _class + if isinstance(ids, list): + for item in ids: + if not isinstance(item, six.string_types): + raise CephClientTypeError( + name='ids', + actual=item, + expected='list of strings') + else: + if not isinstance(ids, six.string_types): + raise CephClientTypeError( + name='ids', + actual=type(ids), + expected=six.string_types) + + if not isinstance(ids, list): + ids = [ids] + kwargs['ids'] = ids + return self._request('osd crush set-device-class', **kwargs) + + def osd_crush_rm_device_class(self, ids, body='json', timeout=None): + """remove class of the osd(s) [...],or use to removeall. """ + kwargs = dict(body=body, timeout=timeout) + if isinstance(ids, list): + for item in ids: + if not isinstance(item, six.string_types): + raise CephClientTypeError( + name='ids', + actual=item, + expected='list of strings') + else: + if not isinstance(ids, six.string_types): + raise CephClientTypeError( + name='ids', + actual=type(ids), + expected=six.string_types) + + if not isinstance(ids, list): + ids = [ids] + kwargs['ids'] = ids + return self._request('osd crush rm-device-class', **kwargs) + + def osd_crush_class_rename( + self, srcname, dstname, body='json', timeout=None): + """rename crush device class to """ + kwargs = dict(body=body, timeout=timeout) + if not isinstance(srcname, six.string_types): + raise CephClientTypeError( + name='srcname', + actual=type(srcname), + expected=six.string_types) + + kwargs['srcname'] = srcname + if not isinstance(dstname, six.string_types): + raise CephClientTypeError( + name='dstname', + actual=type(dstname), + expected=six.string_types) + + kwargs['dstname'] = dstname + return self._request('osd crush class rename', **kwargs) + + def osd_crush_create_or_move( + self, _id, weight, args, body='json', timeout=None): + """create entry or move existing entry for at/to location """ + kwargs = dict(body=body, timeout=timeout) + if isinstance(_id, six.integer_types): + pass + elif isinstance(_id, six.string_types): + _id = _id.lower() + prefix = 'osd.' + if not _id.startswith(prefix): + raise CephClientInvalidOsdIdValue(osdid=_id) + _id = int(_id[len(prefix):]) + else: + raise CephClientTypeError( + name='_id', + actual=type(_id), + expected='int or string') + + kwargs['id'] = _id + if not isinstance(weight, (six.integer_types, float)): + raise CephClientTypeError( + name='weight', + actual=type(weight), + expected=int) + if weight < 0.0: + raise CephClientValueOutOfBounds( + name='weight', + actual=weight, + min=0.0, + max='unlimited') + + kwargs['weight'] = weight + if isinstance(args, list): + for item in args: + if not isinstance(item, six.string_types): + raise CephClientTypeError( + name='args', + actual=item, + expected='list of strings') + else: + if not isinstance(args, six.string_types): + raise CephClientTypeError( + name='args', + actual=type(args), + expected=six.string_types) + + if not isinstance(args, list): + args = [args] + kwargs['args'] = args + return self._request('osd crush create-or-move', **kwargs) + + def osd_crush_move(self, name, args, body='json', timeout=None): + """move existing entry for to location """ + kwargs = dict(body=body, timeout=timeout) + if not isinstance(name, six.string_types): + raise CephClientTypeError( + name='name', + actual=type(name), + expected=six.string_types) + + kwargs['name'] = name + if isinstance(args, list): + for item in args: + if not isinstance(item, six.string_types): + raise CephClientTypeError( + name='args', + actual=item, + expected='list of strings') + else: + if not isinstance(args, six.string_types): + raise CephClientTypeError( + name='args', + actual=type(args), + expected=six.string_types) + + if not isinstance(args, list): + args = [args] + kwargs['args'] = args + return self._request('osd crush move', **kwargs) + + OSD_CRUSH_SWAP_BUCKET_FORCE_VALUES = ['--yes-i-really-mean-it'] + + def osd_crush_swap_bucket( + self, source, dest, force=None, body='json', timeout=None): + """swap existing bucket contents from (orphan) bucket and """ + kwargs = dict(body=body, timeout=timeout) + if not isinstance(source, six.string_types): + raise CephClientTypeError( + name='source', + actual=type(source), + expected=six.string_types) + + kwargs['source'] = source + if not isinstance(dest, six.string_types): + raise CephClientTypeError( + name='dest', + actual=type(dest), + expected=six.string_types) + + kwargs['dest'] = dest + if force is not None: + if not isinstance(force, six.string_types): + raise CephClientTypeError( + name='force', + actual=type(force), + expected=six.string_types) + supported = CephClient.OSD_CRUSH_SWAP_BUCKET_FORCE_VALUES + if force not in supported: + raise CephClientInvalidChoice( + function='osd_crush_swap_bucket', + option='force', + value=force, + supported=', '.join(supported)) + kwargs['force'] = force + return self._request('osd crush swap-bucket', **kwargs) + + def osd_crush_link(self, name, args, body='json', timeout=None): + """link existing entry for under location """ + kwargs = dict(body=body, timeout=timeout) + if not isinstance(name, six.string_types): + raise CephClientTypeError( + name='name', + actual=type(name), + expected=six.string_types) + + kwargs['name'] = name + if isinstance(args, list): + for item in args: + if not isinstance(item, six.string_types): + raise CephClientTypeError( + name='args', + actual=item, + expected='list of strings') + else: + if not isinstance(args, six.string_types): + raise CephClientTypeError( + name='args', + actual=type(args), + expected=six.string_types) + + if not isinstance(args, list): + args = [args] + kwargs['args'] = args + return self._request('osd crush link', **kwargs) + + def osd_crush_rm(self, name, ancestor=None, body='json', timeout=None): + """remove from crush map (everywhere, or just at )""" + kwargs = dict(body=body, timeout=timeout) + if not isinstance(name, six.string_types): + raise CephClientTypeError( + name='name', + actual=type(name), + expected=six.string_types) + + kwargs['name'] = name + if ancestor is not None: + if not isinstance(ancestor, six.string_types): + raise CephClientTypeError( + name='ancestor', + actual=type(ancestor), + expected=six.string_types) + kwargs['ancestor'] = ancestor + return self._request('osd crush rm', **kwargs) + + def osd_crush_remove(self, name, ancestor=None, + body='json', timeout=None): + """remove from crush map (everywhere, or just at )""" + kwargs = dict(body=body, timeout=timeout) + if not isinstance(name, six.string_types): + raise CephClientTypeError( + name='name', + actual=type(name), + expected=six.string_types) + + kwargs['name'] = name + if ancestor is not None: + if not isinstance(ancestor, six.string_types): + raise CephClientTypeError( + name='ancestor', + actual=type(ancestor), + expected=six.string_types) + kwargs['ancestor'] = ancestor + return self._request('osd crush remove', **kwargs) + + def osd_crush_unlink(self, name, ancestor=None, + body='json', timeout=None): + """unlink from crush map (everywhere, or just at )""" + kwargs = dict(body=body, timeout=timeout) + if not isinstance(name, six.string_types): + raise CephClientTypeError( + name='name', + actual=type(name), + expected=six.string_types) + + kwargs['name'] = name + if ancestor is not None: + if not isinstance(ancestor, six.string_types): + raise CephClientTypeError( + name='ancestor', + actual=type(ancestor), + expected=six.string_types) + kwargs['ancestor'] = ancestor + return self._request('osd crush unlink', **kwargs) + + def osd_crush_reweight_all(self, body='json', timeout=None): + """recalculate the weights for the tree to ensure they sum correctly""" + return self._request('osd crush reweight-all', + body=body, timeout=timeout) + + def osd_crush_reweight(self, name, weight, body='json', timeout=None): + """change 's weight to in crush map""" + kwargs = dict(body=body, timeout=timeout) + if not isinstance(name, six.string_types): + raise CephClientTypeError( + name='name', + actual=type(name), + expected=six.string_types) + + kwargs['name'] = name + if not isinstance(weight, (six.integer_types, float)): + raise CephClientTypeError( + name='weight', + actual=type(weight), + expected=int) + if weight < 0.0: + raise CephClientValueOutOfBounds( + name='weight', + actual=weight, + min=0.0, + max='unlimited') + + kwargs['weight'] = weight + return self._request('osd crush reweight', **kwargs) + + def osd_crush_reweight_subtree( + self, name, weight, body='json', timeout=None): + """change all leaf items beneath to in crush map""" + kwargs = dict(body=body, timeout=timeout) + if not isinstance(name, six.string_types): + raise CephClientTypeError( + name='name', + actual=type(name), + expected=six.string_types) + + kwargs['name'] = name + if not isinstance(weight, (six.integer_types, float)): + raise CephClientTypeError( + name='weight', + actual=type(weight), + expected=int) + if weight < 0.0: + raise CephClientValueOutOfBounds( + name='weight', + actual=weight, + min=0.0, + max='unlimited') + + kwargs['weight'] = weight + return self._request('osd crush reweight-subtree', **kwargs) + + OSD_CRUSH_TUNABLES_PROFILE_VALUES = \ + ['legacy', 'argonaut', 'bobtail', 'firefly', + 'hammer', 'jewel', 'optimal', 'default'] + + def osd_crush_tunables(self, profile, body='json', timeout=None): + """set crush tunables values to """ + kwargs = dict(body=body, timeout=timeout) + if not isinstance(profile, six.string_types): + raise CephClientTypeError( + name='profile', + actual=type(profile), + expected=six.string_types) + supported = CephClient.OSD_CRUSH_TUNABLES_PROFILE_VALUES + if profile not in supported: + raise CephClientInvalidChoice( + function='osd_crush_tunables', + option='profile', + value=profile, + supported=', '.join(supported)) + + kwargs['profile'] = profile + return self._request('osd crush tunables', **kwargs) + + OSD_CRUSH_SET_TUNABLE_TUNABLE_VALUES = ['straw_calc_version'] + + def osd_crush_set_tunable( + self, tunable, value, body='json', timeout=None): + """set crush tunable to """ + kwargs = dict(body=body, timeout=timeout) + if not isinstance(tunable, six.string_types): + raise CephClientTypeError( + name='tunable', + actual=type(tunable), + expected=six.string_types) + supported = CephClient.OSD_CRUSH_SET_TUNABLE_TUNABLE_VALUES + if tunable not in supported: + raise CephClientInvalidChoice( + function='osd_crush_set_tunable', + option='tunable', + value=tunable, + supported=', '.join(supported)) + + kwargs['tunable'] = tunable + if not isinstance(value, six.integer_types): + raise CephClientTypeError( + name='value', + actual=type(value), + expected=int) + + kwargs['value'] = value + return self._request('osd crush set-tunable', **kwargs) + + OSD_CRUSH_GET_TUNABLE_TUNABLE_VALUES = ['straw_calc_version'] + + def osd_crush_get_tunable(self, tunable, body='json', timeout=None): + """get crush tunable """ + kwargs = dict(body=body, timeout=timeout) + if not isinstance(tunable, six.string_types): + raise CephClientTypeError( + name='tunable', + actual=type(tunable), + expected=six.string_types) + supported = CephClient.OSD_CRUSH_GET_TUNABLE_TUNABLE_VALUES + if tunable not in supported: + raise CephClientInvalidChoice( + function='osd_crush_get_tunable', + option='tunable', + value=tunable, + supported=', '.join(supported)) + + kwargs['tunable'] = tunable + return self._request('osd crush get-tunable', **kwargs) + + def osd_crush_show_tunables(self, body='json', timeout=None): + """show current crush tunables""" + return self._request('osd crush show-tunables', + body=body, timeout=timeout) + + OSD_CRUSH_RULE_CREATE_SIMPLE_MODE_VALUES = ['firstn', 'indep'] + + def osd_crush_rule_create_simple( + self, name, root, _type, mode=None, body='json', timeout=None): + """create crush rule to start from , replicate acrossbuckets of type , using a choose mode of (default firstn; indep best for erasure pools) """ + kwargs = dict(body=body, timeout=timeout) + if not isinstance(name, six.string_types): + raise CephClientTypeError( + name='name', + actual=type(name), + expected=six.string_types) + + kwargs['name'] = name + if not isinstance(root, six.string_types): + raise CephClientTypeError( + name='root', + actual=type(root), + expected=six.string_types) + + kwargs['root'] = root + if not isinstance(_type, six.string_types): + raise CephClientTypeError( + name='_type', + actual=type(_type), + expected=six.string_types) + + kwargs['type'] = _type + if mode is not None: + if not isinstance(mode, six.string_types): + raise CephClientTypeError( + name='mode', + actual=type(mode), + expected=six.string_types) + supported = CephClient.OSD_CRUSH_RULE_CREATE_SIMPLE_MODE_VALUES + if mode not in supported: + raise CephClientInvalidChoice( + function='osd_crush_rule_create_simple', + option='mode', + value=mode, + supported=', '.join(supported)) + kwargs['mode'] = mode + return self._request('osd crush rule create-simple', **kwargs) + + def osd_crush_rule_create_replicated( + self, name, root, _type, _class=None, body='json', + timeout=None): + """create crush rule for replicated pool to start from ,replicate across buckets of type , using a choose mode of (default firstn; indep best for erasure pools) """ + kwargs = dict(body=body, timeout=timeout) + if not isinstance(name, six.string_types): + raise CephClientTypeError( + name='name', + actual=type(name), + expected=six.string_types) + + kwargs['name'] = name + if not isinstance(root, six.string_types): + raise CephClientTypeError( + name='root', + actual=type(root), + expected=six.string_types) + + kwargs['root'] = root + if not isinstance(_type, six.string_types): + raise CephClientTypeError( + name='_type', + actual=type(_type), + expected=six.string_types) + + kwargs['type'] = _type + if _class is not None: + if not isinstance(_class, six.string_types): + raise CephClientTypeError( + name='_class', + actual=type(_class), + expected=six.string_types) + kwargs['class'] = _class + return self._request('osd crush rule create-replicated', **kwargs) + + def osd_crush_rule_create_erasure( + self, name, profile=None, body='json', timeout=None): + """create crush rule for erasure coded pool created with (default default) """ + kwargs = dict(body=body, timeout=timeout) + if not isinstance(name, six.string_types): + raise CephClientTypeError( + name='name', + actual=type(name), + expected=six.string_types) + + kwargs['name'] = name + if profile is not None: + if not isinstance(profile, six.string_types): + raise CephClientTypeError( + name='profile', + actual=type(profile), + expected=six.string_types) + kwargs['profile'] = profile + return self._request('osd crush rule create-erasure', **kwargs) + + def osd_crush_rule_rm(self, name, body='json', timeout=None): + """remove crush rule """ + kwargs = dict(body=body, timeout=timeout) + if not isinstance(name, six.string_types): + raise CephClientTypeError( + name='name', + actual=type(name), + expected=six.string_types) + + kwargs['name'] = name + return self._request('osd crush rule rm', **kwargs) + + def osd_crush_rule_rename( + self, srcname, dstname, body='json', timeout=None): + """rename crush rule to """ + kwargs = dict(body=body, timeout=timeout) + if not isinstance(srcname, six.string_types): + raise CephClientTypeError( + name='srcname', + actual=type(srcname), + expected=six.string_types) + + kwargs['srcname'] = srcname + if not isinstance(dstname, six.string_types): + raise CephClientTypeError( + name='dstname', + actual=type(dstname), + expected=six.string_types) + + kwargs['dstname'] = dstname + return self._request('osd crush rule rename', **kwargs) + + OSD_CRUSH_TREE_SHADOW_VALUES = ['--show-shadow'] + + def osd_crush_tree(self, shadow=None, body='json', timeout=None): + """dump crush buckets and items in a tree view""" + kwargs = dict(body=body, timeout=timeout) + if shadow is not None: + if not isinstance(shadow, six.string_types): + raise CephClientTypeError( + name='shadow', + actual=type(shadow), + expected=six.string_types) + supported = CephClient.OSD_CRUSH_TREE_SHADOW_VALUES + if shadow not in supported: + raise CephClientInvalidChoice( + function='osd_crush_tree', + option='shadow', + value=shadow, + supported=', '.join(supported)) + kwargs['shadow'] = shadow + return self._request('osd crush tree', **kwargs) + + def osd_crush_ls(self, node, body='json', timeout=None): + """list items beneath a node in the CRUSH tree""" + kwargs = dict(body=body, timeout=timeout) + if not isinstance(node, six.string_types): + raise CephClientTypeError( + name='node', + actual=type(node), + expected=six.string_types) + + kwargs['node'] = node + return self._request('osd crush ls', **kwargs) + + def osd_crush_class_ls(self, body='json', timeout=None): + """list all crush device classes""" + return self._request('osd crush class ls', + body=body, timeout=timeout) + + def osd_crush_class_ls_osd(self, _class, body='json', timeout=None): + """list all osds belonging to the specific """ + kwargs = dict(body=body, timeout=timeout) + if not isinstance(_class, six.string_types): + raise CephClientTypeError( + name='_class', + actual=type(_class), + expected=six.string_types) + + kwargs['class'] = _class + return self._request('osd crush class ls-osd', **kwargs) + + def osd_crush_weight_set_ls(self, body='json', timeout=None): + """list crush weight sets""" + return self._request('osd crush weight-set ls', + body=body, timeout=timeout) + + def osd_crush_weight_set_dump(self, body='json', timeout=None): + """dump crush weight sets""" + return self._request('osd crush weight-set dump', + body=body, timeout=timeout) + + def osd_crush_weight_set_create_compat( + self, body='json', timeout=None): + """create a default backward-compatible weight-set""" + return self._request( + 'osd crush weight-set create-compat', body=body, + timeout=timeout) + + OSD_CRUSH_WEIGHT_SET_CREATE_MODE_VALUES = ['flat', 'positional'] + + def osd_crush_weight_set_create( + self, pool, mode, body='json', timeout=None): + """create a weight-set for a given pool""" + kwargs = dict(body=body, timeout=timeout) + if not isinstance(pool, six.string_types): + raise CephClientTypeError( + name='pool', + actual=type(pool), + expected=six.string_types) + + kwargs['pool'] = pool + if not isinstance(mode, six.string_types): + raise CephClientTypeError( + name='mode', + actual=type(mode), + expected=six.string_types) + supported = CephClient.OSD_CRUSH_WEIGHT_SET_CREATE_MODE_VALUES + if mode not in supported: + raise CephClientInvalidChoice( + function='osd_crush_weight_set_create', + option='mode', + value=mode, + supported=', '.join(supported)) + + kwargs['mode'] = mode + return self._request('osd crush weight-set create', **kwargs) + + def osd_crush_weight_set_rm(self, pool, body='json', timeout=None): + """remove the weight-set for a given pool""" + kwargs = dict(body=body, timeout=timeout) + if not isinstance(pool, six.string_types): + raise CephClientTypeError( + name='pool', + actual=type(pool), + expected=six.string_types) + + kwargs['pool'] = pool + return self._request('osd crush weight-set rm', **kwargs) + + def osd_crush_weight_set_rm_compat(self, body='json', timeout=None): + """remove the backward-compatible weight-set""" + return self._request( + 'osd crush weight-set rm-compat', body=body, timeout=timeout) + + def osd_crush_weight_set_reweight( + self, pool, item, weight, body='json', timeout=None): + """set weight for an item (bucket or osd) in a pool's weight-set""" + kwargs = dict(body=body, timeout=timeout) + if not isinstance(pool, six.string_types): + raise CephClientTypeError( + name='pool', + actual=type(pool), + expected=six.string_types) + + kwargs['pool'] = pool + if not isinstance(item, six.string_types): + raise CephClientTypeError( + name='item', + actual=type(item), + expected=six.string_types) + + kwargs['item'] = item + if not isinstance(weight, (six.integer_types, float)): + raise CephClientTypeError( + name='weight', + actual=type(weight), + expected=int) + if weight < 0.0: + raise CephClientValueOutOfBounds( + name='weight', + actual=weight, + min=0.0, + max='unlimited') + + if not isinstance(weight, list): + weight = [weight] + kwargs['weight'] = weight + return self._request('osd crush weight-set reweight', **kwargs) + + def osd_crush_weight_set_reweight_compat( + self, item, weight, body='json', timeout=None): + """set weight for an item (bucket or osd) in the backward-compatibleweight-set """ + kwargs = dict(body=body, timeout=timeout) + if not isinstance(item, six.string_types): + raise CephClientTypeError( + name='item', + actual=type(item), + expected=six.string_types) + + kwargs['item'] = item + if not isinstance(weight, (six.integer_types, float)): + raise CephClientTypeError( + name='weight', + actual=type(weight), + expected=int) + if weight < 0.0: + raise CephClientValueOutOfBounds( + name='weight', + actual=weight, + min=0.0, + max='unlimited') + + if not isinstance(weight, list): + weight = [weight] + kwargs['weight'] = weight + return self._request( + 'osd crush weight-set reweight-compat', **kwargs) + + def osd_setmaxosd(self, newmax, body='json', timeout=None): + """set new maximum osd value""" + kwargs = dict(body=body, timeout=timeout) + if not isinstance(newmax, six.integer_types): + raise CephClientTypeError( + name='newmax', + actual=type(newmax), + expected=int) + if newmax < 0: + raise CephClientValueOutOfBounds( + name='newmax', + actual=newmax, + min=0, + max='unlimited') + + kwargs['newmax'] = newmax + return self._request('osd setmaxosd', **kwargs) + + def osd_set_full_ratio(self, ratio, body='json', timeout=None): + """set usage ratio at which OSDs are marked full""" + kwargs = dict(body=body, timeout=timeout) + if not isinstance(ratio, (six.integer_types, float)): + raise CephClientTypeError( + name='ratio', + actual=type(ratio), + expected=int) + if ratio < 0.0 or ratio > 1.0: + raise CephClientValueOutOfBounds( + name='ratio', + actual=ratio, + min=0.0, + max=1.0) + + kwargs['ratio'] = ratio + return self._request('osd set-full-ratio', **kwargs) + + def osd_set_backfillfull_ratio(self, ratio, body='json', timeout=None): + """set usage ratio at which OSDs are marked too full to backfill""" + kwargs = dict(body=body, timeout=timeout) + if not isinstance(ratio, (six.integer_types, float)): + raise CephClientTypeError( + name='ratio', + actual=type(ratio), + expected=int) + if ratio < 0.0 or ratio > 1.0: + raise CephClientValueOutOfBounds( + name='ratio', + actual=ratio, + min=0.0, + max=1.0) + + kwargs['ratio'] = ratio + return self._request('osd set-backfillfull-ratio', **kwargs) + + def osd_set_nearfull_ratio(self, ratio, body='json', timeout=None): + """set usage ratio at which OSDs are marked near-full""" + kwargs = dict(body=body, timeout=timeout) + if not isinstance(ratio, (six.integer_types, float)): + raise CephClientTypeError( + name='ratio', + actual=type(ratio), + expected=int) + if ratio < 0.0 or ratio > 1.0: + raise CephClientValueOutOfBounds( + name='ratio', + actual=ratio, + min=0.0, + max=1.0) + + kwargs['ratio'] = ratio + return self._request('osd set-nearfull-ratio', **kwargs) + + def osd_get_require_min_compat_client(self, body='json', timeout=None): + """get the minimum client version we will maintain compatibility with""" + return self._request( + 'osd get-require-min-compat-client', body=body, + timeout=timeout) + + OSD_SET_REQUIRE_MIN_COMPAT_CLIENT_SURE_VALUES = [ + '--yes-i-really-mean-it'] + + def osd_set_require_min_compat_client( + self, version, sure=None, body='json', timeout=None): + """set the minimum client version we will maintain compatibility with""" + kwargs = dict(body=body, timeout=timeout) + if not isinstance(version, six.string_types): + raise CephClientTypeError( + name='version', + actual=type(version), + expected=six.string_types) + + kwargs['version'] = version + if sure is not None: + if not isinstance(sure, six.string_types): + raise CephClientTypeError( + name='sure', + actual=type(sure), + expected=six.string_types) + supported = CephClient.OSD_SET_REQUIRE_MIN_COMPAT_CLIENT_SURE_VALUES # noqa E501 + if sure not in supported: + raise CephClientInvalidChoice( + function='osd_set_require_min_compat_client', + option='sure', + value=sure, + supported=', '.join(supported)) + kwargs['sure'] = sure + return self._request('osd set-require-min-compat-client', **kwargs) + + def osd_pause(self, body='json', timeout=None): + """pause osd""" + return self._request('osd pause', body=body, timeout=timeout) + + def osd_unpause(self, body='json', timeout=None): + """unpause osd""" + return self._request('osd unpause', body=body, timeout=timeout) + + def osd_erasure_code_profile_set( + self, name, profile=None, body='json', timeout=None): + """create erasure code profile with [ ...] pairs. Adda --force at the end to override an existing profile (VERY DANGEROUS) """ + kwargs = dict(body=body, timeout=timeout) + if not isinstance(name, six.string_types): + raise CephClientTypeError( + name='name', + actual=type(name), + expected=six.string_types) + + kwargs['name'] = name + if profile is not None: + if isinstance(profile, list): + for item in profile: + if not isinstance(item, six.string_types): + raise CephClientTypeError( + name='profile', + actual=item, + expected='list of strings') + else: + if not isinstance(profile, six.string_types): + raise CephClientTypeError( + name='profile', + actual=type(profile), + expected=six.string_types) + if not isinstance(profile, list): + profile = [profile] + kwargs['profile'] = profile + return self._request('osd erasure-code-profile set', **kwargs) + + def osd_erasure_code_profile_get( + self, name, body='json', timeout=None): + """get erasure code profile """ + kwargs = dict(body=body, timeout=timeout) + if not isinstance(name, six.string_types): + raise CephClientTypeError( + name='name', + actual=type(name), + expected=six.string_types) + + kwargs['name'] = name + return self._request('osd erasure-code-profile get', **kwargs) + + def osd_erasure_code_profile_rm(self, name, body='json', timeout=None): + """remove erasure code profile """ + kwargs = dict(body=body, timeout=timeout) + if not isinstance(name, six.string_types): + raise CephClientTypeError( + name='name', + actual=type(name), + expected=six.string_types) + + kwargs['name'] = name + return self._request('osd erasure-code-profile rm', **kwargs) + + def osd_erasure_code_profile_ls(self, body='json', timeout=None): + """list all erasure code profiles""" + return self._request( + 'osd erasure-code-profile ls', body=body, timeout=timeout) + + OSD_SET_KEY_VALUES = \ + ['full', 'pause', 'noup', 'nodown', 'noout', + 'noin', 'nobackfill', 'norebalance', + 'norecover', 'noscrub', 'nodeep-scrub', + 'notieragent', 'nosnaptrim', 'sortbitwise', + 'recovery_deletes', 'require_jewel_osds', + 'require_kraken_osds'] + + OSD_SET_SURE_VALUES = ['--yes-i-really-mean-it'] + + def osd_set(self, key, sure=None, body='json', timeout=None): + """set """ + kwargs = dict(body=body, timeout=timeout) + if not isinstance(key, six.string_types): + raise CephClientTypeError( + name='key', + actual=type(key), + expected=six.string_types) + supported = CephClient.OSD_SET_KEY_VALUES + if key not in supported: + raise CephClientInvalidChoice( + function='osd_set', + option='key', + value=key, + supported=', '.join(supported)) + + kwargs['key'] = key + if sure is not None: + if not isinstance(sure, six.string_types): + raise CephClientTypeError( + name='sure', + actual=type(sure), + expected=six.string_types) + supported = CephClient.OSD_SET_SURE_VALUES + if sure not in supported: + raise CephClientInvalidChoice( + function='osd_set', + option='sure', + value=sure, + supported=', '.join(supported)) + kwargs['sure'] = sure + return self._request('osd set', **kwargs) + + OSD_UNSET_KEY_VALUES = \ + ['full', 'pause', 'noup', 'nodown', 'noout', + 'noin', 'nobackfill', 'norebalance', + 'norecover', 'noscrub', 'nodeep-scrub', + 'notieragent', 'nosnaptrim'] + + def osd_unset(self, key, body='json', timeout=None): + """unset """ + kwargs = dict(body=body, timeout=timeout) + if not isinstance(key, six.string_types): + raise CephClientTypeError( + name='key', + actual=type(key), + expected=six.string_types) + supported = CephClient.OSD_UNSET_KEY_VALUES + if key not in supported: + raise CephClientInvalidChoice( + function='osd_unset', + option='key', + value=key, + supported=', '.join(supported)) + + kwargs['key'] = key + return self._request('osd unset', **kwargs) + + OSD_REQUIRE_OSD_RELEASE_RELEASE_VALUES = ['luminous', 'mimic'] + + OSD_REQUIRE_OSD_RELEASE_SURE_VALUES = ['--yes-i-really-mean-it'] + + def osd_require_osd_release( + self, release, sure=None, body='json', timeout=None): + """set the minimum allowed OSD release to participate in the cluster""" + kwargs = dict(body=body, timeout=timeout) + if not isinstance(release, six.string_types): + raise CephClientTypeError( + name='release', + actual=type(release), + expected=six.string_types) + supported = CephClient.OSD_REQUIRE_OSD_RELEASE_RELEASE_VALUES + if release not in supported: + raise CephClientInvalidChoice( + function='osd_require_osd_release', + option='release', + value=release, + supported=', '.join(supported)) + + kwargs['release'] = release + if sure is not None: + if not isinstance(sure, six.string_types): + raise CephClientTypeError( + name='sure', + actual=type(sure), + expected=six.string_types) + supported = CephClient.OSD_REQUIRE_OSD_RELEASE_SURE_VALUES + if sure not in supported: + raise CephClientInvalidChoice( + function='osd_require_osd_release', + option='sure', + value=sure, + supported=', '.join(supported)) + kwargs['sure'] = sure + return self._request('osd require-osd-release', **kwargs) + + def osd_down(self, ids, body='json', timeout=None): + """set osd(s) [...] down, or use to set all osds down""" + kwargs = dict(body=body, timeout=timeout) + if isinstance(ids, list): + for item in ids: + if not isinstance(item, six.string_types): + raise CephClientTypeError( + name='ids', + actual=item, + expected='list of strings') + else: + if not isinstance(ids, six.string_types): + raise CephClientTypeError( + name='ids', + actual=type(ids), + expected=six.string_types) + + if not isinstance(ids, list): + ids = [ids] + kwargs['ids'] = ids + return self._request('osd down', **kwargs) + + def osd_out(self, ids, body='json', timeout=None): + """set osd(s) [...] out, or use to set all osds out""" + kwargs = dict(body=body, timeout=timeout) + if isinstance(ids, list): + for item in ids: + if not isinstance(item, six.string_types): + raise CephClientTypeError( + name='ids', + actual=item, + expected='list of strings') + else: + if not isinstance(ids, six.string_types): + raise CephClientTypeError( + name='ids', + actual=type(ids), + expected=six.string_types) + + if not isinstance(ids, list): + ids = [ids] + kwargs['ids'] = ids + return self._request('osd out', **kwargs) + + def osd_in(self, ids, body='json', timeout=None): + """set osd(s) [...] in, can use to automatically setall previously out osds in """ + kwargs = dict(body=body, timeout=timeout) + if isinstance(ids, list): + for item in ids: + if not isinstance(item, six.string_types): + raise CephClientTypeError( + name='ids', + actual=item, + expected='list of strings') + else: + if not isinstance(ids, six.string_types): + raise CephClientTypeError( + name='ids', + actual=type(ids), + expected=six.string_types) + + if not isinstance(ids, list): + ids = [ids] + kwargs['ids'] = ids + return self._request('osd in', **kwargs) + + def osd_rm(self, ids, body='json', timeout=None): + """remove osd(s) [...], or use to remove all osds""" + kwargs = dict(body=body, timeout=timeout) + if isinstance(ids, list): + for item in ids: + if not isinstance(item, six.string_types): + raise CephClientTypeError( + name='ids', + actual=item, + expected='list of strings') + else: + if not isinstance(ids, six.string_types): + raise CephClientTypeError( + name='ids', + actual=type(ids), + expected=six.string_types) + + if not isinstance(ids, list): + ids = [ids] + kwargs['ids'] = ids + return self._request('osd rm', **kwargs) + + def osd_add_noup(self, ids, body='json', timeout=None): + """mark osd(s) [...] as noup, or use to mark all osdsas noup """ + kwargs = dict(body=body, timeout=timeout) + if isinstance(ids, list): + for item in ids: + if not isinstance(item, six.string_types): + raise CephClientTypeError( + name='ids', + actual=item, + expected='list of strings') + else: + if not isinstance(ids, six.string_types): + raise CephClientTypeError( + name='ids', + actual=type(ids), + expected=six.string_types) + + if not isinstance(ids, list): + ids = [ids] + kwargs['ids'] = ids + return self._request('osd add-noup', **kwargs) + + def osd_add_nodown(self, ids, body='json', timeout=None): + """mark osd(s) [...] as nodown, or use to mark allosds as nodown """ + kwargs = dict(body=body, timeout=timeout) + if isinstance(ids, list): + for item in ids: + if not isinstance(item, six.string_types): + raise CephClientTypeError( + name='ids', + actual=item, + expected='list of strings') + else: + if not isinstance(ids, six.string_types): + raise CephClientTypeError( + name='ids', + actual=type(ids), + expected=six.string_types) + + if not isinstance(ids, list): + ids = [ids] + kwargs['ids'] = ids + return self._request('osd add-nodown', **kwargs) + + def osd_add_noin(self, ids, body='json', timeout=None): + """mark osd(s) [...] as noin, or use to mark all osdsas noin """ + kwargs = dict(body=body, timeout=timeout) + if isinstance(ids, list): + for item in ids: + if not isinstance(item, six.string_types): + raise CephClientTypeError( + name='ids', + actual=item, + expected='list of strings') + else: + if not isinstance(ids, six.string_types): + raise CephClientTypeError( + name='ids', + actual=type(ids), + expected=six.string_types) + + if not isinstance(ids, list): + ids = [ids] + kwargs['ids'] = ids + return self._request('osd add-noin', **kwargs) + + def osd_add_noout(self, ids, body='json', timeout=None): + """mark osd(s) [...] as noout, or use to mark all osdsas noout """ + kwargs = dict(body=body, timeout=timeout) + if isinstance(ids, list): + for item in ids: + if not isinstance(item, six.string_types): + raise CephClientTypeError( + name='ids', + actual=item, + expected='list of strings') + else: + if not isinstance(ids, six.string_types): + raise CephClientTypeError( + name='ids', + actual=type(ids), + expected=six.string_types) + + if not isinstance(ids, list): + ids = [ids] + kwargs['ids'] = ids + return self._request('osd add-noout', **kwargs) + + def osd_rm_noup(self, ids, body='json', timeout=None): + """allow osd(s) [...] to be marked up (if they are currentlymarked as noup), can use to automatically filter out all noup osds """ + kwargs = dict(body=body, timeout=timeout) + if isinstance(ids, list): + for item in ids: + if not isinstance(item, six.string_types): + raise CephClientTypeError( + name='ids', + actual=item, + expected='list of strings') + else: + if not isinstance(ids, six.string_types): + raise CephClientTypeError( + name='ids', + actual=type(ids), + expected=six.string_types) + + if not isinstance(ids, list): + ids = [ids] + kwargs['ids'] = ids + return self._request('osd rm-noup', **kwargs) + + def osd_rm_nodown(self, ids, body='json', timeout=None): + """allow osd(s) [...] to be marked down (if they are currentlymarked as nodown), can use to automatically filter out all nodown osds """ + kwargs = dict(body=body, timeout=timeout) + if isinstance(ids, list): + for item in ids: + if not isinstance(item, six.string_types): + raise CephClientTypeError( + name='ids', + actual=item, + expected='list of strings') + else: + if not isinstance(ids, six.string_types): + raise CephClientTypeError( + name='ids', + actual=type(ids), + expected=six.string_types) + + if not isinstance(ids, list): + ids = [ids] + kwargs['ids'] = ids + return self._request('osd rm-nodown', **kwargs) + + def osd_rm_noin(self, ids, body='json', timeout=None): + """allow osd(s) [...] to be marked in (if they are currentlymarked as noin), can use to automatically filter out all noin osds """ + kwargs = dict(body=body, timeout=timeout) + if isinstance(ids, list): + for item in ids: + if not isinstance(item, six.string_types): + raise CephClientTypeError( + name='ids', + actual=item, + expected='list of strings') + else: + if not isinstance(ids, six.string_types): + raise CephClientTypeError( + name='ids', + actual=type(ids), + expected=six.string_types) + + if not isinstance(ids, list): + ids = [ids] + kwargs['ids'] = ids + return self._request('osd rm-noin', **kwargs) + + def osd_rm_noout(self, ids, body='json', timeout=None): + """allow osd(s) [...] to be marked out (if they are currentlymarked as noout), can use to automatically filter out all noout osds """ + kwargs = dict(body=body, timeout=timeout) + if isinstance(ids, list): + for item in ids: + if not isinstance(item, six.string_types): + raise CephClientTypeError( + name='ids', + actual=item, + expected='list of strings') + else: + if not isinstance(ids, six.string_types): + raise CephClientTypeError( + name='ids', + actual=type(ids), + expected=six.string_types) + + if not isinstance(ids, list): + ids = [ids] + kwargs['ids'] = ids + return self._request('osd rm-noout', **kwargs) + + def osd_reweight(self, _id, weight, body='json', timeout=None): + """reweight osd to 0.0 < < 1.0""" + kwargs = dict(body=body, timeout=timeout) + if isinstance(_id, six.integer_types): + pass + elif isinstance(_id, six.string_types): + _id = _id.lower() + prefix = 'osd.' + if not _id.startswith(prefix): + raise CephClientInvalidOsdIdValue(osdid=_id) + _id = int(_id[len(prefix):]) + else: + raise CephClientTypeError( + name='_id', + actual=type(_id), + expected='int or string') + + kwargs['id'] = _id + if not isinstance(weight, (six.integer_types, float)): + raise CephClientTypeError( + name='weight', + actual=type(weight), + expected=int) + if weight < 0.0 or weight > 1.0: + raise CephClientValueOutOfBounds( + name='weight', + actual=weight, + min=0.0, + max=1.0) + + kwargs['weight'] = weight + return self._request('osd reweight', **kwargs) + + def osd_reweightn(self, weights, body='json', timeout=None): + """reweight osds with {: ,...})""" + kwargs = dict(body=body, timeout=timeout) + if not isinstance(weights, six.string_types): + raise CephClientTypeError( + name='weights', + actual=type(weights), + expected=six.string_types) + + kwargs['weights'] = weights + return self._request('osd reweightn', **kwargs) + + OSD_FORCE_CREATE_PG_SURE_VALUES = ['--yes-i-really-mean-it'] + + def osd_force_create_pg(self, pgid, sure=None, + body='json', timeout=None): + """force creation of pg """ + kwargs = dict(body=body, timeout=timeout) + if not isinstance(pgid, six.string_types): + raise CephClientTypeError( + name='pgid', + actual=type(pgid), + expected=six.string_types) + if not re.match(r'[0-9]+\.[0-9a-fA-F]+', pgid): + raise CephClientInvalidPgid( + name='pgid', + actual=pgid) + + kwargs['pgid'] = pgid + if sure is not None: + if not isinstance(sure, six.string_types): + raise CephClientTypeError( + name='sure', + actual=type(sure), + expected=six.string_types) + supported = CephClient.OSD_FORCE_CREATE_PG_SURE_VALUES + if sure not in supported: + raise CephClientInvalidChoice( + function='osd_force_create_pg', + option='sure', + value=sure, + supported=', '.join(supported)) + kwargs['sure'] = sure + return self._request('osd force-create-pg', **kwargs) + + def osd_pg_temp(self, pgid, _id=None, body='json', timeout=None): + """set pg_temp mapping pgid:[ [...]] (developers only)""" + kwargs = dict(body=body, timeout=timeout) + if not isinstance(pgid, six.string_types): + raise CephClientTypeError( + name='pgid', + actual=type(pgid), + expected=six.string_types) + if not re.match(r'[0-9]+\.[0-9a-fA-F]+', pgid): + raise CephClientInvalidPgid( + name='pgid', + actual=pgid) + + kwargs['pgid'] = pgid + if _id is not None: + if isinstance(_id, six.integer_types): + pass + elif isinstance(_id, six.string_types): + _id = _id.lower() + prefix = 'osd.' + if not _id.startswith(prefix): + raise CephClientInvalidOsdIdValue(osdid=_id) + _id = int(_id[len(prefix):]) + else: + raise CephClientTypeError( + name='_id', + actual=type(_id), + expected='int or string') + if not isinstance(_id, list): + _id = [_id] + kwargs['id'] = _id + return self._request('osd pg-temp', **kwargs) + + def osd_pg_upmap(self, pgid, _id, body='json', timeout=None): + """set pg_upmap mapping :[ [...]] (developers only)""" + kwargs = dict(body=body, timeout=timeout) + if not isinstance(pgid, six.string_types): + raise CephClientTypeError( + name='pgid', + actual=type(pgid), + expected=six.string_types) + if not re.match(r'[0-9]+\.[0-9a-fA-F]+', pgid): + raise CephClientInvalidPgid( + name='pgid', + actual=pgid) + + kwargs['pgid'] = pgid + if isinstance(_id, six.integer_types): + pass + elif isinstance(_id, six.string_types): + _id = _id.lower() + prefix = 'osd.' + if not _id.startswith(prefix): + raise CephClientInvalidOsdIdValue(osdid=_id) + _id = int(_id[len(prefix):]) + else: + raise CephClientTypeError( + name='_id', + actual=type(_id), + expected='int or string') + + if not isinstance(_id, list): + _id = [_id] + kwargs['id'] = _id + return self._request('osd pg-upmap', **kwargs) + + def osd_rm_pg_upmap(self, pgid, body='json', timeout=None): + """clear pg_upmap mapping for (developers only)""" + kwargs = dict(body=body, timeout=timeout) + if not isinstance(pgid, six.string_types): + raise CephClientTypeError( + name='pgid', + actual=type(pgid), + expected=six.string_types) + if not re.match(r'[0-9]+\.[0-9a-fA-F]+', pgid): + raise CephClientInvalidPgid( + name='pgid', + actual=pgid) + + kwargs['pgid'] = pgid + return self._request('osd rm-pg-upmap', **kwargs) + + def osd_pg_upmap_items(self, pgid, _id, body='json', timeout=None): + """set pg_upmap_items mapping :{ to , [...]} (developersonly) """ + kwargs = dict(body=body, timeout=timeout) + if not isinstance(pgid, six.string_types): + raise CephClientTypeError( + name='pgid', + actual=type(pgid), + expected=six.string_types) + if not re.match(r'[0-9]+\.[0-9a-fA-F]+', pgid): + raise CephClientInvalidPgid( + name='pgid', + actual=pgid) + + kwargs['pgid'] = pgid + if isinstance(_id, six.integer_types): + pass + elif isinstance(_id, six.string_types): + _id = _id.lower() + prefix = 'osd.' + if not _id.startswith(prefix): + raise CephClientInvalidOsdIdValue(osdid=_id) + _id = int(_id[len(prefix):]) + else: + raise CephClientTypeError( + name='_id', + actual=type(_id), + expected='int or string') + + if not isinstance(_id, list): + _id = [_id] + kwargs['id'] = _id + return self._request('osd pg-upmap-items', **kwargs) + + def osd_rm_pg_upmap_items(self, pgid, body='json', timeout=None): + """clear pg_upmap_items mapping for (developers only)""" + kwargs = dict(body=body, timeout=timeout) + if not isinstance(pgid, six.string_types): + raise CephClientTypeError( + name='pgid', + actual=type(pgid), + expected=six.string_types) + if not re.match(r'[0-9]+\.[0-9a-fA-F]+', pgid): + raise CephClientInvalidPgid( + name='pgid', + actual=pgid) + + kwargs['pgid'] = pgid + return self._request('osd rm-pg-upmap-items', **kwargs) + + def osd_primary_temp(self, pgid, _id, body='json', timeout=None): + """set primary_temp mapping pgid:|-1 (developers only)""" + kwargs = dict(body=body, timeout=timeout) + if not isinstance(pgid, six.string_types): + raise CephClientTypeError( + name='pgid', + actual=type(pgid), + expected=six.string_types) + if not re.match(r'[0-9]+\.[0-9a-fA-F]+', pgid): + raise CephClientInvalidPgid( + name='pgid', + actual=pgid) + + kwargs['pgid'] = pgid + if isinstance(_id, six.integer_types): + pass + elif isinstance(_id, six.string_types): + _id = _id.lower() + prefix = 'osd.' + if not _id.startswith(prefix): + raise CephClientInvalidOsdIdValue(osdid=_id) + _id = int(_id[len(prefix):]) + else: + raise CephClientTypeError( + name='_id', + actual=type(_id), + expected='int or string') + + kwargs['id'] = _id + return self._request('osd primary-temp', **kwargs) + + def osd_primary_affinity(self, _id, weight, body='json', timeout=None): + """adjust osd primary-affinity from 0.0 <= <= 1.0""" + kwargs = dict(body=body, timeout=timeout) + if isinstance(_id, six.integer_types): + pass + elif isinstance(_id, six.string_types): + _id = _id.lower() + prefix = 'osd.' + if not _id.startswith(prefix): + raise CephClientInvalidOsdIdValue(osdid=_id) + _id = int(_id[len(prefix):]) + else: + raise CephClientTypeError( + name='_id', + actual=type(_id), + expected='int or string') + + kwargs['id'] = _id + if not isinstance(weight, (six.integer_types, float)): + raise CephClientTypeError( + name='weight', + actual=type(weight), + expected=int) + if weight < 0.0 or weight > 1.0: + raise CephClientValueOutOfBounds( + name='weight', + actual=weight, + min=0.0, + max=1.0) + + kwargs['weight'] = weight + return self._request('osd primary-affinity', **kwargs) + + OSD_DESTROY_SURE_VALUES = ['--yes-i-really-mean-it'] + + def osd_destroy(self, _id, sure=None, body='json', timeout=None): + """mark osd as being destroyed. Keeps the ID intact (allowing reuse), butremoves cephx keys, config-key data and lockbox keys, rendering data permanently unreadable. """ + kwargs = dict(body=body, timeout=timeout) + if isinstance(_id, six.integer_types): + pass + elif isinstance(_id, six.string_types): + _id = _id.lower() + prefix = 'osd.' + if not _id.startswith(prefix): + raise CephClientInvalidOsdIdValue(osdid=_id) + _id = int(_id[len(prefix):]) + else: + raise CephClientTypeError( + name='_id', + actual=type(_id), + expected='int or string') + + kwargs['id'] = _id + if sure is not None: + if not isinstance(sure, six.string_types): + raise CephClientTypeError( + name='sure', + actual=type(sure), + expected=six.string_types) + supported = CephClient.OSD_DESTROY_SURE_VALUES + if sure not in supported: + raise CephClientInvalidChoice( + function='osd_destroy', + option='sure', + value=sure, + supported=', '.join(supported)) + kwargs['sure'] = sure + return self._request('osd destroy', **kwargs) + + OSD_PURGE_NEW_SURE_VALUES = ['--yes-i-really-mean-it'] + + def osd_purge_new(self, _id, sure=None, body='json', timeout=None): + """purge all traces of an OSD that was partially created but neverstarted """ + kwargs = dict(body=body, timeout=timeout) + if isinstance(_id, six.integer_types): + pass + elif isinstance(_id, six.string_types): + _id = _id.lower() + prefix = 'osd.' + if not _id.startswith(prefix): + raise CephClientInvalidOsdIdValue(osdid=_id) + _id = int(_id[len(prefix):]) + else: + raise CephClientTypeError( + name='_id', + actual=type(_id), + expected='int or string') + + kwargs['id'] = _id + if sure is not None: + if not isinstance(sure, six.string_types): + raise CephClientTypeError( + name='sure', + actual=type(sure), + expected=six.string_types) + supported = CephClient.OSD_PURGE_NEW_SURE_VALUES + if sure not in supported: + raise CephClientInvalidChoice( + function='osd_purge_new', + option='sure', + value=sure, + supported=', '.join(supported)) + kwargs['sure'] = sure + return self._request('osd purge-new', **kwargs) + + OSD_PURGE_SURE_VALUES = ['--yes-i-really-mean-it'] + + def osd_purge(self, _id, sure=None, body='json', timeout=None): + """purge all osd data from the monitors. Combines `osd destroy`, `osdrm`, and `osd crush rm`. """ + kwargs = dict(body=body, timeout=timeout) + if isinstance(_id, six.integer_types): + pass + elif isinstance(_id, six.string_types): + _id = _id.lower() + prefix = 'osd.' + if not _id.startswith(prefix): + raise CephClientInvalidOsdIdValue(osdid=_id) + _id = int(_id[len(prefix):]) + else: + raise CephClientTypeError( + name='_id', + actual=type(_id), + expected='int or string') + + kwargs['id'] = _id + if sure is not None: + if not isinstance(sure, six.string_types): + raise CephClientTypeError( + name='sure', + actual=type(sure), + expected=six.string_types) + supported = CephClient.OSD_PURGE_SURE_VALUES + if sure not in supported: + raise CephClientInvalidChoice( + function='osd_purge', + option='sure', + value=sure, + supported=', '.join(supported)) + kwargs['sure'] = sure + return self._request('osd purge', **kwargs) + + OSD_LOST_SURE_VALUES = ['--yes-i-really-mean-it'] + + def osd_lost(self, _id, sure=None, body='json', timeout=None): + """mark osd as permanently lost. THIS DESTROYS DATA IF NO MORE REPLICAS EXIST, BE CAREFUL """ + kwargs = dict(body=body, timeout=timeout) + if isinstance(_id, six.integer_types): + pass + elif isinstance(_id, six.string_types): + _id = _id.lower() + prefix = 'osd.' + if not _id.startswith(prefix): + raise CephClientInvalidOsdIdValue(osdid=_id) + _id = int(_id[len(prefix):]) + else: + raise CephClientTypeError( + name='_id', + actual=type(_id), + expected='int or string') + + kwargs['id'] = _id + if sure is not None: + if not isinstance(sure, six.string_types): + raise CephClientTypeError( + name='sure', + actual=type(sure), + expected=six.string_types) + supported = CephClient.OSD_LOST_SURE_VALUES + if sure not in supported: + raise CephClientInvalidChoice( + function='osd_lost', + option='sure', + value=sure, + supported=', '.join(supported)) + kwargs['sure'] = sure + return self._request('osd lost', **kwargs) + + def osd_create(self, uuid=None, _id=None, body='json', timeout=None): + """create new osd (with optional UUID and ID)""" + kwargs = dict(body=body, timeout=timeout) + if uuid is not None: + + kwargs['uuid'] = uuid + if _id is not None: + if isinstance(_id, six.integer_types): + pass + elif isinstance(_id, six.string_types): + _id = _id.lower() + prefix = 'osd.' + if not _id.startswith(prefix): + raise CephClientInvalidOsdIdValue(osdid=_id) + _id = int(_id[len(prefix):]) + else: + raise CephClientTypeError( + name='_id', + actual=type(_id), + expected='int or string') + kwargs['id'] = _id + return self._request('osd create', **kwargs) + + def osd_new(self, uuid, _id=None, body='json', timeout=None): + """Create a new OSD. If supplied, the `id` to be replaced needs to existand have been previously destroyed. Reads secrets from JSON file via `-i ` (see man page). """ + kwargs = dict(body=body, timeout=timeout) + kwargs['uuid'] = uuid + if _id is not None: + if isinstance(_id, six.integer_types): + pass + elif isinstance(_id, six.string_types): + _id = _id.lower() + prefix = 'osd.' + if not _id.startswith(prefix): + raise CephClientInvalidOsdIdValue(osdid=_id) + _id = int(_id[len(prefix):]) + else: + raise CephClientTypeError( + name='_id', + actual=type(_id), + expected='int or string') + kwargs['id'] = _id + return self._request('osd new', **kwargs) + + OSD_BLACKLIST_BLACKLISTOP_VALUES = ['add', 'rm'] + + def osd_blacklist( + self, blacklistop, addr, expire=None, body='json', + timeout=None): + """add (optionally until seconds from now) or remove fromblacklist """ + kwargs = dict(body=body, timeout=timeout) + if not isinstance(blacklistop, six.string_types): + raise CephClientTypeError( + name='blacklistop', + actual=type(blacklistop), + expected=six.string_types) + supported = CephClient.OSD_BLACKLIST_BLACKLISTOP_VALUES + if blacklistop not in supported: + raise CephClientInvalidChoice( + function='osd_blacklist', + option='blacklistop', + value=blacklistop, + supported=', '.join(supported)) + + kwargs['blacklistop'] = blacklistop + + kwargs['addr'] = addr + if expire is not None: + if not isinstance(expire, (six.integer_types, float)): + raise CephClientTypeError( + name='expire', + actual=type(expire), + expected=int) + if expire < 0.0: + raise CephClientValueOutOfBounds( + name='expire', + actual=expire, + min=0.0, + max='unlimited') + kwargs['expire'] = expire + return self._request('osd blacklist', **kwargs) + + def osd_blacklist_ls(self, body='json', timeout=None): + """show blacklisted clients""" + return self._request('osd blacklist ls', + body=body, timeout=timeout) + + def osd_blacklist_clear(self, body='json', timeout=None): + """clear all blacklisted clients""" + return self._request('osd blacklist clear', + body=body, timeout=timeout) + + def osd_pool_mksnap(self, pool, snap, body='json', timeout=None): + """make snapshot in """ + kwargs = dict(body=body, timeout=timeout) + if not isinstance(pool, six.string_types): + raise CephClientTypeError( + name='pool', + actual=type(pool), + expected=six.string_types) + + kwargs['pool'] = pool + if not isinstance(snap, six.string_types): + raise CephClientTypeError( + name='snap', + actual=type(snap), + expected=six.string_types) + + kwargs['snap'] = snap + return self._request('osd pool mksnap', **kwargs) + + def osd_pool_rmsnap(self, pool, snap, body='json', timeout=None): + """remove snapshot from """ + kwargs = dict(body=body, timeout=timeout) + if not isinstance(pool, six.string_types): + raise CephClientTypeError( + name='pool', + actual=type(pool), + expected=six.string_types) + + kwargs['pool'] = pool + if not isinstance(snap, six.string_types): + raise CephClientTypeError( + name='snap', + actual=type(snap), + expected=six.string_types) + + kwargs['snap'] = snap + return self._request('osd pool rmsnap', **kwargs) + + OSD_POOL_LS_DETAIL_VALUES = ['detail'] + + def osd_pool_ls(self, detail=None, body='json', timeout=None): + """list pools""" + kwargs = dict(body=body, timeout=timeout) + if detail is not None: + if not isinstance(detail, six.string_types): + raise CephClientTypeError( + name='detail', + actual=type(detail), + expected=six.string_types) + supported = CephClient.OSD_POOL_LS_DETAIL_VALUES + if detail not in supported: + raise CephClientInvalidChoice( + function='osd_pool_ls', + option='detail', + value=detail, + supported=', '.join(supported)) + kwargs['detail'] = detail + return self._request('osd pool ls', **kwargs) + + OSD_POOL_CREATE_POOL_TYPE_VALUES = ['replicated', 'erasure'] + + def osd_pool_create( + self, pool, pg_num, pgp_num=None, pool_type=None, + erasure_code_profile=None, rule=None, + expected_num_objects=None, body='json', timeout=None): + """create pool""" + kwargs = dict(body=body, timeout=timeout) + if not isinstance(pool, six.string_types): + raise CephClientTypeError( + name='pool', + actual=type(pool), + expected=six.string_types) + + kwargs['pool'] = pool + if not isinstance(pg_num, six.integer_types): + raise CephClientTypeError( + name='pg_num', + actual=type(pg_num), + expected=int) + if pg_num < 0: + raise CephClientValueOutOfBounds( + name='pg_num', + actual=pg_num, + min=0, + max='unlimited') + + kwargs['pg_num'] = pg_num + if pgp_num is not None: + if not isinstance(pgp_num, six.integer_types): + raise CephClientTypeError( + name='pgp_num', + actual=type(pgp_num), + expected=int) + if pgp_num < 0: + raise CephClientValueOutOfBounds( + name='pgp_num', + actual=pgp_num, + min=0, + max='unlimited') + kwargs['pgp_num'] = pgp_num + if pool_type is not None: + if not isinstance(pool_type, six.string_types): + raise CephClientTypeError( + name='pool_type', + actual=type(pool_type), + expected=six.string_types) + supported = CephClient.OSD_POOL_CREATE_POOL_TYPE_VALUES + if pool_type not in supported: + raise CephClientInvalidChoice( + function='osd_pool_create', + option='pool_type', + value=pool_type, + supported=', '.join(supported)) + kwargs['pool_type'] = pool_type + if erasure_code_profile is not None: + if not isinstance(erasure_code_profile, six.string_types): + raise CephClientTypeError( + name='erasure_code_profile', + actual=type(erasure_code_profile), + expected=six.string_types) + kwargs['erasure_code_profile'] = erasure_code_profile + if rule is not None: + if not isinstance(rule, six.string_types): + raise CephClientTypeError( + name='rule', + actual=type(rule), + expected=six.string_types) + kwargs['rule'] = rule + if expected_num_objects is not None: + if not isinstance(expected_num_objects, six.integer_types): + raise CephClientTypeError( + name='expected_num_objects', + actual=type(expected_num_objects), + expected=int) + kwargs['expected_num_objects'] = expected_num_objects + return self._request('osd pool create', **kwargs) + + def osd_pool_delete(self, pool, pool2=None, + sure=None, body='json', timeout=None): + """delete pool""" + kwargs = dict(body=body, timeout=timeout) + if not isinstance(pool, six.string_types): + raise CephClientTypeError( + name='pool', + actual=type(pool), + expected=six.string_types) + + kwargs['pool'] = pool + if pool2 is not None: + if not isinstance(pool2, six.string_types): + raise CephClientTypeError( + name='pool2', + actual=type(pool2), + expected=six.string_types) + kwargs['pool2'] = pool2 + if sure is not None: + if not isinstance(sure, six.string_types): + raise CephClientTypeError( + name='sure', + actual=type(sure), + expected=six.string_types) + kwargs['sure'] = sure + return self._request('osd pool delete', **kwargs) + + def osd_pool_rm(self, pool, pool2=None, sure=None, + body='json', timeout=None): + """remove pool""" + kwargs = dict(body=body, timeout=timeout) + if not isinstance(pool, six.string_types): + raise CephClientTypeError( + name='pool', + actual=type(pool), + expected=six.string_types) + + kwargs['pool'] = pool + if pool2 is not None: + if not isinstance(pool2, six.string_types): + raise CephClientTypeError( + name='pool2', + actual=type(pool2), + expected=six.string_types) + kwargs['pool2'] = pool2 + if sure is not None: + if not isinstance(sure, six.string_types): + raise CephClientTypeError( + name='sure', + actual=type(sure), + expected=six.string_types) + kwargs['sure'] = sure + return self._request('osd pool rm', **kwargs) + + def osd_pool_rename(self, srcpool, destpool, + body='json', timeout=None): + """rename to """ + kwargs = dict(body=body, timeout=timeout) + if not isinstance(srcpool, six.string_types): + raise CephClientTypeError( + name='srcpool', + actual=type(srcpool), + expected=six.string_types) + + kwargs['srcpool'] = srcpool + if not isinstance(destpool, six.string_types): + raise CephClientTypeError( + name='destpool', + actual=type(destpool), + expected=six.string_types) + + kwargs['destpool'] = destpool + return self._request('osd pool rename', **kwargs) + + OSD_POOL_GET_VAR_VALUES = \ + ['size', 'min_size', 'pg_num', 'pgp_num', + 'crush_rule', 'hashpspool', 'nodelete', + 'nopgchange', 'nosizechange', + 'write_fadvise_dontneed', 'noscrub', + 'nodeep-scrub', 'hit_set_type', + 'hit_set_period', 'hit_set_count', + 'hit_set_fpp', 'use_gmt_hitset', 'auid', + 'target_max_objects', 'target_max_bytes', + 'cache_target_dirty_ratio', + 'cache_target_dirty_high_ratio', + 'cache_target_full_ratio', + 'cache_min_flush_age', 'cache_min_evict_age', + 'erasure_code_profile', + 'min_read_recency_for_promote', 'all', + 'min_write_recency_for_promote', 'fast_read', + 'hit_set_grade_decay_rate', + 'hit_set_search_last_n', 'scrub_min_interval', + 'scrub_max_interval', 'deep_scrub_interval', + 'recovery_priority', 'recovery_op_priority', + 'scrub_priority', 'compression_mode', + 'compression_algorithm', + 'compression_required_ratio', + 'compression_max_blob_size', + 'compression_min_blob_size', 'csum_type', + 'csum_min_block', 'csum_max_block', + 'allow_ec_overwrites'] + + def osd_pool_get(self, pool, var, body='json', timeout=None): + """get pool parameter """ + kwargs = dict(body=body, timeout=timeout) + if not isinstance(pool, six.string_types): + raise CephClientTypeError( + name='pool', + actual=type(pool), + expected=six.string_types) + + kwargs['pool'] = pool + if not isinstance(var, six.string_types): + raise CephClientTypeError( + name='var', + actual=type(var), + expected=six.string_types) + supported = CephClient.OSD_POOL_GET_VAR_VALUES + if var not in supported: + raise CephClientInvalidChoice( + function='osd_pool_get', + option='var', + value=var, + supported=', '.join(supported)) + + kwargs['var'] = var + return self._request('osd pool get', **kwargs) + + OSD_POOL_SET_VAR_VALUES = \ + ['size', 'min_size', 'pg_num', 'pgp_num', + 'crush_rule', 'hashpspool', 'nodelete', + 'nopgchange', 'nosizechange', + 'write_fadvise_dontneed', 'noscrub', + 'nodeep-scrub', 'hit_set_type', + 'hit_set_period', 'hit_set_count', + 'hit_set_fpp', 'use_gmt_hitset', + 'target_max_bytes', 'target_max_objects', + 'cache_target_dirty_ratio', + 'cache_target_dirty_high_ratio', + 'cache_target_full_ratio', + 'cache_min_flush_age', 'cache_min_evict_age', + 'auid', 'min_read_recency_for_promote', + 'min_write_recency_for_promote', 'fast_read', + 'hit_set_grade_decay_rate', + 'hit_set_search_last_n', 'scrub_min_interval', + 'scrub_max_interval', 'deep_scrub_interval', + 'recovery_priority', 'recovery_op_priority', + 'scrub_priority', 'compression_mode', + 'compression_algorithm', + 'compression_required_ratio', + 'compression_max_blob_size', + 'compression_min_blob_size', 'csum_type', + 'csum_min_block', 'csum_max_block', + 'allow_ec_overwrites'] + + OSD_POOL_SET_FORCE_VALUES = ['--yes-i-really-mean-it'] + + def osd_pool_set(self, pool, var, val, force=None, + body='json', timeout=None): + """set pool parameter to """ + kwargs = dict(body=body, timeout=timeout) + if not isinstance(pool, six.string_types): + raise CephClientTypeError( + name='pool', + actual=type(pool), + expected=six.string_types) + + kwargs['pool'] = pool + if not isinstance(var, six.string_types): + raise CephClientTypeError( + name='var', + actual=type(var), + expected=six.string_types) + supported = CephClient.OSD_POOL_SET_VAR_VALUES + if var not in supported: + raise CephClientInvalidChoice( + function='osd_pool_set', + option='var', + value=var, + supported=', '.join(supported)) + + kwargs['var'] = var + if not isinstance(val, six.string_types): + raise CephClientTypeError( + name='val', + actual=type(val), + expected=six.string_types) + + kwargs['val'] = val + if force is not None: + if not isinstance(force, six.string_types): + raise CephClientTypeError( + name='force', + actual=type(force), + expected=six.string_types) + supported = CephClient.OSD_POOL_SET_FORCE_VALUES + if force not in supported: + raise CephClientInvalidChoice( + function='osd_pool_set', + option='force', + value=force, + supported=', '.join(supported)) + kwargs['force'] = force + return self._request('osd pool set', **kwargs) + + OSD_POOL_SET_QUOTA_FIELD_VALUES = ['max_objects', 'max_bytes'] + + def osd_pool_set_quota(self, pool, field, val, + body='json', timeout=None): + """set object or byte limit on pool""" + kwargs = dict(body=body, timeout=timeout) + if not isinstance(pool, six.string_types): + raise CephClientTypeError( + name='pool', + actual=type(pool), + expected=six.string_types) + + kwargs['pool'] = pool + if not isinstance(field, six.string_types): + raise CephClientTypeError( + name='field', + actual=type(field), + expected=six.string_types) + supported = CephClient.OSD_POOL_SET_QUOTA_FIELD_VALUES + if field not in supported: + raise CephClientInvalidChoice( + function='osd_pool_set_quota', + option='field', + value=field, + supported=', '.join(supported)) + + kwargs['field'] = field + if not isinstance(val, six.string_types): + raise CephClientTypeError( + name='val', + actual=type(val), + expected=six.string_types) + + kwargs['val'] = val + return self._request('osd pool set-quota', **kwargs) + + def osd_pool_get_quota(self, pool, body='json', timeout=None): + """obtain object or byte limits for pool""" + kwargs = dict(body=body, timeout=timeout) + if not isinstance(pool, six.string_types): + raise CephClientTypeError( + name='pool', + actual=type(pool), + expected=six.string_types) + + kwargs['pool'] = pool + return self._request('osd pool get-quota', **kwargs) + + OSD_POOL_APPLICATION_ENABLE_FORCE_VALUES = ['--yes-i-really-mean-it'] + + def osd_pool_application_enable( + self, pool, app, force=None, body='json', timeout=None): + """enable use of an application [cephfs,rbd,rgw] on pool """ + kwargs = dict(body=body, timeout=timeout) + if not isinstance(pool, six.string_types): + raise CephClientTypeError( + name='pool', + actual=type(pool), + expected=six.string_types) + + kwargs['pool'] = pool + if not isinstance(app, six.string_types): + raise CephClientTypeError( + name='app', + actual=type(app), + expected=six.string_types) + + kwargs['app'] = app + if force is not None: + if not isinstance(force, six.string_types): + raise CephClientTypeError( + name='force', + actual=type(force), + expected=six.string_types) + supported = CephClient.OSD_POOL_APPLICATION_ENABLE_FORCE_VALUES + if force not in supported: + raise CephClientInvalidChoice( + function='osd_pool_application_enable', + option='force', + value=force, + supported=', '.join(supported)) + kwargs['force'] = force + return self._request('osd pool application enable', **kwargs) + + OSD_POOL_APPLICATION_DISABLE_FORCE_VALUES = ['--yes-i-really-mean-it'] + + def osd_pool_application_disable( + self, pool, app, force=None, body='json', timeout=None): + """disables use of an application on pool """ + kwargs = dict(body=body, timeout=timeout) + if not isinstance(pool, six.string_types): + raise CephClientTypeError( + name='pool', + actual=type(pool), + expected=six.string_types) + + kwargs['pool'] = pool + if not isinstance(app, six.string_types): + raise CephClientTypeError( + name='app', + actual=type(app), + expected=six.string_types) + + kwargs['app'] = app + if force is not None: + if not isinstance(force, six.string_types): + raise CephClientTypeError( + name='force', + actual=type(force), + expected=six.string_types) + supported = CephClient.OSD_POOL_APPLICATION_DISABLE_FORCE_VALUES + if force not in supported: + raise CephClientInvalidChoice( + function='osd_pool_application_disable', + option='force', + value=force, + supported=', '.join(supported)) + kwargs['force'] = force + return self._request('osd pool application disable', **kwargs) + + def osd_pool_application_set( + self, pool, app, key, value, body='json', timeout=None): + """sets application metadata key to on pool """ + kwargs = dict(body=body, timeout=timeout) + if not isinstance(pool, six.string_types): + raise CephClientTypeError( + name='pool', + actual=type(pool), + expected=six.string_types) + + kwargs['pool'] = pool + if not isinstance(app, six.string_types): + raise CephClientTypeError( + name='app', + actual=type(app), + expected=six.string_types) + + kwargs['app'] = app + if not isinstance(key, six.string_types): + raise CephClientTypeError( + name='key', + actual=type(key), + expected=six.string_types) + + kwargs['key'] = key + if not isinstance(value, six.string_types): + raise CephClientTypeError( + name='value', + actual=type(value), + expected=six.string_types) + + kwargs['value'] = value + return self._request('osd pool application set', **kwargs) + + def osd_pool_application_rm( + self, pool, app, key, body='json', timeout=None): + """removes application metadata key on pool """ + kwargs = dict(body=body, timeout=timeout) + if not isinstance(pool, six.string_types): + raise CephClientTypeError( + name='pool', + actual=type(pool), + expected=six.string_types) + + kwargs['pool'] = pool + if not isinstance(app, six.string_types): + raise CephClientTypeError( + name='app', + actual=type(app), + expected=six.string_types) + + kwargs['app'] = app + if not isinstance(key, six.string_types): + raise CephClientTypeError( + name='key', + actual=type(key), + expected=six.string_types) + + kwargs['key'] = key + return self._request('osd pool application rm', **kwargs) + + def osd_pool_application_get( + self, pool, app=None, key=None, body='json', timeout=None): + """get value of key of application on pool """ + kwargs = dict(body=body, timeout=timeout) + if not isinstance(pool, six.string_types): + raise CephClientTypeError( + name='pool', + actual=type(pool), + expected=six.string_types) + + kwargs['pool'] = pool + if app is not None: + if not isinstance(app, six.string_types): + raise CephClientTypeError( + name='app', + actual=type(app), + expected=six.string_types) + kwargs['app'] = app + if key is not None: + if not isinstance(key, six.string_types): + raise CephClientTypeError( + name='key', + actual=type(key), + expected=six.string_types) + kwargs['key'] = key + return self._request('osd pool application get', **kwargs) + + def osd_utilization(self, body='json', timeout=None): + """get basic pg distribution stats""" + return self._request('osd utilization', body=body, timeout=timeout) + + OSD_TIER_ADD_FORCE_NONEMPTY_VALUES = ['--force-nonempty'] + + def osd_tier_add( + self, pool, tierpool, force_nonempty=None, body='json', + timeout=None): + """add the tier (the second one) to base pool (thefirst one)""" + kwargs = dict(body=body, timeout=timeout) + if not isinstance(pool, six.string_types): + raise CephClientTypeError( + name='pool', + actual=type(pool), + expected=six.string_types) + + kwargs['pool'] = pool + if not isinstance(tierpool, six.string_types): + raise CephClientTypeError( + name='tierpool', + actual=type(tierpool), + expected=six.string_types) + + kwargs['tierpool'] = tierpool + if force_nonempty is not None: + if not isinstance(force_nonempty, six.string_types): + raise CephClientTypeError( + name='force_nonempty', + actual=type(force_nonempty), + expected=six.string_types) + supported = CephClient.OSD_TIER_ADD_FORCE_NONEMPTY_VALUES + if force_nonempty not in supported: + raise CephClientInvalidChoice( + function='osd_tier_add', + option='force_nonempty', + value=force_nonempty, + supported=', '.join(supported)) + kwargs['force_nonempty'] = force_nonempty + return self._request('osd tier add', **kwargs) + + def osd_tier_rm(self, pool, tierpool, body='json', timeout=None): + """remove the tier (the second one) from base pool (thefirst one)""" + kwargs = dict(body=body, timeout=timeout) + if not isinstance(pool, six.string_types): + raise CephClientTypeError( + name='pool', + actual=type(pool), + expected=six.string_types) + + kwargs['pool'] = pool + if not isinstance(tierpool, six.string_types): + raise CephClientTypeError( + name='tierpool', + actual=type(tierpool), + expected=six.string_types) + + kwargs['tierpool'] = tierpool + return self._request('osd tier rm', **kwargs) + + def osd_tier_remove(self, pool, tierpool, body='json', timeout=None): + """remove the tier (the second one) from base pool (thefirst one)""" + kwargs = dict(body=body, timeout=timeout) + if not isinstance(pool, six.string_types): + raise CephClientTypeError( + name='pool', + actual=type(pool), + expected=six.string_types) + + kwargs['pool'] = pool + if not isinstance(tierpool, six.string_types): + raise CephClientTypeError( + name='tierpool', + actual=type(tierpool), + expected=six.string_types) + + kwargs['tierpool'] = tierpool + return self._request('osd tier remove', **kwargs) + + OSD_TIER_CACHE_MODE_MODE_VALUES = \ + ['none', 'writeback', 'forward', 'readonly', + 'readforward', 'proxy', 'readproxy'] + + OSD_TIER_CACHE_MODE_SURE_VALUES = ['--yes-i-really-mean-it'] + + def osd_tier_cache_mode( + self, pool, mode, sure=None, body='json', timeout=None): + """specify the caching mode for cache tier """ + kwargs = dict(body=body, timeout=timeout) + if not isinstance(pool, six.string_types): + raise CephClientTypeError( + name='pool', + actual=type(pool), + expected=six.string_types) + + kwargs['pool'] = pool + if not isinstance(mode, six.string_types): + raise CephClientTypeError( + name='mode', + actual=type(mode), + expected=six.string_types) + supported = CephClient.OSD_TIER_CACHE_MODE_MODE_VALUES + if mode not in supported: + raise CephClientInvalidChoice( + function='osd_tier_cache_mode', + option='mode', + value=mode, + supported=', '.join(supported)) + + kwargs['mode'] = mode + if sure is not None: + if not isinstance(sure, six.string_types): + raise CephClientTypeError( + name='sure', + actual=type(sure), + expected=six.string_types) + supported = CephClient.OSD_TIER_CACHE_MODE_SURE_VALUES + if sure not in supported: + raise CephClientInvalidChoice( + function='osd_tier_cache_mode', + option='sure', + value=sure, + supported=', '.join(supported)) + kwargs['sure'] = sure + return self._request('osd tier cache-mode', **kwargs) + + def osd_tier_set_overlay( + self, pool, overlaypool, body='json', timeout=None): + """set the overlay pool for base pool to be """ + kwargs = dict(body=body, timeout=timeout) + if not isinstance(pool, six.string_types): + raise CephClientTypeError( + name='pool', + actual=type(pool), + expected=six.string_types) + + kwargs['pool'] = pool + if not isinstance(overlaypool, six.string_types): + raise CephClientTypeError( + name='overlaypool', + actual=type(overlaypool), + expected=six.string_types) + + kwargs['overlaypool'] = overlaypool + return self._request('osd tier set-overlay', **kwargs) + + def osd_tier_rm_overlay(self, pool, body='json', timeout=None): + """remove the overlay pool for base pool """ + kwargs = dict(body=body, timeout=timeout) + if not isinstance(pool, six.string_types): + raise CephClientTypeError( + name='pool', + actual=type(pool), + expected=six.string_types) + + kwargs['pool'] = pool + return self._request('osd tier rm-overlay', **kwargs) + + def osd_tier_remove_overlay(self, pool, body='json', timeout=None): + """remove the overlay pool for base pool """ + kwargs = dict(body=body, timeout=timeout) + if not isinstance(pool, six.string_types): + raise CephClientTypeError( + name='pool', + actual=type(pool), + expected=six.string_types) + + kwargs['pool'] = pool + return self._request('osd tier remove-overlay', **kwargs) + + def osd_tier_add_cache(self, pool, tierpool, size, + body='json', timeout=None): + + """add a cache (the second one) of size to existingpool (the first one) """ + kwargs = dict(body=body, timeout=timeout) + if not isinstance(pool, six.string_types): + raise CephClientTypeError( + name='pool', + actual=type(pool), + expected=six.string_types) + + kwargs['pool'] = pool + if not isinstance(tierpool, six.string_types): + raise CephClientTypeError( + name='tierpool', + actual=type(tierpool), + expected=six.string_types) + + kwargs['tierpool'] = tierpool + if not isinstance(size, six.integer_types): + raise CephClientTypeError( + name='size', + actual=type(size), + expected=int) + if size < 0: + raise CephClientValueOutOfBounds( + name='size', + actual=size, + min=0, + max='unlimited') + + kwargs['size'] = size + return self._request('osd tier add-cache', **kwargs) + + def config_key_get(self, key, body='json', timeout=None): + """get """ + kwargs = dict(body=body, timeout=timeout) + if not isinstance(key, six.string_types): + raise CephClientTypeError( + name='key', + actual=type(key), + expected=six.string_types) + + kwargs['key'] = key + return self._request('config-key get', **kwargs) + + def config_key_set(self, key, val=None, body='json', timeout=None): + """set to value """ + kwargs = dict(body=body, timeout=timeout) + if not isinstance(key, six.string_types): + raise CephClientTypeError( + name='key', + actual=type(key), + expected=six.string_types) + + kwargs['key'] = key + if val is not None: + if not isinstance(val, six.string_types): + raise CephClientTypeError( + name='val', + actual=type(val), + expected=six.string_types) + kwargs['val'] = val + return self._request('config-key set', **kwargs) + + def config_key_put(self, key, val=None, body='json', timeout=None): + """put , value """ + kwargs = dict(body=body, timeout=timeout) + if not isinstance(key, six.string_types): + raise CephClientTypeError( + name='key', + actual=type(key), + expected=six.string_types) + + kwargs['key'] = key + if val is not None: + if not isinstance(val, six.string_types): + raise CephClientTypeError( + name='val', + actual=type(val), + expected=six.string_types) + kwargs['val'] = val + return self._request('config-key put', **kwargs) + + def config_key_del(self, key, body='json', timeout=None): + """delete """ + kwargs = dict(body=body, timeout=timeout) + if not isinstance(key, six.string_types): + raise CephClientTypeError( + name='key', + actual=type(key), + expected=six.string_types) + + kwargs['key'] = key + return self._request('config-key del', **kwargs) + + def config_key_rm(self, key, body='json', timeout=None): + """rm """ + kwargs = dict(body=body, timeout=timeout) + if not isinstance(key, six.string_types): + raise CephClientTypeError( + name='key', + actual=type(key), + expected=six.string_types) + + kwargs['key'] = key + return self._request('config-key rm', **kwargs) + + def config_key_exists(self, key, body='json', timeout=None): + """check for 's existence""" + kwargs = dict(body=body, timeout=timeout) + if not isinstance(key, six.string_types): + raise CephClientTypeError( + name='key', + actual=type(key), + expected=six.string_types) + + kwargs['key'] = key + return self._request('config-key exists', **kwargs) + + def config_key_list(self, body='json', timeout=None): + """list keys""" + return self._request('config-key list', body=body, timeout=timeout) + + def config_key_ls(self, body='json', timeout=None): + """list keys""" + return self._request('config-key ls', body=body, timeout=timeout) + + def config_key_dump(self, key=None, body='json', timeout=None): + """dump keys and values (with optional prefix)""" + kwargs = dict(body=body, timeout=timeout) + if key is not None: + if not isinstance(key, six.string_types): + raise CephClientTypeError( + name='key', + actual=type(key), + expected=six.string_types) + kwargs['key'] = key + return self._request('config-key dump', **kwargs) + + def mgr_dump(self, epoch=None, body='json', timeout=None): + """dump the latest MgrMap""" + kwargs = dict(body=body, timeout=timeout) + if epoch is not None: + if not isinstance(epoch, six.integer_types): + raise CephClientTypeError( + name='epoch', + actual=type(epoch), + expected=int) + if epoch < 0: + raise CephClientValueOutOfBounds( + name='epoch', + actual=epoch, + min=0, + max='unlimited') + kwargs['epoch'] = epoch + return self._request('mgr dump', **kwargs) + + def mgr_fail(self, who, body='json', timeout=None): + """treat the named manager daemon as failed""" + kwargs = dict(body=body, timeout=timeout) + if not isinstance(who, six.string_types): + raise CephClientTypeError( + name='who', + actual=type(who), + expected=six.string_types) + + kwargs['who'] = who + return self._request('mgr fail', **kwargs) + + def mgr_module_ls(self, body='json', timeout=None): + """list active mgr modules""" + return self._request('mgr module ls', body=body, timeout=timeout) + + def mgr_services(self, body='json', timeout=None): + """list service endpoints provided by mgr modules""" + return self._request('mgr services', body=body, timeout=timeout) + + MGR_MODULE_ENABLE_FORCE_VALUES = ['--force'] + + def mgr_module_enable(self, module, force=None, + body='json', timeout=None): + """enable mgr module""" + kwargs = dict(body=body, timeout=timeout) + if not isinstance(module, six.string_types): + raise CephClientTypeError( + name='module', + actual=type(module), + expected=six.string_types) + + kwargs['module'] = module + if force is not None: + if not isinstance(force, six.string_types): + raise CephClientTypeError( + name='force', + actual=type(force), + expected=six.string_types) + supported = CephClient.MGR_MODULE_ENABLE_FORCE_VALUES + if force not in supported: + raise CephClientInvalidChoice( + function='mgr_module_enable', + option='force', + value=force, + supported=', '.join(supported)) + kwargs['force'] = force + return self._request('mgr module enable', **kwargs) + + def mgr_module_disable(self, module, body='json', timeout=None): + """disable mgr module""" + kwargs = dict(body=body, timeout=timeout) + if not isinstance(module, six.string_types): + raise CephClientTypeError( + name='module', + actual=type(module), + expected=six.string_types) + + kwargs['module'] = module + return self._request('mgr module disable', **kwargs) + + def mgr_metadata(self, who=None, body='json', timeout=None): + """dump metadata for all daemons or a specific daemon""" + kwargs = dict(body=body, timeout=timeout) + if who is not None: + if not isinstance(who, six.string_types): + raise CephClientTypeError( + name='who', + actual=type(who), + expected=six.string_types) + kwargs['who'] = who + return self._request('mgr metadata', **kwargs) + + def mgr_count_metadata(self, _property, body='json', timeout=None): + """count ceph-mgr daemons by metadata field property""" + kwargs = dict(body=body, timeout=timeout) + if not isinstance(_property, six.string_types): + raise CephClientTypeError( + name='_property', + actual=type(_property), + expected=six.string_types) + + kwargs['property'] = _property + return self._request('mgr count-metadata', **kwargs) + + def mgr_versions(self, body='json', timeout=None): + """check running versions of ceph-mgr daemons""" + return self._request('mgr versions', body=body, timeout=timeout) + + def config_set(self, who, name, value, body='json', timeout=None): + """Set a configuration option for one or more entities""" + kwargs = dict(body=body, timeout=timeout) + if not isinstance(who, six.string_types): + raise CephClientTypeError( + name='who', + actual=type(who), + expected=six.string_types) + + kwargs['who'] = who + if not isinstance(name, six.string_types): + raise CephClientTypeError( + name='name', + actual=type(name), + expected=six.string_types) + + kwargs['name'] = name + if not isinstance(value, six.string_types): + raise CephClientTypeError( + name='value', + actual=type(value), + expected=six.string_types) + + kwargs['value'] = value + return self._request('config set', **kwargs) + + def config_rm(self, who, name, body='json', timeout=None): + """Clear a configuration option for one or more entities""" + kwargs = dict(body=body, timeout=timeout) + if not isinstance(who, six.string_types): + raise CephClientTypeError( + name='who', + actual=type(who), + expected=six.string_types) + + kwargs['who'] = who + if not isinstance(name, six.string_types): + raise CephClientTypeError( + name='name', + actual=type(name), + expected=six.string_types) + + kwargs['name'] = name + return self._request('config rm', **kwargs) + + def config_get(self, who, key, body='json', timeout=None): + """Show configuration option(s) for an entity""" + kwargs = dict(body=body, timeout=timeout) + if not isinstance(who, six.string_types): + raise CephClientTypeError( + name='who', + actual=type(who), + expected=six.string_types) + + kwargs['who'] = who + if not isinstance(key, six.string_types): + raise CephClientTypeError( + name='key', + actual=type(key), + expected=six.string_types) + + kwargs['key'] = key + return self._request('config get', **kwargs) + + def config_dump(self, body='json', timeout=None): + """Show all configuration option(s)""" + return self._request('config dump', body=body, timeout=timeout) + + def config_help(self, key, body='json', timeout=None): + """Describe a configuration option""" + kwargs = dict(body=body, timeout=timeout) + if not isinstance(key, six.string_types): + raise CephClientTypeError( + name='key', + actual=type(key), + expected=six.string_types) + + kwargs['key'] = key + return self._request('config help', **kwargs) + + def config_assimilate_conf(self, body='json', timeout=None): + """Assimilate options from a conf, and return a new, minimal conf file""" + return self._request('config assimilate-conf', + body=body, timeout=timeout) + + def config_log(self, num, body='json', timeout=None): + """Show recent history of config changes""" + kwargs = dict(body=body, timeout=timeout) + if not isinstance(num, six.integer_types): + raise CephClientTypeError( + name='num', + actual=type(num), + expected=int) + + kwargs['num'] = num + return self._request('config log', **kwargs) + + def config_reset(self, num, body='json', timeout=None): + """Revert configuration to previous state""" + kwargs = dict(body=body, timeout=timeout) + if not isinstance(num, six.integer_types): + raise CephClientTypeError( + name='num', + actual=type(num), + expected=int) + + kwargs['num'] = num + return self._request('config reset', **kwargs) diff --git a/ceph/python-cephclient/python-cephclient/cephclient/exception.py b/ceph/python-cephclient/python-cephclient/cephclient/exception.py new file mode 100644 index 00000000..03d2d027 --- /dev/null +++ b/ceph/python-cephclient/python-cephclient/cephclient/exception.py @@ -0,0 +1,100 @@ +# +# Copyright (c) 2019 Wind River Systems, Inc. +# +# SPDX-License-Identifier: Apache-2.0 +# + + +class CephClientException(Exception): + message = "generic ceph client exception" + + def __init__(self, *args, **kwargs): + if "message" not in kwargs: + try: + message = self.message.format(*args, **kwargs) + except Exception: # noqa + message = '{}, args:{}, kwargs: {}'.format( + self.message, args, kwargs) + else: + message = kwargs["message"] + super(CephClientException, self).__init__(message) + + +class CephMonRestfulListKeysError(CephClientException): + message = "Failed to get ceph-mgr restful plugin keys. {}" + + +class CephMonRestfulJsonError(CephClientException): + message = "Failed to decode ceph-mgr restful plugin JSON response: {}" + + +class CephMonRestfulMissingUserCredentials(CephClientException): + message = "Failed to get ceph-mgr restful plugin credentials for user: {}" + + +class CephMgrDumpError(CephClientException): + message = "Failed to get ceph manager info. {}" + + +class CephMgrJsonError(CephClientException): + message = "Failed to decode ceph manager JSON response: {}" + + +class CephMgrMissingRestfulService(CephClientException): + message = "Missing restful service. Available services: {}" + + +class CephClientFormatNotSupported(CephClientException): + message = "Command '{prefix}' does not support request format '{format}'" + + +class CephClientResponseFormatNotImplemented(CephClientException): + message = ("Can't decode response. Support for '{format}' format " + "is not implemented. Response: {reason}") + + +class CephClientFunctionNotImplemented(CephClientException): + message = "Function '{name}' is not implemented" + + +class CephClientInvalidChoice(CephClientException): + message = ("Function '{function}' does not support option " + "{option}='{value}'. Supported values are: {supported}") + + +class CephClientTypeError(CephClientException): + message = ("Expecting option '{name}' of type {expected}. " + "Got {actual} instead") + + +class CephClientValueOutOfBounds(CephClientException): + message = ("Argument '{name}' should be within range: {min} .. {max} " + ". Got value '{actual}' instead") + + +class CephClientInvalidPgid(CephClientException): + message = ("Argument '{name}' is not a valid Ceph PG id. Expected " + "n.xxx where n is an int > 0, xxx is a hex number > 0. " + "Got value '{actual}' instead") + + +class CephClientInvalidIPAddr(CephClientException): + message = ("Argument '{name}' should be a valid IPv4 or IPv6 address. " + "Got value '{actual}' instead") + + +class CephClientInvalidOsdIdValue(CephClientException): + message = ("Invalid OSD ID value '{osdid}'. Should start with 'osd.'") + + +class CephClientInvalidOsdIdType(CephClientException): + message = ("Invalid OSD ID type for '{osdid}'. " + "Expected integer or 'osd.NNN'") + + +class CephClientNoSuchUser(CephClientException): + message = ("No such user '{user}'.") + + +class CephClientIncorrectPassword(CephClientException): + message = ("Incorrect password for user '{user}'.") diff --git a/ceph/python-cephclient/python-cephclient/cephclient/tests/__init__.py b/ceph/python-cephclient/python-cephclient/cephclient/tests/__init__.py new file mode 100644 index 00000000..0c941312 --- /dev/null +++ b/ceph/python-cephclient/python-cephclient/cephclient/tests/__init__.py @@ -0,0 +1,5 @@ +# +# Copyright (c) 2019 Wind River Systems, Inc. +# +# SPDX-License-Identifier: Apache-2.0 +# diff --git a/ceph/python-cephclient/python-cephclient/cephclient/wrapper.py b/ceph/python-cephclient/python-cephclient/cephclient/wrapper.py new file mode 100644 index 00000000..0f976a3f --- /dev/null +++ b/ceph/python-cephclient/python-cephclient/cephclient/wrapper.py @@ -0,0 +1,268 @@ +# +# Copyright (c) 2019 Wind River Systems, Inc. +# +# SPDX-License-Identifier: Apache-2.0 +# +import six + +from cephclient.client import CephClient +from cephclient.exception import CephClientFunctionNotImplemented +from cephclient.exception import CephClientInvalidOsdIdValue +from cephclient.exception import CephClientTypeError + + +class CephWrapper(CephClient): + + def __init__(self, endpoint=''): + super(CephWrapper, self).__init__() + + def auth_import(self, body='json', timeout=None): + raise CephClientFunctionNotImplemented(name='auth_import') + + def _sanitize_osdid_to_str(self, _id): + if isinstance(_id, six.string_types): + prefix = 'osd.' + if not _id.startswith(prefix): + try: + int(_id) + except ValueError: + raise CephClientInvalidOsdIdValue( + osdid=_id) + _id = prefix + _id + elif isinstance(_id, six.integer_types): + _id = 'osd.{}'.format(_id) + else: + raise CephClientInvalidOsdIdValue( + osdid=_id) + return _id + + def _sanitize_osdid_to_int(self, _id): + if isinstance(_id, six.string_types): + prefix = 'osd.' + if _id.startswith(prefix): + _id = _id[len(prefix):] + try: + _id = int(_id) + except ValueError: + raise CephClientInvalidOsdIdValue( + osdid=_id) + elif not isinstance(_id, six.integer_types): + raise CephClientInvalidOsdIdValue( + osdid=_id) + return _id + + def osd_create(self, uuid, body='json', timeout=None, params=None): + """create new osd (with optional UUID and ID) + + Notes: + 1. osd create declares it accepts osd id as string but only works when + given an integer value; it automatically generates an ID otherwise + instead of using the one provided by 'osd create id=...' + + 2. old cephclient passes osd id through params dictionary + """ + kwargs = dict(uuid=uuid, body=body, timeout=timeout) + try: + kwargs['id'] = self._sanitize_osdid_to_int(params['id']) + except (KeyError, TypeError): + pass + return self._request('osd create', **kwargs) + + def osd_rm(self, ids, body='json', timeout=None): + """remove osd(s) [...], or use to remove all osds """ + if isinstance(ids, list): + ids = [self._sanitize_osdid_to_str(_id) + for _id in ids] + else: + ids = self._sanitize_osdid_to_str(ids) + return super(CephWrapper, self).osd_rm( + ids=ids, body=body, timeout=timeout) + + def osd_remove(self, ids, body='json', timeout=None): + return self.osd_rm(ids, body=body, timeout=timeout) + + def osd_down(self, ids, body='json', timeout=None): + """set osd(s) [...] down, or use to set all osds down """ + if isinstance(ids, list): + ids = [self._sanitize_osdid_to_str(_id) + for _id in ids] + else: + ids = self._sanitize_osdid_to_str(ids) + return super(CephWrapper, self).osd_down( + ids=ids, body=body, timeout=timeout) + + OSD_CRUSH_TREE_CONVERTED_FIELDS = [ + 'crush_weight', 'depth', 'id', 'name', 'type', 'type_id'] + + def _osd_crush_tree_convert_node(self, node): + return {k: node[k] for k in self.OSD_CRUSH_TREE_CONVERTED_FIELDS + if k in node} + + def _osd_crush_tree_populate_tree(self, node, node_map): + children = node.get('children') + node = self._osd_crush_tree_convert_node(node) + if children: + node['items'] = [] + for _id in children: + node['items'].append( + self._osd_crush_tree_populate_tree( + node_map[_id], node_map)) + return node + + def osd_crush_tree(self, shadow=None, body='json', timeout=None): + """dump crush buckets and items in a tree view """ + response, _body = super(CephWrapper, self).osd_crush_tree( + shadow=shadow, body=body, timeout=timeout) + trees = [] + if response.ok and body == 'json' \ + and 'output' in _body: + node_map = {} + root_nodes = [] + for node in _body['output']: + node_map[node['id']] = node + if node['type'] == 'root': + root_nodes.append(node) + for root in root_nodes: + trees.append( + self._osd_crush_tree_populate_tree( + root, node_map)) + _body['output'] = trees + return response, _body + + def _osd_crush_rule_by_ruleset(self, ruleset, timeout=None): + response, _body = self.osd_crush_rule_dump( + body='json', timeout=timeout) + if not response.ok: + return response, _body + name = None + for rule in _body['output']: + if rule.get('ruleset') == ruleset: + name = rule.get('rule_name') + _body['output'] = dict(rule=name) + return response, _body + + def _osd_crush_ruleset_by_rule(self, rule, timeout=None): + response, _body = self.osd_crush_rule_dump( + name=rule, body='json', timeout=timeout) + return response, _body + + def osd_pool_create(self, pool, pg_num, pgp_num=None, pool_type=None, + erasure_code_profile=None, ruleset=None, + expected_num_objects=None, body='json', timeout=None): + """create pool + + Notes: + 1. map 'ruleset' to 'rule' (assuming 1:1 correspondence) + """ + response, _body = self._osd_crush_rule_by_ruleset(ruleset) + if not response.ok: + return response, _body + rule = _body['output']['rule'] + return super(CephWrapper, self).osd_pool_create( + pool, pg_num, pgp_num=pgp_num, pool_type=pool_type, + erasure_code_profile=erasure_code_profile, rule=rule, + expected_num_objects=expected_num_objects, body=body, + timeout=timeout) + + def osd_get_pool_param(self, pool, var, body='json', timeout=None): + """get pool parameter """ + if var == 'crush_ruleset': + response, _body = super(CephWrapper, self).osd_pool_get( + pool, 'crush_rule', body='json', timeout=timeout) + if response.ok: + rule = _body['output']['crush_rule'] + del _body['output']['crush_rule'] + response, _body = self._osd_crush_ruleset_by_rule( + rule, timeout=timeout) + if response.ok: + _body['output'] = dict( + crush_ruleset=_body['output']['ruleset']) + return response, _body + else: + return super(CephWrapper, self).osd_pool_get( + pool, var, body=body, timeout=timeout) + + def osd_pool_set(self, pool, var, val, force=None, + body='json', timeout=None): + """set pool parameter to """ + return super(CephWrapper, self).osd_pool_set( + pool=pool, var=var, val=str(val), + force=force, body=body, timeout=timeout) + + def osd_set_pool_param(self, pool, var, val, force=None, + body='json', timeout=None): + """set pool parameter to """ + if var == 'crush_ruleset': + var = 'crush_rule' + response, _body = self._osd_crush_rule_by_ruleset( + val, timeout=timeout) + if not response.ok: + return response, _body + val = _body['output']['rule'] + return super(CephWrapper, self).osd_pool_set( + pool, var, str(val), force=None, + body=body, timeout=timeout) + + def osd_get_pool_quota(self, pool, body='json', timeout=None): + """obtain object or byte limits for pool """ + return super(CephWrapper, self).osd_pool_get_quota( + pool, body=body, timeout=timeout) + + def osd_set_pool_quota(self, pool, field, val, body='json', timeout=None): + """set object or byte limit on pool """ + return super(CephWrapper, self).osd_pool_set_quota( + pool, field, str(val), body=body, timeout=timeout) + + def osd_pool_set_quota(self, pool, field, val, + body='json', timeout=None): + """set object or byte limit on pool """ + return super(CephWrapper, self).osd_pool_set_quota( + pool=pool, field=field, val=str(val), + body=body, timeout=timeout) + + def _auth_convert_caps(self, caps): + if caps: + if not isinstance(caps, dict): + raise CephClientTypeError( + name='caps', + actual=type(caps), + expected=dict) + _caps = [] + for key, value in list(caps.items()): + _caps.append(key) + _caps.append(value) + caps = _caps + return caps + + def auth_add(self, entity, caps=None, body='json', timeout=None): + """add auth info for from input file, or random key if no input is given, and/or any caps specified in the command """ + caps = self._auth_convert_caps(caps) + return super(CephWrapper, self).auth_add( + entity, caps=caps, body=body, timeout=timeout) + + def auth_caps(self, entity, caps, body='json', timeout=None): + """update caps for from caps specified in the command """ + caps = self._auth_convert_caps(caps) + return super(CephWrapper, self).auth_caps( + entity, caps=caps, body=body, timeout=timeout) + + def auth_get_or_create(self, entity, caps=None, body='json', timeout=None): + """add auth info for from input file, or random key if no input given, and/or any caps specified in the command """ + caps = self._auth_convert_caps(caps) + return super(CephWrapper, self).auth_get_or_create( + entity, caps, body=body, timeout=timeout) + + def auth_get_or_create_key(self, entity, caps=None, + body='json', timeout=None): + + """get, or add, key for from system/caps pairs specified in the command. If key already exists, any given caps must match the existing caps for that key. """ + caps = self._auth_convert_caps(caps) + response, _body = super(CephWrapper, self).auth_get_or_create_key( + entity, caps, body=body, timeout=timeout) + if response.ok: + _body['output'] = _body['output'] + return response, _body + + def osd_set_key(self, key, sure=None, body='json', timeout=None): + """set """ + return self.osd_set(key, sure=sure, body=body, timeout=timeout) diff --git a/ceph/python-cephclient/python-cephclient/requirements.txt b/ceph/python-cephclient/python-cephclient/requirements.txt new file mode 100644 index 00000000..ea6e06dc --- /dev/null +++ b/ceph/python-cephclient/python-cephclient/requirements.txt @@ -0,0 +1,3 @@ +ipaddress +requests +six diff --git a/ceph/python-cephclient/python-cephclient/setup.py b/ceph/python-cephclient/python-cephclient/setup.py new file mode 100644 index 00000000..c8368e31 --- /dev/null +++ b/ceph/python-cephclient/python-cephclient/setup.py @@ -0,0 +1,34 @@ +#!/usr/bin/env python +# +# Copyright (c) 2019 Wind River Systems, Inc. +# +# SPDX-License-Identifier: Apache-2.0 +# +import setuptools + +setuptools.setup( + name='python-cephclient', + packages=['cephclient'], + version='13.2.2.0', + url='https://github.com/openstack/stx-integ/tree/master/ceph/python-cephclient/python-cephclient', # noqa E501 + author='Daniel Badea', + author_email='daniel.badea@windriver.com', + description=( + 'A client library in Python for Ceph Mgr RESTful plugin ' + 'providing REST API access to the cluster over an SSL-secured ' + 'connection. Python API is compatible with the old Python ' + 'Ceph client at https://github.com/dmsimard/python-cephclient ' + 'that no longer works in Ceph mimic because Ceph REST API ' + 'component was removed.'), + license='Apache-2.0', + keywords='ceph rest api ceph-rest-api client library', + install_requires=['ipaddress', 'requests', 'six'], + classifiers=[ + 'License :: OSI Approved :: Apache Software License', + 'Development Status :: 1 - Alpha', + 'Intended Audience :: Developers', + 'Intended Audience :: System Administrators', + 'Intended Audience :: Information Technology', + 'Programming Language :: Python', + 'Topic :: Utilities' + ]) diff --git a/ceph/python-cephclient/python-cephclient/test-requirements.txt b/ceph/python-cephclient/python-cephclient/test-requirements.txt new file mode 100644 index 00000000..7f573334 --- /dev/null +++ b/ceph/python-cephclient/python-cephclient/test-requirements.txt @@ -0,0 +1,6 @@ +# The order of packages is significant, because pip processes them in the order +# of appearance. Changing the order has an impact on the overall integration +# process, which may cause wedges in the gate later. + +flake8 +pytest diff --git a/ceph/python-cephclient/python-cephclient/tox.ini b/ceph/python-cephclient/python-cephclient/tox.ini new file mode 100644 index 00000000..c948b756 --- /dev/null +++ b/ceph/python-cephclient/python-cephclient/tox.ini @@ -0,0 +1,19 @@ +[tox] +envlist = py27,pep8 +skipsdist = True +toxworkdir = /tmp/{env:USER}_ceph_manager_tox + +[testenv] +setenv = VIRTUAL_ENV={envdir} +usedevelop = True +install_command = pip install --no-binary --upgrade --force-reinstall {opts} {packages} +deps = -r{toxinidir}/test-requirements.txt +commands = py.test {posargs} +whitelist_externals = bash + +[testenv:pep8] +commands = + flake8 {posargs} + +[flake8] +exclude = .venv,.git,.tox,dist,doc,*lib/python*,*egg,build