├── setup ├── __init__.py └── _setup_hooks.py ├── .coveragerc ├── docs ├── MANIFEST.in ├── _themes │ ├── .gitignore │ ├── kr │ │ ├── theme.conf │ │ ├── relations.html │ │ └── layout.html │ ├── kr_small │ │ ├── theme.conf │ │ ├── layout.html │ │ └── static │ │ │ └── flasky.css_t │ ├── LICENSE │ └── flask_theme_support.py ├── dev │ ├── authors.rst │ └── hacking.rst ├── _static │ ├── equation.png │ └── forkme.png ├── api.rst ├── _templates │ ├── sidebarlogo.html │ └── sidebarintro.html ├── quick.py ├── crush.8 ├── index.rst └── ceph │ └── optimize.rst ├── tests ├── sample-bugous-crushmap.json ├── weights.json ├── ceph │ ├── weights-notfloat.json │ ├── osdmap-invalid.json │ ├── ceph-report-compat-converted.txt │ ├── ceph-report-compat-optimized.txt │ └── osdmap.json ├── sample-ceph-crushmap.crush ├── sample-crushmap.json ├── test_ceph_optimize.py ├── test_ceph_compare.py ├── sample-ceph-crushmap.txt ├── sample-ceph-crushmap-compat.txt ├── test_main.py ├── test_ceph_convert.py ├── weights-crushmap.json ├── sample-ceph-crushmap-compat.python-json ├── test_ceph_crush.py └── test_ceph_analyze.py ├── .gitlab-ci.yml ├── MANIFEST.in ├── AUTHORS.rst ├── manylinux ├── Dockerfile └── build-wheels.sh ├── run-tests.sh ├── crush ├── libcrush │ ├── placeholders │ │ ├── ceph_context.h │ │ ├── common │ │ │ └── dout.h │ │ └── include │ │ │ └── assert.h │ ├── common │ │ ├── errno.h │ │ ├── page.cc │ │ ├── armor.h │ │ ├── valgrind.h │ │ ├── errno.cc │ │ ├── likely.h │ │ ├── compiler_extensions.h │ │ ├── debug.h │ │ ├── simple_spin.h │ │ ├── HTMLFormatter.h │ │ ├── escape.h │ │ ├── strtol.h │ │ ├── safe_io.h │ │ ├── armor.c │ │ ├── mempool.cc │ │ ├── HTMLFormatter.cc │ │ ├── escape.c │ │ ├── safe_io.c │ │ └── strtol.cc │ ├── include │ │ ├── unordered_set.h │ │ ├── memory.h │ │ ├── unordered_map.h │ │ ├── buffer_fwd.h │ │ ├── page.h │ │ ├── err.h │ │ ├── stringify.h │ │ ├── int_types.h │ │ ├── demangle.h │ │ ├── hash.h │ │ ├── Spinlock.h │ │ ├── byteorder.h │ │ ├── inline_memory.h │ │ ├── atomic.h │ │ ├── compat.h │ │ └── intarith.h │ ├── crush │ │ ├── types.h │ │ ├── hash.h │ │ ├── crush_compat.h │ │ ├── CrushWrapper.i │ │ ├── CrushCompiler.h │ │ ├── crush.c │ │ ├── mapper.h │ │ ├── hash.c │ │ └── CrushTreeDumper.h │ ├── ceph_read_write.h │ ├── CMakeLists.txt │ ├── config-h.in.cmake │ ├── module.c │ └── libcrush.h ├── ceph │ └── convert.py └── main.py ├── .gitignore ├── requirements.txt ├── requirements-docs.txt ├── requirements-dev.txt ├── tox.ini ├── bootstrap ├── setup.py ├── bin └── crush ├── setup.cfg └── README.rst /setup/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /.coveragerc: -------------------------------------------------------------------------------- 1 | [paths] 2 | source = crush 3 | -------------------------------------------------------------------------------- /docs/MANIFEST.in: -------------------------------------------------------------------------------- 1 | include README.rst LICENSE -------------------------------------------------------------------------------- /docs/_themes/.gitignore: -------------------------------------------------------------------------------- 1 | *.pyc 2 | *.pyo 3 | .DS_Store 4 | -------------------------------------------------------------------------------- /tests/sample-bugous-crushmap.json: -------------------------------------------------------------------------------- 1 | { 2 | "a": 1, 3 | } 4 | -------------------------------------------------------------------------------- /.gitlab-ci.yml: -------------------------------------------------------------------------------- 1 | integration: 2 | script: "bash -x run-tests.sh" 3 | -------------------------------------------------------------------------------- /tests/weights.json: -------------------------------------------------------------------------------- 1 | { 2 | "osd.0": 0.0, 3 | "osd.2": 0.5 4 | } -------------------------------------------------------------------------------- /tests/ceph/weights-notfloat.json: -------------------------------------------------------------------------------- 1 | { 2 | "osd.0": null, 3 | "osd.2": 0.5 4 | } -------------------------------------------------------------------------------- /docs/dev/authors.rst: -------------------------------------------------------------------------------- 1 | Authors 2 | ======= 3 | 4 | 5 | .. include:: ../../AUTHORS.rst 6 | -------------------------------------------------------------------------------- /docs/_static/equation.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ceph/python-crush/HEAD/docs/_static/equation.png -------------------------------------------------------------------------------- /docs/_static/forkme.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ceph/python-crush/HEAD/docs/_static/forkme.png -------------------------------------------------------------------------------- /MANIFEST.in: -------------------------------------------------------------------------------- 1 | include AUTHORS.rst 2 | include ChangeLog 3 | 4 | exclude .gitignore 5 | 6 | global-exclude *.pyc 7 | -------------------------------------------------------------------------------- /tests/sample-ceph-crushmap.crush: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ceph/python-crush/HEAD/tests/sample-ceph-crushmap.crush -------------------------------------------------------------------------------- /AUTHORS.rst: -------------------------------------------------------------------------------- 1 | - Loic Dachary 2 | - Sage Weil 3 | - Xavier Villaneau 4 | -------------------------------------------------------------------------------- /manylinux/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM quay.io/pypa/manylinux1_x86_64 2 | 3 | RUN yum install -y gcc gcc-c++ cmake boost148-devel libatomic_ops-devel 4 | -------------------------------------------------------------------------------- /docs/api.rst: -------------------------------------------------------------------------------- 1 | The Crush class 2 | ---------------------- 3 | 4 | .. module:: crush 5 | 6 | .. autoclass:: Crush 7 | :members: 8 | :special-members: __init__ 9 | -------------------------------------------------------------------------------- /docs/_themes/kr/theme.conf: -------------------------------------------------------------------------------- 1 | [theme] 2 | inherit = basic 3 | stylesheet = flasky.css 4 | pygments_style = flask_theme_support.FlaskyStyle 5 | 6 | [options] 7 | touch_icon = 8 | -------------------------------------------------------------------------------- /run-tests.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | set -ex 4 | source bootstrap 5 | pip install -r requirements-docs.txt 6 | python setup.py checkdocs 7 | python setup.py build_sphinx 8 | tox 9 | -------------------------------------------------------------------------------- /crush/libcrush/placeholders/ceph_context.h: -------------------------------------------------------------------------------- 1 | #ifndef FAKE_CEPH_CONTEXT 2 | #define FAKE_CEPH_CONTEXT 3 | 4 | class CephContext { 5 | public: 6 | }; 7 | 8 | #endif // FAKE_CEPH_CONTEXT 9 | -------------------------------------------------------------------------------- /crush/libcrush/placeholders/common/dout.h: -------------------------------------------------------------------------------- 1 | #ifndef CEPH_DOUT_H 2 | #define CEPH_DOUT_H 3 | 4 | #define ldout(cct, v) cout 5 | #define lderr(cct) cerr 6 | #define dendl "\n" 7 | 8 | #endif 9 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *.so 2 | *~ 3 | *.pyc 4 | *.pyo 5 | .coverage* 6 | .tox 7 | *.egg-info 8 | *.egg 9 | dist 10 | .cache/ 11 | .eggs/ 12 | virtualenv/ 13 | AUTHORS 14 | build/ 15 | .idea/ 16 | wheelhouse/ 17 | -------------------------------------------------------------------------------- /crush/libcrush/common/errno.h: -------------------------------------------------------------------------------- 1 | #ifndef CEPH_ERRNO_H 2 | #define CEPH_ERRNO_H 3 | 4 | #include 5 | 6 | /* Return a given error code as a string */ 7 | std::string cpp_strerror(int err); 8 | 9 | #endif 10 | -------------------------------------------------------------------------------- /crush/libcrush/include/unordered_set.h: -------------------------------------------------------------------------------- 1 | #ifndef CEPH_UNORDERED_SET_H 2 | #define CEPH_UNORDERED_SET_H 3 | 4 | #include 5 | 6 | #include 7 | 8 | namespace ceph { 9 | using std::unordered_set; 10 | } 11 | 12 | #endif 13 | -------------------------------------------------------------------------------- /crush/libcrush/placeholders/include/assert.h: -------------------------------------------------------------------------------- 1 | #include 2 | // make __ASSERT_FUNCTION empty (/usr/include/assert.h makes it a function) 3 | // and make our encoding macros break if it non-empty. 4 | #undef __ASSERT_FUNCTION 5 | #define __ASSERT_FUNCTION 6 | -------------------------------------------------------------------------------- /docs/_themes/kr_small/theme.conf: -------------------------------------------------------------------------------- 1 | [theme] 2 | inherit = basic 3 | stylesheet = flasky.css 4 | nosidebar = true 5 | pygments_style = flask_theme_support.FlaskyStyle 6 | 7 | [options] 8 | index_logo = '' 9 | index_logo_height = 120px 10 | github_fork = '' 11 | -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | d2to1==0.2.12.post1 2 | numpy==1.12.1 3 | pandas==0.19.2 4 | pbr==3.0.0 5 | ## The following requirements were added by pip freeze: 6 | pyparsing==2.2.0 7 | python-dateutil==2.6.0 8 | pytz==2017.2 9 | six==1.10.0 10 | appdirs==1.4.3 11 | packaging==16.8 12 | -------------------------------------------------------------------------------- /crush/libcrush/include/memory.h: -------------------------------------------------------------------------------- 1 | #ifndef CEPH_MEMORY_H 2 | #define CEPH_MEMORY_H 3 | 4 | #include 5 | 6 | namespace ceph { 7 | using std::shared_ptr; 8 | using std::weak_ptr; 9 | using std::unique_ptr; 10 | using std::static_pointer_cast; 11 | } 12 | 13 | #endif 14 | -------------------------------------------------------------------------------- /crush/libcrush/include/unordered_map.h: -------------------------------------------------------------------------------- 1 | #ifndef CEPH_UNORDERED_MAP_H 2 | #define CEPH_UNORDERED_MAP_H 3 | 4 | #include 5 | 6 | #include 7 | 8 | namespace ceph { 9 | using std::unordered_map; 10 | using std::unordered_multimap; 11 | } 12 | 13 | #endif 14 | -------------------------------------------------------------------------------- /crush/libcrush/crush/types.h: -------------------------------------------------------------------------------- 1 | #ifndef CEPH_CRUSH_TYPES_H 2 | #define CEPH_CRUSH_TYPES_H 3 | 4 | #ifdef KERNEL 5 | # define free(x) kfree(x) 6 | #else 7 | # include 8 | #endif 9 | 10 | 11 | #include /* just for int types */ 12 | 13 | #ifndef BUG_ON 14 | # define BUG_ON(x) assert(!(x)) 15 | #endif 16 | 17 | #endif 18 | -------------------------------------------------------------------------------- /crush/libcrush/include/buffer_fwd.h: -------------------------------------------------------------------------------- 1 | #ifndef BUFFER_FWD_H 2 | #define BUFFER_FWD_H 3 | 4 | namespace ceph { 5 | namespace buffer { 6 | class ptr; 7 | class list; 8 | class hash; 9 | } 10 | 11 | using bufferptr = buffer::ptr; 12 | using bufferlist = buffer::list; 13 | using bufferhash = buffer::hash; 14 | } 15 | 16 | #endif 17 | 18 | -------------------------------------------------------------------------------- /docs/_templates/sidebarlogo.html: -------------------------------------------------------------------------------- 1 | 6 |

7 | 8 |

9 | crush 10 | library to control placement in a hierarchy. 11 |

12 | -------------------------------------------------------------------------------- /requirements-docs.txt: -------------------------------------------------------------------------------- 1 | Sphinx==1.5.5 2 | mock==2.0.0 3 | collective.checkdocs==0.2 4 | ## The following requirements were added by pip freeze: 5 | docutils==0.13.1 6 | alabaster==0.7.10 7 | Babel==2.4.0 8 | funcsigs==1.0.2 9 | imagesize==0.7.1 10 | Jinja2==2.9.6 11 | MarkupSafe==1.0 12 | Pygments==2.2.0 13 | requests==2.13.0 14 | six==1.10.0 15 | snowballstemmer==1.2.1 16 | -------------------------------------------------------------------------------- /setup/_setup_hooks.py: -------------------------------------------------------------------------------- 1 | import os 2 | 3 | 4 | def build_pre_hook(cmdobj): 5 | 6 | os.system(""" set -ex 7 | rm -fr build/tmp 8 | mkdir -p build/tmp 9 | ( cd build/tmp ; cmake ../../crush/libcrush ) 10 | cp build/tmp/acconfig.h crush/libcrush 11 | rm -fr build/tmp 12 | """) 13 | 14 | 15 | def build_post_hook(cmdobj): 16 | os.unlink("crush/libcrush/acconfig.h") 17 | -------------------------------------------------------------------------------- /tests/ceph/osdmap-invalid.json: -------------------------------------------------------------------------------- 1 | { 2 | "epoch": 57, 3 | "fsid": "0bbbb5a6-0e72-11e7-addd-080027759979", 4 | "pools": [], 5 | "osds": [ 6 | { 7 | "osd": 0, 8 | "weight": 1.000000 9 | }, 10 | { 11 | "osd": 1, 12 | "weight": 0.950000 13 | }, 14 | { 15 | "osd": 2 16 | } 17 | ] 18 | } 19 | -------------------------------------------------------------------------------- /crush/libcrush/include/page.h: -------------------------------------------------------------------------------- 1 | #ifndef CEPH_PAGE_H 2 | #define CEPH_PAGE_H 3 | 4 | namespace ceph { 5 | // these are in common/page.cc 6 | extern unsigned _page_size; 7 | extern unsigned long _page_mask; 8 | extern unsigned _page_shift; 9 | } 10 | 11 | #endif 12 | 13 | 14 | #define CEPH_PAGE_SIZE ceph::_page_size 15 | #define CEPH_PAGE_MASK ceph::_page_mask 16 | #define CEPH_PAGE_SHIFT ceph::_page_shift 17 | 18 | 19 | -------------------------------------------------------------------------------- /crush/libcrush/ceph_read_write.h: -------------------------------------------------------------------------------- 1 | #ifdef __cplusplus 2 | extern "C" { 3 | #endif /* __cplusplus */ 4 | 5 | #include "libcrush.h" 6 | 7 | int ceph_read_txt_to_json(const char *in, char **out); 8 | int ceph_read_binary_to_json(const char *in, char **out); 9 | int ceph_write(LibCrush *self, const char *path, const char *format, PyObject *info); 10 | int ceph_incompat(LibCrush *self, int *out); 11 | #ifdef __cplusplus 12 | } 13 | #endif /* __cplusplus */ 14 | -------------------------------------------------------------------------------- /crush/libcrush/common/page.cc: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | namespace ceph { 4 | 5 | // page size crap, see page.h 6 | int _get_bits_of(int v) { 7 | int n = 0; 8 | while (v) { 9 | n++; 10 | v = v >> 1; 11 | } 12 | return n; 13 | } 14 | 15 | unsigned _page_size = sysconf(_SC_PAGESIZE); 16 | unsigned long _page_mask = ~(unsigned long)(_page_size - 1); 17 | unsigned _page_shift = _get_bits_of(_page_size - 1); 18 | 19 | } 20 | -------------------------------------------------------------------------------- /crush/libcrush/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 2.4.0) 2 | 3 | project(libcrush) 4 | 5 | include(CheckIncludeFiles) 6 | 7 | # must be kept in sync with upstream libcrush/CMakeLists.txt 8 | 9 | CHECK_INCLUDE_FILES("inttypes.h" HAVE_INTTYPES_H) 10 | CHECK_INCLUDE_FILES("stdint.h" HAVE_STDINT_H) 11 | CHECK_INCLUDE_FILES("linux/types.h" HAVE_LINUX_TYPES_H) 12 | 13 | configure_file( 14 | ${CMAKE_SOURCE_DIR}/config-h.in.cmake 15 | ${CMAKE_BINARY_DIR}/acconfig.h 16 | ) 17 | -------------------------------------------------------------------------------- /requirements-dev.txt: -------------------------------------------------------------------------------- 1 | tox==2.7.0 2 | coverage==4.3.4 3 | pytest==3.0.7 4 | flake8==3.3.0 5 | pytest-capturelog==0.7 6 | twine==1.8.1 7 | ## The following requirements were added by pip freeze: 8 | args==0.1.0 9 | clint==0.5.1 10 | configparser==3.5.0 11 | docutils==0.13.1 12 | enum34==1.1.6 13 | mccabe==0.6.1 14 | pkginfo==1.4.1 15 | pluggy==0.4.0 16 | py==1.4.33 17 | pycodestyle==2.3.1 18 | pyflakes==1.5.0 19 | requests==2.13.0 20 | requests-toolbelt==0.7.1 21 | virtualenv==15.1.0 22 | -------------------------------------------------------------------------------- /crush/libcrush/common/armor.h: -------------------------------------------------------------------------------- 1 | #ifndef CEPH_ARMOR_H 2 | #define CEPH_ARMOR_H 3 | 4 | #ifdef __cplusplus 5 | extern "C" { 6 | #endif 7 | 8 | int ceph_armor(char *dst, const char *dst_end, 9 | const char *src, const char *end); 10 | 11 | int ceph_armor_linebreak(char *dst, const char *dst_end, 12 | const char *src, const char *end, 13 | int line_width); 14 | int ceph_unarmor(char *dst, const char *dst_end, 15 | const char *src, const char *end); 16 | #ifdef __cplusplus 17 | } 18 | #endif 19 | 20 | #endif 21 | -------------------------------------------------------------------------------- /crush/libcrush/common/valgrind.h: -------------------------------------------------------------------------------- 1 | // -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*- 2 | // vim: ts=8 sw=2 smarttab 3 | 4 | #ifndef CEPH_VALGRIND_H 5 | #define CEPH_VALGRIND_H 6 | 7 | #include "acconfig.h" 8 | 9 | #ifdef HAVE_VALGRIND_HELGRIND_H 10 | #include 11 | #else 12 | #define ANNOTATE_HAPPENS_AFTER(x) (void)0 13 | #define ANNOTATE_HAPPENS_BEFORE_FORGET_ALL(x) (void)0 14 | #define ANNOTATE_HAPPENS_BEFORE(x) (void)0 15 | 16 | #define ANNOTATE_BENIGN_RACE_SIZED(address, size, description) (void)0 17 | #endif 18 | 19 | #endif // CEPH_VALGRIND_H 20 | -------------------------------------------------------------------------------- /crush/libcrush/include/err.h: -------------------------------------------------------------------------------- 1 | #ifndef CEPH_ERR_H 2 | #define CEPH_ERR_H 3 | 4 | /* 5 | * adapted from linux 2.6.24 include/linux/err.h 6 | */ 7 | #define MAX_ERRNO 4095 8 | #define IS_ERR_VALUE(x) ((x) >= (unsigned long)-MAX_ERRNO) 9 | 10 | #include 11 | 12 | /* this generates a warning in c++; caller can do the cast manually 13 | static inline void *ERR_PTR(long error) 14 | { 15 | return (void *) error; 16 | } 17 | */ 18 | 19 | static inline long PTR_ERR(const void *ptr) 20 | { 21 | return (long) ptr; 22 | } 23 | 24 | static inline long IS_ERR(const void *ptr) 25 | { 26 | return IS_ERR_VALUE((unsigned long)ptr); 27 | } 28 | 29 | #endif 30 | -------------------------------------------------------------------------------- /crush/libcrush/common/errno.cc: -------------------------------------------------------------------------------- 1 | #include "common/errno.h" 2 | #include "acconfig.h" 3 | 4 | #include 5 | #include 6 | 7 | #include 8 | 9 | std::string cpp_strerror(int err) 10 | { 11 | char buf[128]; 12 | char *errmsg; 13 | 14 | if (err < 0) 15 | err = -err; 16 | std::ostringstream oss; 17 | buf[0] = '\0'; 18 | 19 | // strerror_r returns char * on Linux, and does not always fill buf 20 | #ifdef STRERROR_R_CHAR_P 21 | errmsg = strerror_r(err, buf, sizeof(buf)); 22 | #else 23 | strerror_r(err, buf, sizeof(buf)); 24 | errmsg = buf; 25 | #endif 26 | 27 | oss << "(" << err << ") " << errmsg; 28 | 29 | return oss.str(); 30 | } 31 | -------------------------------------------------------------------------------- /docs/_themes/kr/relations.html: -------------------------------------------------------------------------------- 1 |

Related Topics

2 | 20 | -------------------------------------------------------------------------------- /tox.ini: -------------------------------------------------------------------------------- 1 | [tox] 2 | envlist = flake8,py27,py3 3 | skip_missing_interpreters = True 4 | 5 | [testenv] 6 | setenv = 7 | VIRTUAL_ENV={envdir} 8 | passenv = LONG 9 | usedevelop = true 10 | deps = 11 | -r{toxinidir}/requirements.txt 12 | -r{toxinidir}/requirements-dev.txt 13 | whitelist_externals = * 14 | commands = env \ 15 | {envbindir}/coverage run --source=crush \ 16 | {envbindir}/pytest -v {posargs:tests} 17 | {envbindir}/coverage report --show-missing # --fail-under=100 18 | 19 | [testenv:flake8] 20 | deps = 21 | -r{toxinidir}/requirements.txt 22 | -r{toxinidir}/requirements-dev.txt 23 | commands = flake8 --ignore=H105,H405 bin crush tests 24 | -------------------------------------------------------------------------------- /crush/libcrush/common/likely.h: -------------------------------------------------------------------------------- 1 | // -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*- 2 | // vim: ts=8 sw=2 smarttab 3 | /* 4 | * Ceph - scalable distributed file system 5 | * 6 | * Copyright (C) 2010 Dreamhost 7 | * 8 | * This is free software; you can redistribute it and/or 9 | * modify it under the terms of the GNU Lesser General Public 10 | * License version 2.1, as published by the Free Software 11 | * Foundation. See file COPYING. 12 | * 13 | */ 14 | 15 | #ifndef CEPH_LIKELY_DOT_H 16 | #define CEPH_LIKELY_DOT_H 17 | 18 | /* 19 | * Likely / Unlikely macros 20 | */ 21 | #define likely(x) __builtin_expect((x),1) 22 | #define unlikely(x) __builtin_expect((x),0) 23 | 24 | #endif 25 | -------------------------------------------------------------------------------- /crush/libcrush/crush/hash.h: -------------------------------------------------------------------------------- 1 | #ifndef CEPH_CRUSH_HASH_H 2 | #define CEPH_CRUSH_HASH_H 3 | 4 | #ifdef __KERNEL__ 5 | # include 6 | #else 7 | # include "crush_compat.h" 8 | #endif 9 | 10 | #define CRUSH_HASH_RJENKINS1 0 11 | 12 | #define CRUSH_HASH_DEFAULT CRUSH_HASH_RJENKINS1 13 | 14 | extern const char *crush_hash_name(int type); 15 | 16 | extern __u32 crush_hash32(int type, __u32 a); 17 | extern __u32 crush_hash32_2(int type, __u32 a, __u32 b); 18 | extern __u32 crush_hash32_3(int type, __u32 a, __u32 b, __u32 c); 19 | extern __u32 crush_hash32_4(int type, __u32 a, __u32 b, __u32 c, __u32 d); 20 | extern __u32 crush_hash32_5(int type, __u32 a, __u32 b, __u32 c, __u32 d, 21 | __u32 e); 22 | 23 | #endif 24 | -------------------------------------------------------------------------------- /bootstrap: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | [ $(id -u) == 0 ] || SUDO=sudo 4 | $SUDO $(which apt-get || which true) update 5 | $SUDO $(which apt-get || which true) install -y git python-virtualenv python-tox gcc g++ python-all-dev libpython3-all-dev cmake libboost-all-dev libatomic-ops-dev 6 | $SUDO $(which yum || which dnf || which zypper || which true) install -y git python-virtualenv gcc gcc-c++ python-devel python3-devel python-pip make cmake boost-devel libatomic_ops-devel 7 | 8 | git submodule sync 9 | git submodule update --force --init --recursive 10 | 11 | rm -fr virtualenv .tox 12 | virtualenv virtualenv 13 | source virtualenv/bin/activate 14 | pip install --upgrade pip virtualenv # in case virtualenv is old (CentOS 7) 15 | pip install -v -e . 16 | -------------------------------------------------------------------------------- /crush/libcrush/include/stringify.h: -------------------------------------------------------------------------------- 1 | #ifndef __CEPH_STRINGIFY_H 2 | #define __CEPH_STRINGIFY_H 3 | 4 | #include 5 | #include 6 | 7 | template 8 | inline std::string stringify(const T& a) { 9 | #if defined(__GNUC__) && !(defined(__clang__) || defined(__INTEL_COMPILER)) 10 | static __thread std::ostringstream ss; 11 | ss.str(""); 12 | #else 13 | std::ostringstream ss; 14 | #endif 15 | ss << a; 16 | return ss.str(); 17 | } 18 | 19 | template 20 | T joinify(const A &begin, const A &end, const T &t) 21 | { 22 | T result; 23 | for (A it = begin; it != end; it++) { 24 | if (!result.empty()) 25 | result.append(t); 26 | result.append(*it); 27 | } 28 | return result; 29 | } 30 | 31 | #endif 32 | -------------------------------------------------------------------------------- /docs/_themes/kr/layout.html: -------------------------------------------------------------------------------- 1 | {%- extends "basic/layout.html" %} 2 | {%- block extrahead %} 3 | {{ super() }} 4 | {% if theme_touch_icon %} 5 | 6 | {% endif %} 7 | 8 | {% endblock %} 9 | {%- block relbar2 %}{% endblock %} 10 | {%- block footer %} 11 | 14 | 15 | Fork me 16 | 17 | 18 | {%- endblock %} 19 | -------------------------------------------------------------------------------- /docs/_themes/kr_small/layout.html: -------------------------------------------------------------------------------- 1 | {% extends "basic/layout.html" %} 2 | {% block header %} 3 | {{ super() }} 4 | {% if pagename == 'index' %} 5 |
6 | {% endif %} 7 | {% endblock %} 8 | {% block footer %} 9 | {% if pagename == 'index' %} 10 |
11 | {% endif %} 12 | {% endblock %} 13 | {# do not display relbars #} 14 | {% block relbar1 %}{% endblock %} 15 | {% block relbar2 %} 16 | {% if theme_github_fork %} 17 | Fork me on GitHub 19 | {% endif %} 20 | {% endblock %} 21 | {% block sidebar1 %}{% endblock %} 22 | {% block sidebar2 %}{% endblock %} 23 | -------------------------------------------------------------------------------- /docs/_templates/sidebarintro.html: -------------------------------------------------------------------------------- 1 | 6 | 7 |

8 | crush library to control placement in a hierarchy. 9 |

10 | 11 | 12 |

Useful Links

13 | 20 | -------------------------------------------------------------------------------- /setup.py: -------------------------------------------------------------------------------- 1 | # -*- mode: python; coding: utf-8 -*- 2 | # 3 | # Copyright (C) 2017 4 | # 5 | # Author: Loic Dachary 6 | # 7 | # This program is free software: you can redistribute it and/or modify 8 | # it under the terms of the GNU General Public License as published by 9 | # the Free Software Foundation, either version 3 of the License, or 10 | # (at your option) any later version. 11 | # 12 | # This program is distributed in the hope that it will be useful, 13 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | # GNU General Public License for more details. 16 | # 17 | # You should have received a copy of the GNU General Public License 18 | # along with this program. If not, see . 19 | # 20 | import setuptools 21 | 22 | setuptools.setup( 23 | setup_requires=['d2to1', 'pbr'], 24 | d2to1=True) 25 | -------------------------------------------------------------------------------- /bin/crush: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # -*- mode: python; coding: utf-8 -*- 3 | # 4 | # Copyright (C) 2017 5 | # 6 | # Author: Loic Dachary 7 | # 8 | # This program is free software: you can redistribute it and/or modify 9 | # it under the terms of the GNU General Public License as published by 10 | # the Free Software Foundation, either version 3 of the License, or 11 | # (at your option) any later version. 12 | # 13 | # This program is distributed in the hope that it will be useful, 14 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 15 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16 | # GNU General Public License for more details. 17 | # 18 | # You should have received a copy of the GNU General Public License 19 | # along with this program. If not, see . 20 | # 21 | import sys 22 | from crush.ceph import Ceph 23 | 24 | sys.exit(Ceph().main(sys.argv[1:])) 25 | -------------------------------------------------------------------------------- /crush/libcrush/common/compiler_extensions.h: -------------------------------------------------------------------------------- 1 | // -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*- 2 | // vim: ts=8 sw=2 smarttab 3 | /* 4 | * Ceph - scalable distributed file system 5 | * 6 | * Copyright (C) 2011 New Dream Network 7 | * 8 | * This is free software; you can redistribute it and/or 9 | * modify it under the terms of the GNU Lesser General Public 10 | * License version 2.1, as published by the Free Software 11 | * Foundation. See file COPYING. 12 | * 13 | */ 14 | 15 | #ifndef CEPH_COMPILER_EXTENSIONS_H 16 | #define CEPH_COMPILER_EXTENSIONS_H 17 | 18 | /* We should be able to take advantage of nice nonstandard features of gcc 19 | * and other compilers, but still maintain portability. 20 | */ 21 | 22 | #ifdef __GNUC__ 23 | // GCC 24 | #define WARN_UNUSED_RESULT __attribute__((warn_unused_result)) 25 | #else 26 | // some other compiler - just make it a no-op 27 | #define WARN_UNUSED_RESULT 28 | #endif 29 | 30 | #endif 31 | -------------------------------------------------------------------------------- /crush/libcrush/common/debug.h: -------------------------------------------------------------------------------- 1 | // -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*- 2 | // vim: ts=8 sw=2 smarttab 3 | /* 4 | * Ceph - scalable distributed file system 5 | * 6 | * Copyright (C) 2004-2011 New Dream Network 7 | * 8 | * This is free software; you can redistribute it and/or 9 | * modify it under the terms of the GNU Lesser General Public 10 | * License version 2.1, as published by the Free Software 11 | * Foundation. See file COPYING. 12 | * 13 | */ 14 | 15 | #ifndef CEPH_DEBUG_H 16 | #define CEPH_DEBUG_H 17 | 18 | #include "common/dout.h" 19 | 20 | /* Global version of the stuff in common/dout.h 21 | */ 22 | 23 | #define dout(v) ldout((dout_context), v) 24 | 25 | #define pdout(v, p) lpdout((dout_context), v, p) 26 | 27 | #define dlog_p(sub, v) ldlog_p1((dout_context), sub, v) 28 | 29 | #define generic_dout(v) lgeneric_dout((dout_context), v) 30 | 31 | #define derr lderr((dout_context)) 32 | 33 | #define generic_derr lgeneric_derr((dout_context)) 34 | 35 | #endif 36 | -------------------------------------------------------------------------------- /crush/libcrush/include/int_types.h: -------------------------------------------------------------------------------- 1 | #ifndef CEPH_INTTYPES_H 2 | #define CEPH_INTTYPES_H 3 | 4 | #include "acconfig.h" 5 | 6 | #include 7 | 8 | #ifdef HAVE_LINUX_TYPES_H 9 | #include 10 | #else 11 | #ifndef HAVE___U8 12 | typedef uint8_t __u8; 13 | #endif 14 | 15 | #ifndef HAVE___S8 16 | typedef int8_t __s8; 17 | #endif 18 | 19 | #ifndef HAVE___U16 20 | typedef uint16_t __u16; 21 | #endif 22 | 23 | #ifndef HAVE___S16 24 | typedef int16_t __s16; 25 | #endif 26 | 27 | #ifndef HAVE___U32 28 | typedef uint32_t __u32; 29 | #endif 30 | 31 | #ifndef HAVE___S32 32 | typedef int32_t __s32; 33 | #endif 34 | 35 | #ifndef HAVE___U64 36 | typedef uint64_t __u64; 37 | #endif 38 | 39 | #ifndef HAVE___S64 40 | typedef int64_t __s64; 41 | #endif 42 | #endif /* LINUX_TYPES_H */ 43 | 44 | #define __bitwise__ 45 | 46 | typedef __u16 __bitwise__ __le16; 47 | typedef __u16 __bitwise__ __be16; 48 | typedef __u32 __bitwise__ __le32; 49 | typedef __u32 __bitwise__ __be32; 50 | typedef __u64 __bitwise__ __le64; 51 | typedef __u64 __bitwise__ __be64; 52 | 53 | #endif 54 | -------------------------------------------------------------------------------- /manylinux/build-wheels.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | set -e -x 3 | 4 | yum install -y gcc gcc-c++ cmake boost148-devel libatomic_ops-devel 5 | 6 | : ${PYBINS:=/opt/python/cp27-cp27mu/bin /opt/python/cp3[4-9]-cp3[4-9]m/bin} 7 | 8 | for PYBIN in $PYBINS; do 9 | "${PYBIN}/pip" install -r /io/requirements.txt 10 | rm -fr /tmp/crush 11 | git clone /io/ /tmp/crush 12 | "${PYBIN}/pip" wheel /tmp/crush/ -w wheelhouse/ 13 | done 14 | 15 | # Bundle external shared libraries into the wheels 16 | mkdir repaired 17 | for whl in wheelhouse/*.whl; do 18 | if echo $whl | grep --quiet crush ; then 19 | auditwheel repair "$whl" -w repaired/ 20 | else 21 | cp $whl repaired/ 22 | fi 23 | done 24 | 25 | # Install packages and test 26 | for PYBIN in $PYBINS ; do 27 | "${PYBIN}/pip" install virtualenv 28 | rm -fr /tmp/v 29 | "${PYBIN}/virtualenv" /tmp/v 30 | /tmp/v/bin/pip install crush --no-index -f repaired/ 31 | /tmp/v/bin/crush --help 32 | done 33 | 34 | rm -fr /io/wheelhouse 35 | mkdir /io/wheelhouse 36 | cp repaired/*crush* /io/wheelhouse 37 | -------------------------------------------------------------------------------- /crush/libcrush/common/simple_spin.h: -------------------------------------------------------------------------------- 1 | // -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*- 2 | // vim: ts=8 sw=2 smarttab 3 | /* 4 | * Ceph - scalable distributed file system 5 | * 6 | * Copyright (C) 2011 New Dream Network 7 | * 8 | * This is free software; you can redistribute it and/or 9 | * modify it under the terms of the GNU Lesser General Public 10 | * License version 2.1, as published by the Free Software 11 | * Foundation. See file COPYING. 12 | * 13 | */ 14 | 15 | #ifndef CEPH_SIMPLE_SPIN_H 16 | #define CEPH_SIMPLE_SPIN_H 17 | 18 | #include 19 | 20 | inline void simple_spin_lock(std::atomic_flag& lock) 21 | { 22 | while(lock.test_and_set(std::memory_order_acquire)) 23 | ; 24 | } 25 | 26 | inline void simple_spin_unlock(std::atomic_flag& lock) 27 | { 28 | lock.clear(std::memory_order_release); 29 | } 30 | 31 | inline void simple_spin_lock(std::atomic_flag *lock) 32 | { 33 | simple_spin_lock(*lock); 34 | } 35 | 36 | inline void simple_spin_unlock(std::atomic_flag *lock) 37 | { 38 | simple_spin_unlock(*lock); 39 | } 40 | 41 | #endif 42 | -------------------------------------------------------------------------------- /crush/libcrush/crush/crush_compat.h: -------------------------------------------------------------------------------- 1 | #ifndef CEPH_CRUSH_COMPAT_H 2 | #define CEPH_CRUSH_COMPAT_H 3 | 4 | #include "include/int_types.h" 5 | 6 | #include 7 | #include 8 | #include 9 | #include 10 | 11 | /* asm-generic/bug.h */ 12 | 13 | #define BUG_ON(x) assert(!(x)) 14 | 15 | /* linux/kernel.h */ 16 | 17 | #define U8_MAX ((__u8)~0U) 18 | #define S8_MAX ((__s8)(U8_MAX>>1)) 19 | #define S8_MIN ((__s8)(-S8_MAX - 1)) 20 | #define U16_MAX ((__u16)~0U) 21 | #define S16_MAX ((__s16)(U16_MAX>>1)) 22 | #define S16_MIN ((__s16)(-S16_MAX - 1)) 23 | #define U32_MAX ((__u32)~0U) 24 | #define S32_MAX ((__s32)(U32_MAX>>1)) 25 | #define S32_MIN ((__s32)(-S32_MAX - 1)) 26 | #define U64_MAX ((__u64)~0ULL) 27 | #define S64_MAX ((__s64)(U64_MAX>>1)) 28 | #define S64_MIN ((__s64)(-S64_MAX - 1)) 29 | 30 | /* linux/math64.h */ 31 | 32 | #define div64_s64(dividend, divisor) ((dividend) / (divisor)) 33 | 34 | /* linux/slab.h */ 35 | 36 | #define kmalloc(size, flags) malloc(size) 37 | #define kfree(x) do { if (x) free(x); } while (0) 38 | 39 | #endif /* CEPH_CRUSH_COMPAT_H */ 40 | -------------------------------------------------------------------------------- /crush/libcrush/crush/CrushWrapper.i: -------------------------------------------------------------------------------- 1 | /* File : CrushWrapper.i */ 2 | %module CrushWrapper 3 | %{ 4 | #include "CrushWrapper.h" 5 | %} 6 | 7 | %include typemaps.i 8 | 9 | // This tells SWIG to treat 'int *data' as a special case 10 | %typemap(in) int *items { 11 | AV *tempav; 12 | I32 len; 13 | int i; 14 | SV **tv; 15 | // int view; 16 | 17 | 18 | //printf("typemap\n"); 19 | 20 | if (!SvROK($input)) 21 | croak("$input is not a reference."); 22 | if (SvTYPE(SvRV($input)) != SVt_PVAV) 23 | croak("$input is not an array."); 24 | 25 | tempav = (AV*)SvRV($input); 26 | len = av_len(tempav); 27 | //printf("typemap len: %i\n",len); 28 | $1 = (int *) malloc((len+1)*sizeof(int)); 29 | for (i = 0; i <= len; i++) { 30 | tv = av_fetch(tempav, i, 0); 31 | $1[i] = (int) SvIV(*tv); 32 | 33 | /* 34 | view = SvIV(*tv); 35 | printf("view: %d",view); 36 | printf("\n"); 37 | */ 38 | } 39 | } 40 | 41 | %apply int *items { int *weights }; 42 | %apply double *OUTPUT { double *min, double *max, double *avg }; 43 | 44 | /* Let's just grab the original header file here */ 45 | %include "CrushWrapper.h" 46 | 47 | %clear double *min, double *max, double *avg; 48 | -------------------------------------------------------------------------------- /docs/quick.py: -------------------------------------------------------------------------------- 1 | import json 2 | from crush import Crush 3 | 4 | crushmap = """ 5 | { 6 | "trees": [ 7 | { 8 | "type": "root", "name": "dc1", "id": -1, 9 | "children": [ 10 | { 11 | "type": "host", "name": "host0", "id": -2, 12 | "children": [ 13 | { "id": 0, "name": "device0", "weight": 65536 }, 14 | { "id": 1, "name": "device1", "weight": 131072 } 15 | ] 16 | }, 17 | { 18 | "type": "host", "name": "host1", "id": -3, 19 | "children": [ 20 | { "id": 2, "name": "device2", "weight": 65536 }, 21 | { "id": 3, "name": "device3", "weight": 131072 } 22 | ] 23 | }, 24 | { 25 | "type": "host", "name": "host2", "id": -4, 26 | "children": [ 27 | { "id": 4, "name": "device4", "weight": 65536 }, 28 | { "id": 5, "name": "device5", "weight": 131072 } 29 | ] 30 | } 31 | ] 32 | } 33 | ], 34 | "rules": { 35 | "data": [ 36 | [ "take", "dc1" ], 37 | [ "chooseleaf", "firstn", 0, "type", "host" ], 38 | [ "emit" ] 39 | ] 40 | } 41 | } 42 | 43 | """ 44 | 45 | c = Crush() 46 | c.parse(json.loads(crushmap)) 47 | print(c.map(rule="data", value=1234, replication_count=1)) 48 | print(c.map(rule="data", value=1234, replication_count=2)) 49 | -------------------------------------------------------------------------------- /crush/libcrush/include/demangle.h: -------------------------------------------------------------------------------- 1 | // -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*- 2 | // vim: ts=8 sw=2 smarttab 3 | /* 4 | * Ceph - scalable distributed file system 5 | * 6 | * Copyright (C) 2016 Allen Samuels 7 | * 8 | * This is free software; you can redistribute it and/or 9 | * modify it under the terms of the GNU Lesser General Public 10 | * License version 2.1, as published by the Free Software 11 | * Foundation. See file COPYING. 12 | * 13 | */ 14 | 15 | #ifndef CEPH_INCLUDE_DEMANGLE 16 | #define CEPH_INCLUDE_DEMANGLE 17 | 18 | //// Stole this code from http://stackoverflow.com/questions/281818/unmangling-the-result-of-stdtype-infoname 19 | #ifdef __GNUG__ 20 | #include 21 | #include 22 | #include 23 | 24 | static std::string ceph_demangle(const char* name) 25 | { 26 | int status = -4; // some arbitrary value to eliminate the compiler warning 27 | 28 | // enable c++11 by passing the flag -std=c++11 to g++ 29 | std::unique_ptr res { 30 | abi::__cxa_demangle(name, NULL, NULL, &status), 31 | std::free 32 | }; 33 | 34 | return (status == 0) ? res.get() : name ; 35 | } 36 | 37 | #else 38 | 39 | // does nothing if not g++ 40 | static std::string demangle(const char* name) 41 | { 42 | return name; 43 | } 44 | 45 | #endif 46 | 47 | 48 | #endif 49 | -------------------------------------------------------------------------------- /crush/libcrush/common/HTMLFormatter.h: -------------------------------------------------------------------------------- 1 | // -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*- 2 | // vim: ts=8 sw=2 smarttab 3 | #ifndef CEPH_HTML_FORMATTER_H 4 | #define CEPH_HTML_FORMATTER_H 5 | 6 | #include 7 | #include 8 | #include 9 | 10 | #include "Formatter.h" 11 | 12 | namespace ceph { 13 | class HTMLFormatter : public XMLFormatter { 14 | public: 15 | explicit HTMLFormatter(bool pretty = false); 16 | ~HTMLFormatter() override; 17 | void reset() override; 18 | 19 | void set_status(int status, const char* status_name) override; 20 | void output_header() override; 21 | 22 | void dump_unsigned(const char *name, uint64_t u) override; 23 | void dump_int(const char *name, int64_t u) override; 24 | void dump_float(const char *name, double d) override; 25 | void dump_string(const char *name, const std::string& s) override; 26 | std::ostream& dump_stream(const char *name) override; 27 | void dump_format_va(const char *name, const char *ns, bool quoted, const char *fmt, va_list ap) override; 28 | 29 | /* with attrs */ 30 | void dump_string_with_attrs(const char *name, const std::string& s, const FormatterAttrs& attrs) override; 31 | private: 32 | template void dump_template(const char *name, T arg); 33 | 34 | int m_status; 35 | const char* m_status_name; 36 | }; 37 | 38 | } 39 | 40 | #endif 41 | -------------------------------------------------------------------------------- /docs/crush.8: -------------------------------------------------------------------------------- 1 | .\" -*- nroff -*- 2 | .\" Copyright (C) 2017 Loic Dachary 3 | .\" This file is part of python-crush 4 | .\" Author: Loic Dachary 5 | .\" 6 | .\" This program is free software; you can redistribute it and/or modify 7 | .\" it under the terms of the GNU General Public License version 3 as 8 | .\" published by the Free Software Foundation. 9 | .\" 10 | .\" This program is distributed in the hope that it will be useful, 11 | .\" but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | .\" MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | .\" GNU General Public License for more details. 14 | .\" 15 | .\" You should have received a copy of the GNU General Public License 16 | .\" along with this program; if not, write to the Free Software Foundation, 17 | .\" Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. 18 | .\" 19 | .TH crush 8 "April 2017" "python-crush" 20 | .SH NAME 21 | crush - tool for modelling/analyzing CRUSH maps 22 | .SH SYNOPSIS 23 | .B crush --help 24 | .LP 25 | .SH DESCRIPTION 26 | .B crush 27 | The crush module wraps the libcrush C library implementing CRUSH, a scalable 28 | pseudo-random data distribution function designed for distributed object 29 | storage systems that efficiently maps data objects to storage devices without 30 | relying on a central directory. 31 | .LP 32 | .SH AUTHOR 33 | .B crush 34 | is part of the 35 | .B python-crush 36 | package, which was written by Loic Dachary . 37 | -------------------------------------------------------------------------------- /tests/sample-crushmap.json: -------------------------------------------------------------------------------- 1 | { 2 | "trees": [ 3 | { 4 | "type": "root", "name": "dc1", "id": -1, 5 | "children": [ 6 | { 7 | "type": "host", "name": "host0", "id": -2, 8 | "children": [ 9 | { "id": 0, "name": "device0", "weight": 1.0 }, 10 | { "id": 1, "name": "device1", "weight": 2.0 } 11 | ] 12 | }, 13 | { 14 | "type": "host", "name": "host1", "id": -3, 15 | "children": [ 16 | { "id": 2, "name": "device2", "weight": 1.0 }, 17 | { "id": 3, "name": "device3", "weight": 2.0 } 18 | ] 19 | }, 20 | { 21 | "type": "host", "name": "host2", "id": -4, 22 | "children": [ 23 | { "id": 4, "name": "device4", "weight": 1.0 }, 24 | { "id": 5, "name": "device5", "weight": 2.0 } 25 | ] 26 | } 27 | ] 28 | } 29 | ], 30 | "rules": { 31 | "data": [ 32 | [ "take", "dc1" ], 33 | [ "chooseleaf", "firstn", 0, "type", "host" ], 34 | [ "emit" ] 35 | ] 36 | }, 37 | "choose_args": { 38 | "1": [ 39 | { 40 | "bucket_id": -1, 41 | "ids": [ 42 | -20, 43 | -30, 44 | -25 45 | ], 46 | "weight_set": [ 47 | [ 48 | 1.0, 49 | 2.0, 50 | 5.0 51 | ], 52 | [ 53 | 3.0, 54 | 2.0, 55 | 5.0 56 | ] 57 | ] 58 | } 59 | ] 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /crush/libcrush/common/escape.h: -------------------------------------------------------------------------------- 1 | // -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*- 2 | // vim: ts=8 sw=2 smarttab 3 | /* 4 | * Ceph - scalable distributed file system 5 | * 6 | * Copyright (C) 2011 New Dream Network 7 | * 8 | * This is free software; you can redistribute it and/or 9 | * modify it under the terms of the GNU Lesser General Public 10 | * License version 2.1, as published by the Free Software 11 | * Foundation. See file COPYING. 12 | * 13 | */ 14 | 15 | #ifndef CEPH_RGW_ESCAPE_H 16 | #define CEPH_RGW_ESCAPE_H 17 | 18 | #ifdef __cplusplus 19 | extern "C" { 20 | #endif 21 | 22 | /* Returns the length of a buffer that would be needed to escape 'buf' 23 | * as an XML attrribute 24 | */ 25 | int escape_xml_attr_len(const char *buf); 26 | 27 | /* Escapes 'buf' as an XML attribute. Assumes that 'out' is at least long 28 | * enough to fit the output. You can find out the required length by calling 29 | * escape_xml_attr_len first. 30 | */ 31 | void escape_xml_attr(const char *buf, char *out); 32 | 33 | /* Returns the length of a buffer that would be needed to escape 'buf' 34 | * as an JSON attrribute 35 | */ 36 | int escape_json_attr_len(const char *buf, int src_len); 37 | 38 | /* Escapes 'buf' as an JSON attribute. Assumes that 'out' is at least long 39 | * enough to fit the output. You can find out the required length by calling 40 | * escape_json_attr_len first. 41 | */ 42 | void escape_json_attr(const char *buf, int src_len, char *out); 43 | 44 | /* Note: we escape control characters. Although the XML spec doesn't actually 45 | * require this, Amazon does it in their XML responses. 46 | */ 47 | 48 | #ifdef __cplusplus 49 | } 50 | #endif 51 | 52 | #endif 53 | -------------------------------------------------------------------------------- /crush/libcrush/include/hash.h: -------------------------------------------------------------------------------- 1 | #ifndef CEPH_HASH_H 2 | #define CEPH_HASH_H 3 | 4 | #include "acconfig.h" 5 | 6 | // Robert Jenkins' function for mixing 32-bit values 7 | // http://burtleburtle.net/bob/hash/evahash.html 8 | // a, b = random bits, c = input and output 9 | 10 | #define hashmix(a,b,c) \ 11 | a=a-b; a=a-c; a=a^(c>>13); \ 12 | b=b-c; b=b-a; b=b^(a<<8); \ 13 | c=c-a; c=c-b; c=c^(b>>13); \ 14 | a=a-b; a=a-c; a=a^(c>>12); \ 15 | b=b-c; b=b-a; b=b^(a<<16); \ 16 | c=c-a; c=c-b; c=c^(b>>5); \ 17 | a=a-b; a=a-c; a=a^(c>>3); \ 18 | b=b-c; b=b-a; b=b^(a<<10); \ 19 | c=c-a; c=c-b; c=c^(b>>15); 20 | 21 | 22 | //namespace ceph { 23 | 24 | template struct rjhash { }; 25 | 26 | inline uint64_t rjhash64(uint64_t key) { 27 | key = (~key) + (key << 21); // key = (key << 21) - key - 1; 28 | key = key ^ (key >> 24); 29 | key = (key + (key << 3)) + (key << 8); // key * 265 30 | key = key ^ (key >> 14); 31 | key = (key + (key << 2)) + (key << 4); // key * 21 32 | key = key ^ (key >> 28); 33 | key = key + (key << 31); 34 | return key; 35 | } 36 | 37 | inline uint32_t rjhash32(uint32_t a) { 38 | a = (a+0x7ed55d16) + (a<<12); 39 | a = (a^0xc761c23c) ^ (a>>19); 40 | a = (a+0x165667b1) + (a<<5); 41 | a = (a+0xd3a2646c) ^ (a<<9); 42 | a = (a+0xfd7046c5) + (a<<3); 43 | a = (a^0xb55a4f09) ^ (a>>16); 44 | return a; 45 | } 46 | 47 | 48 | template<> struct rjhash { 49 | inline size_t operator()(const uint32_t x) const { 50 | return rjhash32(x); 51 | } 52 | }; 53 | 54 | template<> struct rjhash { 55 | inline size_t operator()(const uint64_t x) const { 56 | return rjhash64(x); 57 | } 58 | }; 59 | 60 | //} 61 | 62 | 63 | 64 | #endif 65 | -------------------------------------------------------------------------------- /crush/libcrush/common/strtol.h: -------------------------------------------------------------------------------- 1 | // -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*- 2 | // vim: ts=8 sw=2 smarttab 3 | /* 4 | * Ceph - scalable distributed file system 5 | * 6 | * Copyright (C) 2011 New Dream Network 7 | * 8 | * This is free software; you can redistribute it and/or 9 | * modify it under the terms of the GNU Lesser General Public 10 | * License version 2.1, as published by the Free Software 11 | * Foundation. See file COPYING. 12 | * 13 | */ 14 | 15 | #ifndef CEPH_COMMON_STRTOL_H 16 | #define CEPH_COMMON_STRTOL_H 17 | 18 | #include 19 | extern "C" { 20 | #include 21 | } 22 | 23 | long long strict_strtoll(const char *str, int base, std::string *err); 24 | 25 | int strict_strtol(const char *str, int base, std::string *err); 26 | 27 | double strict_strtod(const char *str, std::string *err); 28 | 29 | float strict_strtof(const char *str, std::string *err); 30 | 31 | uint64_t strict_sistrtoll(const char *str, std::string *err); 32 | 33 | template 34 | T strict_si_cast(const char *str, std::string *err); 35 | 36 | /* On enter buf points to the end of the buffer, e.g. where the least 37 | * significant digit of the input number will be printed. Returns pointer to 38 | * where the most significant digit were printed, including zero padding. 39 | * Does NOT add zero at the end of buffer, this is responsibility of the caller. 40 | */ 41 | template 42 | static inline 43 | char* ritoa(T u, char *buf) 44 | { 45 | static_assert(std::is_unsigned::value, "signed types are not supported"); 46 | static_assert(base <= 16, "extend character map below to support higher bases"); 47 | unsigned digits = 0; 48 | while (u) { 49 | *--buf = "0123456789abcdef"[u % base]; 50 | u /= base; 51 | digits++; 52 | } 53 | while (digits++ < width) 54 | *--buf = '0'; 55 | return buf; 56 | } 57 | 58 | #endif 59 | -------------------------------------------------------------------------------- /crush/libcrush/config-h.in.cmake: -------------------------------------------------------------------------------- 1 | /* config.h file expanded by Cmake for build */ 2 | 3 | #ifndef CONFIG_H 4 | #define CONFIG_H 5 | 6 | /* Define to 1 if you have the header file. */ 7 | #cmakedefine HAVE_SYS_TYPES_H 1 8 | 9 | /* Define to 1 if the system has the type `__be16'. */ 10 | #cmakedefine HAVE___BE16 1 11 | 12 | /* Define to 1 if the system has the type `__be32'. */ 13 | #cmakedefine HAVE___BE32 1 14 | 15 | /* Define to 1 if the system has the type `__be64'. */ 16 | #cmakedefine HAVE___BE64 1 17 | 18 | /* Define to 1 if the system has the type `__le16'. */ 19 | #cmakedefine HAVE___LE16 1 20 | 21 | /* Define to 1 if the system has the type `__le32'. */ 22 | #cmakedefine HAVE___LE32 1 23 | 24 | /* Define to 1 if the system has the type `__le64'. */ 25 | #cmakedefine HAVE___LE64 1 26 | 27 | /* Define to 1 if the system has the type `__s16'. */ 28 | #cmakedefine HAVE___S16 1 29 | 30 | /* Define to 1 if the system has the type `__s32'. */ 31 | #cmakedefine HAVE___S32 1 32 | 33 | /* Define to 1 if the system has the type `__s64'. */ 34 | #cmakedefine HAVE___S64 1 35 | 36 | /* Define to 1 if the system has the type `__s8'. */ 37 | #cmakedefine HAVE___S8 1 38 | 39 | /* Define to 1 if the system has the type `__u16'. */ 40 | #cmakedefine HAVE___U16 1 41 | 42 | /* Define to 1 if the system has the type `__u32'. */ 43 | #cmakedefine HAVE___U32 1 44 | 45 | /* Define to 1 if the system has the type `__u64'. */ 46 | #cmakedefine HAVE___U64 1 47 | 48 | /* Define to 1 if the system has the type `__u8'. */ 49 | #cmakedefine HAVE___U8 1 50 | 51 | /* Define to 1 if you have the header file. */ 52 | #cmakedefine HAVE_INTTYPES_H 1 53 | 54 | /* Define to 1 if you have the header file. */ 55 | #cmakedefine HAVE_STDINT_H 1 56 | 57 | /* Define to 1 if you have the header file. */ 58 | #cmakedefine HAVE_LINUX_TYPES_H 1 59 | 60 | /* Version number of package */ 61 | #cmakedefine VERSION "@VERSION@" 62 | 63 | #endif /* CONFIG_H */ 64 | -------------------------------------------------------------------------------- /docs/_themes/LICENSE: -------------------------------------------------------------------------------- 1 | Modifications: 2 | 3 | Copyright (c) 2011 Kenneth Reitz. 4 | 5 | 6 | Original Project: 7 | 8 | Copyright (c) 2010 by Armin Ronacher. 9 | 10 | 11 | Some rights reserved. 12 | 13 | Redistribution and use in source and binary forms of the theme, with or 14 | without modification, are permitted provided that the following conditions 15 | are met: 16 | 17 | * Redistributions of source code must retain the above copyright 18 | notice, this list of conditions and the following disclaimer. 19 | 20 | * Redistributions in binary form must reproduce the above 21 | copyright notice, this list of conditions and the following 22 | disclaimer in the documentation and/or other materials provided 23 | with the distribution. 24 | 25 | * The names of the contributors may not be used to endorse or 26 | promote products derived from this software without specific 27 | prior written permission. 28 | 29 | We kindly ask you to only use these themes in an unmodified manner just 30 | for Flask and Flask-related products, not for unrelated projects. If you 31 | like the visual style and want to use it for your own projects, please 32 | consider making some larger changes to the themes (such as changing 33 | font faces, sizes, colors or margins). 34 | 35 | THIS THEME IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 36 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 37 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 38 | ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE 39 | LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 40 | CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 41 | SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 42 | INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 43 | CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 44 | ARISING IN ANY WAY OUT OF THE USE OF THIS THEME, EVEN IF ADVISED OF THE 45 | POSSIBILITY OF SUCH DAMAGE. 46 | -------------------------------------------------------------------------------- /tests/ceph/ceph-report-compat-converted.txt: -------------------------------------------------------------------------------- 1 | tunable straw_calc_version 0 2 | tunable chooseleaf_vary_r 0 3 | tunable chooseleaf_stable 0 4 | tunable chooseleaf_descend_once 0 5 | tunable choose_total_tries 19 6 | tunable choose_local_tries 2 7 | tunable choose_local_fallback_tries 5 8 | # begin crush map 9 | 10 | # devices 11 | device 0 device0 12 | device 1 device1 13 | device 2 device2 14 | 15 | # types 16 | type 0 device 17 | type 1 host 18 | type 2 rack 19 | type 3 root 20 | 21 | # buckets 22 | host host0 { 23 | id -1 # do not change unnecessarily 24 | # weight 1.000 25 | alg straw2 26 | hash 0 # rjenkins1 27 | item device0 weight 1.000 28 | } 29 | host host1 { 30 | id -2 # do not change unnecessarily 31 | # weight 1.000 32 | alg straw2 33 | hash 0 # rjenkins1 34 | item device1 weight 1.000 35 | } 36 | host host2 { 37 | id -5 # do not change unnecessarily 38 | # weight 1.000 39 | alg straw2 40 | hash 0 # rjenkins1 41 | item device2 weight 1.000 42 | } 43 | rack rack0 { 44 | id -3 # do not change unnecessarily 45 | # weight 3.000 46 | alg straw2 47 | hash 0 # rjenkins1 48 | item host0 weight 1.000 49 | item host1 weight 1.000 50 | item host2 weight 1.000 51 | } 52 | root root { 53 | id -4 # do not change unnecessarily 54 | # weight 4.000 55 | alg straw2 56 | hash 0 # rjenkins1 57 | item rack0 weight 4.000 58 | } 59 | 60 | # rules 61 | rule data { 62 | ruleset 0 63 | type replicated 64 | min_size 2 65 | max_size 2 66 | step take root 67 | step chooseleaf firstn 0 type host 68 | step emit 69 | } 70 | 71 | # choose_args 72 | choose_args 3 { 73 | { 74 | bucket_id -1 75 | weight_set [ 76 | [ 1.000 ] 77 | ] 78 | } 79 | { 80 | bucket_id -2 81 | weight_set [ 82 | [ 7.000 ] 83 | ] 84 | } 85 | { 86 | bucket_id -3 87 | weight_set [ 88 | [ 1.000 1.000 1.000 ] 89 | ] 90 | } 91 | { 92 | bucket_id -4 93 | weight_set [ 94 | [ 4.000 ] 95 | ] 96 | } 97 | { 98 | bucket_id -5 99 | weight_set [ 100 | [ 1.000 ] 101 | ] 102 | } 103 | } 104 | 105 | # end crush map 106 | -------------------------------------------------------------------------------- /tests/test_ceph_optimize.py: -------------------------------------------------------------------------------- 1 | # -*- mode: python; coding: utf-8 -*- 2 | # 3 | # Copyright (C) 2017 4 | # 5 | # Author: Loic Dachary 6 | # 7 | # This program is free software: you can redistribute it and/or modify 8 | # it under the terms of the GNU General Public License as published by 9 | # the Free Software Foundation, either version 3 of the License, or 10 | # (at your option) any later version. 11 | # 12 | # This program is distributed in the hope that it will be useful, 13 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | # GNU General Public License for more details. 16 | # 17 | # You should have received a copy of the GNU General Public License 18 | # along with this program. If not, see . 19 | # 20 | import pytest 21 | from crush.ceph import Ceph 22 | 23 | 24 | class TestCompare(object): 25 | 26 | def test_sanity_check_args(self): 27 | a = Ceph().constructor([ 28 | 'optimize', 29 | ]) 30 | with pytest.raises(Exception) as e: 31 | a.pre_sanity_check_args() 32 | assert 'missing --crushmap' in str(e.value) 33 | 34 | a = Ceph().constructor([ 35 | 'optimize', 36 | '--crushmap', 'CRUSHMAP', 37 | '--out-path', 'OUT PATH', 38 | '--rule', 'RULE', 39 | '--choose-args', 'CHOOSE ARGS', 40 | '--pool', '3', 41 | '--values-count', '8', 42 | ]) 43 | a.pre_sanity_check_args() 44 | with pytest.raises(Exception) as e: 45 | a.post_sanity_check_args() 46 | assert '--pool and --values-count are mutually exclusive' in str(e.value) 47 | 48 | a = Ceph().constructor([ 49 | 'optimize', 50 | '--crushmap', 'CRUSHMAP', 51 | '--out-path', 'OUT PATH', 52 | '--rule', 'RULE', 53 | '--choose-args', 'CHOOSE ARGS', 54 | ]) 55 | a.pre_sanity_check_args() 56 | a.post_sanity_check_args() 57 | 58 | # Local Variables: 59 | # compile-command: "cd .. ; tox -e py27 -- -s -vv tests/test_ceph_optimize.py" 60 | # End: 61 | -------------------------------------------------------------------------------- /tests/test_ceph_compare.py: -------------------------------------------------------------------------------- 1 | # -*- mode: python; coding: utf-8 -*- 2 | # 3 | # Copyright (C) 2017 4 | # 5 | # Author: Loic Dachary 6 | # 7 | # This program is free software: you can redistribute it and/or modify 8 | # it under the terms of the GNU General Public License as published by 9 | # the Free Software Foundation, either version 3 of the License, or 10 | # (at your option) any later version. 11 | # 12 | # This program is distributed in the hope that it will be useful, 13 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | # GNU General Public License for more details. 16 | # 17 | # You should have received a copy of the GNU General Public License 18 | # along with this program. If not, see . 19 | # 20 | import pytest 21 | from crush.ceph import Ceph 22 | 23 | 24 | class TestCompare(object): 25 | 26 | def test_sanity_check_args(self): 27 | a = Ceph().constructor([ 28 | 'compare', 29 | ]) 30 | with pytest.raises(Exception) as e: 31 | a.pre_sanity_check_args() 32 | assert 'missing --origin' in str(e.value) 33 | 34 | a = Ceph().constructor([ 35 | 'compare', 36 | '--origin', 'ORIGIN', 37 | '--destination', 'DESTINATION', 38 | '--rule', 'RULE', 39 | '--origin-choose-args', 'ORIGIN CHOOSE ARGS', 40 | '--destination-choose-args', 'DESTINATION CHOOSE ARGS', 41 | ]) 42 | a.pre_sanity_check_args() 43 | a.post_sanity_check_args() 44 | 45 | a = Ceph().constructor([ 46 | 'compare', 47 | '--origin', 'ORIGIN', 48 | '--destination', 'DESTINATION', 49 | '--rule', 'RULE', 50 | '--origin-choose-args', 'ORIGIN CHOOSE ARGS', 51 | '--destination-choose-args', 'DESTINATION CHOOSE ARGS', 52 | '--pool', '3', 53 | '--values-count', '10', 54 | ]) 55 | a.pre_sanity_check_args() 56 | with pytest.raises(Exception) as e: 57 | a.post_sanity_check_args() 58 | assert 'mutually exclusive' in str(e.value) 59 | 60 | # Local Variables: 61 | # compile-command: "cd .. ; tox -e py27 -- -s -vv tests/test_ceph_compare.py" 62 | # End: 63 | -------------------------------------------------------------------------------- /crush/libcrush/common/safe_io.h: -------------------------------------------------------------------------------- 1 | // -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*- 2 | // vim: ts=8 sw=2 smarttab 3 | /* 4 | * Ceph - scalable distributed file system 5 | * 6 | * Copyright (C) 2011 New Dream Network 7 | * 8 | * This is free software; you can redistribute it and/or 9 | * modify it under the terms of the GNU Lesser General Public 10 | * License version 2.1, as published by the Free Software 11 | * Foundation. See file COPYING. 12 | * 13 | */ 14 | 15 | #ifndef CEPH_SAFE_IO 16 | #define CEPH_SAFE_IO 17 | 18 | #include "acconfig.h" 19 | #include "common/compiler_extensions.h" 20 | #include 21 | 22 | #ifdef __cplusplus 23 | extern "C" { 24 | #endif 25 | 26 | /* 27 | * Safe functions wrapping the raw read() and write() libc functions. 28 | * These retry on EINTR, and on error return -errno instead of returning 29 | * -1 and setting errno). 30 | */ 31 | ssize_t safe_read(int fd, void *buf, size_t count) 32 | WARN_UNUSED_RESULT; 33 | ssize_t safe_write(int fd, const void *buf, size_t count) 34 | WARN_UNUSED_RESULT; 35 | ssize_t safe_pread(int fd, void *buf, size_t count, off_t offset) 36 | WARN_UNUSED_RESULT; 37 | ssize_t safe_pwrite(int fd, const void *buf, size_t count, off_t offset) 38 | WARN_UNUSED_RESULT; 39 | #ifdef CEPH_HAVE_SPLICE 40 | /* 41 | * Similar to the above (non-exact version) and below (exact version). 42 | * See splice(2) for parameter descriptions. 43 | */ 44 | ssize_t safe_splice(int fd_in, off_t *off_in, int fd_out, off_t *off_out, 45 | size_t len, unsigned int flags) 46 | WARN_UNUSED_RESULT; 47 | ssize_t safe_splice_exact(int fd_in, off_t *off_in, int fd_out, 48 | off_t *off_out, size_t len, unsigned int flags) 49 | WARN_UNUSED_RESULT; 50 | #endif 51 | 52 | /* 53 | * Same as the above functions, but return -EDOM unless exactly the requested 54 | * number of bytes can be read. 55 | */ 56 | ssize_t safe_read_exact(int fd, void *buf, size_t count) 57 | WARN_UNUSED_RESULT; 58 | ssize_t safe_pread_exact(int fd, void *buf, size_t count, off_t offset) 59 | WARN_UNUSED_RESULT; 60 | 61 | 62 | /* 63 | * Safe functions to read and write an entire file. 64 | */ 65 | int safe_write_file(const char *base, const char *file, 66 | const char *val, size_t vallen); 67 | int safe_read_file(const char *base, const char *file, 68 | char *val, size_t vallen); 69 | 70 | #ifdef __cplusplus 71 | } 72 | #endif 73 | 74 | #endif 75 | -------------------------------------------------------------------------------- /crush/libcrush/module.c: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (C) 2017 3 | // 4 | // Author: Loic Dachary 5 | // 6 | // This program is free software; you can redistribute it and/or modify 7 | // it under the terms of the GNU General Public License as published by 8 | // the Free Software Foundation; either version 2 of the License, or 9 | // (at your option) any later version. 10 | // 11 | // This program is distributed in the hope that it will be useful, 12 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | // GNU General Public License for more details. 15 | // 16 | // You should have received a copy of the GNU General Public License 17 | // along with this program; if not, write to the Free Software 18 | // Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA 19 | // 20 | #include "libcrush.h" 21 | 22 | /* Module definition */ 23 | 24 | #define MODULE_DOC PyDoc_STR("python wrapper for libcrush.") 25 | 26 | #if PY_MAJOR_VERSION >= 3 27 | 28 | static PyModuleDef 29 | moduledef = { 30 | PyModuleDef_HEAD_INIT, 31 | "crush.libcrush", 32 | MODULE_DOC, 33 | -1, 34 | NULL, /* methods */ 35 | NULL, 36 | NULL, /* traverse */ 37 | NULL, /* clear */ 38 | NULL 39 | }; 40 | 41 | 42 | PyObject * 43 | PyInit_libcrush(void) 44 | { 45 | PyObject * mod = PyModule_Create(&moduledef); 46 | if (mod == NULL) { 47 | return NULL; 48 | } 49 | 50 | /* Initialize LibCrush */ 51 | LibCrushType.tp_new = PyType_GenericNew; 52 | if (PyType_Ready(&LibCrushType) < 0) { 53 | Py_DECREF(mod); 54 | return NULL; 55 | } 56 | 57 | Py_INCREF(&LibCrushType); 58 | if (PyModule_AddObject(mod, "LibCrush", (PyObject *)&LibCrushType) < 0) { 59 | Py_DECREF(mod); 60 | Py_DECREF(&LibCrushType); 61 | return NULL; 62 | } 63 | 64 | return mod; 65 | } 66 | 67 | #else 68 | 69 | void 70 | initlibcrush(void) 71 | { 72 | PyObject * mod; 73 | 74 | mod = Py_InitModule3("crush.libcrush", NULL, MODULE_DOC); 75 | if (mod == NULL) { 76 | return; 77 | } 78 | 79 | /* Initialize LibCrush */ 80 | LibCrushType.tp_new = PyType_GenericNew; 81 | if (PyType_Ready(&LibCrushType) < 0) { 82 | return; 83 | } 84 | 85 | Py_INCREF(&LibCrushType); 86 | PyModule_AddObject(mod, "LibCrush", (PyObject *)&LibCrushType); 87 | 88 | } 89 | 90 | #endif /* Py3k */ 91 | -------------------------------------------------------------------------------- /setup.cfg: -------------------------------------------------------------------------------- 1 | # 2 | # Interpreted by pbr 3 | # https://pypi.python.org/pypi/pbr 4 | # http://docs.openstack.org/developer/pbr/ 5 | # 6 | [metadata] 7 | name = crush 8 | version = 1.0.36 9 | summary = library to control placement in a hierarchy 10 | description-file = 11 | README.rst 12 | author = Loic Dachary 13 | author-email = loic@dachary.org 14 | home-page = http://libcrush.org/main/python-crush 15 | classifier = 16 | Environment :: Console 17 | Intended Audience :: Information Technology 18 | Intended Audience :: System Administrators 19 | Operating System :: POSIX :: Linux 20 | License :: OSI Approved :: GNU General Public License v3 or later (GPLv3+) 21 | Programming Language :: Python 22 | Programming Language :: Python :: 2 23 | Programming Language :: Python :: 3 24 | Topic :: Utilities 25 | 26 | [files] 27 | scripts = 28 | bin/crush 29 | packages = crush 30 | 31 | [build_sphinx] 32 | all_files = 1 33 | build-dir = build 34 | source-dir = docs 35 | warning-is-error = 1 36 | 37 | [global] 38 | setup-hooks = 39 | pbr.hooks.setup_hook 40 | 41 | [extension=crush.libcrush] 42 | 43 | extra_compile_args = -std=c++11 44 | 45 | define_macros = __STANDALONE_CRUSH__ 46 | 47 | # /usr/include/boost148 is for manylinux/build-wheels.sh on CentOS 5 48 | include_dirs = 49 | /usr/include/boost148 50 | crush/libcrush 51 | crush/libcrush/crush 52 | crush/libcrush/placeholders 53 | 54 | sources = 55 | crush/libcrush/module.c 56 | crush/libcrush/libcrush.c 57 | crush/libcrush/ceph_read_write.cc 58 | crush/libcrush/crush/crush.c 59 | crush/libcrush/crush/builder.c 60 | crush/libcrush/crush/mapper.c 61 | crush/libcrush/crush/hash.c 62 | crush/libcrush/crush/CrushCompiler.cc 63 | crush/libcrush/crush/CrushWrapper.cc 64 | crush/libcrush/common/errno.cc 65 | crush/libcrush/common/buffer.cc 66 | crush/libcrush/common/safe_io.c 67 | crush/libcrush/common/armor.c 68 | crush/libcrush/common/escape.c 69 | crush/libcrush/common/page.cc 70 | crush/libcrush/common/Formatter.cc 71 | crush/libcrush/common/HTMLFormatter.cc 72 | crush/libcrush/common/mempool.cc 73 | crush/libcrush/common/strtol.cc 74 | 75 | [build_ext] 76 | # populate crush/libcrush with files from libcrush/crush 77 | # as required by sources & libcrush_headers above 78 | pre-hook.build_post_hook = setup._setup_hooks.build_pre_hook 79 | post-hook.build_post_hook = setup._setup_hooks.build_post_hook 80 | 81 | [flake8] 82 | max-line-length = 100 83 | -------------------------------------------------------------------------------- /tests/sample-ceph-crushmap.txt: -------------------------------------------------------------------------------- 1 | tunable straw_calc_version 0 2 | tunable chooseleaf_vary_r 0 3 | tunable chooseleaf_stable 0 4 | tunable chooseleaf_descend_once 0 5 | tunable choose_total_tries 19 6 | tunable choose_local_tries 2 7 | tunable choose_local_fallback_tries 5 8 | # begin crush map 9 | 10 | # devices 11 | device 0 device0 12 | device 1 device1 13 | device 2 device2 14 | 15 | # types 16 | type 0 device 17 | type 1 host 18 | type 2 rack 19 | type 3 root 20 | 21 | # buckets 22 | host host0 { 23 | id -1 # do not change unnecessarily 24 | # weight 1.000 25 | alg straw 26 | hash 0 # rjenkins1 27 | item device0 weight 1.000 28 | } 29 | host host1 { 30 | id -2 # do not change unnecessarily 31 | # weight 1.000 32 | alg straw 33 | hash 0 # rjenkins1 34 | item device1 weight 1.000 35 | } 36 | host host2 { 37 | id -5 # do not change unnecessarily 38 | # weight 1.000 39 | alg straw 40 | hash 0 # rjenkins1 41 | item device2 weight 1.000 42 | } 43 | rack rack0 { 44 | id -3 # do not change unnecessarily 45 | # weight 3.000 46 | alg straw 47 | hash 0 # rjenkins1 48 | item host0 weight 1.000 49 | item host1 weight 1.000 50 | item host2 weight 1.000 51 | } 52 | root root { 53 | id -4 # do not change unnecessarily 54 | # weight 4.000 55 | alg straw 56 | hash 0 # rjenkins1 57 | item rack0 weight 4.000 58 | } 59 | 60 | # rules 61 | rule data { 62 | ruleset 3 63 | type replicated 64 | min_size 2 65 | max_size 2 66 | step take root 67 | step chooseleaf firstn 0 type rack 68 | step emit 69 | } 70 | 71 | # choose_args 72 | choose_args 1 { 73 | } 74 | choose_args 2 { 75 | { 76 | bucket_id -3 77 | ids [ -20 30 -25 ] 78 | } 79 | } 80 | choose_args 3 { 81 | { 82 | bucket_id -3 83 | weight_set [ 84 | [ 1.000 2.000 5.000 ] 85 | [ 3.000 2.000 5.000 ] 86 | ] 87 | ids [ -20 -30 -25 ] 88 | } 89 | } 90 | choose_args 4 { 91 | { 92 | bucket_id -2 93 | weight_set [ 94 | [ 1.000 ] 95 | [ 3.000 ] 96 | ] 97 | } 98 | } 99 | choose_args 5 { 100 | { 101 | bucket_id -1 102 | ids [ -450 ] 103 | } 104 | } 105 | choose_args 6 { 106 | { 107 | bucket_id -1 108 | ids [ -450 ] 109 | } 110 | { 111 | bucket_id -2 112 | weight_set [ 113 | [ 1.000 ] 114 | [ 3.000 ] 115 | ] 116 | } 117 | { 118 | bucket_id -3 119 | weight_set [ 120 | [ 1.000 2.000 5.000 ] 121 | [ 3.000 2.000 5.000 ] 122 | ] 123 | ids [ -20 -30 -25 ] 124 | } 125 | } 126 | 127 | # end crush map 128 | -------------------------------------------------------------------------------- /tests/sample-ceph-crushmap-compat.txt: -------------------------------------------------------------------------------- 1 | tunable straw_calc_version 0 2 | tunable chooseleaf_vary_r 0 3 | tunable chooseleaf_stable 0 4 | tunable chooseleaf_descend_once 0 5 | tunable choose_total_tries 19 6 | tunable choose_local_tries 2 7 | tunable choose_local_fallback_tries 5 8 | # begin crush map 9 | 10 | # devices 11 | device 0 device0 12 | device 1 device1 13 | device 2 device2 14 | 15 | # types 16 | type 0 device 17 | type 1 host 18 | type 2 rack 19 | type 3 root 20 | 21 | # buckets 22 | host host0 { 23 | id -1 # do not change unnecessarily 24 | # weight 1.000 25 | alg straw 26 | hash 0 # rjenkins1 27 | item device0 weight 1.000 28 | } 29 | host host1 { 30 | id -2 # do not change unnecessarily 31 | # weight 7.000 32 | alg straw 33 | hash 0 # rjenkins1 34 | item device1 weight 7.000 35 | } 36 | host host2 { 37 | id -5 # do not change unnecessarily 38 | # weight 1.000 39 | alg straw 40 | hash 0 # rjenkins1 41 | item device2 weight 1.000 42 | } 43 | rack rack0 { 44 | id -3 # do not change unnecessarily 45 | # weight 3.000 46 | alg straw 47 | hash 0 # rjenkins1 48 | item host0 weight 1.000 49 | item host1 weight 1.000 50 | item host2 weight 1.000 51 | } 52 | root root { 53 | id -4 # do not change unnecessarily 54 | # weight 4.000 55 | alg straw 56 | hash 0 # rjenkins1 57 | item rack0 weight 4.000 58 | } 59 | host host0-target-weight { 60 | id -8 # do not change unnecessarily 61 | # weight 1.000 62 | alg straw 63 | hash 0 # rjenkins1 64 | item device0 weight 1.000 65 | } 66 | host host1-target-weight { 67 | id -9 # do not change unnecessarily 68 | # weight 1.000 69 | alg straw 70 | hash 0 # rjenkins1 71 | item device1 weight 1.000 72 | } 73 | host host2-target-weight { 74 | id -10 # do not change unnecessarily 75 | # weight 1.000 76 | alg straw 77 | hash 0 # rjenkins1 78 | item device2 weight 1.000 79 | } 80 | rack rack0-target-weight { 81 | id -7 # do not change unnecessarily 82 | # weight 3.000 83 | alg straw 84 | hash 0 # rjenkins1 85 | item host0-target-weight weight 1.000 86 | item host1-target-weight weight 1.000 87 | item host2-target-weight weight 1.000 88 | } 89 | root root-target-weight { 90 | id -6 # do not change unnecessarily 91 | # weight 4.000 92 | alg straw 93 | hash 0 # rjenkins1 94 | item rack0-target-weight weight 4.000 95 | } 96 | 97 | # rules 98 | rule data { 99 | ruleset 3 100 | type replicated 101 | min_size 2 102 | max_size 2 103 | step take root 104 | step chooseleaf firstn 0 type rack 105 | step emit 106 | } 107 | 108 | # end crush map 109 | -------------------------------------------------------------------------------- /tests/ceph/ceph-report-compat-optimized.txt: -------------------------------------------------------------------------------- 1 | tunable straw_calc_version 0 2 | tunable chooseleaf_vary_r 0 3 | tunable chooseleaf_stable 0 4 | tunable chooseleaf_descend_once 0 5 | tunable choose_total_tries 19 6 | tunable choose_local_tries 2 7 | tunable choose_local_fallback_tries 5 8 | # begin crush map 9 | 10 | # devices 11 | device 0 device0 12 | device 1 device1 13 | device 2 device2 14 | 15 | # types 16 | type 0 device 17 | type 1 host 18 | type 2 rack 19 | type 3 root 20 | 21 | # buckets 22 | host host0 { 23 | id -1 # do not change unnecessarily 24 | # weight 1.000 25 | alg straw2 26 | hash 0 # rjenkins1 27 | item device0 weight 1.000 28 | } 29 | host host1 { 30 | id -2 # do not change unnecessarily 31 | # weight 7.000 32 | alg straw2 33 | hash 0 # rjenkins1 34 | item device1 weight 7.000 35 | } 36 | host host2 { 37 | id -5 # do not change unnecessarily 38 | # weight 1.000 39 | alg straw2 40 | hash 0 # rjenkins1 41 | item device2 weight 1.000 42 | } 43 | rack rack0 { 44 | id -3 # do not change unnecessarily 45 | # weight 3.000 46 | alg straw2 47 | hash 0 # rjenkins1 48 | item host0 weight 1.000 49 | item host1 weight 1.000 50 | item host2 weight 1.000 51 | } 52 | root root { 53 | id -4 # do not change unnecessarily 54 | # weight 4.000 55 | alg straw2 56 | hash 0 # rjenkins1 57 | item rack0 weight 4.000 58 | } 59 | host host0-target-weight { 60 | id -8 # do not change unnecessarily 61 | # weight 1.000 62 | alg straw2 63 | hash 0 # rjenkins1 64 | item device0 weight 1.000 65 | } 66 | host host1-target-weight { 67 | id -9 # do not change unnecessarily 68 | # weight 1.000 69 | alg straw2 70 | hash 0 # rjenkins1 71 | item device1 weight 1.000 72 | } 73 | host host2-target-weight { 74 | id -10 # do not change unnecessarily 75 | # weight 1.000 76 | alg straw2 77 | hash 0 # rjenkins1 78 | item device2 weight 1.000 79 | } 80 | rack rack0-target-weight { 81 | id -7 # do not change unnecessarily 82 | # weight 3.000 83 | alg straw2 84 | hash 0 # rjenkins1 85 | item host0-target-weight weight 1.000 86 | item host1-target-weight weight 1.000 87 | item host2-target-weight weight 1.000 88 | } 89 | root root-target-weight { 90 | id -6 # do not change unnecessarily 91 | # weight 4.000 92 | alg straw2 93 | hash 0 # rjenkins1 94 | item rack0-target-weight weight 4.000 95 | } 96 | 97 | # rules 98 | rule data { 99 | ruleset 0 100 | type replicated 101 | min_size 2 102 | max_size 2 103 | step take root 104 | step chooseleaf firstn 0 type host 105 | step emit 106 | } 107 | 108 | # end crush map 109 | -------------------------------------------------------------------------------- /tests/test_main.py: -------------------------------------------------------------------------------- 1 | # -*- mode: python; coding: utf-8 -*- 2 | # 3 | # Copyright (C) 2017 4 | # 5 | # Author: Loic Dachary 6 | # 7 | # This program is free software: you can redistribute it and/or modify 8 | # it under the terms of the GNU General Public License as published by 9 | # the Free Software Foundation, either version 3 of the License, or 10 | # (at your option) any later version. 11 | # 12 | # This program is distributed in the hope that it will be useful, 13 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | # GNU General Public License for more details. 16 | # 17 | # You should have received a copy of the GNU General Public License 18 | # along with this program. If not, see . 19 | # 20 | import argparse 21 | import pickle 22 | import pprint 23 | 24 | from crush import analyze 25 | from crush import main 26 | from crush import optimize 27 | 28 | 29 | class TestCrush(object): 30 | 31 | def test_run(self): 32 | # needs a bit more love to actually work 33 | pass 34 | # c = main.Crush() 35 | # argv = [] 36 | # c.run(argv) 37 | # assert (logging.getLogger('crush').getEffectiveLevel() == 38 | # logging.INFO) 39 | # c.run(['--verbose'] + argv) 40 | # assert (logging.getLogger('crush').getEffectiveLevel() == 41 | # logging.DEBUG) 42 | 43 | def test_pickle(self): 44 | m = main.Main() 45 | m.parse(['optimize']) 46 | p = pickle.dumps(m) 47 | n = pickle.loads(p) 48 | assert n.argv == m.argv 49 | 50 | def test_get_trimmed_argv(self): 51 | d_parser = optimize.Optimize.get_parser() 52 | parser = argparse.ArgumentParser( 53 | parents=[ 54 | d_parser, 55 | ], 56 | conflict_handler='resolve', 57 | ) 58 | parser.add_argument('--something-with-arg') 59 | parser.add_argument('--no-arg', action='store_true') 60 | pprint.pprint(vars(parser)) 61 | argv_discarded = [ 62 | '--no-arg', 63 | '--something-with-arg', 'arg', 64 | '--step', '200', 65 | ] 66 | argv_preserved = [ 67 | '--type', 'device', 68 | ] 69 | argv = argv_discarded + argv_preserved 70 | args = parser.parse_args(argv) 71 | trimmed_argv = main.Main.get_trimmed_argv( 72 | analyze.Analyze.get_parser(), args) 73 | expected = ['--type', 'device'] 74 | assert expected == trimmed_argv 75 | 76 | 77 | # Local Variables: 78 | # compile-command: "cd .. ; tox -e py27 -- -s -vv tests/test_main.py" 79 | # End: 80 | -------------------------------------------------------------------------------- /crush/libcrush/include/Spinlock.h: -------------------------------------------------------------------------------- 1 | // -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*- 2 | // vim: ts=8 sw=2 smarttab 3 | /* 4 | * Ceph - scalable distributed file system 5 | * 6 | * Copyright (C) 2013 Inktank 7 | * 8 | * This is free software; you can redistribute it and/or 9 | * modify it under the terms of the GNU Lesser General Public 10 | * License version 2.1, as published by the Free Software 11 | * Foundation. See file COPYING. 12 | * 13 | * @author Sage Weil 14 | */ 15 | 16 | #ifndef CEPH_SPINLOCK_H 17 | #define CEPH_SPINLOCK_H 18 | 19 | #include "acconfig.h" 20 | 21 | #include 22 | 23 | typedef struct { 24 | #ifdef HAVE_PTHREAD_SPINLOCK 25 | pthread_spinlock_t lock; 26 | #else 27 | pthread_mutex_t lock; 28 | #endif 29 | } ceph_spinlock_t; 30 | 31 | #ifdef HAVE_PTHREAD_SPINLOCK 32 | 33 | static inline int ceph_spin_init(ceph_spinlock_t *l) 34 | { 35 | return pthread_spin_init(&l->lock, PTHREAD_PROCESS_PRIVATE); 36 | } 37 | 38 | static inline int ceph_spin_destroy(ceph_spinlock_t *l) 39 | { 40 | return pthread_spin_destroy(&l->lock); 41 | } 42 | 43 | static inline int ceph_spin_lock(ceph_spinlock_t *l) 44 | { 45 | return pthread_spin_lock(&l->lock); 46 | } 47 | 48 | static inline int ceph_spin_unlock(ceph_spinlock_t *l) 49 | { 50 | return pthread_spin_unlock(&l->lock); 51 | } 52 | 53 | #else /* !HAVE_PTHREAD_SPINLOCK */ 54 | 55 | static inline int ceph_spin_init(ceph_spinlock_t *l) 56 | { 57 | return pthread_mutex_init(&l->lock, NULL); 58 | } 59 | 60 | static inline int ceph_spin_destroy(ceph_spinlock_t *l) 61 | { 62 | return pthread_mutex_destroy(&l->lock); 63 | } 64 | 65 | static inline int ceph_spin_lock(ceph_spinlock_t *l) 66 | { 67 | return pthread_mutex_lock(&l->lock); 68 | } 69 | 70 | static inline int ceph_spin_unlock(ceph_spinlock_t *l) 71 | { 72 | return pthread_mutex_unlock(&l->lock); 73 | } 74 | 75 | #endif 76 | 77 | class Spinlock { 78 | mutable ceph_spinlock_t _lock; 79 | 80 | public: 81 | Spinlock() { 82 | ceph_spin_init(&_lock); 83 | } 84 | ~Spinlock() { 85 | ceph_spin_destroy(&_lock); 86 | } 87 | 88 | // don't allow copying. 89 | void operator=(Spinlock& s); 90 | Spinlock(const Spinlock& s); 91 | 92 | /// acquire spinlock 93 | void lock() const { 94 | ceph_spin_lock(&_lock); 95 | } 96 | /// release spinlock 97 | void unlock() const { 98 | ceph_spin_unlock(&_lock); 99 | } 100 | 101 | class Locker { 102 | const Spinlock& spinlock; 103 | public: 104 | Locker(const Spinlock& s) : spinlock(s) { 105 | spinlock.lock(); 106 | } 107 | ~Locker() { 108 | spinlock.unlock(); 109 | } 110 | }; 111 | }; 112 | 113 | #endif 114 | -------------------------------------------------------------------------------- /crush/libcrush/include/byteorder.h: -------------------------------------------------------------------------------- 1 | // -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*- 2 | 3 | #pragma once 4 | 5 | #include 6 | #include "acconfig.h" 7 | #include "int_types.h" 8 | 9 | 10 | #ifdef __GNUC__ 11 | template 12 | inline typename std::enable_if::type 13 | swab(T val) { 14 | return __builtin_bswap16(val); 15 | } 16 | template 17 | inline typename std::enable_if::type 18 | swab(T val) { 19 | return __builtin_bswap32(val); 20 | } 21 | template 22 | inline typename std::enable_if::type 23 | swab(T val) { 24 | return __builtin_bswap64(val); 25 | } 26 | #else 27 | template 28 | inline typename std::enable_if::type 29 | swab(T val) { 30 | return (val >> 8) | (val << 8); 31 | } 32 | template 33 | inline typename std::enable_if::type 34 | swab(T val) { 35 | return (( val >> 24) | 36 | ((val >> 8) & 0xff00) | 37 | ((val << 8) & 0xff0000) | 38 | ((val << 24))); 39 | } 40 | template 41 | inline typename std::enable_if::type 42 | swab(T val) { 43 | return (( val >> 56) | 44 | ((val >> 40) & 0xff00ull) | 45 | ((val >> 24) & 0xff0000ull) | 46 | ((val >> 8) & 0xff000000ull) | 47 | ((val << 8) & 0xff00000000ull) | 48 | ((val << 24) & 0xff0000000000ull) | 49 | ((val << 40) & 0xff000000000000ull) | 50 | ((val << 56))); 51 | } 52 | #endif 53 | 54 | // mswab == maybe swab (if not LE) 55 | #ifdef CEPH_BIG_ENDIAN 56 | template 57 | inline T mswab(T val) { 58 | return swab(val); 59 | } 60 | #else 61 | template 62 | inline T mswab(T val) { 63 | return val; 64 | } 65 | #endif 66 | 67 | template 68 | struct ceph_le { 69 | T v; 70 | ceph_le& operator=(T nv) { 71 | v = mswab(nv); 72 | return *this; 73 | } 74 | operator T() const { return mswab(v); } 75 | } __attribute__ ((packed)); 76 | 77 | template 78 | inline bool operator==(ceph_le a, ceph_le b) { 79 | return a.v == b.v; 80 | } 81 | 82 | using ceph_le64 = ceph_le<__u64>; 83 | using ceph_le32 = ceph_le<__u32>; 84 | using ceph_le16 = ceph_le<__u16>; 85 | 86 | inline __u64 init_le64(__u64 x) { 87 | return mswab<__u64>(x); 88 | } 89 | inline __u32 init_le32(__u32 x) { 90 | return mswab<__u32>(x); 91 | } 92 | inline __u16 init_le16(__u16 x) { 93 | return mswab<__u16>(x); 94 | } 95 | 96 | /* 97 | #define cpu_to_le64(x) (x) 98 | #define cpu_to_le32(x) (x) 99 | #define cpu_to_le16(x) (x) 100 | */ 101 | #define le64_to_cpu(x) ((uint64_t)x) 102 | #define le32_to_cpu(x) ((__u32)x) 103 | #define le16_to_cpu(x) ((__u16)x) 104 | -------------------------------------------------------------------------------- /crush/libcrush/crush/CrushCompiler.h: -------------------------------------------------------------------------------- 1 | // -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*- 2 | // vim: ts=8 sw=2 smarttab 3 | 4 | #ifndef CEPH_CRUSH_COMPILER_H 5 | #define CEPH_CRUSH_COMPILER_H 6 | 7 | #include "crush/CrushWrapper.h" 8 | #include "crush/grammar.h" 9 | 10 | #include 11 | #include 12 | #include 13 | 14 | class CrushCompiler { 15 | CrushWrapper& crush; 16 | ostream& err; 17 | int verbose; 18 | bool unsafe_tunables; 19 | 20 | // decompile 21 | enum dcb_state_t { 22 | DCB_STATE_IN_PROGRESS = 0, 23 | DCB_STATE_DONE 24 | }; 25 | 26 | int decompile_weight_set_weights(crush_weight_set weight_set, 27 | ostream &out); 28 | int decompile_weight_set(crush_weight_set *weight_set, 29 | __u32 size, 30 | ostream &out); 31 | int decompile_choose_arg(crush_choose_arg *arg, 32 | int bucket_id, 33 | ostream &out); 34 | int decompile_ids(int *ids, 35 | __u32 size, 36 | ostream &out); 37 | int decompile_choose_arg_map(crush_choose_arg_map arg_map, 38 | ostream &out); 39 | int decompile_choose_args(const std::pair &i, 40 | ostream &out); 41 | int decompile_bucket_impl(int i, ostream &out); 42 | int decompile_bucket(int cur, 43 | std::map& dcb_states, 44 | ostream &out); 45 | 46 | // compile 47 | typedef char const* iterator_t; 48 | typedef tree_match parse_tree_match_t; 49 | typedef parse_tree_match_t::tree_iterator iter_t; 50 | typedef parse_tree_match_t::node_t node_t; 51 | 52 | map item_id; 53 | map id_item; 54 | map item_weight; 55 | map type_id; 56 | map rule_id; 57 | 58 | string string_node(node_t &node); 59 | int int_node(node_t &node); 60 | float float_node(node_t &node); 61 | 62 | int parse_tunable(iter_t const& i); 63 | int parse_device(iter_t const& i); 64 | int parse_bucket_type(iter_t const& i); 65 | int parse_bucket(iter_t const& i); 66 | int parse_rule(iter_t const& i); 67 | int parse_weight_set_weights(iter_t const& i, int bucket_id, crush_weight_set *weight_set); 68 | int parse_weight_set(iter_t const& i, int bucket_id, crush_choose_arg *arg); 69 | int parse_choose_arg_ids(iter_t const& i, int bucket_id, crush_choose_arg *args); 70 | int parse_choose_arg(iter_t const& i, crush_choose_arg *args); 71 | int parse_choose_args(iter_t const& i); 72 | void find_used_bucket_ids(iter_t const& i); 73 | int parse_crush(iter_t const& i); 74 | void dump(iter_t const& i, int ind=1); 75 | string consolidate_whitespace(string in); 76 | int adjust_bucket_item_place(iter_t const &i); 77 | 78 | public: 79 | CrushCompiler(CrushWrapper& c, ostream& eo, int verbosity=0) 80 | : crush(c), err(eo), verbose(verbosity), 81 | unsafe_tunables(false) {} 82 | ~CrushCompiler() {} 83 | 84 | void enable_unsafe_tunables() { 85 | unsafe_tunables = true; 86 | } 87 | 88 | int decompile(ostream& out); 89 | int compile(istream& in, const char *infn=0); 90 | }; 91 | 92 | #endif 93 | -------------------------------------------------------------------------------- /README.rst: -------------------------------------------------------------------------------- 1 | crush 2 | ===== 3 | 4 | crush is a library to control placement in a hierarchy 5 | 6 | - Home page : http://http://libcrush.org/main/python-crush 7 | - Documentation : http://crush.readthedocs.org/ 8 | - PyPi : https://pypi.python.org/pypi/crush 9 | 10 | GNU/Linux Installation 11 | ====================== 12 | 13 | * pip install crush 14 | 15 | Other Installation 16 | ================== 17 | 18 | When using pip versions lower than 8.1 or other operating systems, 19 | compilation is necessary and packages must be installed first. 20 | 21 | * apt-get install -y gcc g++ python-pip python-all-dev libpython3-all-dev cmake libboost-all-dev libatomic-ops-dev 22 | * dnf / yum / zypper install -y gcc gcc-c++ python-pip python-devel python3-devel cmake make boost-devel libatomic_ops-devel 23 | * pip install crush 24 | 25 | Hacking 26 | ======= 27 | 28 | * Get the code:: 29 | 30 | git clone http://libcrush.org/main/python-crush.git 31 | cd python-crush 32 | 33 | * Set up the development environment:: 34 | 35 | deactivate || true ; source bootstrap 36 | 37 | * Run the tests:: 38 | 39 | deactivate || true ; bash run-tests.sh 40 | 41 | * Sync the libcrush submodule:: 42 | 43 | git submodule update --remote libcrush 44 | 45 | * Run a single test:: 46 | 47 | tox -e py27 -- -s -k test_one tests/test_crush.py 48 | 49 | * Check the documentation:: 50 | 51 | python setup.py build_sphinx 52 | firefox build/html/index.html 53 | 54 | * Update requirements 55 | 56 | rm -fr virtualenv 57 | virtualenv virtualenv 58 | source virtualenv/bin/activate 59 | # update some module in requirements.txt 60 | tox 61 | # if that works 62 | pip install -r requirements.txt 63 | pip freeze -r requirements.txt > new-requirements.txt 64 | .tox/py3/bin/pip freeze -r requirements-dev.txt > new-requirements-dev.txt 65 | diff <(.tox/py27/bin/pip freeze -r requirements-dev.txt) new-requirements-dev.txt 66 | # all lines after the first "were added by pip freeze" are indirect dependencies 67 | remove pkg-resources==0.0.0 https://bugs.launchpad.net/ubuntu/+source/python-pip/+bug/1635463 68 | 69 | Release management 70 | ================== 71 | 72 | * Prepare a new version 73 | 74 | - git checkout master ; git pull 75 | - version=1.0.0 ; perl -pi -e "s/^version.*/version = $version/" setup.cfg ; for i in 1 2 ; do python setup.py sdist ; amend=$(git log -1 --oneline | grep --quiet "version $version" && echo --amend) ; git commit $amend -m "version $version" ChangeLog setup.cfg ; git tag -a -f -m "version $version" $version ; done 76 | 77 | * Publish a new version 78 | 79 | - docker build --tag manylinux manylinux 80 | - docker run --rm -v $(pwd):/io manylinux /io/manylinux/build-wheels.sh 81 | OR docker run --rm -v $(pwd):/io manylinux env PYBINS=/opt/python/cp27-cp27mu/bin /io/manylinux/build-wheels.sh 82 | - sudo chown -R $(id -u) wheelhouse/ 83 | - twine upload --sign wheelhouse/*crush* 84 | 85 | - rm -fr dist 86 | - python setup.py sdist 87 | - twine upload --sign dist/\*.tar.gz 88 | 89 | - git push ; git push --tags 90 | 91 | * pypi maintenance 92 | 93 | - trim old versions at https://pypi.python.org/pypi/crush 94 | -------------------------------------------------------------------------------- /crush/libcrush/common/armor.c: -------------------------------------------------------------------------------- 1 | 2 | #if defined(__linux__) 3 | #include 4 | #else 5 | #include 6 | #endif 7 | 8 | /* 9 | * base64 encode/decode. 10 | */ 11 | 12 | const char *pem_key = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; 13 | 14 | static int encode_bits(int c) 15 | { 16 | return pem_key[c]; 17 | } 18 | 19 | static int decode_bits(char c) 20 | { 21 | if (c >= 'A' && c <= 'Z') 22 | return c - 'A'; 23 | if (c >= 'a' && c <= 'z') 24 | return c - 'a' + 26; 25 | if (c >= '0' && c <= '9') 26 | return c - '0' + 52; 27 | if (c == '+' || c == '-') 28 | return 62; 29 | if (c == '/' || c == '_') 30 | return 63; 31 | if (c == '=') 32 | return 0; /* just non-negative, please */ 33 | return -EINVAL; 34 | } 35 | 36 | static int set_str_val(char **pdst, const char *end, char c) 37 | { 38 | if (*pdst < end) { 39 | char *p = *pdst; 40 | *p = c; 41 | (*pdst)++; 42 | } else 43 | return -ERANGE; 44 | 45 | return 0; 46 | } 47 | 48 | int ceph_armor_line_break(char *dst, const char *dst_end, const char *src, const char *end, int line_width) 49 | { 50 | int olen = 0; 51 | int line = 0; 52 | 53 | #define SET_DST(c) do { \ 54 | int __ret = set_str_val(&dst, dst_end, c); \ 55 | if (__ret < 0) \ 56 | return __ret; \ 57 | } while (0); 58 | 59 | while (src < end) { 60 | unsigned char a; 61 | 62 | a = *src++; 63 | SET_DST(encode_bits(a >> 2)); 64 | if (src < end) { 65 | unsigned char b; 66 | b = *src++; 67 | SET_DST(encode_bits(((a & 3) << 4) | (b >> 4))); 68 | if (src < end) { 69 | unsigned char c; 70 | c = *src++; 71 | SET_DST(encode_bits(((b & 15) << 2) | 72 | (c >> 6))); 73 | SET_DST(encode_bits(c & 63)); 74 | } else { 75 | SET_DST(encode_bits((b & 15) << 2)); 76 | SET_DST('='); 77 | } 78 | } else { 79 | SET_DST(encode_bits(((a & 3) << 4))); 80 | SET_DST('='); 81 | SET_DST('='); 82 | } 83 | olen += 4; 84 | line += 4; 85 | if (line_width && line == line_width) { 86 | line = 0; 87 | SET_DST('\n'); 88 | olen++; 89 | } 90 | } 91 | return olen; 92 | } 93 | 94 | int ceph_armor(char *dst, const char *dst_end, const char *src, const char *end) 95 | { 96 | return ceph_armor_line_break(dst, dst_end, src, end, 0); 97 | } 98 | 99 | int ceph_unarmor(char *dst, const char *dst_end, const char *src, const char *end) 100 | { 101 | int olen = 0; 102 | 103 | while (src < end) { 104 | int a, b, c, d; 105 | 106 | if (src[0] == '\n') { 107 | src++; 108 | continue; 109 | } 110 | 111 | if (src + 4 > end) 112 | return -EINVAL; 113 | a = decode_bits(src[0]); 114 | b = decode_bits(src[1]); 115 | c = decode_bits(src[2]); 116 | d = decode_bits(src[3]); 117 | if (a < 0 || b < 0 || c < 0 || d < 0) 118 | return -EINVAL; 119 | 120 | SET_DST((a << 2) | (b >> 4)); 121 | if (src[2] == '=') 122 | return olen + 1; 123 | SET_DST(((b & 15) << 4) | (c >> 2)); 124 | if (src[3] == '=') 125 | return olen + 2; 126 | SET_DST(((c & 3) << 6) | d); 127 | olen += 3; 128 | src += 4; 129 | } 130 | return olen; 131 | } 132 | -------------------------------------------------------------------------------- /crush/libcrush/libcrush.h: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (C) 2017 3 | // 4 | // Author: Loic Dachary 5 | // 6 | // This program is free software; you can redistribute it and/or modify 7 | // it under the terms of the GNU General Public License as published by 8 | // the Free Software Foundation; either version 2 of the License, or 9 | // (at your option) any later version. 10 | // 11 | // This program is distributed in the hope that it will be useful, 12 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | // GNU General Public License for more details. 15 | // 16 | // You should have received a copy of the GNU General Public License 17 | // along with this program; if not, write to the Free Software 18 | // Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA 19 | // 20 | #ifndef _LIBCRUSH_H 21 | #define _LIBCRUSH_H 22 | 23 | #include 24 | #include 25 | #include 26 | #include 27 | 28 | #if PY_MAJOR_VERSION >= 3 29 | 30 | #define MyInt_Check PyLong_Check 31 | #define MyText_Check PyUnicode_Check 32 | #define MyText_Type PyUnicode_Type 33 | #define MyText_AS_BYTES(o) PyUnicode_AsASCIIString(o) 34 | #define MyBytes_GET_SIZE(o) PyBytes_GET_SIZE(o) 35 | #define MyBytes_AS_STRING(o) PyBytes_AS_STRING(o) 36 | #define MyText_AsString(o) PyUnicode_AsUTF8(o) 37 | #define MyText_FromFormat PyUnicode_FromFormat 38 | #define MyInt_FromInt(i) PyLong_FromLong((long)i) 39 | #define MyInt_AsInt(o) (int)PyLong_AsLong(o) 40 | #define MyText_InternFromString(s) PyUnicode_InternFromString(s) 41 | 42 | #define MyType_HEAD_INIT PyVarObject_HEAD_INIT(NULL, 0) 43 | 44 | #else 45 | 46 | #define MyInt_Check PyInt_Check 47 | #define MyText_Check(a) PyString_Check(a) || PyUnicode_Check(a) 48 | #define MyText_Type PyString_Type 49 | #define MyText_AS_BYTES(o) (Py_INCREF(o), o) 50 | #define MyBytes_GET_SIZE(o) PyString_GET_SIZE(o) 51 | #define MyBytes_AS_STRING(o) PyString_AS_STRING(o) 52 | #define MyText_AsString(o) PyString_AsString(o) 53 | #define MyText_FromFormat PyUnicode_FromFormat 54 | #define MyInt_FromInt(i) PyInt_FromLong((long)i) 55 | #define MyInt_AsInt(o) (int)PyInt_AsLong(o) 56 | #define MyText_InternFromString(s) PyString_InternFromString(s) 57 | 58 | #define MyType_HEAD_INIT PyObject_HEAD_INIT(NULL) 0, 59 | 60 | #endif /* Py3k */ 61 | 62 | /* The LibCrush type. */ 63 | 64 | #include "crush.h" 65 | 66 | typedef struct LibCrush { 67 | PyObject_HEAD 68 | 69 | int verbose; 70 | int backward_compatibility; 71 | struct crush_map *tunables; 72 | 73 | int has_bucket_weights; 74 | struct crush_map *map; 75 | PyObject *types; 76 | PyObject *items; 77 | PyObject *ritems; 78 | int highest_device_id; 79 | PyObject *rules; 80 | PyObject *choose_args; 81 | } LibCrush; 82 | 83 | extern PyTypeObject LibCrushType; 84 | 85 | #endif /* _LIBCRUSH_H */ 86 | -------------------------------------------------------------------------------- /docs/dev/hacking.rst: -------------------------------------------------------------------------------- 1 | Hacking 2 | ======= 3 | 4 | Synchronisation with libcrush 5 | ----------------------------- 6 | 7 | The `crush/libcrush` directory tracks master from 8 | http://github.com/ceph/ceph and should be updated to get the latest 9 | changes with:: 10 | 11 | git clone http://github.com/ceph/ceph /tmp/ceph 12 | git revert 8f07936 # commit with change to make crush standalone 13 | cd crush/libcrush 14 | for file in {common,crush,include}/*.[ch] {common,crush}/*.cc ; do 15 | cp /tmp/ceph/src/$file $file 16 | done 17 | git commit -m 'libcrush: sync with Ceph' 18 | git cherry-pick 8f07936 19 | # update the hash above with the new hash 20 | git commit -m 'doc: reference to the last sync with Ceph' 21 | 22 | The commit message should then be updated with the hash of the 23 | ceph repository from which the files were copied. 24 | 25 | Part of the sources it contains are compiled in the crush.libcrush 26 | module, as instructed in the **[extension-crush.libcrush]** section of 27 | the `setup.cfg` file: 28 | 29 | .. literalinclude:: ../../setup.cfg 30 | :start-after: [extension=crush.libcrush] 31 | :end-before: [build_ext] 32 | :language: ini 33 | 34 | The acconfig.h file is created by `cmake` via the build hooks 35 | mentionned in the `[build_ext]` section of the `setup.cfg` file: 36 | 37 | .. literalinclude:: ../../setup.cfg 38 | :start-after: [build_ext] 39 | :language: ini 40 | 41 | The definition of the functions are in the `setup/_setup_hooks.py` file: 42 | 43 | .. literalinclude:: ../../setup/_setup_hooks.py 44 | :language: python 45 | 46 | Debuging 47 | -------- 48 | 49 | The `libcrush` CPython module can be debugged by running tests from 50 | `test/test_libcrush.py` in verbose mode with commands such as:: 51 | 52 | tox -e py27 -- -vv -s -k test_parse_invalid_type tests/test_libcrush.py 53 | 54 | For some reason py3 does not flush stdout and should be avoided. It is quite useful 55 | to see the output up to the point where a core dump happens. 56 | 57 | Increasing libcrush verbosity 58 | ----------------------------- 59 | 60 | The `cursh/libcrush` module files must be recompiled to display a 61 | verbose output when they show lines like:: 62 | 63 | #define dprintk(args...) /* printf(args) */ 64 | 65 | Testing installation on RPM OS 66 | ------------------------------ 67 | 68 | The instructions are duplicated in: 69 | 70 | - docs/index.rst 71 | - README.rst 72 | - boostrap 73 | 74 | Start by updating the `bootstrap` instructions and try it manually with:: 75 | 76 | cd ~/software/libcrush/libcrush 77 | sudo docker run -ti -v $(pwd):$(pwd) -w $(pwd) opensuse:42.2 bash 78 | zypper install -y which 79 | source bootstrap 80 | 81 | Debugging readthedocs 82 | --------------------- 83 | 84 | - https://readthedocs.org/dashboard/crush/versions/ 85 | - click "Active" for the desired branch from http://libcrush.org/main/python-crush/ 86 | - https://readthedocs.org/projects/crush/builds/ 87 | - explicitly ask a build for the designated branch, does not happen automatically 88 | - in https://readthedocs.org/projects/crush/builds/\*/ check all lines from the bottom 89 | some may contain error messages that are do not fail the build but do nothing useful 90 | either 91 | -------------------------------------------------------------------------------- /crush/libcrush/common/mempool.cc: -------------------------------------------------------------------------------- 1 | // -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*- 2 | // vim: ts=8 sw=2 smarttab 3 | /* 4 | * Ceph - scalable distributed file system 5 | * 6 | * Copyright (C) 2016 Allen Samuels 7 | * 8 | * This is free software; you can redistribute it and/or 9 | * modify it under the terms of the GNU Lesser General Public 10 | * License version 2.1, as published by the Free Software 11 | * Foundation. See file COPYING. 12 | * 13 | */ 14 | 15 | #include "include/mempool.h" 16 | #include "include/demangle.h" 17 | 18 | 19 | // default to debug_mode off 20 | bool mempool::debug_mode = false; 21 | 22 | // -------------------------------------------------------------- 23 | 24 | mempool::pool_t& mempool::get_pool(mempool::pool_index_t ix) 25 | { 26 | // We rely on this array being initialized before any invocation of 27 | // this function, even if it is called by ctors in other compilation 28 | // units that are being initialized before this compilation unit. 29 | static mempool::pool_t table[num_pools]; 30 | return table[ix]; 31 | } 32 | 33 | const char *mempool::get_pool_name(mempool::pool_index_t ix) { 34 | #define P(x) #x, 35 | static const char *names[num_pools] = { 36 | DEFINE_MEMORY_POOLS_HELPER(P) 37 | }; 38 | #undef P 39 | return names[ix]; 40 | } 41 | 42 | void mempool::dump(ceph::Formatter *f) 43 | { 44 | for (size_t i = 0; i < num_pools; ++i) { 45 | const pool_t &pool = mempool::get_pool((pool_index_t)i); 46 | f->open_object_section(get_pool_name((pool_index_t)i)); 47 | pool.dump(f); 48 | f->close_section(); 49 | } 50 | } 51 | 52 | void mempool::set_debug_mode(bool d) 53 | { 54 | debug_mode = d; 55 | } 56 | 57 | // -------------------------------------------------------------- 58 | // pool_t 59 | 60 | size_t mempool::pool_t::allocated_bytes() const 61 | { 62 | ssize_t result = 0; 63 | for (size_t i = 0; i < num_shards; ++i) { 64 | result += shard[i].bytes; 65 | } 66 | assert(result >= 0); 67 | return (size_t) result; 68 | } 69 | 70 | size_t mempool::pool_t::allocated_items() const 71 | { 72 | ssize_t result = 0; 73 | for (size_t i = 0; i < num_shards; ++i) { 74 | result += shard[i].items; 75 | } 76 | assert(result >= 0); 77 | return (size_t) result; 78 | } 79 | 80 | void mempool::pool_t::get_stats( 81 | stats_t *total, 82 | std::map *by_type) const 83 | { 84 | for (size_t i = 0; i < num_shards; ++i) { 85 | total->items += shard[i].items; 86 | total->bytes += shard[i].bytes; 87 | } 88 | if (debug_mode) { 89 | std::unique_lock shard_lock(lock); 90 | for (auto &p : type_map) { 91 | std::string n = ceph_demangle(p.second.type_name); 92 | stats_t &s = (*by_type)[n]; 93 | s.bytes = p.second.items * p.second.item_size; 94 | s.items = p.second.items; 95 | } 96 | } 97 | } 98 | 99 | void mempool::pool_t::dump(ceph::Formatter *f) const 100 | { 101 | stats_t total; 102 | std::map by_type; 103 | get_stats(&total, &by_type); 104 | f->dump_object("total", total); 105 | if (!by_type.empty()) { 106 | for (auto &i : by_type) { 107 | f->open_object_section(i.first.c_str()); 108 | i.second.dump(f); 109 | f->close_section(); 110 | } 111 | } 112 | } 113 | -------------------------------------------------------------------------------- /crush/libcrush/crush/crush.c: -------------------------------------------------------------------------------- 1 | #ifdef __KERNEL__ 2 | # include 3 | # include 4 | #else 5 | # include "crush_compat.h" 6 | # include "crush.h" 7 | #endif 8 | 9 | const char *crush_bucket_alg_name(int alg) 10 | { 11 | switch (alg) { 12 | case CRUSH_BUCKET_UNIFORM: return "uniform"; 13 | case CRUSH_BUCKET_LIST: return "list"; 14 | case CRUSH_BUCKET_TREE: return "tree"; 15 | case CRUSH_BUCKET_STRAW: return "straw"; 16 | case CRUSH_BUCKET_STRAW2: return "straw2"; 17 | default: return "unknown"; 18 | } 19 | } 20 | 21 | /** 22 | * crush_get_bucket_item_weight - Get weight of an item in given bucket 23 | * @b: bucket pointer 24 | * @p: item index in bucket 25 | */ 26 | int crush_get_bucket_item_weight(const struct crush_bucket *b, int p) 27 | { 28 | if ((__u32)p >= b->size) 29 | return 0; 30 | 31 | switch (b->alg) { 32 | case CRUSH_BUCKET_UNIFORM: 33 | return ((struct crush_bucket_uniform *)b)->item_weight; 34 | case CRUSH_BUCKET_LIST: 35 | return ((struct crush_bucket_list *)b)->item_weights[p]; 36 | case CRUSH_BUCKET_TREE: 37 | return ((struct crush_bucket_tree *)b)->node_weights[crush_calc_tree_node(p)]; 38 | case CRUSH_BUCKET_STRAW: 39 | return ((struct crush_bucket_straw *)b)->item_weights[p]; 40 | case CRUSH_BUCKET_STRAW2: 41 | return ((struct crush_bucket_straw2 *)b)->item_weights[p]; 42 | } 43 | return 0; 44 | } 45 | 46 | void crush_destroy_bucket_uniform(struct crush_bucket_uniform *b) 47 | { 48 | kfree(b->h.items); 49 | kfree(b); 50 | } 51 | 52 | void crush_destroy_bucket_list(struct crush_bucket_list *b) 53 | { 54 | kfree(b->item_weights); 55 | kfree(b->sum_weights); 56 | kfree(b->h.items); 57 | kfree(b); 58 | } 59 | 60 | void crush_destroy_bucket_tree(struct crush_bucket_tree *b) 61 | { 62 | kfree(b->h.items); 63 | kfree(b->node_weights); 64 | kfree(b); 65 | } 66 | 67 | void crush_destroy_bucket_straw(struct crush_bucket_straw *b) 68 | { 69 | kfree(b->straws); 70 | kfree(b->item_weights); 71 | kfree(b->h.items); 72 | kfree(b); 73 | } 74 | 75 | void crush_destroy_bucket_straw2(struct crush_bucket_straw2 *b) 76 | { 77 | kfree(b->item_weights); 78 | kfree(b->h.items); 79 | kfree(b); 80 | } 81 | 82 | void crush_destroy_bucket(struct crush_bucket *b) 83 | { 84 | switch (b->alg) { 85 | case CRUSH_BUCKET_UNIFORM: 86 | crush_destroy_bucket_uniform((struct crush_bucket_uniform *)b); 87 | break; 88 | case CRUSH_BUCKET_LIST: 89 | crush_destroy_bucket_list((struct crush_bucket_list *)b); 90 | break; 91 | case CRUSH_BUCKET_TREE: 92 | crush_destroy_bucket_tree((struct crush_bucket_tree *)b); 93 | break; 94 | case CRUSH_BUCKET_STRAW: 95 | crush_destroy_bucket_straw((struct crush_bucket_straw *)b); 96 | break; 97 | case CRUSH_BUCKET_STRAW2: 98 | crush_destroy_bucket_straw2((struct crush_bucket_straw2 *)b); 99 | break; 100 | } 101 | } 102 | 103 | /** 104 | * crush_destroy - Destroy a crush_map 105 | * @map: crush_map pointer 106 | */ 107 | void crush_destroy(struct crush_map *map) 108 | { 109 | /* buckets */ 110 | if (map->buckets) { 111 | __s32 b; 112 | for (b = 0; b < map->max_buckets; b++) { 113 | if (map->buckets[b] == NULL) 114 | continue; 115 | crush_destroy_bucket(map->buckets[b]); 116 | } 117 | kfree(map->buckets); 118 | } 119 | 120 | /* rules */ 121 | if (map->rules) { 122 | __u32 b; 123 | for (b = 0; b < map->max_rules; b++) 124 | crush_destroy_rule(map->rules[b]); 125 | kfree(map->rules); 126 | } 127 | 128 | #ifndef __KERNEL__ 129 | kfree(map->choose_tries); 130 | #endif 131 | kfree(map); 132 | } 133 | 134 | void crush_destroy_rule(struct crush_rule *rule) 135 | { 136 | kfree(rule); 137 | } 138 | -------------------------------------------------------------------------------- /crush/libcrush/crush/mapper.h: -------------------------------------------------------------------------------- 1 | #ifndef CEPH_CRUSH_MAPPER_H 2 | #define CEPH_CRUSH_MAPPER_H 3 | 4 | /* 5 | * CRUSH functions for find rules and then mapping an input to an 6 | * output set. 7 | * 8 | * LGPL2 9 | */ 10 | 11 | #include "crush.h" 12 | 13 | extern int crush_find_rule(const struct crush_map *map, int ruleset, int type, int size); 14 | /** @ingroup API 15 | * 16 | * Map __x__ to __result_max__ items and store them in the __result__ 17 | * array. The mapping is done by following each step of the rule 18 | * __ruleno__. See crush_make_rule(), crush_rule_set_step() and 19 | * crush_add_rule() for more information on how the rules are created, 20 | * populated and added to the crush __map__. 21 | * 22 | * The return value is the the number of items in the __result__ 23 | * array. If the caller asked for __result_max__ items and the return 24 | * value is X where X < __result_max__, the content of __result[0,X[__ 25 | * is defined but the content of __result[X,result_max[__ is 26 | * undefined. For example: 27 | * 28 | * crush_do_rule(map, ruleno=1, x=1, result, result_max=3,...) == 1 29 | * result[0] is set 30 | * result[1] is undefined 31 | * result[2] is undefined 32 | * 33 | * An entry in the __result__ array is either an item in the crush 34 | * __map__ or ::CRUSH_ITEM_NONE if no item was found. For example: 35 | * 36 | * crush_do_rule(map, ruleno=1, x=1, result, result_max=4,...) == 2 37 | * result[0] is CRUSH_ITEM_NONE 38 | * result[1] is item number 5 39 | * result[2] is undefined 40 | * result[3] is undefined 41 | * 42 | * The __weight__ array contains the probabilities that a leaf is 43 | * ignored even if it is selected. It is a 16.16 fixed point 44 | * number in the range [0x00000,0x10000]. The lower the value, the 45 | * more often the leaf is ignored. For instance: 46 | * 47 | * - weight[leaf] == 0x00000 == 0.0 always ignore 48 | * - weight[leaf] == 0x10000 == 1.0 never ignore 49 | * - weight[leaf] == 0x08000 == 0.5 ignore 50% of the time 50 | * - weight[leaf] == 0x04000 == 0.25 ignore 75% of the time 51 | * - etc. 52 | * 53 | * During mapping, each leaf is checked against the __weight__ array, 54 | * using the leaf as an index. If there is no entry in __weight__ for 55 | * the leaf, it is ignored. If there is an entry, the leaf will be 56 | * ignored some of the time, depending on the probability. 57 | * 58 | * The __cwin__ argument must be set as follows: 59 | * 60 | * char __cwin__[crush_work_size(__map__, __result_max__)]; 61 | * crush_init_workspace(__map__, __cwin__); 62 | * 63 | * @param map the crush_map 64 | * @param ruleno a positive integer < __CRUSH_MAX_RULES__ 65 | * @param x the value to map to __result_max__ items 66 | * @param result an array of items of size __result_max__ 67 | * @param result_max the size of the __result__ array 68 | * @param weights an array of weights of size __weight_max__ 69 | * @param weight_max the size of the __weights__ array 70 | * @param cwin must be an char array initialized by crush_init_workspace 71 | * @param choose_args weights and ids for each known bucket 72 | * 73 | * @return 0 on error or the size of __result__ on success 74 | */ 75 | extern int crush_do_rule(const struct crush_map *map, 76 | int ruleno, 77 | int x, int *result, int result_max, 78 | const __u32 *weights, int weight_max, 79 | void *cwin, const struct crush_choose_arg *choose_args); 80 | 81 | /* Returns the exact amount of workspace that will need to be used 82 | for a given combination of crush_map and result_max. The caller can 83 | then allocate this much on its own, either on the stack, in a 84 | per-thread long-lived buffer, or however it likes. */ 85 | 86 | static inline size_t crush_work_size(const struct crush_map *map, 87 | int result_max) { 88 | return map->working_size + result_max * 3 * sizeof(__u32); 89 | } 90 | 91 | extern void crush_init_workspace(const struct crush_map *m, void *v); 92 | 93 | #endif 94 | -------------------------------------------------------------------------------- /crush/libcrush/crush/hash.c: -------------------------------------------------------------------------------- 1 | #ifdef __KERNEL__ 2 | # include 3 | #else 4 | # include "hash.h" 5 | #endif 6 | 7 | /* 8 | * Robert Jenkins' function for mixing 32-bit values 9 | * http://burtleburtle.net/bob/hash/evahash.html 10 | * a, b = random bits, c = input and output 11 | */ 12 | #define crush_hashmix(a, b, c) do { \ 13 | a = a-b; a = a-c; a = a^(c>>13); \ 14 | b = b-c; b = b-a; b = b^(a<<8); \ 15 | c = c-a; c = c-b; c = c^(b>>13); \ 16 | a = a-b; a = a-c; a = a^(c>>12); \ 17 | b = b-c; b = b-a; b = b^(a<<16); \ 18 | c = c-a; c = c-b; c = c^(b>>5); \ 19 | a = a-b; a = a-c; a = a^(c>>3); \ 20 | b = b-c; b = b-a; b = b^(a<<10); \ 21 | c = c-a; c = c-b; c = c^(b>>15); \ 22 | } while (0) 23 | 24 | #define crush_hash_seed 1315423911 25 | 26 | static __u32 crush_hash32_rjenkins1(__u32 a) 27 | { 28 | __u32 hash = crush_hash_seed ^ a; 29 | __u32 b = a; 30 | __u32 x = 231232; 31 | __u32 y = 1232; 32 | crush_hashmix(b, x, hash); 33 | crush_hashmix(y, a, hash); 34 | return hash; 35 | } 36 | 37 | static __u32 crush_hash32_rjenkins1_2(__u32 a, __u32 b) 38 | { 39 | __u32 hash = crush_hash_seed ^ a ^ b; 40 | __u32 x = 231232; 41 | __u32 y = 1232; 42 | crush_hashmix(a, b, hash); 43 | crush_hashmix(x, a, hash); 44 | crush_hashmix(b, y, hash); 45 | return hash; 46 | } 47 | 48 | static __u32 crush_hash32_rjenkins1_3(__u32 a, __u32 b, __u32 c) 49 | { 50 | __u32 hash = crush_hash_seed ^ a ^ b ^ c; 51 | __u32 x = 231232; 52 | __u32 y = 1232; 53 | crush_hashmix(a, b, hash); 54 | crush_hashmix(c, x, hash); 55 | crush_hashmix(y, a, hash); 56 | crush_hashmix(b, x, hash); 57 | crush_hashmix(y, c, hash); 58 | return hash; 59 | } 60 | 61 | static __u32 crush_hash32_rjenkins1_4(__u32 a, __u32 b, __u32 c, __u32 d) 62 | { 63 | __u32 hash = crush_hash_seed ^ a ^ b ^ c ^ d; 64 | __u32 x = 231232; 65 | __u32 y = 1232; 66 | crush_hashmix(a, b, hash); 67 | crush_hashmix(c, d, hash); 68 | crush_hashmix(a, x, hash); 69 | crush_hashmix(y, b, hash); 70 | crush_hashmix(c, x, hash); 71 | crush_hashmix(y, d, hash); 72 | return hash; 73 | } 74 | 75 | static __u32 crush_hash32_rjenkins1_5(__u32 a, __u32 b, __u32 c, __u32 d, 76 | __u32 e) 77 | { 78 | __u32 hash = crush_hash_seed ^ a ^ b ^ c ^ d ^ e; 79 | __u32 x = 231232; 80 | __u32 y = 1232; 81 | crush_hashmix(a, b, hash); 82 | crush_hashmix(c, d, hash); 83 | crush_hashmix(e, x, hash); 84 | crush_hashmix(y, a, hash); 85 | crush_hashmix(b, x, hash); 86 | crush_hashmix(y, c, hash); 87 | crush_hashmix(d, x, hash); 88 | crush_hashmix(y, e, hash); 89 | return hash; 90 | } 91 | 92 | 93 | __u32 crush_hash32(int type, __u32 a) 94 | { 95 | switch (type) { 96 | case CRUSH_HASH_RJENKINS1: 97 | return crush_hash32_rjenkins1(a); 98 | default: 99 | return 0; 100 | } 101 | } 102 | 103 | __u32 crush_hash32_2(int type, __u32 a, __u32 b) 104 | { 105 | switch (type) { 106 | case CRUSH_HASH_RJENKINS1: 107 | return crush_hash32_rjenkins1_2(a, b); 108 | default: 109 | return 0; 110 | } 111 | } 112 | 113 | __u32 crush_hash32_3(int type, __u32 a, __u32 b, __u32 c) 114 | { 115 | switch (type) { 116 | case CRUSH_HASH_RJENKINS1: 117 | return crush_hash32_rjenkins1_3(a, b, c); 118 | default: 119 | return 0; 120 | } 121 | } 122 | 123 | __u32 crush_hash32_4(int type, __u32 a, __u32 b, __u32 c, __u32 d) 124 | { 125 | switch (type) { 126 | case CRUSH_HASH_RJENKINS1: 127 | return crush_hash32_rjenkins1_4(a, b, c, d); 128 | default: 129 | return 0; 130 | } 131 | } 132 | 133 | __u32 crush_hash32_5(int type, __u32 a, __u32 b, __u32 c, __u32 d, __u32 e) 134 | { 135 | switch (type) { 136 | case CRUSH_HASH_RJENKINS1: 137 | return crush_hash32_rjenkins1_5(a, b, c, d, e); 138 | default: 139 | return 0; 140 | } 141 | } 142 | 143 | const char *crush_hash_name(int type) 144 | { 145 | switch (type) { 146 | case CRUSH_HASH_RJENKINS1: 147 | return "rjenkins1"; 148 | default: 149 | return "unknown"; 150 | } 151 | } 152 | -------------------------------------------------------------------------------- /crush/libcrush/include/inline_memory.h: -------------------------------------------------------------------------------- 1 | // -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*- 2 | // vim: ts=8 sw=2 smarttab 3 | /* 4 | * Ceph - scalable distributed file system 5 | * 6 | * Copyright (C) 2004-2006 Sage Weil 7 | * 8 | * This is free software; you can redistribute it and/or 9 | * modify it under the terms of the GNU Lesser General Public 10 | * License version 2.1, as published by the Free Software 11 | * Foundation. See file COPYING. 12 | * 13 | */ 14 | #ifndef CEPH_INLINE_MEMORY_H 15 | #define CEPH_INLINE_MEMORY_H 16 | 17 | #if defined(__GNUC__) 18 | 19 | // optimize for the common case, which is very small copies 20 | static inline void *maybe_inline_memcpy(void *dest, const void *src, size_t l, 21 | size_t inline_len) 22 | __attribute__((always_inline)); 23 | 24 | void *maybe_inline_memcpy(void *dest, const void *src, size_t l, 25 | size_t inline_len) 26 | { 27 | if (l > inline_len) { 28 | return memcpy(dest, src, l); 29 | } 30 | switch (l) { 31 | case 8: 32 | return __builtin_memcpy(dest, src, 8); 33 | case 4: 34 | return __builtin_memcpy(dest, src, 4); 35 | case 3: 36 | return __builtin_memcpy(dest, src, 3); 37 | case 2: 38 | return __builtin_memcpy(dest, src, 2); 39 | case 1: 40 | return __builtin_memcpy(dest, src, 1); 41 | default: 42 | int cursor = 0; 43 | while (l >= sizeof(uint64_t)) { 44 | __builtin_memcpy((char*)dest + cursor, (char*)src + cursor, 45 | sizeof(uint64_t)); 46 | cursor += sizeof(uint64_t); 47 | l -= sizeof(uint64_t); 48 | } 49 | while (l >= sizeof(uint32_t)) { 50 | __builtin_memcpy((char*)dest + cursor, (char*)src + cursor, 51 | sizeof(uint32_t)); 52 | cursor += sizeof(uint32_t); 53 | l -= sizeof(uint32_t); 54 | } 55 | while (l > 0) { 56 | *((char*)dest + cursor) = *((char*)src + cursor); 57 | cursor++; 58 | l--; 59 | } 60 | } 61 | return dest; 62 | } 63 | 64 | #else 65 | 66 | #define maybe_inline_memcpy(d, s, l, x) memcpy(d, s, l) 67 | 68 | #endif 69 | 70 | 71 | #if defined(__GNUC__) && defined(__x86_64__) 72 | 73 | typedef unsigned uint128_t __attribute__ ((mode (TI))); 74 | 75 | static inline bool mem_is_zero(const char *data, size_t len) 76 | __attribute__((always_inline)); 77 | 78 | bool mem_is_zero(const char *data, size_t len) 79 | { 80 | // we do have XMM registers in x86-64, so if we need to check at least 81 | // 16 bytes, make use of them 82 | if (len / sizeof(uint128_t) > 0) { 83 | // align data pointer to 16 bytes, otherwise it'll segfault due to bug 84 | // in (at least some) GCC versions (using MOVAPS instead of MOVUPS). 85 | // check up to 15 first bytes while at it. 86 | while (((unsigned long long)data) & 15) { 87 | if (*(uint8_t*)data != 0) { 88 | return false; 89 | } 90 | data += sizeof(uint8_t); 91 | --len; 92 | } 93 | 94 | const char* data_start = data; 95 | const char* max128 = data + (len / sizeof(uint128_t))*sizeof(uint128_t); 96 | 97 | while (data < max128) { 98 | if (*(uint128_t*)data != 0) { 99 | return false; 100 | } 101 | data += sizeof(uint128_t); 102 | } 103 | len -= (data - data_start); 104 | } 105 | 106 | const char* max = data + len; 107 | const char* max32 = data + (len / sizeof(uint32_t))*sizeof(uint32_t); 108 | while (data < max32) { 109 | if (*(uint32_t*)data != 0) { 110 | return false; 111 | } 112 | data += sizeof(uint32_t); 113 | } 114 | while (data < max) { 115 | if (*(uint8_t*)data != 0) { 116 | return false; 117 | } 118 | data += sizeof(uint8_t); 119 | } 120 | return true; 121 | } 122 | 123 | #else // gcc and x86_64 124 | 125 | static inline bool mem_is_zero(const char *data, size_t len) { 126 | const char *end = data + len; 127 | while (data < end) { 128 | if (*data != 0) { 129 | return false; 130 | } 131 | ++data; 132 | } 133 | return true; 134 | } 135 | 136 | #endif // !x86_64 137 | 138 | #endif 139 | -------------------------------------------------------------------------------- /tests/test_ceph_convert.py: -------------------------------------------------------------------------------- 1 | # -*- mode: python; coding: utf-8 -*- 2 | # 3 | # Copyright (C) 2017 4 | # 5 | # Author: Loic Dachary 6 | # 7 | # This program is free software: you can redistribute it and/or modify 8 | # it under the terms of the GNU General Public License as published by 9 | # the Free Software Foundation, either version 3 of the License, or 10 | # (at your option) any later version. 11 | # 12 | # This program is distributed in the hope that it will be useful, 13 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | # GNU General Public License for more details. 16 | # 17 | # You should have received a copy of the GNU General Public License 18 | # along with this program. If not, see . 19 | # 20 | 21 | from crush.ceph import CephCrush, Ceph 22 | import pytest 23 | 24 | 25 | class TestConvert(object): 26 | 27 | def test_sanity_check_args(self): 28 | a = Ceph().constructor([ 29 | 'convert', 30 | ]) 31 | with pytest.raises(Exception) as e: 32 | a.pre_sanity_check_args() 33 | assert 'missing --in-path' in str(e.value) 34 | 35 | a = Ceph().constructor([ 36 | 'convert', 37 | '--in-path', 'IN', 38 | ]) 39 | with pytest.raises(Exception) as e: 40 | a.post_sanity_check_args() 41 | assert 'missing --out-path' in str(e.value) 42 | 43 | a = Ceph().constructor([ 44 | 'convert', 45 | '--in-path', 'IN', 46 | '--out-path', 'OUT', 47 | ]) 48 | a.pre_sanity_check_args() 49 | a.post_sanity_check_args() 50 | 51 | def define_crushmap(self, host_count): 52 | crushmap = { 53 | "trees": [ 54 | { 55 | "type": "root", 56 | "id": -1, 57 | "name": "dc1", 58 | "children": [], 59 | } 60 | ], 61 | "rules": { 62 | "firstn": [ 63 | ["take", "dc1"], 64 | ["chooseleaf", "firstn", 0, "type", "host"], 65 | ["emit"] 66 | ], 67 | "indep": [ 68 | ["take", "dc1"], 69 | ["chooseleaf", "indep", 0, "type", "host"], 70 | ["emit"] 71 | ], 72 | } 73 | } 74 | crushmap['trees'][0]['children'].extend([ 75 | { 76 | "type": "host", 77 | "id": -(i + 2), 78 | "name": "host%d" % i, 79 | "weight": 3, 80 | "children": [ 81 | {"id": (2 * i), "name": "device%02d" % (2 * i), "weight": 1}, 82 | {"id": (2 * i + 1), "name": "device%02d" % (2 * i + 1), "weight": 2}, 83 | ], 84 | } for i in range(0, host_count) 85 | ]) 86 | return crushmap 87 | 88 | def test_ceph_version_compat(self): 89 | crushmap = self.define_crushmap(2) 90 | first_weight = 123 91 | second_weight = 456 92 | crushmap['choose_args'] = { 93 | 0: [ 94 | { 95 | 'bucket_id': -1, 96 | 'weight_set': [ 97 | [first_weight, second_weight], 98 | ] 99 | } 100 | ] 101 | } 102 | c = CephCrush() 103 | c.parse(crushmap) 104 | c.ceph_version_compat() 105 | compat = c.get_crushmap() 106 | assert 'choose_args' not in compat 107 | assert compat['trees'][1]['name'] == 'dc1-target-weight' 108 | bucket = compat['trees'][0] 109 | assert bucket['children'][0]['weight'] == first_weight 110 | assert bucket['children'][1]['weight'] == second_weight 111 | 112 | # Local Variables: 113 | # compile-command: "cd .. ; tox -e py27 -- -vv -s tests/test_ceph_convert.py" 114 | # End: 115 | -------------------------------------------------------------------------------- /crush/libcrush/common/HTMLFormatter.cc: -------------------------------------------------------------------------------- 1 | // -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*- 2 | // vim: ts=8 sw=2 smarttab 3 | /* 4 | * Ceph - scalable distributed file system 5 | * 6 | * Copyright (C) 2011 New Dream Network 7 | * 8 | * This is free software; you can redistribute it and/or 9 | * modify it under the terms of the GNU Lesser General Public 10 | * License version 2.1, as published by the Free Software 11 | * Foundation. See file COPYING. 12 | * 13 | */ 14 | 15 | #define LARGE_SIZE 1024 16 | 17 | #include "Formatter.h" 18 | #include "HTMLFormatter.h" 19 | 20 | #include 21 | #include 22 | #include 23 | #include 24 | #include 25 | #include // for strdup 26 | 27 | // ----------------------- 28 | namespace ceph { 29 | 30 | HTMLFormatter::HTMLFormatter(bool pretty) 31 | : XMLFormatter(pretty), m_status(0), m_status_name(NULL) 32 | { 33 | } 34 | 35 | HTMLFormatter::~HTMLFormatter() 36 | { 37 | if (m_status_name) { 38 | free((void*)m_status_name); 39 | m_status_name = NULL; 40 | } 41 | } 42 | 43 | void HTMLFormatter::reset() 44 | { 45 | XMLFormatter::reset(); 46 | m_header_done = false; 47 | m_status = 0; 48 | if (m_status_name) { 49 | free((void*)m_status_name); 50 | m_status_name = NULL; 51 | } 52 | } 53 | 54 | void HTMLFormatter::set_status(int status, const char* status_name) 55 | { 56 | m_status = status; 57 | if (status_name) { 58 | m_status_name = strdup(status_name); 59 | } 60 | }; 61 | 62 | void HTMLFormatter::output_header() { 63 | if (!m_header_done) { 64 | m_header_done = true; 65 | char buf[16]; 66 | snprintf(buf, sizeof(buf), "%d", m_status); 67 | std::string status_line(buf); 68 | if (m_status_name) { 69 | status_line += " "; 70 | status_line += m_status_name; 71 | } 72 | open_object_section("html"); 73 | print_spaces(); 74 | m_ss << "" << status_line << ""; 75 | if (m_pretty) 76 | m_ss << "\n"; 77 | open_object_section("body"); 78 | print_spaces(); 79 | m_ss << "

" << status_line << "

"; 80 | if (m_pretty) 81 | m_ss << "\n"; 82 | open_object_section("ul"); 83 | } 84 | } 85 | 86 | template 87 | void HTMLFormatter::dump_template(const char *name, T arg) 88 | { 89 | print_spaces(); 90 | m_ss << "
  • " << name << ": " << arg << "
  • "; 91 | if (m_pretty) 92 | m_ss << "\n"; 93 | } 94 | 95 | void HTMLFormatter::dump_unsigned(const char *name, uint64_t u) 96 | { 97 | dump_template(name, u); 98 | } 99 | 100 | void HTMLFormatter::dump_int(const char *name, int64_t u) 101 | { 102 | dump_template(name, u); 103 | } 104 | 105 | void HTMLFormatter::dump_float(const char *name, double d) 106 | { 107 | dump_template(name, d); 108 | } 109 | 110 | void HTMLFormatter::dump_string(const char *name, const std::string& s) 111 | { 112 | dump_template(name, escape_xml_str(s.c_str())); 113 | } 114 | 115 | void HTMLFormatter::dump_string_with_attrs(const char *name, const std::string& s, const FormatterAttrs& attrs) 116 | { 117 | std::string e(name); 118 | std::string attrs_str; 119 | get_attrs_str(&attrs, attrs_str); 120 | print_spaces(); 121 | m_ss << "
  • " << e << ": " << escape_xml_str(s.c_str()) << attrs_str << "
  • "; 122 | if (m_pretty) 123 | m_ss << "\n"; 124 | } 125 | 126 | std::ostream& HTMLFormatter::dump_stream(const char *name) 127 | { 128 | print_spaces(); 129 | m_pending_string_name = "li"; 130 | m_ss << "
  • " << name << ": "; 131 | return m_pending_string; 132 | } 133 | 134 | void HTMLFormatter::dump_format_va(const char* name, const char *ns, bool quoted, const char *fmt, va_list ap) 135 | { 136 | char buf[LARGE_SIZE]; 137 | vsnprintf(buf, LARGE_SIZE, fmt, ap); 138 | 139 | std::string e(name); 140 | print_spaces(); 141 | if (ns) { 142 | m_ss << "
  • " << e << ": " << escape_xml_str(buf) << "
  • "; 143 | } else { 144 | m_ss << "
  • " << e << ": " << escape_xml_str(buf) << "
  • "; 145 | } 146 | 147 | if (m_pretty) 148 | m_ss << "\n"; 149 | } 150 | 151 | } // namespace ceph 152 | -------------------------------------------------------------------------------- /crush/ceph/convert.py: -------------------------------------------------------------------------------- 1 | # -*- mode: python; coding: utf-8 -*- 2 | # 3 | # Copyright (C) 2017 4 | # 5 | # Author: Loic Dachary 6 | # 7 | # This program is free software: you can redistribute it and/or modify 8 | # it under the terms of the GNU General Public License as published by 9 | # the Free Software Foundation, either version 3 of the License, or 10 | # (at your option) any later version. 11 | # 12 | # This program is distributed in the hope that it will be useful, 13 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | # GNU General Public License for more details. 16 | # 17 | # You should have received a copy of the GNU General Public License 18 | # along with this program. If not, see . 19 | # 20 | from __future__ import division 21 | 22 | import argparse 23 | import logging 24 | import textwrap 25 | 26 | 27 | log = logging.getLogger(__name__) 28 | 29 | 30 | class Convert(object): 31 | 32 | def __init__(self, args, main): 33 | self.args = args 34 | self.main = main 35 | 36 | @staticmethod 37 | def get_parser(): 38 | parser = argparse.ArgumentParser( 39 | add_help=False, 40 | conflict_handler='resolve', 41 | ) 42 | parser.add_argument( 43 | '--out-path', 44 | help='path of the output file') 45 | parser.add_argument( 46 | '--pool', 47 | help='pool', 48 | type=int) 49 | return parser 50 | 51 | @staticmethod 52 | def set_parser(subparsers, arguments): 53 | parser = Convert.get_parser() 54 | arguments(parser) 55 | subparsers.add_parser( 56 | 'convert', 57 | formatter_class=argparse.RawDescriptionHelpFormatter, 58 | description=textwrap.dedent("""\ 59 | The Ceph crushmap can be stored in three formats: 60 | 61 | - JSON, the output of *ceph osd crush dump* or *ceph report* 62 | 63 | - txt, the output of *crushtool -d* 64 | 65 | - binary, the output of *crushtool -c* or *ceph getcrushmap* 66 | 67 | The JSON used is different from the python-crush format documented at 68 | http://crush.readthedocs.io/en/latest/api.html#crush.Crush.parse. 69 | 70 | It supports any of the existing Ceph formats and is 71 | compatible with Luminous and below. It converts the 72 | crushmap into the python-crush format and display the 73 | result on the standard output. 74 | 75 | """), 76 | epilog=textwrap.dedent(""" 77 | Examples: 78 | 79 | Convert a Ceph JSON crushmap into a python-crush crushmap: 80 | - crush convert --in-path crushmap-ceph.json --out-path crushmap.json 81 | 82 | Convert a Ceph text crushmap into a python-crush crushmap: 83 | - crush convert --in-path crushmap.txt --out-path crushmap.json 84 | 85 | Convert a binary crushmap to python-crush crushmap: 86 | - crush convert --in-path crushmap.bin --out-path crushmap.json 87 | 88 | Convert a python-crush crushmap to Ceph text crushmap 89 | - crush convert --in-path crushmap.json \\ 90 | --out-path crushmap.json --out-format txt 91 | """), 92 | help='Convert crushmaps', 93 | parents=[parser], 94 | ).set_defaults( 95 | func=Convert, 96 | ) 97 | 98 | def pre_sanity_check_args(self): 99 | self.main.hook_convert_pre_sanity_check_args(self.args) 100 | 101 | def post_sanity_check_args(self): 102 | self.main.hook_convert_post_sanity_check_args(self.args) 103 | if not self.args.out_path: 104 | raise Exception("missing --out-path") 105 | 106 | def run(self): 107 | self.pre_sanity_check_args() 108 | crushmap = self.main.convert_to_crushmap(self.args.in_path) 109 | self.post_sanity_check_args() 110 | self.main.crushmap_to_file(crushmap) 111 | -------------------------------------------------------------------------------- /crush/libcrush/include/atomic.h: -------------------------------------------------------------------------------- 1 | // -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*- 2 | // vim: ts=8 sw=2 smarttab 3 | /* 4 | * Ceph - scalable distributed file system 5 | * 6 | * Copyright (C) 2004-2011 New Dream Network 7 | * 8 | * This is free software; you can redistribute it and/or 9 | * modify it under the terms of the GNU Lesser General Public 10 | * License version 2.1, as published by the Free Software 11 | * Foundation. See file COPYING. 12 | * 13 | * @author Sage Weil 14 | */ 15 | 16 | #ifndef CEPH_ATOMIC_H 17 | #define CEPH_ATOMIC_H 18 | 19 | #ifdef __CEPH__ 20 | # include "acconfig.h" 21 | #endif 22 | 23 | #include 24 | #include "include/Spinlock.h" 25 | 26 | namespace ceph { 27 | template 28 | class atomic_spinlock_t { 29 | mutable ceph_spinlock_t lock; 30 | T val; 31 | public: 32 | atomic_spinlock_t(T i=0) 33 | : val(i) { 34 | ceph_spin_init(&lock); 35 | } 36 | ~atomic_spinlock_t() { 37 | ceph_spin_destroy(&lock); 38 | } 39 | void set(T v) { 40 | ceph_spin_lock(&lock); 41 | val = v; 42 | ceph_spin_unlock(&lock); 43 | } 44 | T inc() { 45 | ceph_spin_lock(&lock); 46 | T r = ++val; 47 | ceph_spin_unlock(&lock); 48 | return r; 49 | } 50 | T dec() { 51 | ceph_spin_lock(&lock); 52 | T r = --val; 53 | ceph_spin_unlock(&lock); 54 | return r; 55 | } 56 | void add(T d) { 57 | ceph_spin_lock(&lock); 58 | val += d; 59 | ceph_spin_unlock(&lock); 60 | } 61 | void sub(T d) { 62 | ceph_spin_lock(&lock); 63 | val -= d; 64 | ceph_spin_unlock(&lock); 65 | } 66 | T read() const { 67 | T ret; 68 | ceph_spin_lock(&lock); 69 | ret = val; 70 | ceph_spin_unlock(&lock); 71 | return ret; 72 | } 73 | bool compare_and_swap(T o, T n) { 74 | bool success = false; 75 | ceph_spin_lock(&lock); 76 | if (val == o) { 77 | success = true; 78 | val = n; 79 | } 80 | ceph_spin_unlock(&lock); 81 | return success; 82 | } 83 | 84 | private: 85 | // forbid copying 86 | atomic_spinlock_t(const atomic_spinlock_t &other); 87 | atomic_spinlock_t &operator=(const atomic_spinlock_t &rhs); 88 | }; 89 | } 90 | 91 | #ifndef NO_ATOMIC_OPS 92 | 93 | // libatomic_ops implementation 94 | #define AO_REQUIRE_CAS 95 | #include 96 | 97 | // reinclude our assert to clobber the system one 98 | #include "include/assert.h" 99 | 100 | namespace ceph { 101 | class atomic_t { 102 | AO_t val; 103 | public: 104 | atomic_t(AO_t i=0) : val(i) {} 105 | void set(AO_t v) { 106 | AO_store(&val, v); 107 | } 108 | AO_t inc() { 109 | return AO_fetch_and_add1(&val) + 1; 110 | } 111 | AO_t dec() { 112 | return AO_fetch_and_sub1_write(&val) - 1; 113 | } 114 | AO_t add(AO_t add_me) { 115 | return AO_fetch_and_add(&val, add_me) + add_me; 116 | } 117 | AO_t sub(AO_t sub_me) { 118 | AO_t negsub = 0 - sub_me; 119 | return AO_fetch_and_add_write(&val, negsub) + negsub; 120 | } 121 | AO_t read() const { 122 | // cast away const on the pointer. this is only needed to build 123 | // on lenny, but not newer debians, so the atomic_ops.h got fixed 124 | // at some point. this hack can go away someday... 125 | return AO_load_full((AO_t *)&val); 126 | } 127 | bool compare_and_swap(AO_t o, AO_t n) { 128 | return AO_compare_and_swap(&val, o, n); 129 | } 130 | 131 | private: 132 | // forbid copying 133 | atomic_t(const atomic_t &other); 134 | atomic_t &operator=(const atomic_t &rhs); 135 | }; 136 | 137 | #if SIZEOF_AO_T == 8 138 | typedef atomic_t atomic64_t; 139 | #else 140 | typedef atomic_spinlock_t atomic64_t; 141 | #endif 142 | 143 | } 144 | 145 | #else 146 | /* 147 | * crappy slow implementation that uses a pthreads spinlock. 148 | */ 149 | #include "include/Spinlock.h" 150 | 151 | namespace ceph { 152 | typedef atomic_spinlock_t atomic_t; 153 | typedef atomic_spinlock_t atomic64_t; 154 | } 155 | 156 | #endif 157 | #endif 158 | -------------------------------------------------------------------------------- /docs/_themes/flask_theme_support.py: -------------------------------------------------------------------------------- 1 | # flasky extensions. flasky pygments style based on tango style 2 | from pygments.style import Style 3 | from pygments.token import Keyword, Name, Comment, String, Error, \ 4 | Number, Operator, Generic, Whitespace, Punctuation, Other, Literal 5 | 6 | 7 | class FlaskyStyle(Style): 8 | background_color = "#f8f8f8" 9 | default_style = "" 10 | 11 | styles = { 12 | # No corresponding class for the following: 13 | #Text: "", # class: '' 14 | Whitespace: "underline #f8f8f8", # class: 'w' 15 | Error: "#a40000 border:#ef2929", # class: 'err' 16 | Other: "#000000", # class 'x' 17 | 18 | Comment: "italic #8f5902", # class: 'c' 19 | Comment.Preproc: "noitalic", # class: 'cp' 20 | 21 | Keyword: "bold #004461", # class: 'k' 22 | Keyword.Constant: "bold #004461", # class: 'kc' 23 | Keyword.Declaration: "bold #004461", # class: 'kd' 24 | Keyword.Namespace: "bold #004461", # class: 'kn' 25 | Keyword.Pseudo: "bold #004461", # class: 'kp' 26 | Keyword.Reserved: "bold #004461", # class: 'kr' 27 | Keyword.Type: "bold #004461", # class: 'kt' 28 | 29 | Operator: "#582800", # class: 'o' 30 | Operator.Word: "bold #004461", # class: 'ow' - like keywords 31 | 32 | Punctuation: "bold #000000", # class: 'p' 33 | 34 | # because special names such as Name.Class, Name.Function, etc. 35 | # are not recognized as such later in the parsing, we choose them 36 | # to look the same as ordinary variables. 37 | Name: "#000000", # class: 'n' 38 | Name.Attribute: "#c4a000", # class: 'na' - to be revised 39 | Name.Builtin: "#004461", # class: 'nb' 40 | Name.Builtin.Pseudo: "#3465a4", # class: 'bp' 41 | Name.Class: "#000000", # class: 'nc' - to be revised 42 | Name.Constant: "#000000", # class: 'no' - to be revised 43 | Name.Decorator: "#888", # class: 'nd' - to be revised 44 | Name.Entity: "#ce5c00", # class: 'ni' 45 | Name.Exception: "bold #cc0000", # class: 'ne' 46 | Name.Function: "#000000", # class: 'nf' 47 | Name.Property: "#000000", # class: 'py' 48 | Name.Label: "#f57900", # class: 'nl' 49 | Name.Namespace: "#000000", # class: 'nn' - to be revised 50 | Name.Other: "#000000", # class: 'nx' 51 | Name.Tag: "bold #004461", # class: 'nt' - like a keyword 52 | Name.Variable: "#000000", # class: 'nv' - to be revised 53 | Name.Variable.Class: "#000000", # class: 'vc' - to be revised 54 | Name.Variable.Global: "#000000", # class: 'vg' - to be revised 55 | Name.Variable.Instance: "#000000", # class: 'vi' - to be revised 56 | 57 | Number: "#990000", # class: 'm' 58 | 59 | Literal: "#000000", # class: 'l' 60 | Literal.Date: "#000000", # class: 'ld' 61 | 62 | String: "#4e9a06", # class: 's' 63 | String.Backtick: "#4e9a06", # class: 'sb' 64 | String.Char: "#4e9a06", # class: 'sc' 65 | String.Doc: "italic #8f5902", # class: 'sd' - like a comment 66 | String.Double: "#4e9a06", # class: 's2' 67 | String.Escape: "#4e9a06", # class: 'se' 68 | String.Heredoc: "#4e9a06", # class: 'sh' 69 | String.Interpol: "#4e9a06", # class: 'si' 70 | String.Other: "#4e9a06", # class: 'sx' 71 | String.Regex: "#4e9a06", # class: 'sr' 72 | String.Single: "#4e9a06", # class: 's1' 73 | String.Symbol: "#4e9a06", # class: 'ss' 74 | 75 | Generic: "#000000", # class: 'g' 76 | Generic.Deleted: "#a40000", # class: 'gd' 77 | Generic.Emph: "italic #000000", # class: 'ge' 78 | Generic.Error: "#ef2929", # class: 'gr' 79 | Generic.Heading: "bold #000080", # class: 'gh' 80 | Generic.Inserted: "#00A000", # class: 'gi' 81 | Generic.Output: "#888", # class: 'go' 82 | Generic.Prompt: "#745334", # class: 'gp' 83 | Generic.Strong: "bold #000000", # class: 'gs' 84 | Generic.Subheading: "bold #800080", # class: 'gu' 85 | Generic.Traceback: "bold #a40000", # class: 'gt' 86 | } 87 | -------------------------------------------------------------------------------- /tests/weights-crushmap.json: -------------------------------------------------------------------------------- 1 | { 2 | "rules": { 3 | "erasure_ruleset": [ 4 | [ 5 | "take", 6 | "default" 7 | ], 8 | [ 9 | "choose", 10 | "indep", 11 | 0, 12 | "type", 13 | 0 14 | ], 15 | [ 16 | "emit" 17 | ] 18 | ], 19 | "replicated_ruleset": [ 20 | [ 21 | "take", 22 | "default" 23 | ], 24 | [ 25 | "set_choose_tries", 26 | 1000 27 | ], 28 | [ 29 | "choose", 30 | "firstn", 31 | 0, 32 | "type", 33 | 0 34 | ], 35 | [ 36 | "emit" 37 | ] 38 | ], 39 | "replicated_ruleset_class": [ 40 | [ 41 | "take", 42 | "default~ssd" 43 | ], 44 | [ 45 | "choose", 46 | "firstn", 47 | 0, 48 | "type", 49 | 0 50 | ], 51 | [ 52 | "emit" 53 | ] 54 | ] 55 | }, 56 | "trees": [ 57 | { 58 | "algorithm": "straw", 59 | "children": [ 60 | { 61 | "algorithm": "straw", 62 | "children": [ 63 | { 64 | "id": 0, 65 | "name": "osd.0", 66 | "weight": 1 67 | }, 68 | { 69 | "id": 1, 70 | "name": "osd.1", 71 | "weight": 1 72 | }, 73 | { 74 | "id": 2, 75 | "name": "osd.2", 76 | "weight": 1 77 | }, 78 | { 79 | "id": 3, 80 | "name": "osd.3", 81 | "weight": 1 82 | }, 83 | { 84 | "id": 4, 85 | "name": "osd.4", 86 | "weight": 1 87 | } 88 | ], 89 | "id": -2, 90 | "name": "fold", 91 | "type": "host", 92 | "weight": 3 93 | } 94 | ], 95 | "id": -1, 96 | "name": "default", 97 | "type": "root", 98 | "weight": 3 99 | }, 100 | { 101 | "algorithm": "straw", 102 | "children": [ 103 | { 104 | "algorithm": "straw", 105 | "children": [ 106 | { 107 | "id": 0, 108 | "name": "osd.0", 109 | "weight": 1 110 | } 111 | ], 112 | "id": -3, 113 | "name": "fold~ssd", 114 | "type": "host", 115 | "weight": 1 116 | } 117 | ], 118 | "id": -4, 119 | "name": "default~ssd", 120 | "type": "root", 121 | "weight": 1 122 | } 123 | ], 124 | "tunables": { 125 | "choose_local_fallback_tries": 0, 126 | "choose_local_tries": 0, 127 | "choose_total_tries": 50, 128 | "chooseleaf_descend_once": 1, 129 | "chooseleaf_stable": 0, 130 | "chooseleaf_vary_r": 1, 131 | "straw_calc_version": 1 132 | }, 133 | "types": [ 134 | { 135 | "name": "osd", 136 | "type_id": 0 137 | }, 138 | { 139 | "name": "host", 140 | "type_id": 1 141 | }, 142 | { 143 | "name": "chassis", 144 | "type_id": 2 145 | }, 146 | { 147 | "name": "rack", 148 | "type_id": 3 149 | }, 150 | { 151 | "name": "row", 152 | "type_id": 4 153 | }, 154 | { 155 | "name": "pdu", 156 | "type_id": 5 157 | }, 158 | { 159 | "name": "pod", 160 | "type_id": 6 161 | }, 162 | { 163 | "name": "room", 164 | "type_id": 7 165 | }, 166 | { 167 | "name": "datacenter", 168 | "type_id": 8 169 | }, 170 | { 171 | "name": "region", 172 | "type_id": 9 173 | }, 174 | { 175 | "name": "root", 176 | "type_id": 10 177 | } 178 | ] 179 | } 180 | -------------------------------------------------------------------------------- /crush/libcrush/common/escape.c: -------------------------------------------------------------------------------- 1 | // -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*- 2 | // vim: ts=8 sw=2 smarttab 3 | /* 4 | * Ceph - scalable distributed file system 5 | * 6 | * Copyright (C) 2011 New Dream Network 7 | * 8 | * This is free software; you can redistribute it and/or 9 | * modify it under the terms of the GNU Lesser General Public 10 | * License version 2.1, as published by the Free Software 11 | * Foundation. See file COPYING. 12 | * 13 | */ 14 | 15 | #include "common/escape.h" 16 | 17 | #include 18 | #include 19 | 20 | /* 21 | * Some functions for escaping RGW responses 22 | */ 23 | 24 | /* Static string length */ 25 | #define SSTRL(x) ((sizeof(x)/sizeof(x[0])) - 1) 26 | 27 | #define LESS_THAN_XESCAPE "<" 28 | #define AMPERSAND_XESCAPE "&" 29 | #define GREATER_THAN_XESCAPE ">" 30 | #define SGL_QUOTE_XESCAPE "'" 31 | #define DBL_QUOTE_XESCAPE """ 32 | 33 | int escape_xml_attr_len(const char *buf) 34 | { 35 | const char *b; 36 | int ret = 0; 37 | for (b = buf; *b; ++b) { 38 | unsigned char c = *b; 39 | switch (c) { 40 | case '<': 41 | ret += SSTRL(LESS_THAN_XESCAPE); 42 | break; 43 | case '&': 44 | ret += SSTRL(AMPERSAND_XESCAPE); 45 | break; 46 | case '>': 47 | ret += SSTRL(GREATER_THAN_XESCAPE); 48 | break; 49 | case '\'': 50 | ret += SSTRL(SGL_QUOTE_XESCAPE); 51 | break; 52 | case '"': 53 | ret += SSTRL(DBL_QUOTE_XESCAPE); 54 | break; 55 | default: 56 | // Escape control characters. 57 | if (((c < 0x20) && (c != 0x09) && (c != 0x0a)) || 58 | (c == 0x7f)) { 59 | ret += 6; 60 | } 61 | else { 62 | ret++; 63 | } 64 | } 65 | } 66 | // leave room for null terminator 67 | ret++; 68 | return ret; 69 | } 70 | 71 | void escape_xml_attr(const char *buf, char *out) 72 | { 73 | char *o = out; 74 | const char *b; 75 | for (b = buf; *b; ++b) { 76 | unsigned char c = *b; 77 | switch (c) { 78 | case '<': 79 | memcpy(o, LESS_THAN_XESCAPE, SSTRL(LESS_THAN_XESCAPE)); 80 | o += SSTRL(LESS_THAN_XESCAPE); 81 | break; 82 | case '&': 83 | memcpy(o, AMPERSAND_XESCAPE, SSTRL(AMPERSAND_XESCAPE)); 84 | o += SSTRL(AMPERSAND_XESCAPE); 85 | break; 86 | case '>': 87 | memcpy(o, GREATER_THAN_XESCAPE, SSTRL(GREATER_THAN_XESCAPE)); 88 | o += SSTRL(GREATER_THAN_XESCAPE); 89 | break; 90 | case '\'': 91 | memcpy(o, SGL_QUOTE_XESCAPE, SSTRL(SGL_QUOTE_XESCAPE)); 92 | o += SSTRL(SGL_QUOTE_XESCAPE); 93 | break; 94 | case '"': 95 | memcpy(o, DBL_QUOTE_XESCAPE, SSTRL(DBL_QUOTE_XESCAPE)); 96 | o += SSTRL(DBL_QUOTE_XESCAPE); 97 | break; 98 | default: 99 | // Escape control characters. 100 | if (((c < 0x20) && (c != 0x09) && (c != 0x0a)) || 101 | (c == 0x7f)) { 102 | snprintf(o, 7, "&#x%02x;", c); 103 | o += 6; 104 | } 105 | else { 106 | *o++ = c; 107 | } 108 | break; 109 | } 110 | } 111 | // null terminator 112 | *o = '\0'; 113 | } 114 | 115 | #define DBL_QUOTE_JESCAPE "\\\"" 116 | #define BACKSLASH_JESCAPE "\\\\" 117 | #define TAB_JESCAPE "\\t" 118 | #define NEWLINE_JESCAPE "\\n" 119 | 120 | int escape_json_attr_len(const char *buf, int src_len) 121 | { 122 | const char *b; 123 | int ret = 0; 124 | int i; 125 | for (i = 0, b = buf; i < src_len; ++i, ++b) { 126 | unsigned char c = *b; 127 | switch (c) { 128 | case '"': 129 | ret += SSTRL(DBL_QUOTE_JESCAPE); 130 | break; 131 | case '\\': 132 | ret += SSTRL(BACKSLASH_JESCAPE); 133 | break; 134 | case '\t': 135 | ret += SSTRL(TAB_JESCAPE); 136 | break; 137 | case '\n': 138 | ret += SSTRL(NEWLINE_JESCAPE); 139 | break; 140 | default: 141 | // Escape control characters. 142 | if ((c < 0x20) || (c == 0x7f)) { 143 | ret += 6; 144 | } 145 | else { 146 | ret++; 147 | } 148 | } 149 | } 150 | // leave room for null terminator 151 | ret++; 152 | return ret; 153 | } 154 | 155 | void escape_json_attr(const char *buf, int src_len, char *out) 156 | { 157 | char *o = out; 158 | const char *b; 159 | int i; 160 | for (i = 0, b = buf; i < src_len; ++i, ++b) { 161 | unsigned char c = *b; 162 | switch (c) { 163 | case '"': 164 | // cppcheck-suppress invalidFunctionArg 165 | memcpy(o, DBL_QUOTE_JESCAPE, SSTRL(DBL_QUOTE_JESCAPE)); 166 | o += SSTRL(DBL_QUOTE_JESCAPE); 167 | break; 168 | case '\\': 169 | // cppcheck-suppress invalidFunctionArg 170 | memcpy(o, BACKSLASH_JESCAPE, SSTRL(BACKSLASH_JESCAPE)); 171 | o += SSTRL(BACKSLASH_JESCAPE); 172 | break; 173 | case '\t': 174 | // cppcheck-suppress invalidFunctionArg 175 | memcpy(o, TAB_JESCAPE, SSTRL(TAB_JESCAPE)); 176 | o += SSTRL(TAB_JESCAPE); 177 | break; 178 | case '\n': 179 | // cppcheck-suppress invalidFunctionArg 180 | memcpy(o, NEWLINE_JESCAPE, SSTRL(NEWLINE_JESCAPE)); 181 | o += SSTRL(NEWLINE_JESCAPE); 182 | break; 183 | default: 184 | // Escape control characters. 185 | if ((c < 0x20) || (c == 0x7f)) { 186 | snprintf(o, 7, "\\u%04x", c); 187 | o += 6; 188 | } 189 | else { 190 | *o++ = c; 191 | } 192 | break; 193 | } 194 | } 195 | // null terminator 196 | *o = '\0'; 197 | } 198 | 199 | -------------------------------------------------------------------------------- /docs/index.rst: -------------------------------------------------------------------------------- 1 | crush 2 | ===== 3 | 4 | `python-crush `_ is a `GPLv3+ 5 | Licensed `_ library to control placement in a hierarchy. 6 | 7 | Introduction 8 | ------------ 9 | 10 | The `crush` module wraps the `libcrush 11 | `_ C library 12 | implementing `CRUSH 13 | `_, a scalable 14 | pseudo-random data distribution function designed for distributed 15 | object storage systems that efficiently maps data objects to storage 16 | devices without relying on a central directory. 17 | 18 | GNU/Linux Installation 19 | ---------------------- 20 | 21 | * pip install crush 22 | 23 | Other Installation 24 | ------------------ 25 | 26 | When using pip versions lower than 8.1 or other operating systems, 27 | compilation is necessary and packages must be installed first. 28 | 29 | * apt-get install -y gcc g++ python-pip python-all-dev libpython3-all-dev cmake libboost-all-dev libatomic-ops-dev 30 | * dnf / yum / zypper install -y gcc gcc-c++ python-pip python-devel python3-devel make cmake boost-devel libatomic_ops-devel 31 | * pip install crush 32 | 33 | API quick start 34 | --------------- 35 | 36 | Mapping the object 1234 to two devices in different hosts: 37 | 38 | .. literalinclude:: quick.py 39 | :language: python 40 | 41 | Output:: 42 | 43 | [u'device1'] 44 | [u'device1', u'device5'] 45 | 46 | API 47 | --- 48 | 49 | .. toctree:: 50 | :maxdepth: 1 51 | 52 | api 53 | 54 | CLI quick start 55 | --------------- 56 | 57 | How many objects will move after adding two new devices in the 58 | crushmap ? 59 | :: 60 | 61 | $ crush compare --rule firstn \ 62 | --replication-count 1 \ 63 | --origin before.json --destination after.json 64 | There are 1000 objects. 65 | 66 | Replacing the crushmap specified with --origin with the crushmap 67 | specified with --destination will move 229 objects (22.9% of the total) 68 | from one item to another. 69 | 70 | The rows below show the number of objects moved from the given 71 | item to each item named in the columns. The objects% at the end of 72 | the rows shows the percentage of the total number of objects that 73 | is moved away from this particular item. The last row shows the 74 | percentage of the total number of objects that is moved to the 75 | item named in the column. 76 | 77 | osd.8 osd.9 objects% 78 | osd.0 3 4 0.70% 79 | osd.1 1 3 0.40% 80 | osd.2 16 16 3.20% 81 | osd.3 19 21 4.00% 82 | osd.4 17 18 3.50% 83 | osd.5 18 23 4.10% 84 | osd.6 14 23 3.70% 85 | osd.7 14 19 3.30% 86 | objects% 10.20% 12.70% 22.90% 87 | 88 | Given a Ceph crushmap, show which hosts will be overfilled or underfilled:: 89 | 90 | $ ceph osd crush dump > crushmap-ceph.json 91 | $ crush analyze --rule replicated --crushmap crushmap-ceph.json 92 | 93 | ~id~ ~weight~ ~objects~ ~over/under filled %~ 94 | ~name~ 95 | host2 -4 1.0 70 5.0 96 | host0 -2 1.0 65 -2.5 97 | host1 -3 1.0 65 -2.5 98 | 99 | Rebalance a Ceph pool:: 100 | 101 | $ ceph report > report.json 102 | $ crush optimize --crushmap report.json --out-path optimized.crush --pool 3 103 | default optimizing 104 | default wants to swap 10 objects 105 | default will swap 10 objects 106 | cloud3-1359 optimizing 107 | cloud3-1360 optimizing 108 | ... 109 | $ ceph osd setcrushmap -i optimized.crush 110 | 111 | CLI 112 | --- 113 | 114 | The `crush` command has a set of subcommands to manipulate and analyze 115 | crushmaps. Each subcommand is fully documented with `crush subcommand -h`:: 116 | 117 | $ crush --help 118 | usage: crush [-h] [-v] [--debug] [--no-backward-compatibility] {analyze,compare,optimize,convert} ... 119 | 120 | Ceph crush compare and analyze 121 | 122 | optional arguments: 123 | -h, --help show this help message and exit 124 | -v, --verbose be more verbose 125 | --debug debugging output, very verbose 126 | --no-backward-compatibility 127 | do not allow backward compatibility tunables (default: allowed) 128 | 129 | subcommands: 130 | valid subcommands 131 | 132 | {analyze,compare,optimize,convert} 133 | sub-command -h 134 | analyze Analyze crushmaps 135 | compare Compare crushmaps 136 | optimize Optimize crushmaps 137 | convert Convert crushmaps 138 | 139 | Cookbook 140 | -------- 141 | 142 | .. toctree:: 143 | :maxdepth: 1 144 | 145 | ceph/optimize 146 | 147 | 148 | Contributor Guide 149 | ----------------- 150 | 151 | If you want to contribute to ``crush``, this part of the documentation is for 152 | you. 153 | 154 | .. toctree:: 155 | :maxdepth: 1 156 | 157 | dev/hacking 158 | dev/authors 159 | -------------------------------------------------------------------------------- /crush/libcrush/include/compat.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Ceph - scalable distributed file system 3 | * 4 | * Copyright (C) 2011 Stanislav Sedov 5 | * 6 | * This is free software; you can redistribute it and/or 7 | * modify it under the terms of the GNU Lesser General Public 8 | * License version 2.1, as published by the Free Software 9 | * Foundation. See file COPYING. 10 | */ 11 | 12 | #ifndef CEPH_COMPAT_H 13 | #define CEPH_COMPAT_H 14 | 15 | #include "acconfig.h" 16 | 17 | #if defined(__linux__) 18 | #define PROCPREFIX 19 | #endif 20 | 21 | #if defined(__FreeBSD__) 22 | 23 | // FreeBSD supports Linux procfs with its compatibility module 24 | // And all compatibility stuff is standard mounted on this 25 | #define PROCPREFIX "/compat/linux" 26 | 27 | /* Make sure that ENODATA is defined in the correct way */ 28 | #ifndef ENODATA 29 | #define ENODATA ENOATTR 30 | #else 31 | #if (ENODATA == 9919) 32 | // #warning ENODATA already defined to be 9919, redefining to fix 33 | // Silencing this warning because it fires at all files where compat.h 34 | // is included after boost files. 35 | // 36 | // This value stems from the definition in the boost library 37 | // And when this case occurs it is due to the fact that boost files 38 | // are included before this file. Redefinition might not help in this 39 | // case since already parsed code has evaluated to the wrong value. 40 | // This would warrrant for d definition that would actually be evaluated 41 | // at the location of usage and report a possible confict. 42 | // This is left up to a future improvement 43 | #elif (ENODATA != 87) 44 | #warning ENODATA already defined to a value different from 87 (ENOATRR), refining to fix 45 | #endif 46 | #undef ENODATA 47 | #define ENODATA ENOATTR 48 | #endif 49 | #ifndef MSG_MORE 50 | #define MSG_MORE 0 51 | #endif 52 | 53 | #ifndef O_DSYNC 54 | #define O_DSYNC O_SYNC 55 | #endif 56 | 57 | // Fix clock accuracy 58 | #if !defined(CLOCK_MONOTONIC_COARSE) 59 | #if defined(CLOCK_MONOTONIC_FAST) 60 | #define CLOCK_MONOTONIC_COARSE CLOCK_MONOTONIC_FAST 61 | #else 62 | #define CLOCK_MONOTONIC_COARSE CLOCK_MONOTONIC 63 | #endif 64 | #endif 65 | #if !defined(CLOCK_REALTIME_COARSE) 66 | #if defined(CLOCK_REALTIME_FAST) 67 | #define CLOCK_REALTIME_COARSE CLOCK_REALTIME_FAST 68 | #else 69 | #define CLOCK_REALTIME_COARSE CLOCK_REALTIME 70 | #endif 71 | #endif 72 | 73 | /* And include the extra required include file */ 74 | #include 75 | 76 | #endif /* !__FreeBSD__ */ 77 | 78 | #if defined(__APPLE__) || defined(__FreeBSD__) 79 | /* get PATH_MAX */ 80 | #include 81 | 82 | #ifndef EREMOTEIO 83 | #define EREMOTEIO 121 84 | #endif 85 | 86 | #ifndef HOST_NAME_MAX 87 | #ifdef MAXHOSTNAMELEN 88 | #define HOST_NAME_MAX MAXHOSTNAMELEN 89 | #else 90 | #define HOST_NAME_MAX 255 91 | #endif 92 | #endif 93 | 94 | #endif /* __APPLE__ */ 95 | 96 | /* O_LARGEFILE is not defined/required on OSX/FreeBSD */ 97 | #ifndef O_LARGEFILE 98 | #define O_LARGEFILE 0 99 | #endif 100 | 101 | /* Could be relevant for other platforms */ 102 | #ifndef ERESTART 103 | #define ERESTART EINTR 104 | #endif 105 | 106 | #ifndef TEMP_FAILURE_RETRY 107 | #define TEMP_FAILURE_RETRY(expression) ({ \ 108 | __typeof(expression) __result; \ 109 | do { \ 110 | __result = (expression); \ 111 | } while (__result == -1 && errno == EINTR); \ 112 | __result; }) 113 | #endif 114 | 115 | #ifdef __cplusplus 116 | # define VOID_TEMP_FAILURE_RETRY(expression) \ 117 | static_cast(TEMP_FAILURE_RETRY(expression)) 118 | #else 119 | # define VOID_TEMP_FAILURE_RETRY(expression) \ 120 | do { (void)TEMP_FAILURE_RETRY(expression); } while (0) 121 | #endif 122 | 123 | #if defined(__FreeBSD__) || defined(__APPLE__) 124 | #define lseek64(fd, offset, whence) lseek(fd, offset, whence) 125 | #endif 126 | 127 | #if defined(__sun) || defined(_AIX) 128 | #define LOG_AUTHPRIV (10<<3) 129 | #define LOG_FTP (11<<3) 130 | #define __STRING(x) "x" 131 | #define IFTODT(mode) (((mode) & 0170000) >> 12) 132 | #endif 133 | 134 | #if defined(_AIX) 135 | #define MSG_DONTWAIT MSG_NONBLOCK 136 | #endif 137 | 138 | #if defined(HAVE_PTHREAD_SETNAME_NP) 139 | #if defined(__APPLE__) 140 | #define ceph_pthread_setname(thread, name) ({ \ 141 | int __result = 0; \ 142 | if (thread == pthread_self()) \ 143 | __result = pthread_setname_np(name); \ 144 | __result; }) 145 | #else 146 | #define ceph_pthread_setname pthread_setname_np 147 | #endif 148 | #elif defined(HAVE_PTHREAD_SET_NAME_NP) 149 | /* Fix a small name diff */ 150 | #define ceph_pthread_setname pthread_set_name_np 151 | #else 152 | /* compiler warning free success noop */ 153 | #define ceph_pthread_setname(thread, name) ({ \ 154 | int __i = 0; \ 155 | __i; }) 156 | #endif 157 | 158 | #if defined(HAVE_PTHREAD_GETNAME_NP) 159 | #define ceph_pthread_getname pthread_getname_np 160 | #else 161 | /* compiler warning free success noop */ 162 | #define ceph_pthread_getname(thread, name, len) ({ \ 163 | if (name != NULL) \ 164 | *name = '\0'; \ 165 | 0; }) 166 | #endif 167 | 168 | #endif /* !CEPH_COMPAT_H */ 169 | -------------------------------------------------------------------------------- /crush/libcrush/crush/CrushTreeDumper.h: -------------------------------------------------------------------------------- 1 | // -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*- 2 | // vim: ts=8 sw=2 smarttab 3 | /* 4 | * Ceph distributed storage system 5 | * 6 | * Copyright (C) 2015 Mirantis Inc 7 | * 8 | * Author: Mykola Golub 9 | * 10 | * This library is free software; you can redistribute it and/or 11 | * modify it under the terms of the GNU Lesser General Public 12 | * License as published by the Free Software Foundation; either 13 | * version 2.1 of the License, or (at your option) any later version. 14 | * 15 | */ 16 | 17 | #ifndef CRUSH_TREE_DUMPER_H 18 | #define CRUSH_TREE_DUMPER_H 19 | 20 | #include "CrushWrapper.h" 21 | 22 | /** 23 | * CrushTreeDumper: 24 | * A helper class and functions to dump a crush tree. 25 | * 26 | * Example: 27 | * 28 | * class SimpleDumper : public CrushTreeDumper::Dumper { 29 | * public: 30 | * SimpleDumper(const CrushWrapper *crush) : 31 | * CrushTreeDumper::Dumper(crush) {} 32 | * protected: 33 | * virtual void dump_item(const CrushTreeDumper::Item &qi, ostream *out) { 34 | * *out << qi.id; 35 | * for (int k = 0; k < qi.depth; k++) 36 | * *out << "-"; 37 | * if (qi.is_bucket()) 38 | * *out << crush->get_item_name(qi.id) 39 | * else 40 | * *out << "osd." << qi.id; 41 | * *out << "\n"; 42 | * } 43 | * }; 44 | * 45 | * SimpleDumper(crush).dump(out); 46 | * 47 | */ 48 | 49 | namespace CrushTreeDumper { 50 | 51 | struct Item { 52 | int id; 53 | int depth; 54 | float weight; 55 | list children; 56 | 57 | Item() : id(0), depth(0), weight(0) {} 58 | Item(int i, int d, float w) : id(i), depth(d), weight(w) {} 59 | 60 | bool is_bucket() const { return id < 0; } 61 | }; 62 | 63 | template 64 | class Dumper : public list { 65 | public: 66 | explicit Dumper(const CrushWrapper *crush_) : crush(crush_) { 67 | crush->find_roots(roots); 68 | root = roots.begin(); 69 | } 70 | 71 | virtual ~Dumper() {} 72 | 73 | virtual void reset() { 74 | root = roots.begin(); 75 | touched.clear(); 76 | clear(); 77 | } 78 | 79 | bool next(Item &qi) { 80 | if (empty()) { 81 | if (root == roots.end()) 82 | return false; 83 | push_back(Item(*root, 0, crush->get_bucket_weightf(*root))); 84 | ++root; 85 | } 86 | 87 | qi = front(); 88 | pop_front(); 89 | touched.insert(qi.id); 90 | 91 | if (qi.is_bucket()) { 92 | // queue bucket contents... 93 | int s = crush->get_bucket_size(qi.id); 94 | for (int k = s - 1; k >= 0; k--) { 95 | int id = crush->get_bucket_item(qi.id, k); 96 | qi.children.push_back(id); 97 | push_front(Item(id, qi.depth + 1, 98 | crush->get_bucket_item_weightf(qi.id, k))); 99 | } 100 | } 101 | return true; 102 | } 103 | 104 | void dump(F *f) { 105 | reset(); 106 | Item qi; 107 | while (next(qi)) 108 | dump_item(qi, f); 109 | } 110 | 111 | bool is_touched(int id) const { return touched.count(id) > 0; } 112 | 113 | protected: 114 | virtual void dump_item(const Item &qi, F *f) = 0; 115 | 116 | protected: 117 | const CrushWrapper *crush; 118 | 119 | private: 120 | set touched; 121 | set roots; 122 | set::iterator root; 123 | }; 124 | 125 | inline void dump_item_fields(const CrushWrapper *crush, 126 | const Item &qi, ceph::Formatter *f) { 127 | f->dump_int("id", qi.id); 128 | if (qi.is_bucket()) { 129 | int type = crush->get_bucket_type(qi.id); 130 | f->dump_string("name", crush->get_item_name(qi.id)); 131 | f->dump_string("type", crush->get_type_name(type)); 132 | f->dump_int("type_id", type); 133 | } else { 134 | f->dump_stream("name") << "osd." << qi.id; 135 | f->dump_string("type", crush->get_type_name(0)); 136 | f->dump_int("type_id", 0); 137 | f->dump_float("crush_weight", qi.weight); 138 | f->dump_unsigned("depth", qi.depth); 139 | } 140 | } 141 | 142 | inline void dump_bucket_children(const CrushWrapper *crush, 143 | const Item &qi, ceph::Formatter *f) { 144 | if (!qi.is_bucket()) 145 | return; 146 | 147 | f->open_array_section("children"); 148 | for (list::const_iterator i = qi.children.begin(); 149 | i != qi.children.end(); 150 | ++i) { 151 | f->dump_int("child", *i); 152 | } 153 | f->close_section(); 154 | } 155 | 156 | class FormattingDumper : public Dumper { 157 | public: 158 | explicit FormattingDumper(const CrushWrapper *crush) : Dumper(crush) {} 159 | 160 | protected: 161 | void dump_item(const Item &qi, ceph::Formatter *f) override { 162 | f->open_object_section("item"); 163 | dump_item_fields(qi, f); 164 | dump_bucket_children(qi, f); 165 | f->close_section(); 166 | } 167 | 168 | virtual void dump_item_fields(const Item &qi, ceph::Formatter *f) { 169 | CrushTreeDumper::dump_item_fields(crush, qi, f); 170 | } 171 | 172 | virtual void dump_bucket_children(const Item &qi, ceph::Formatter *f) { 173 | CrushTreeDumper::dump_bucket_children(crush, qi, f); 174 | } 175 | }; 176 | 177 | } 178 | 179 | #endif 180 | -------------------------------------------------------------------------------- /crush/libcrush/include/intarith.h: -------------------------------------------------------------------------------- 1 | // -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*- 2 | // vim: ts=8 sw=2 smarttab 3 | /* 4 | * Ceph - scalable distributed file system 5 | * 6 | * Copyright (C) 2004-2006 Sage Weil 7 | * 8 | * This is free software; you can redistribute it and/or 9 | * modify it under the terms of the GNU Lesser General Public 10 | * License version 2.1, as published by the Free Software 11 | * Foundation. See file COPYING. 12 | * 13 | */ 14 | 15 | #ifndef CEPH_INTARITH_H 16 | #define CEPH_INTARITH_H 17 | 18 | #ifndef MIN 19 | #define MIN(a,b) ((a) < (b) ? (a):(b)) 20 | #endif 21 | 22 | #ifndef MAX 23 | #define MAX(a,b) ((a) > (b) ? (a):(b)) 24 | #endif 25 | 26 | #ifndef DIV_ROUND_UP 27 | #define DIV_ROUND_UP(n, d) (((n) + (d) - 1) / (d)) 28 | #endif 29 | 30 | #ifndef ROUND_UP_TO 31 | #define ROUND_UP_TO(n, d) ((n)%(d) ? ((n)+(d)-(n)%(d)) : (n)) 32 | #endif 33 | 34 | #ifndef SHIFT_ROUND_UP 35 | #define SHIFT_ROUND_UP(x,y) (((x)+(1<<(y))-1) >> (y)) 36 | #endif 37 | 38 | /* 39 | * Macro to determine if value is a power of 2 40 | */ 41 | #define ISP2(x) (((x) & ((x) - 1)) == 0) 42 | 43 | /* 44 | * Macros for various sorts of alignment and rounding. The "align" must 45 | * be a power of 2. Often times it is a block, sector, or page. 46 | */ 47 | 48 | /* 49 | * return x rounded down to an align boundary 50 | * eg, P2ALIGN(1200, 1024) == 1024 (1*align) 51 | * eg, P2ALIGN(1024, 1024) == 1024 (1*align) 52 | * eg, P2ALIGN(0x1234, 0x100) == 0x1200 (0x12*align) 53 | * eg, P2ALIGN(0x5600, 0x100) == 0x5600 (0x56*align) 54 | */ 55 | #define P2ALIGN(x, align) ((x) & -(align)) 56 | 57 | /* 58 | * return x % (mod) align 59 | * eg, P2PHASE(0x1234, 0x100) == 0x34 (x-0x12*align) 60 | * eg, P2PHASE(0x5600, 0x100) == 0x00 (x-0x56*align) 61 | */ 62 | #define P2PHASE(x, align) ((x) & ((align) - 1)) 63 | 64 | /* 65 | * return how much space is left in this block (but if it's perfectly 66 | * aligned, return 0). 67 | * eg, P2NPHASE(0x1234, 0x100) == 0xcc (0x13*align-x) 68 | * eg, P2NPHASE(0x5600, 0x100) == 0x00 (0x56*align-x) 69 | */ 70 | #define P2NPHASE(x, align) (-(x) & ((align) - 1)) 71 | 72 | /* 73 | * return x rounded up to an align boundary 74 | * eg, P2ROUNDUP(0x1234, 0x100) == 0x1300 (0x13*align) 75 | * eg, P2ROUNDUP(0x5600, 0x100) == 0x5600 (0x56*align) 76 | */ 77 | #define P2ROUNDUP(x, align) (-(-(x) & -(align))) 78 | 79 | #ifdef __cplusplus 80 | // count trailing zeros. 81 | // NOTE: the builtin is nondeterministic on 0 input 82 | template 83 | inline typename std::enable_if< 84 | (std::is_integral::value && 85 | sizeof(T) <= sizeof(unsigned)), 86 | unsigned>::type ctz(T v) { 87 | if (v == 0) 88 | return sizeof(v) * 8; 89 | return __builtin_ctz(v); 90 | } 91 | 92 | template 93 | inline typename std::enable_if< 94 | (std::is_integral::value && 95 | sizeof(T) > sizeof(unsigned int) && 96 | sizeof(T) <= sizeof(unsigned long)), 97 | unsigned>::type ctz(T v) { 98 | if (v == 0) 99 | return sizeof(v) * 8; 100 | return __builtin_ctzl(v); 101 | } 102 | 103 | template 104 | inline typename std::enable_if< 105 | (std::is_integral::value && 106 | sizeof(T) > sizeof(unsigned long) && 107 | sizeof(T) <= sizeof(unsigned long long)), 108 | unsigned>::type ctz(T v) { 109 | if (v == 0) 110 | return sizeof(v) * 8; 111 | return __builtin_ctzll(v); 112 | } 113 | 114 | // count leading zeros 115 | // NOTE: the builtin is nondeterministic on 0 input 116 | template 117 | inline typename std::enable_if< 118 | (std::is_integral::value && 119 | sizeof(T) <= sizeof(unsigned)), 120 | unsigned>::type clz(T v) { 121 | if (v == 0) 122 | return sizeof(v) * 8; 123 | return __builtin_clz(v); 124 | } 125 | 126 | template 127 | inline typename std::enable_if< 128 | (std::is_integral::value && 129 | sizeof(T) > sizeof(unsigned int) && 130 | sizeof(T) <= sizeof(unsigned long)), 131 | unsigned>::type clz(T v) { 132 | if (v == 0) 133 | return sizeof(v) * 8; 134 | return __builtin_clzl(v); 135 | } 136 | 137 | template 138 | inline typename std::enable_if< 139 | (std::is_integral::value && 140 | sizeof(T) > sizeof(unsigned long) && 141 | sizeof(T) <= sizeof(unsigned long long)), 142 | unsigned>::type clz(T v) { 143 | if (v == 0) 144 | return sizeof(v) * 8; 145 | return __builtin_clzll(v); 146 | } 147 | 148 | // count bits (set + any 0's that follow) 149 | template 150 | inline typename std::enable_if< 151 | (std::is_integral::value && 152 | sizeof(T) <= sizeof(unsigned)), 153 | unsigned>::type cbits(T v) { 154 | if (v == 0) 155 | return 0; 156 | return (sizeof(v) * 8) - __builtin_clz(v); 157 | } 158 | 159 | template 160 | inline typename std::enable_if< 161 | (std::is_integral::value && 162 | sizeof(T) > sizeof(unsigned int) && 163 | sizeof(T) <= sizeof(unsigned long)), 164 | unsigned>::type cbits(T v) { 165 | if (v == 0) 166 | return 0; 167 | return (sizeof(v) * 8) - __builtin_clzl(v); 168 | } 169 | 170 | template 171 | inline typename std::enable_if< 172 | (std::is_integral::value && 173 | sizeof(T) > sizeof(unsigned long) && 174 | sizeof(T) <= sizeof(unsigned long long)), 175 | unsigned>::type cbits(T v) { 176 | if (v == 0) 177 | return 0; 178 | return (sizeof(v) * 8) - __builtin_clzll(v); 179 | } 180 | #else // __cplusplus__ 181 | static inline unsigned cbits(unsigned v) { 182 | if (v == 0) 183 | return 0; 184 | return (sizeof(v) * 8) - __builtin_clz(v); 185 | } 186 | #endif // __cplusplus__ 187 | 188 | #endif 189 | -------------------------------------------------------------------------------- /crush/libcrush/common/safe_io.c: -------------------------------------------------------------------------------- 1 | // -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*- 2 | // vim: ts=8 sw=2 smarttab 3 | /* 4 | * Ceph - scalable distributed file system 5 | * 6 | * Copyright (C) 2011 New Dream Network 7 | * 8 | * This is free software; you can redistribute it and/or 9 | * modify it under the terms of the GNU Lesser General Public 10 | * License version 2.1, as published by the Free Software 11 | * Foundation. See file COPYING. 12 | * 13 | */ 14 | 15 | #include 16 | #include 17 | #include 18 | #include 19 | #include 20 | #include 21 | #include 22 | 23 | #include "common/safe_io.h" 24 | #include "include/compat.h" 25 | 26 | ssize_t safe_read(int fd, void *buf, size_t count) 27 | { 28 | size_t cnt = 0; 29 | 30 | while (cnt < count) { 31 | ssize_t r = read(fd, buf, count - cnt); 32 | if (r <= 0) { 33 | if (r == 0) { 34 | // EOF 35 | return cnt; 36 | } 37 | if (errno == EINTR) 38 | continue; 39 | return -errno; 40 | } 41 | cnt += r; 42 | buf = (char *)buf + r; 43 | } 44 | return cnt; 45 | } 46 | 47 | ssize_t safe_read_exact(int fd, void *buf, size_t count) 48 | { 49 | ssize_t ret = safe_read(fd, buf, count); 50 | if (ret < 0) 51 | return ret; 52 | if ((size_t)ret != count) 53 | return -EDOM; 54 | return 0; 55 | } 56 | 57 | ssize_t safe_write(int fd, const void *buf, size_t count) 58 | { 59 | while (count > 0) { 60 | ssize_t r = write(fd, buf, count); 61 | if (r < 0) { 62 | if (errno == EINTR) 63 | continue; 64 | return -errno; 65 | } 66 | count -= r; 67 | buf = (char *)buf + r; 68 | } 69 | return 0; 70 | } 71 | 72 | ssize_t safe_pread(int fd, void *buf, size_t count, off_t offset) 73 | { 74 | size_t cnt = 0; 75 | char *b = (char*)buf; 76 | 77 | while (cnt < count) { 78 | ssize_t r = pread(fd, b + cnt, count - cnt, offset + cnt); 79 | if (r <= 0) { 80 | if (r == 0) { 81 | // EOF 82 | return cnt; 83 | } 84 | if (errno == EINTR) 85 | continue; 86 | return -errno; 87 | } 88 | 89 | cnt += r; 90 | } 91 | return cnt; 92 | } 93 | 94 | ssize_t safe_pread_exact(int fd, void *buf, size_t count, off_t offset) 95 | { 96 | ssize_t ret = safe_pread(fd, buf, count, offset); 97 | if (ret < 0) 98 | return ret; 99 | if ((size_t)ret != count) 100 | return -EDOM; 101 | return 0; 102 | } 103 | 104 | ssize_t safe_pwrite(int fd, const void *buf, size_t count, off_t offset) 105 | { 106 | while (count > 0) { 107 | ssize_t r = pwrite(fd, buf, count, offset); 108 | if (r < 0) { 109 | if (errno == EINTR) 110 | continue; 111 | return -errno; 112 | } 113 | count -= r; 114 | buf = (char *)buf + r; 115 | offset += r; 116 | } 117 | return 0; 118 | } 119 | 120 | #ifdef CEPH_HAVE_SPLICE 121 | ssize_t safe_splice(int fd_in, off_t *off_in, int fd_out, off_t *off_out, 122 | size_t len, unsigned int flags) 123 | { 124 | size_t cnt = 0; 125 | 126 | while (cnt < len) { 127 | ssize_t r = splice(fd_in, off_in, fd_out, off_out, len - cnt, flags); 128 | if (r <= 0) { 129 | if (r == 0) { 130 | // EOF 131 | return cnt; 132 | } 133 | if (errno == EINTR) 134 | continue; 135 | if (errno == EAGAIN) 136 | break; 137 | return -errno; 138 | } 139 | cnt += r; 140 | } 141 | return cnt; 142 | } 143 | 144 | ssize_t safe_splice_exact(int fd_in, off_t *off_in, int fd_out, 145 | off_t *off_out, size_t len, unsigned int flags) 146 | { 147 | ssize_t ret = safe_splice(fd_in, off_in, fd_out, off_out, len, flags); 148 | if (ret < 0) 149 | return ret; 150 | if ((size_t)ret != len) 151 | return -EDOM; 152 | return 0; 153 | } 154 | #endif 155 | 156 | int safe_write_file(const char *base, const char *file, 157 | const char *val, size_t vallen) 158 | { 159 | int ret; 160 | char fn[PATH_MAX]; 161 | char tmp[PATH_MAX]; 162 | int fd; 163 | 164 | // does the file already have correct content? 165 | char oldval[80]; 166 | ret = safe_read_file(base, file, oldval, sizeof(oldval)); 167 | if (ret == (int)vallen && memcmp(oldval, val, vallen) == 0) 168 | return 0; // yes. 169 | 170 | snprintf(fn, sizeof(fn), "%s/%s", base, file); 171 | snprintf(tmp, sizeof(tmp), "%s/%s.tmp", base, file); 172 | fd = open(tmp, O_WRONLY|O_CREAT|O_TRUNC, 0644); 173 | if (fd < 0) { 174 | ret = errno; 175 | return -ret; 176 | } 177 | ret = safe_write(fd, val, vallen); 178 | if (ret) { 179 | VOID_TEMP_FAILURE_RETRY(close(fd)); 180 | return ret; 181 | } 182 | 183 | ret = fsync(fd); 184 | if (ret < 0) ret = -errno; 185 | VOID_TEMP_FAILURE_RETRY(close(fd)); 186 | if (ret < 0) { 187 | unlink(tmp); 188 | return ret; 189 | } 190 | ret = rename(tmp, fn); 191 | if (ret < 0) { 192 | ret = -errno; 193 | unlink(tmp); 194 | return ret; 195 | } 196 | 197 | fd = open(base, O_RDONLY); 198 | if (fd < 0) { 199 | ret = -errno; 200 | return ret; 201 | } 202 | ret = fsync(fd); 203 | if (ret < 0) ret = -errno; 204 | VOID_TEMP_FAILURE_RETRY(close(fd)); 205 | 206 | return ret; 207 | } 208 | 209 | int safe_read_file(const char *base, const char *file, 210 | char *val, size_t vallen) 211 | { 212 | char fn[PATH_MAX]; 213 | int fd, len; 214 | 215 | snprintf(fn, sizeof(fn), "%s/%s", base, file); 216 | fd = open(fn, O_RDONLY); 217 | if (fd < 0) { 218 | return -errno; 219 | } 220 | len = safe_read(fd, val, vallen); 221 | if (len < 0) { 222 | VOID_TEMP_FAILURE_RETRY(close(fd)); 223 | return len; 224 | } 225 | // close sometimes returns errors, but only after write() 226 | VOID_TEMP_FAILURE_RETRY(close(fd)); 227 | 228 | return len; 229 | } 230 | -------------------------------------------------------------------------------- /tests/sample-ceph-crushmap-compat.python-json: -------------------------------------------------------------------------------- 1 | { 2 | "choose_args": { 3 | "4": [ 4 | { 5 | "bucket_id": -2, 6 | "weight_set": [ 7 | [ 8 | 458752 9 | ] 10 | ] 11 | } 12 | ] 13 | }, 14 | "private": { 15 | "rules": [ 16 | { 17 | "max_size": 2, 18 | "min_size": 2, 19 | "rule_id": 0, 20 | "rule_name": "data", 21 | "ruleset": 3, 22 | "steps": [ 23 | { 24 | "item": -4, 25 | "item_name": "root", 26 | "op": "take" 27 | }, 28 | { 29 | "num": 0, 30 | "op": "chooseleaf_firstn", 31 | "type": "rack" 32 | }, 33 | { 34 | "op": "emit" 35 | } 36 | ], 37 | "type": 1 38 | } 39 | ], 40 | "tunables": { 41 | "allowed_bucket_algs": 22, 42 | "choose_local_fallback_tries": 5, 43 | "choose_local_tries": 2, 44 | "choose_total_tries": 19, 45 | "chooseleaf_descend_once": 0, 46 | "chooseleaf_stable": 0, 47 | "chooseleaf_vary_r": 0, 48 | "has_v2_rules": 0, 49 | "has_v3_rules": 0, 50 | "has_v4_buckets": 0, 51 | "has_v5_rules": 0, 52 | "legacy_tunables": 1, 53 | "minimum_required_version": "argonaut", 54 | "optimal_tunables": 0, 55 | "profile": "argonaut", 56 | "require_feature_tunables": 0, 57 | "require_feature_tunables2": 0, 58 | "require_feature_tunables3": 0, 59 | "require_feature_tunables5": 0, 60 | "straw_calc_version": 0 61 | } 62 | }, 63 | "rules": { 64 | "data": [ 65 | [ 66 | "take", 67 | "root" 68 | ], 69 | [ 70 | "chooseleaf", 71 | "firstn", 72 | 0, 73 | "type", 74 | "rack" 75 | ], 76 | [ 77 | "emit" 78 | ] 79 | ] 80 | }, 81 | "trees": [ 82 | { 83 | "algorithm": "straw", 84 | "children": [ 85 | { 86 | "algorithm": "straw", 87 | "children": [ 88 | { 89 | "algorithm": "straw", 90 | "children": [ 91 | { 92 | "id": 0, 93 | "name": "device0", 94 | "weight": 65536 95 | } 96 | ], 97 | "id": -1, 98 | "name": "host0", 99 | "type": "host", 100 | "weight": 65536 101 | }, 102 | { 103 | "algorithm": "straw", 104 | "children": [ 105 | { 106 | "id": 1, 107 | "name": "device1", 108 | "weight": 65536 109 | } 110 | ], 111 | "id": -2, 112 | "name": "host1", 113 | "type": "host", 114 | "weight": 65536 115 | }, 116 | { 117 | "algorithm": "straw", 118 | "children": [ 119 | { 120 | "id": 2, 121 | "name": "device2", 122 | "weight": 65536 123 | } 124 | ], 125 | "id": -5, 126 | "name": "host2", 127 | "type": "host", 128 | "weight": 65536 129 | } 130 | ], 131 | "id": -3, 132 | "name": "rack0", 133 | "type": "rack", 134 | "weight": 262144 135 | } 136 | ], 137 | "id": -4, 138 | "name": "root", 139 | "type": "root", 140 | "weight": 262144 141 | } 142 | ], 143 | "tunables": { 144 | "choose_local_fallback_tries": 5, 145 | "choose_local_tries": 2, 146 | "choose_total_tries": 19, 147 | "chooseleaf_descend_once": 0, 148 | "chooseleaf_stable": 0, 149 | "chooseleaf_vary_r": 0, 150 | "straw_calc_version": 0 151 | }, 152 | "types": [ 153 | { 154 | "name": "device", 155 | "type_id": 0 156 | }, 157 | { 158 | "name": "host", 159 | "type_id": 1 160 | }, 161 | { 162 | "name": "rack", 163 | "type_id": 2 164 | }, 165 | { 166 | "name": "root", 167 | "type_id": 3 168 | } 169 | ] 170 | } -------------------------------------------------------------------------------- /docs/ceph/optimize.rst: -------------------------------------------------------------------------------- 1 | Ceph pool optimization 2 | ====================== 3 | 4 | How to rebalance an empty pool 5 | ------------------------------ 6 | 7 | Rebalancing a pool may move a lot of PGs around and slow down the 8 | cluster if they contain a lot of objects. This is not a concern when 9 | the pool was just created and is empty. 10 | 11 | Get the report from the ceph cluster: it contains the crushmap, the 12 | osdmap and the information about all pools:: 13 | 14 | $ ceph report > report.json 15 | 16 | Run the optimization for a given pool with:: 17 | 18 | $ crush optimize --crushmap report.json --out-path optimized.crush --pool 3 19 | 20 | Upload the crushmap to the ceph cluster with:: 21 | 22 | $ ceph osd setcrushmap -i optimized.crush 23 | 24 | How to rebalance a pool step by step 25 | ------------------------------------ 26 | 27 | When a pool contains objects, rebalancing can be done in small 28 | increments (as specified by --step) to limit the number of PGs being 29 | moved. 30 | 31 | Get the report from the ceph cluster: it contains the crushmap, the 32 | osdmap and the information about all pools:: 33 | 34 | $ ceph report > report.json 35 | 36 | Run the optimization for a given pool and move as few PGs as possible 37 | with:: 38 | 39 | $ crush optimize \ 40 | --step 1 \ 41 | --crushmap report.json --out-path optimized.crush \ 42 | --pool 3 43 | 44 | Upload the crushmap to the ceph cluster with:: 45 | 46 | $ ceph osd setcrushmap -i optimized.crush 47 | 48 | Repeat until it cannot be optimized further. 49 | 50 | Compare the crushmaps 51 | --------------------- 52 | 53 | To verify which OSD are going to get or give PGs, the `crush compare` 54 | command can be used:: 55 | 56 | $ crush compare --origin report.json \ 57 | --destination optimized.crush \ 58 | --pool 3 59 | 60 | Adding and removing OSDs in Luminous (or above) clusters 61 | -------------------------------------------------------- 62 | 63 | When an OSD is added and the `osd_crush_update_on_start` option is 64 | true (which is the default), its value in the weight set will be the 65 | same as its target weight. For instance if an OSD with weight 4 is 66 | added, its weight set will also be 4. It is unlikely to be the desired 67 | value in the weight set and it is recommended to run `crush optimize` 68 | to rebalance the cluster. 69 | 70 | Alternatively the crushmap can be manually edited and uploaded. 71 | 72 | Requirements 73 | ------------ 74 | 75 | - All buckets in the pool must use the straw2 algorithm. 76 | - The OSDs must all have the default primary affinity. 77 | - The cluster must be HEALTH_OK 78 | 79 | Backward compatibility 80 | ---------------------- 81 | 82 | For clusters before Luminous, a pool can be rebalanced provided it is 83 | the sole user of the crush rule. If two pools use the same crush rule, 84 | rebalancing one of them will have an impact on the other. 85 | 86 | The first optimization step saves the item weights by creating a 87 | shadow tree in the crushmap, duplicating all buckets and appending 88 | `-target-weight` to their name. For instance:: 89 | 90 | root root { 91 | id -4 92 | # weight 4.000 93 | alg straw2 94 | hash 0 # rjenkins1 95 | item rack0 weight 4.000 96 | } 97 | 98 | root root-target-weight { 99 | id -6 100 | # weight 4.000 101 | alg straw2 102 | hash 0 # rjenkins1 103 | item rack0-target-weight weight 4.000 104 | } 105 | 106 | Commands such as `ceph df` will display the weights modified by `crush 107 | optimize` and will no longer show relevant values for the deviation 108 | between the expected distribution and the actual OSD usage. 109 | 110 | Compare the crushmap created after the first optimization step with:: 111 | 112 | $ crush compare --origin report.json \ 113 | --destination optimized.crush --destination-choose-args 0 \ 114 | --pool 3 115 | 116 | Compare the next crushmaps:: 117 | 118 | $ crush compare --origin report.json \ 119 | --destination optimized.crush --choose-args 0 \ 120 | --pool 3 121 | 122 | The difference is that the shadow tree (`-target-weight`) does not 123 | exist the first time and the `--choose-args` flag only makes sense for 124 | the destination crushmap, not the original ceph report. Also note that 125 | the `--choose-args` must be set to zero instead of the pool number. 126 | 127 | Adding and removing OSDs in pre-Luminous clusters 128 | ------------------------------------------------- 129 | 130 | When an OSD is added and the `osd_crush_update_on_start` option is 131 | true (which is the default), it will be inserted in the crushmap, as 132 | an item of the host. The cluster knows nothing about the 133 | `-target-weight` shadow tree and will not update it. It also knows 134 | nothing about the modified weights and part of the rebalancing benefit 135 | will be lost. The same happens when an OSD (or a bucket) is removed or 136 | moved. 137 | 138 | It is recommended to run `crush optimize` again as soon as OSDs are 139 | added or removed. The weights of the new OSDs will be copied over to 140 | the `-target-weight` hierarchy and optimized with the existing ones. 141 | 142 | Upgrading to Luminous 143 | --------------------- 144 | 145 | After the cluster is upgraded from a pre-Luminous version to Luminous 146 | or above, the crushmap can be converted to remove the `-target-weight` 147 | shadow tree meant for backward compatibility only. It can be done with 148 | `crush convert`:: 149 | 150 | $ ceph report > report.json 151 | $ crush convert --in-path report.json --out-path crushmap.crush 152 | $ ceph osd setcrushmap -i optimized.crush 153 | 154 | It cannot be done with the pre-Luminous crushmap alone because it does 155 | not contain enough information. 156 | -------------------------------------------------------------------------------- /tests/test_ceph_crush.py: -------------------------------------------------------------------------------- 1 | # -*- mode: python; coding: utf-8 -*- 2 | # 3 | # Copyright (C) 2017 4 | # 5 | # Author: Loic Dachary 6 | # 7 | # This program is free software: you can redistribute it and/or modify 8 | # it under the terms of the GNU General Public License as published by 9 | # the Free Software Foundation, either version 3 of the License, or 10 | # (at your option) any later version. 11 | # 12 | # This program is distributed in the hope that it will be useful, 13 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | # GNU General Public License for more details. 16 | # 17 | # You should have received a copy of the GNU General Public License 18 | # along with this program. If not, see . 19 | # 20 | import pytest 21 | 22 | from crush.ceph import CephCrushmapConverter, CephCrush 23 | 24 | 25 | class TestCephCrush(object): 26 | 27 | def test_convert_to_crushmap(self): 28 | c = CephCrush() 29 | crushmap = {} 30 | assert crushmap == c._convert_to_crushmap(crushmap) 31 | crushmap = c._convert_to_crushmap("tests/sample-crushmap.json") 32 | assert 'trees' in crushmap 33 | crushmap = c._convert_to_crushmap("tests/sample-ceph-crushmap.txt") 34 | assert 'trees' in crushmap 35 | crushmap = c._convert_to_crushmap("tests/sample-ceph-crushmap.crush") 36 | assert 'trees' in crushmap 37 | crushmap = c._convert_to_crushmap("tests/sample-ceph-crushmap.json") 38 | assert 'trees' in crushmap 39 | with pytest.raises(ValueError) as e: 40 | crushmap = c._convert_to_crushmap("tests/sample-bugous-crushmap.json") 41 | assert "Expecting property name" in str(e.value) 42 | 43 | 44 | class TestCephCrushmapConverter(object): 45 | 46 | def test_recover_choose_args(self): 47 | ceph = { 48 | 'buckets': [ 49 | { 50 | 'name': 'SELF-target-weight', 51 | 'id': -10, 52 | 'items': [ 53 | {'weight': 1 * 0x10000, 'id': 1}, 54 | {'weight': 2 * 0x10000, 'id': 2} 55 | ] 56 | }, 57 | { 58 | 'name': 'SELF', 59 | 'id': -1, 60 | 'items': [ 61 | {'weight': 10 * 0x10000, 'id': 1}, 62 | {'weight': 20 * 0x10000, 'id': 2} 63 | ] 64 | }, 65 | ] 66 | } 67 | CephCrushmapConverter.recover_choose_args(ceph) 68 | expected = { 69 | 'choose_args': {' placeholder ': [{'bucket_id': -1, 'weight_set': [[10, 20]]}]}, 70 | 'buckets': [ 71 | { 72 | 'name': 'SELF', 73 | 'id': -1, 74 | 'items': [ 75 | {'weight': 1 * 0x10000, 'id': 1}, 76 | {'weight': 2 * 0x10000, 'id': 2} 77 | ] 78 | }, 79 | ] 80 | } 81 | assert expected == ceph 82 | 83 | def test_recover_choose_args_added(self): 84 | ceph = { 85 | 'buckets': [ 86 | { 87 | 'name': 'SELF-target-weight', 88 | 'id': -10, 89 | 'items': [ 90 | {'weight': 1 * 0x10000, 'id': 1}, 91 | ] 92 | }, 93 | { 94 | 'name': 'SELF', 95 | 'id': -1, 96 | 'items': [ 97 | {'weight': 10 * 0x10000, 'id': 1}, 98 | {'weight': 2 * 0x10000, 'id': 2} 99 | ] 100 | }, 101 | ] 102 | } 103 | CephCrushmapConverter.recover_choose_args(ceph) 104 | expected = { 105 | 'choose_args': {' placeholder ': [{'bucket_id': -1, 'weight_set': [[10, 0]]}]}, 106 | 'buckets': [ 107 | { 108 | 'name': 'SELF', 109 | 'id': -1, 110 | 'items': [ 111 | {'weight': 1 * 0x10000, 'id': 1}, 112 | {'weight': 2 * 0x10000, 'id': 2} 113 | ] 114 | }, 115 | ] 116 | } 117 | assert expected == ceph 118 | 119 | def test_recover_choose_args_removed(self): 120 | ceph = { 121 | 'buckets': [ 122 | { 123 | 'name': 'SELF-target-weight', 124 | 'id': -10, 125 | 'items': [ 126 | {'weight': 1 * 0x10000, 'id': 1}, 127 | {'weight': 2 * 0x10000, 'id': 2} 128 | ] 129 | }, 130 | { 131 | 'name': 'SELF', 132 | 'id': -1, 133 | 'items': [ 134 | {'weight': 20 * 0x10000, 'id': 2} 135 | ] 136 | }, 137 | ] 138 | } 139 | CephCrushmapConverter.recover_choose_args(ceph) 140 | expected = { 141 | 'choose_args': {' placeholder ': [{'bucket_id': -1, 'weight_set': [[20]]}]}, 142 | 'buckets': [ 143 | { 144 | 'name': 'SELF', 145 | 'id': -1, 146 | 'items': [ 147 | {'weight': 2 * 0x10000, 'id': 2} 148 | ] 149 | }, 150 | ] 151 | } 152 | assert expected == ceph 153 | 154 | # Local Variables: 155 | # compile-command: "cd .. ; tox -e py27 -- -s -vv tests/test_ceph_crush.py" 156 | # End: 157 | -------------------------------------------------------------------------------- /crush/libcrush/common/strtol.cc: -------------------------------------------------------------------------------- 1 | // -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*- 2 | // vim: ts=8 sw=2 smarttab 3 | /* 4 | * Ceph - scalable distributed file system 5 | * 6 | * Copyright (C) 2011 New Dream Network 7 | * 8 | * This is free software; you can redistribute it and/or 9 | * modify it under the terms of the GNU Lesser General Public 10 | * License version 2.1, as published by the Free Software 11 | * Foundation. See file COPYING. 12 | * 13 | */ 14 | 15 | #include "strtol.h" 16 | 17 | #include 18 | #include 19 | #include 20 | 21 | using std::ostringstream; 22 | 23 | long long strict_strtoll(const char *str, int base, std::string *err) 24 | { 25 | char *endptr; 26 | std::string errStr; 27 | errno = 0; /* To distinguish success/failure after call (see man page) */ 28 | long long ret = strtoll(str, &endptr, base); 29 | 30 | if (endptr == str) { 31 | errStr = "Expected option value to be integer, got '"; 32 | errStr.append(str); 33 | errStr.append("'"); 34 | *err = errStr; 35 | return 0; 36 | } 37 | if ((errno == ERANGE && (ret == LLONG_MAX || ret == LLONG_MIN)) 38 | || (errno != 0 && ret == 0)) { 39 | errStr = "The option value '"; 40 | errStr.append(str); 41 | errStr.append("'"); 42 | errStr.append(" seems to be invalid"); 43 | *err = errStr; 44 | return 0; 45 | } 46 | if (*endptr != '\0') { 47 | errStr = "The option value '"; 48 | errStr.append(str); 49 | errStr.append("'"); 50 | errStr.append(" contains invalid digits"); 51 | *err = errStr; 52 | return 0; 53 | } 54 | *err = ""; 55 | return ret; 56 | } 57 | 58 | int strict_strtol(const char *str, int base, std::string *err) 59 | { 60 | std::string errStr; 61 | long long ret = strict_strtoll(str, base, err); 62 | if (!err->empty()) 63 | return 0; 64 | if ((ret <= INT_MIN) || (ret >= INT_MAX)) { 65 | errStr = "The option value '"; 66 | errStr.append(str); 67 | errStr.append("'"); 68 | errStr.append(" seems to be invalid"); 69 | *err = errStr; 70 | return 0; 71 | } 72 | return static_cast(ret); 73 | } 74 | 75 | double strict_strtod(const char *str, std::string *err) 76 | { 77 | char *endptr; 78 | errno = 0; /* To distinguish success/failure after call (see man page) */ 79 | double ret = strtod(str, &endptr); 80 | if (errno == ERANGE) { 81 | ostringstream oss; 82 | oss << "strict_strtod: floating point overflow or underflow parsing '" 83 | << str << "'"; 84 | *err = oss.str(); 85 | return 0.0; 86 | } 87 | if (endptr == str) { 88 | ostringstream oss; 89 | oss << "strict_strtod: expected double, got: '" << str << "'"; 90 | *err = oss.str(); 91 | return 0; 92 | } 93 | if (*endptr != '\0') { 94 | ostringstream oss; 95 | oss << "strict_strtod: garbage at end of string. got: '" << str << "'"; 96 | *err = oss.str(); 97 | return 0; 98 | } 99 | *err = ""; 100 | return ret; 101 | } 102 | 103 | float strict_strtof(const char *str, std::string *err) 104 | { 105 | char *endptr; 106 | errno = 0; /* To distinguish success/failure after call (see man page) */ 107 | float ret = strtof(str, &endptr); 108 | if (errno == ERANGE) { 109 | ostringstream oss; 110 | oss << "strict_strtof: floating point overflow or underflow parsing '" 111 | << str << "'"; 112 | *err = oss.str(); 113 | return 0.0; 114 | } 115 | if (endptr == str) { 116 | ostringstream oss; 117 | oss << "strict_strtof: expected float, got: '" << str << "'"; 118 | *err = oss.str(); 119 | return 0; 120 | } 121 | if (*endptr != '\0') { 122 | ostringstream oss; 123 | oss << "strict_strtof: garbage at end of string. got: '" << str << "'"; 124 | *err = oss.str(); 125 | return 0; 126 | } 127 | *err = ""; 128 | return ret; 129 | } 130 | 131 | template 132 | T strict_si_cast(const char *str, std::string *err) 133 | { 134 | std::string s(str); 135 | if (s.empty()) { 136 | *err = "strict_sistrtoll: value not specified"; 137 | return 0; 138 | } 139 | const char &u = s.back(); 140 | int m = 0; 141 | if (u == 'B') 142 | m = 0; 143 | else if (u == 'K') 144 | m = 10; 145 | else if (u == 'M') 146 | m = 20; 147 | else if (u == 'G') 148 | m = 30; 149 | else if (u == 'T') 150 | m = 40; 151 | else if (u == 'P') 152 | m = 50; 153 | else if (u == 'E') 154 | m = 60; 155 | else 156 | m = -1; 157 | 158 | if (m >= 0) 159 | s.pop_back(); 160 | else 161 | m = 0; 162 | 163 | long long ll = strict_strtoll(s.c_str(), 10, err); 164 | if (ll < 0 && !std::numeric_limits::is_signed) { 165 | *err = "strict_sistrtoll: value should not be negative"; 166 | return 0; 167 | } 168 | if (static_cast(m) >= sizeof(T) * CHAR_BIT) { 169 | *err = ("strict_sistrtoll: the SI prefix is too large for the designated " 170 | "type"); 171 | return 0; 172 | } 173 | using promoted_t = typename std::common_type::type; 174 | if (static_cast(ll) < 175 | static_cast(std::numeric_limits::min()) >> m) { 176 | *err = "strict_sistrtoll: value seems to be too small"; 177 | return 0; 178 | } 179 | if (static_cast(ll) > 180 | static_cast(std::numeric_limits::max()) >> m) { 181 | *err = "strict_sistrtoll: value seems to be too large"; 182 | return 0; 183 | } 184 | return (ll << m); 185 | } 186 | 187 | template int strict_si_cast(const char *str, std::string *err); 188 | template long strict_si_cast(const char *str, std::string *err); 189 | template long long strict_si_cast(const char *str, std::string *err); 190 | template uint64_t strict_si_cast(const char *str, std::string *err); 191 | template uint32_t strict_si_cast(const char *str, std::string *err); 192 | 193 | uint64_t strict_sistrtoll(const char *str, std::string *err) 194 | { 195 | return strict_si_cast(str, err); 196 | } 197 | -------------------------------------------------------------------------------- /tests/test_ceph_analyze.py: -------------------------------------------------------------------------------- 1 | # -*- mode: python; coding: utf-8 -*- 2 | # 3 | # Copyright (C) 2017 4 | # 5 | # Author: Loic Dachary 6 | # 7 | # This program is free software: you can redistribute it and/or modify 8 | # it under the terms of the GNU General Public License as published by 9 | # the Free Software Foundation, either version 3 of the License, or 10 | # (at your option) any later version. 11 | # 12 | # This program is distributed in the hope that it will be useful, 13 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | # GNU General Public License for more details. 16 | # 17 | # You should have received a copy of the GNU General Public License 18 | # along with this program. If not, see . 19 | # 20 | import pytest 21 | from crush.ceph import Ceph 22 | 23 | 24 | class TestAnalyze(object): 25 | 26 | def test_sanity_check_args(self): 27 | a = Ceph().constructor([ 28 | 'analyze', 29 | ]) 30 | with pytest.raises(Exception) as e: 31 | a.pre_sanity_check_args() 32 | assert 'missing --crushmap' in str(e.value) 33 | 34 | a = Ceph().constructor([ 35 | 'analyze', 36 | '--crushmap', 'CRUSHMAP', 37 | ]) 38 | a.pre_sanity_check_args() 39 | 40 | a = Ceph().constructor([ 41 | 'analyze', 42 | '--crushmap', 'CRUSHMAP', 43 | ]) 44 | with pytest.raises(Exception) as e: 45 | a.post_sanity_check_args() 46 | assert 'missing --rule' in str(e.value) 47 | 48 | a = Ceph().constructor([ 49 | 'analyze', 50 | '--crushmap', 'CRUSHMAP', 51 | '--rule', 'RULE', 52 | ]) 53 | a.post_sanity_check_args() 54 | 55 | a = Ceph().constructor([ 56 | 'analyze', 57 | '--crushmap', 'CRUSHMAP', 58 | '--rule', 'RULE', 59 | '--pool', '3', 60 | '--values-count', '8', 61 | ]) 62 | with pytest.raises(Exception) as e: 63 | a.post_sanity_check_args() 64 | assert '--pool and --values-count are mutually exclusive' in str(e.value) 65 | 66 | a = Ceph().constructor([ 67 | 'analyze', 68 | '--crushmap', 'CRUSHMAP', 69 | '--rule', 'RULE', 70 | '--pool', '3', 71 | ]) 72 | with pytest.raises(Exception) as e: 73 | a.post_sanity_check_args() 74 | assert '--pg-num is required' in str(e.value) 75 | 76 | a = Ceph().constructor([ 77 | 'analyze', 78 | '--crushmap', 'CRUSHMAP', 79 | '--rule', 'RULE', 80 | '--pool', '3', 81 | '--pg-num', '10', 82 | ]) 83 | with pytest.raises(Exception) as e: 84 | a.post_sanity_check_args() 85 | assert '--pgp-num is required' in str(e.value) 86 | 87 | a = Ceph().constructor([ 88 | 'analyze', 89 | '--crushmap', 'CRUSHMAP', 90 | '--rule', 'RULE', 91 | '--pool', '3', 92 | '--pg-num', '10', 93 | '--pgp-num', '10', 94 | ]) 95 | a.post_sanity_check_args() 96 | 97 | def test_report_compat(self): 98 | # 99 | # verify --choose-args is set to the pool when the crushmap contains 100 | # *-target-weights buckets. 101 | # 102 | for p in ([], 103 | ['--choose-args=3'], 104 | ['--pool=3'], 105 | ['--choose-args=3', '--pool=3']): 106 | a = Ceph().constructor([ 107 | '--verbose', 108 | 'analyze', 109 | '--crushmap', 'tests/ceph/ceph-report-compat.json', 110 | ] + p) 111 | d = a.analyze_report(*a.analyze()) 112 | print(str(d)) 113 | expected = """\ 114 | ~id~ ~weight~ ~PGs~ ~over/under filled %~ 115 | ~name~ 116 | host0 -1 1.0 1 0.0 117 | host1 -2 1.0 1 0.0 118 | host2 -5 1.0 1 0.0 119 | 120 | Worst case scenario if a host fails: 121 | 122 | ~over filled %~ 123 | ~type~ 124 | device 33.33 125 | host 33.33 126 | rack 0.00 127 | root 0.00\ 128 | """ # noqa trailing whitespaces are expected 129 | assert expected == str(d) 130 | 131 | def test_report_compat_hammer(self): 132 | # 133 | # hammer does not know about the stable tunable, verify this 134 | # is handled properly. It must be set to zero otherwise the 135 | # ceph report below will have unexpected mappings. 136 | # 137 | a = Ceph().constructor([ 138 | '--verbose', 139 | 'analyze', 140 | '--crushmap', 'tests/ceph/ceph-report-compat-hammer.json', 141 | '--pool', '42', 142 | ]) 143 | d = a.analyze_report(*a.analyze()) 144 | print(str(d)) 145 | expected = """\ 146 | ~id~ ~weight~ ~PGs~ ~over/under filled %~ 147 | ~name~ 148 | node-8v -6 1.08 5 56.25 149 | node-5v -3 1.08 4 25.00 150 | node-6v -4 1.08 3 -6.25 151 | node-7v -5 1.08 3 -6.25 152 | node-4 -2 1.08 1 -68.75 153 | 154 | Worst case scenario if a host fails: 155 | 156 | ~over filled %~ 157 | ~type~ 158 | device 150.0 159 | host 75.0 160 | root 0.0\ 161 | """ # noqa trailing whitespaces are expected 162 | assert expected == str(d) 163 | 164 | # Local Variables: 165 | # compile-command: "cd .. ; tox -e py27 -- -s -vv tests/test_ceph_analyze.py" 166 | # End: 167 | -------------------------------------------------------------------------------- /docs/_themes/kr_small/static/flasky.css_t: -------------------------------------------------------------------------------- 1 | /* 2 | * flasky.css_t 3 | * ~~~~~~~~~~~~ 4 | * 5 | * Sphinx stylesheet -- flasky theme based on nature theme. 6 | * 7 | * :copyright: Copyright 2007-2010 by the Sphinx team, see AUTHORS. 8 | * :license: BSD, see LICENSE for details. 9 | * 10 | */ 11 | 12 | @import url("basic.css"); 13 | 14 | /* -- page layout ----------------------------------------------------------- */ 15 | 16 | body { 17 | font-family: 'Georgia', serif; 18 | font-size: 17px; 19 | color: #000; 20 | background: white; 21 | margin: 0; 22 | padding: 0; 23 | } 24 | 25 | div.documentwrapper { 26 | float: left; 27 | width: 100%; 28 | } 29 | 30 | div.bodywrapper { 31 | margin: 40px auto 0 auto; 32 | width: 700px; 33 | } 34 | 35 | hr { 36 | border: 1px solid #B1B4B6; 37 | } 38 | 39 | div.body { 40 | background-color: #ffffff; 41 | color: #3E4349; 42 | padding: 0 30px 30px 30px; 43 | } 44 | 45 | img.floatingflask { 46 | padding: 0 0 10px 10px; 47 | float: right; 48 | } 49 | 50 | div.footer { 51 | text-align: right; 52 | color: #888; 53 | padding: 10px; 54 | font-size: 14px; 55 | width: 650px; 56 | margin: 0 auto 40px auto; 57 | } 58 | 59 | div.footer a { 60 | color: #888; 61 | text-decoration: underline; 62 | } 63 | 64 | div.related { 65 | line-height: 32px; 66 | color: #888; 67 | } 68 | 69 | div.related ul { 70 | padding: 0 0 0 10px; 71 | } 72 | 73 | div.related a { 74 | color: #444; 75 | } 76 | 77 | /* -- body styles ----------------------------------------------------------- */ 78 | 79 | a { 80 | color: #004B6B; 81 | text-decoration: underline; 82 | } 83 | 84 | a:hover { 85 | color: #6D4100; 86 | text-decoration: underline; 87 | } 88 | 89 | div.body { 90 | padding-bottom: 40px; /* saved for footer */ 91 | } 92 | 93 | div.body h1, 94 | div.body h2, 95 | div.body h3, 96 | div.body h4, 97 | div.body h5, 98 | div.body h6 { 99 | font-family: 'Garamond', 'Georgia', serif; 100 | font-weight: normal; 101 | margin: 30px 0px 10px 0px; 102 | padding: 0; 103 | } 104 | 105 | {% if theme_index_logo %} 106 | div.indexwrapper h1 { 107 | text-indent: -999999px; 108 | background: url({{ theme_index_logo }}) no-repeat center center; 109 | height: {{ theme_index_logo_height }}; 110 | } 111 | {% endif %} 112 | 113 | div.body h2 { font-size: 180%; } 114 | div.body h3 { font-size: 150%; } 115 | div.body h4 { font-size: 130%; } 116 | div.body h5 { font-size: 100%; } 117 | div.body h6 { font-size: 100%; } 118 | 119 | a.headerlink { 120 | color: white; 121 | padding: 0 4px; 122 | text-decoration: none; 123 | } 124 | 125 | a.headerlink:hover { 126 | color: #444; 127 | background: #eaeaea; 128 | } 129 | 130 | div.body p, div.body dd, div.body li { 131 | line-height: 1.4em; 132 | } 133 | 134 | div.admonition { 135 | background: #fafafa; 136 | margin: 20px -30px; 137 | padding: 10px 30px; 138 | border-top: 1px solid #ccc; 139 | border-bottom: 1px solid #ccc; 140 | } 141 | 142 | div.admonition p.admonition-title { 143 | font-family: 'Garamond', 'Georgia', serif; 144 | font-weight: normal; 145 | font-size: 24px; 146 | margin: 0 0 10px 0; 147 | padding: 0; 148 | line-height: 1; 149 | } 150 | 151 | div.admonition p.last { 152 | margin-bottom: 0; 153 | } 154 | 155 | div.highlight{ 156 | background-color: white; 157 | } 158 | 159 | dt:target, .highlight { 160 | background: #FAF3E8; 161 | } 162 | 163 | div.note { 164 | background-color: #eee; 165 | border: 1px solid #ccc; 166 | } 167 | 168 | div.seealso { 169 | background-color: #ffc; 170 | border: 1px solid #ff6; 171 | } 172 | 173 | div.topic { 174 | background-color: #eee; 175 | } 176 | 177 | div.warning { 178 | background-color: #ffe4e4; 179 | border: 1px solid #f66; 180 | } 181 | 182 | p.admonition-title { 183 | display: inline; 184 | } 185 | 186 | p.admonition-title:after { 187 | content: ":"; 188 | } 189 | 190 | pre, tt { 191 | font-family: 'Consolas', 'Menlo', 'Deja Vu Sans Mono', 'Bitstream Vera Sans Mono', monospace; 192 | font-size: 0.85em; 193 | } 194 | 195 | img.screenshot { 196 | } 197 | 198 | tt.descname, tt.descclassname { 199 | font-size: 0.95em; 200 | } 201 | 202 | tt.descname { 203 | padding-right: 0.08em; 204 | } 205 | 206 | img.screenshot { 207 | -moz-box-shadow: 2px 2px 4px #eee; 208 | -webkit-box-shadow: 2px 2px 4px #eee; 209 | box-shadow: 2px 2px 4px #eee; 210 | } 211 | 212 | table.docutils { 213 | border: 1px solid #888; 214 | -moz-box-shadow: 2px 2px 4px #eee; 215 | -webkit-box-shadow: 2px 2px 4px #eee; 216 | box-shadow: 2px 2px 4px #eee; 217 | } 218 | 219 | table.docutils td, table.docutils th { 220 | border: 1px solid #888; 221 | padding: 0.25em 0.7em; 222 | } 223 | 224 | table.field-list, table.footnote { 225 | border: none; 226 | -moz-box-shadow: none; 227 | -webkit-box-shadow: none; 228 | box-shadow: none; 229 | } 230 | 231 | table.footnote { 232 | margin: 15px 0; 233 | width: 100%; 234 | border: 1px solid #eee; 235 | } 236 | 237 | table.field-list th { 238 | padding: 0 0.8em 0 0; 239 | } 240 | 241 | table.field-list td { 242 | padding: 0; 243 | } 244 | 245 | table.footnote td { 246 | padding: 0.5em; 247 | } 248 | 249 | dl { 250 | margin: 0; 251 | padding: 0; 252 | } 253 | 254 | dl dd { 255 | margin-left: 30px; 256 | } 257 | 258 | pre { 259 | padding: 0; 260 | margin: 15px -30px; 261 | padding: 8px; 262 | line-height: 1.3em; 263 | padding: 7px 30px; 264 | background: #eee; 265 | border-radius: 2px; 266 | -moz-border-radius: 2px; 267 | -webkit-border-radius: 2px; 268 | } 269 | 270 | dl pre { 271 | margin-left: -60px; 272 | padding-left: 60px; 273 | } 274 | 275 | tt { 276 | background-color: #ecf0f3; 277 | color: #222; 278 | /* padding: 1px 2px; */ 279 | } 280 | 281 | tt.xref, a tt { 282 | background-color: #FBFBFB; 283 | } 284 | 285 | a:hover tt { 286 | background: #EEE; 287 | } 288 | -------------------------------------------------------------------------------- /tests/ceph/osdmap.json: -------------------------------------------------------------------------------- 1 | { 2 | "epoch": 57, 3 | "fsid": "0bbbb5a6-0e72-11e7-addd-080027759979", 4 | "created": "2017-03-21 16:07:46.516760", 5 | "modified": "2017-03-23 08:29:40.874423", 6 | "flags": "", 7 | "cluster_snapshot": "", 8 | "pool_max": 2, 9 | "max_osd": 3, 10 | "pools": [ 11 | { 12 | "pool": 0, 13 | "pool_name": "rbd", 14 | "flags": 1, 15 | "flags_names": "hashpspool", 16 | "type": 1, 17 | "size": 3, 18 | "min_size": 1, 19 | "crush_ruleset": 0, 20 | "object_hash": 2, 21 | "pg_num": 64, 22 | "pg_placement_num": 64, 23 | "crash_replay_interval": 0, 24 | "last_change": "1", 25 | "last_force_op_resend": "0", 26 | "auid": 0, 27 | "snap_mode": "selfmanaged", 28 | "snap_seq": 0, 29 | "snap_epoch": 0, 30 | "pool_snaps": [], 31 | "removed_snaps": "[]", 32 | "quota_max_bytes": 0, 33 | "quota_max_objects": 0, 34 | "tiers": [], 35 | "tier_of": -1, 36 | "read_tier": -1, 37 | "write_tier": -1, 38 | "cache_mode": "none", 39 | "target_max_bytes": 0, 40 | "target_max_objects": 0, 41 | "cache_target_dirty_ratio_micro": 0, 42 | "cache_target_full_ratio_micro": 0, 43 | "cache_min_flush_age": 0, 44 | "cache_min_evict_age": 0, 45 | "erasure_code_profile": "", 46 | "hit_set_params": { 47 | "type": "none" 48 | }, 49 | "hit_set_period": 0, 50 | "hit_set_count": 0, 51 | "use_gmt_hitset": true, 52 | "min_read_recency_for_promote": 0, 53 | "stripe_width": 0, 54 | "expected_num_objects": 0 55 | }, 56 | { 57 | "pool": 1, 58 | "pool_name": "erasure_k2_m1", 59 | "flags": 1, 60 | "flags_names": "hashpspool", 61 | "type": 3, 62 | "size": 3, 63 | "min_size": 2, 64 | "crush_ruleset": 2, 65 | "object_hash": 2, 66 | "pg_num": 512, 67 | "pg_placement_num": 512, 68 | "crash_replay_interval": 0, 69 | "last_change": "60", 70 | "last_force_op_resend": "0", 71 | "auid": 0, 72 | "snap_mode": "selfmanaged", 73 | "snap_seq": 0, 74 | "snap_epoch": 0, 75 | "pool_snaps": [], 76 | "removed_snaps": "[]", 77 | "quota_max_bytes": 0, 78 | "quota_max_objects": 0, 79 | "tiers": [], 80 | "tier_of": -1, 81 | "read_tier": -1, 82 | "write_tier": -1, 83 | "cache_mode": "none", 84 | "target_max_bytes": 0, 85 | "target_max_objects": 0, 86 | "cache_target_dirty_ratio_micro": 400000, 87 | "cache_target_full_ratio_micro": 800000, 88 | "cache_min_flush_age": 0, 89 | "cache_min_evict_age": 0, 90 | "erasure_code_profile": "k2_m1_jerasure", 91 | "hit_set_params": { 92 | "type": "none" 93 | }, 94 | "hit_set_period": 0, 95 | "hit_set_count": 0, 96 | "use_gmt_hitset": true, 97 | "min_read_recency_for_promote": 0, 98 | "stripe_width": 4128, 99 | "expected_num_objects": 0 100 | } 101 | ], 102 | "osds": [ 103 | { 104 | "osd": 0, 105 | "uuid": "8c748cf6-57e1-4510-9187-f09b19f60e4d", 106 | "up": 1, 107 | "in": 1, 108 | "weight": 1.000000, 109 | "primary_affinity": 1.000000, 110 | "last_clean_begin": 43, 111 | "last_clean_end": 49, 112 | "up_from": 51, 113 | "up_thru": 56, 114 | "down_at": 49, 115 | "lost_at": 0, 116 | "public_addr": "192.168.0.10:6800\/1974", 117 | "cluster_addr": "192.168.1.10:6806\/1001974", 118 | "heartbeat_back_addr": "192.168.1.10:6809\/1001974", 119 | "heartbeat_front_addr": "192.168.0.10:6803\/1001974", 120 | "state": [ 121 | "exists", 122 | "up" 123 | ] 124 | }, 125 | { 126 | "osd": 1, 127 | "uuid": "4c04e0d5-306b-4eff-80e5-70a8c49f9801", 128 | "up": 1, 129 | "in": 1, 130 | "weight": 0.950000, 131 | "primary_affinity": 1.000000, 132 | "last_clean_begin": 43, 133 | "last_clean_end": 49, 134 | "up_from": 51, 135 | "up_thru": 56, 136 | "down_at": 49, 137 | "lost_at": 0, 138 | "public_addr": "192.168.0.10:6802\/2189", 139 | "cluster_addr": "192.168.1.10:6807\/1002189", 140 | "heartbeat_back_addr": "192.168.1.10:6801\/1002189", 141 | "heartbeat_front_addr": "192.168.0.10:6801\/1002189", 142 | "state": [ 143 | "exists", 144 | "up" 145 | ] 146 | }, 147 | { 148 | "osd": 2, 149 | "uuid": "953b44c3-8057-4ea7-a732-d747218db95d", 150 | "up": 1, 151 | "in": 1, 152 | "weight": 1.000000, 153 | "primary_affinity": 1.000000, 154 | "last_clean_begin": 43, 155 | "last_clean_end": 49, 156 | "up_from": 51, 157 | "up_thru": 56, 158 | "down_at": 49, 159 | "lost_at": 0, 160 | "public_addr": "192.168.0.10:6804\/2463", 161 | "cluster_addr": "192.168.1.10:6808\/1002463", 162 | "heartbeat_back_addr": "192.168.1.10:6803\/1002463", 163 | "heartbeat_front_addr": "192.168.0.10:6806\/1002463", 164 | "state": [ 165 | "exists", 166 | "up" 167 | ] 168 | } 169 | ], 170 | "osd_xinfo": [ 171 | { 172 | "osd": 0, 173 | "down_stamp": "2017-03-23 08:29:31.505642", 174 | "laggy_probability": 0.300000, 175 | "laggy_interval": 0, 176 | "features": 55169095435288575, 177 | "old_weight": 0 178 | }, 179 | { 180 | "osd": 1, 181 | "down_stamp": "2017-03-23 08:29:31.505642", 182 | "laggy_probability": 0.300000, 183 | "laggy_interval": 0, 184 | "features": 55169095435288575, 185 | "old_weight": 0 186 | }, 187 | { 188 | "osd": 2, 189 | "down_stamp": "2017-03-23 08:29:31.505642", 190 | "laggy_probability": 0.300000, 191 | "laggy_interval": 0, 192 | "features": 55169095435288575, 193 | "old_weight": 0 194 | } 195 | ], 196 | "pg_temp": [], 197 | "primary_temp": [], 198 | "blacklist": [], 199 | "erasure_code_profiles": { 200 | "k2_m1_jerasure": { 201 | "directory": "\/usr\/lib64\/ceph\/erasure-code", 202 | "k": "2", 203 | "m": "1", 204 | "plugin": "jerasure", 205 | "technique": "reed_sol_van" 206 | } 207 | } 208 | } 209 | -------------------------------------------------------------------------------- /crush/main.py: -------------------------------------------------------------------------------- 1 | # -*- mode: python; coding: utf-8 -*- 2 | # 3 | # Copyright (C) 2017 4 | # 5 | # Author: Loic Dachary 6 | # 7 | # This program is free software: you can redistribute it and/or modify 8 | # it under the terms of the GNU General Public License as published by 9 | # the Free Software Foundation, either version 3 of the License, or 10 | # (at your option) any later version. 11 | # 12 | # This program is distributed in the hope that it will be useful, 13 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | # GNU General Public License for more details. 16 | # 17 | # You should have received a copy of the GNU General Public License 18 | # along with this program. If not, see . 19 | # 20 | import argparse 21 | import collections 22 | import logging 23 | import textwrap 24 | 25 | from crush import Crush 26 | from crush import analyze 27 | from crush import compare 28 | from crush import optimize 29 | 30 | log = logging.getLogger('crush') 31 | 32 | 33 | class Main(object): 34 | 35 | def __init__(self): 36 | self.create_parser() 37 | 38 | if self.parser.get_default('backward_compatibility') is None: 39 | self.parser.set_defaults(backward_compatibility=False) 40 | 41 | self.parser.add_argument( 42 | '-v', '--verbose', 43 | action='store_true', default=None, 44 | help='be more verbose', 45 | ) 46 | 47 | self.parser.add_argument( 48 | '--debug', 49 | action='store_true', default=None, 50 | help='debugging output, very verbose', 51 | ) 52 | 53 | self.subparsers = self.parser.add_subparsers( 54 | title='subcommands', 55 | description='valid subcommands', 56 | help='sub-command -h', 57 | ) 58 | 59 | analyze.Analyze.set_parser(self.subparsers, self.hook_analyze_args) 60 | compare.Compare.set_parser(self.subparsers, self.hook_compare_args) 61 | optimize.Optimize.set_parser(self.subparsers, self.hook_optimize_args) 62 | 63 | def create_parser(self): 64 | self.parser = argparse.ArgumentParser( 65 | formatter_class=argparse.RawDescriptionHelpFormatter, 66 | description=textwrap.dedent("""\ 67 | A library to control placement in a hierarchy 68 | """)) 69 | 70 | def __getstate__(self): 71 | return (self.argv,) 72 | 73 | def __setstate__(self, state): 74 | self.__init__() 75 | self.parse(state[0]) 76 | 77 | def clone(self): 78 | return Main() 79 | 80 | def parse(self, argv): 81 | self.argv = argv 82 | self.args = self.parser.parse_args(argv) 83 | 84 | if self.args.debug: 85 | level = logging.DEBUG 86 | format = '%(asctime)s %(funcName)20s %(message)s' 87 | elif self.args.verbose: 88 | level = logging.INFO 89 | format = '%(asctime)s %(funcName)20s %(message)s' 90 | else: 91 | level = logging.WARNING 92 | format = '%(asctime)s %(message)s' 93 | if log.getEffectiveLevel() == 0 or log.getEffectiveLevel() > level: 94 | log.setLevel(level) 95 | logging.basicConfig(format=format) 96 | 97 | @staticmethod 98 | def get_trimmed_argv(to_parser, args): 99 | options = [] 100 | positionals = [] 101 | dest2option = {} 102 | known_dests = [] 103 | for action in to_parser._actions: 104 | option = list(filter(lambda o: o in to_parser._option_string_actions, 105 | action.option_strings)) 106 | if len(option) > 0: 107 | dest2option[action.dest] = option[0] 108 | known_dests.append(action.dest) 109 | v = collections.OrderedDict(sorted(vars(args).items(), 110 | key=lambda t: t[0])) 111 | for (key, value) in v.items(): 112 | log.debug('get_trimmed_argv: checking ' + 113 | str(key) + "=" + str(value)) 114 | if key not in known_dests: 115 | log.debug('get_trimmed_argv: skip unknown ' + str(key)) 116 | continue 117 | if key in dest2option: 118 | option = dest2option[key] 119 | action = to_parser._option_string_actions[option] 120 | if value != action.default: 121 | if action.nargs is None or action.nargs == 1: 122 | options.extend([option, str(value)]) 123 | elif action.nargs == 0: 124 | options.append(option) 125 | else: 126 | log.debug('get_trimmed_argv: positional ' + 127 | str(key) + "=" + str(value)) 128 | positionals.extend(value) 129 | return options + positionals 130 | 131 | def constructor(self, argv): 132 | self.parse(argv) 133 | return self.args.func(self.args, self) 134 | 135 | def main(self, argv): 136 | return self.constructor(argv).run() 137 | 138 | def hook_analyze_args(self, parser): 139 | pass 140 | 141 | def hook_analyze_pre_sanity_check_args(self, args): 142 | if not args.crushmap: 143 | raise Exception("missing --crushmap") 144 | 145 | def hook_analyze_post_sanity_check_args(self, args): 146 | if not args.rule: 147 | raise Exception("missing --rule") 148 | 149 | def hook_compare_args(self, parser): 150 | pass 151 | 152 | def hook_compare_pre_sanity_check_args(self, args): 153 | if not args.origin: 154 | raise Exception("missing --origin") 155 | if not args.destination: 156 | raise Exception("missing --destination") 157 | 158 | def hook_compare_post_sanity_check_args(self, args): 159 | if not args.rule: 160 | raise Exception("missing --rule") 161 | 162 | def hook_optimize_args(self, parser): 163 | pass 164 | 165 | def hook_optimize_pre_sanity_check_args(self, args): 166 | self.hook_analyze_pre_sanity_check_args(args) 167 | if self.args.with_forecast is False and not self.args.step: 168 | raise Exception("--no-forecast is only valid with --step") 169 | if not self.args.out_path: 170 | raise Exception("missing --out-path") 171 | 172 | def hook_optimize_post_sanity_check_args(self, args): 173 | self.hook_analyze_post_sanity_check_args(args) 174 | if not self.args.choose_args: 175 | raise Exception("missing --choose-args") 176 | 177 | def hook_create_values(self): 178 | values = range(0, self.args.values_count) 179 | return dict(zip(values, values)) 180 | 181 | def value_name(self): 182 | return 'objects' 183 | 184 | def convert_to_crushmap(self, crushmap): 185 | c = Crush(verbose=self.args.debug, 186 | backward_compatibility=self.args.backward_compatibility) 187 | c.parse(crushmap) 188 | return c.get_crushmap() 189 | 190 | def crushmap_to_file(self, crushmap): 191 | c = Crush(verbose=self.args.debug, 192 | backward_compatibility=self.args.backward_compatibility) 193 | c.parse(crushmap) 194 | c.to_file(self.args.out_path) 195 | --------------------------------------------------------------------------------