From 920d00545dfc4fc40c19ea51582f6d75751f3b16 Mon Sep 17 00:00:00 2001 From: Mathias Meyer Date: Thu, 14 Apr 2011 13:01:37 +0200 Subject: [PATCH] Squash Riaknostic commits prior to Weather Report fork I think I'm done with shell scripts for now. Port to Perl. Some cleaning up of the Perl code. Add check for ring_creation_size != num_partitions. Check if the Riak node is running. Prefer riak over riaksearch when looking for commands. Make it a proper hash. Fetch number of nodes in the ring. Look for crash dumps and emfile errors. Add checks for number of partitions vs. nodes and for not being part of the ring. Align status output for easier parsing. Check number of connected nodes vs. ring members. Use a better ratio for partitions vs. nodes. Add some checks if the node is running to prevent errors in checks. Start porting Riaknostic to Erlang. Remove io:format that's broken anyway. Remove shell script version. Fetch and start printing data from the Riak instance. Add Riak installation detection. Find Riak logs. Less code is good. Just tail-recurse it. Change run code to use a config dict. Add module to test for ring membership. Add more check modules. Add module to check for connected nodes. Fixed ping riak to work more consistantly. General code cleanup. Moved log directories into the riaknostic.app. Removed unneccassary filter code. Updated rebar to the current version Scriptized. Name and cookie can be passed in as parameters. Added a README Added type specs. Added more output to nodes connected. Added node to Config dict. Added initial version of disk check. Added noatime check for all mounted disks. Removed flag to specify vm name because it's not needed. Exrcised riaknostic node from connected node list. Removed perl script. Added dizzy's bitcask large value check. Better output from nodes connected Got rid of unnecessary sup. More readable output. Fixed incorrect application start callback return. Memory use stats Improved organization Added util library Added ability to output warnings and errors from riaknostic modules. Added a gen_server for logging. Improved logging with more logical strucutre Using list:keyfind for OTP release per Sean's comment Added conversion from binary to float Fixed issue with higher memory usage check vm.args can now be parsed in A bit of cleanup Integrated lager Improved code organization Moved from dicts to basic prop lists Added all ebin directories in riak lib to path Riaknostics are discovered via their run/1 methods Added lots of command line configuration Added sibling and vclock options to large value check Added a guard to bitcask_threshold_check function Key vals are binary_to_termed before printing. Inserted tabs in readme - usage Fixed broken lager:warning call. Add license headers to all source files. Closes #3. Upgrade rebar. Add a Makefile, copied from lager. Add check-module behavior according to plan. Starting refactor of check modules. Refactor memory use, add TODOs. Refactor ring membership. Added noatime check. Getting the DataDir from riaknostic_config won't work, but if DataDir is set correctly, it is a valid noatime check. Refactor nodes connected and fix some compilation bugs. Refactor ring size check. Fix typo/syntax error. Rename disk check module. Add ability to identify modules that are checks. Update TODOs. Refactor Joe's disk check module. Add a little documentation to the private functions. Add getopt, cleanup unused or antiquated modules. Switching to use a global notion of the config, probably app env but TBD. Do a little line-wrapping. WIP riaknostic_config accessors. Add top-level script with getopt and check descriptions. Remove high-impact bitcask check. Implement a huge swath of the runner, disk check works! application:get_env/2 returns {ok, Value}. Absolutize data directories. Expose base_dir/0 and etc_dir/0. Adjust crash dump detector to use base_dir(). Recognize -sname switch and distinguish between short and long names. Added riaknostic_node module for interacting with the local/cluster nodes. Fix a few bugs and enhance debugging information. * Messages are properly sorted now. * Match output of ps command properly. * Add debug logging of node-connection logic. * Improve detection of node connectivity. Fix docs target, ignore generated docs. Add edoc overview, initial stylesheet. Finish up some styles and documentation, more detail on behaviour needed. Add a more verbose description of the behaviour. Make clear that this stylesheet is for edoc. Initial version of the landing page. Don't need to link to edoc, reflow some of those paragraphs. Ignore parts of the gh-pages branch. WIP make pages. Add forkme ribbon. Make sure to ignore root PNG files and add the new image to the pages. Remove useless memsup info. Added Dr. Basho. Thanks @jgnewman! Closes #13. Build package tarball. Closes #12. Update the README. Minor wording correction, add missing docs to riaknostic_node. Add a word of caution. Solaris ps doesn't understand -o command and we don't use it anyway. Forgot to stage this line. Use -nocookie to prevent usage of the .erlang.cookie file. Closes #16. Fedora installs Riak libraries to /usr/lib64. Closes #18 Setup dialyzer. Fix dialyzer warnings. Check for: * Ring sizes not a multiple of 2 * Deployments where vnodes/node < 3% of ring size * Deployments where vnodes/node > 70% of ring size Change ring size inappropriate check from multiple of 2 to power of 2. Leave out the ring size/vnode messages until we have a better understanding of the relationship and can give better advice. Fix a few mistakes. Fix cluster_command 1 Added check for ring preflists satisfying n_val Check whether search is enabled on all nodes v1.0.1 Update lager dependency Changed regex split to string tokens. Fixes failure in Ubuntu Fix xargs argument for Linux eaccess -> eacces to catch the error correctly Add can_connect_all to check if all nodes are available. The reason for this, is that for search we're checking if search is enabled or disabled on all nodes. If a node is down, this is not a valid test, and errors out otherwise. Check if connected first before running all connected Travis CI config Ignore .eunit folder Add meck as a dependency Initial eunit test for riaknostic_check_ring using meck v1.0.2 Add Travis CI Build Status to README.md some work on the docs re: 26 & 29 add lines for the autosaves of the one true editor Added some reassuring output. Just a few lines so that the runner of the command knows that riaknostic is running and that it exited without error. Update README.md SmartOS has different paths from standard Solaris, using pkg_add package with current version 1.2 add basic sysctl checking Removed freeBSD stuff end of the day temp commit, code still kind of broken move stuff to zip rather than os:cmd added multiple platform support. a couple of bugs/features: - we also need to be able to just grab a copy of a file - we need a list of tests for each platform - need cases for sunos and freebsd - fold in regular diagnostic messages (once I land the fix for #14). - there is a bug in shelling out, only some of the output is actually recorded. another broken checkin, so I can work on something else added the ability to copy out named files changed the where the files were stored before cleanup to CWD. update to flesh out the export command a bit more. still needs much testing, especially on smartos clean up, fix some bugs, add directory-grabbing added a (bad) first pass at machine-readable output midstream checking to get back to work on export added a (bad) first pass at machine-readable output midstream checking to get back to work on export Changed getopt version, one other fix Fix default output Added some comments and TODO's Fix lager dependency version now that lager was updated Fixate lager dependency on 1.2.1 Change dep on lager to 1.2.2 to match the rest of riak Roll version riaknostic 1.1.0 Clarify that Riak 1.3 already has Riaknostic installed removed misplaced parathesis Add OpenBSD bits Update lager dep to 2.0.0rc2 Lager to 2.0.0 final Un-escriptize riaknostic and modify for lager 2.0 compatability Add an extra log line for clarity when running non-existent checks newline fix Restore riaknostic output to console When riaknostic became part of Riak instead of a separate app, its output (through lager) ended up in the node's console.log instead of being output by 'riak-admin diag'. Among other things, this broke the riaknostic_rt riak test. This adds a layer on top of lager, so messages can be directed to the console again, simply by using io:format. This way, messages are sent to the group_leader instead of the user process, which is what the lager backend does. When riaknostic is invoked through RPC by riak-admin, the caller becomes the group leader and picks up those messages. I wish there was a cleaner way to do this leveraging something in lager, but I couldn't find any. Roll riaknostic version 1.2.0 Pin meck dependency to a specific tag Remove sysctl checks Sysctl checks are now handled by the riak_kv_env module. Standardize meck dep Standardize on a rebar.config dep format to reduce conflicts pull app.config and vm.args from init:get_arguments added extra -vm_args to CONFIG_ARGS for easy access by erlang vm Roll riaknostic 1.2.1 to pull in lager 2.0.1 Fix rebar.config url to stay consistent Bump lager dep to 2.0.2 Bump lager dep to 2.0.3 - Adds a check for strong consistency configuration -- warning when strong consistency is enabled and AAE is disabled (defect https://github.com/basho/riak_kv/issues/959) --- src/weatherreport/.gitignore | 13 + src/weatherreport/.manifest | 5 + src/weatherreport/.travis.yml | 8 + src/weatherreport/DEVELOPMENT.md | 86 ++++++ src/weatherreport/LICENSE | 178 +++++++++++ src/weatherreport/Makefile | 85 ++++++ src/weatherreport/README.md | 146 +++++++++ src/weatherreport/dialyzer.ignore-warnings | 0 src/weatherreport/doc/overview.edoc | 23 ++ src/weatherreport/priv/ForkMe_Blk.png | Bin 0 -> 8149 bytes src/weatherreport/priv/doctorbasho.jpg | Bin 0 -> 23178 bytes src/weatherreport/priv/edoc.css | 130 ++++++++ src/weatherreport/priv/index.html | 183 ++++++++++++ src/weatherreport/rebar | Bin 0 -> 148150 bytes src/weatherreport/rebar.config | 36 +++ src/weatherreport/riaknostic.sh | 47 --- src/weatherreport/src/riaknostic.app.src | 34 +++ src/weatherreport/src/riaknostic.erl | 172 +++++++++++ src/weatherreport/src/riaknostic_check.erl | 102 +++++++ .../src/riaknostic_check_disk.erl | 165 ++++++++++ .../src/riaknostic_check_dumps.erl | 79 +++++ .../src/riaknostic_check_memory_use.erl | 61 ++++ .../src/riaknostic_check_nodes_connected.erl | 53 ++++ .../src/riaknostic_check_ring_membership.erl | 66 ++++ .../src/riaknostic_check_ring_preflists.erl | 51 ++++ .../src/riaknostic_check_ring_size.erl | 76 +++++ .../src/riaknostic_check_search.erl | 57 ++++ .../riaknostic_check_strong_consistency.erl | 58 ++++ src/weatherreport/src/riaknostic_config.erl | 282 ++++++++++++++++++ src/weatherreport/src/riaknostic_export.erl | 176 +++++++++++ src/weatherreport/src/riaknostic_node.erl | 208 +++++++++++++ src/weatherreport/src/riaknostic_util.erl | 89 ++++++ 32 files changed, 2622 insertions(+), 47 deletions(-) create mode 100644 src/weatherreport/.gitignore create mode 100644 src/weatherreport/.manifest create mode 100644 src/weatherreport/.travis.yml create mode 100644 src/weatherreport/DEVELOPMENT.md create mode 100644 src/weatherreport/LICENSE create mode 100644 src/weatherreport/Makefile create mode 100644 src/weatherreport/README.md create mode 100644 src/weatherreport/dialyzer.ignore-warnings create mode 100644 src/weatherreport/doc/overview.edoc create mode 100644 src/weatherreport/priv/ForkMe_Blk.png create mode 100644 src/weatherreport/priv/doctorbasho.jpg create mode 100644 src/weatherreport/priv/edoc.css create mode 100644 src/weatherreport/priv/index.html create mode 100755 src/weatherreport/rebar create mode 100644 src/weatherreport/rebar.config delete mode 100644 src/weatherreport/riaknostic.sh create mode 100644 src/weatherreport/src/riaknostic.app.src create mode 100644 src/weatherreport/src/riaknostic.erl create mode 100644 src/weatherreport/src/riaknostic_check.erl create mode 100644 src/weatherreport/src/riaknostic_check_disk.erl create mode 100644 src/weatherreport/src/riaknostic_check_dumps.erl create mode 100644 src/weatherreport/src/riaknostic_check_memory_use.erl create mode 100644 src/weatherreport/src/riaknostic_check_nodes_connected.erl create mode 100644 src/weatherreport/src/riaknostic_check_ring_membership.erl create mode 100644 src/weatherreport/src/riaknostic_check_ring_preflists.erl create mode 100644 src/weatherreport/src/riaknostic_check_ring_size.erl create mode 100644 src/weatherreport/src/riaknostic_check_search.erl create mode 100644 src/weatherreport/src/riaknostic_check_strong_consistency.erl create mode 100644 src/weatherreport/src/riaknostic_config.erl create mode 100644 src/weatherreport/src/riaknostic_export.erl create mode 100644 src/weatherreport/src/riaknostic_node.erl create mode 100644 src/weatherreport/src/riaknostic_util.erl diff --git a/src/weatherreport/.gitignore b/src/weatherreport/.gitignore new file mode 100644 index 00000000000..a74403bdd99 --- /dev/null +++ b/src/weatherreport/.gitignore @@ -0,0 +1,13 @@ +doc/ +deps/ +ebin/* +log/ +edoc/ +index.html +riaknostic +*.png +pkg/ +erl_crash.dump +.eunit/ +*~ +#*# \ No newline at end of file diff --git a/src/weatherreport/.manifest b/src/weatherreport/.manifest new file mode 100644 index 00000000000..0fb9b124969 --- /dev/null +++ b/src/weatherreport/.manifest @@ -0,0 +1,5 @@ +src +riaknostic +doc +LICENSE +README.md diff --git a/src/weatherreport/.travis.yml b/src/weatherreport/.travis.yml new file mode 100644 index 00000000000..ec6b937b095 --- /dev/null +++ b/src/weatherreport/.travis.yml @@ -0,0 +1,8 @@ +language: erlang +notifications: + disabled: true +env: + - R15B + - R14B04 + - R14B03 + - R14B02 diff --git a/src/weatherreport/DEVELOPMENT.md b/src/weatherreport/DEVELOPMENT.md new file mode 100644 index 00000000000..13337ab6720 --- /dev/null +++ b/src/weatherreport/DEVELOPMENT.md @@ -0,0 +1,86 @@ +# Riaknostic Development + +Riaknostic requires a sane GNU build system and a recent version of +Erlang. It has `lager` and `getopt` as dependencies, so those must be +compatible with your version of Erlang. Release versions are currently +built with Erlang version R14B03, while development versions are targeted at Erlang version R14B04. + +See the `rebar.config` file for more details. + +To build Riaknostic, simply run `make`: + +```bash +$ make +./rebar get-deps +==> riaknostic (get-deps) +Pulling lager from {git,"git://github.com/basho/lager",{branch,"master"}} +Cloning into lager... +Pulling getopt from {git,"git://github.com/jcomellas/getopt.git","2981dfe"} +Cloning into getopt... +==> lager (get-deps) +==> getopt (get-deps) +./rebar compile +==> lager (compile) +Compiled src/lager_util.erl +Compiled src/lager_transform.erl +Compiled src/lager_sup.erl +Compiled src/lager_mochiglobal.erl +Compiled src/lager_stdlib.erl +Compiled src/lager_handler_watcher_sup.erl +Compiled src/lager_handler_watcher.erl +Compiled src/lager_trunc_io.erl +Compiled src/lager_crash_log.erl +Compiled src/lager_file_backend.erl +Compiled src/lager_app.erl +Compiled src/lager.erl +Compiled src/lager_console_backend.erl +Compiled src/lager_format.erl +Compiled src/error_logger_lager_h.erl +==> getopt (compile) +Compiled src/getopt.erl +==> riaknostic (compile) +Compiled src/riaknostic_check.erl +Compiled src/riaknostic_util.erl +Compiled src/riaknostic_node.erl +Compiled src/riaknostic_check_ring_size.erl +Compiled src/riaknostic_check_ring_membership.erl +Compiled src/riaknostic_config.erl +Compiled src/riaknostic_check_memory_use.erl +Compiled src/riaknostic_check_nodes_connected.erl +Compiled src/riaknostic_check_dumps.erl +Compiled src/riaknostic.erl +Compiled src/riaknostic_check_disk.erl +./rebar escriptize +==> lager (escriptize) +==> getopt (escriptize) +==> riaknostic (escriptize) +``` + +Now you can invoke the script manually via the below command: + +```bash +$ ./riaknostic --etc ~/code/riak/rel/riak/etc --base ~/code/riak/rel/riak --user `whoami` [other options] +``` + +To generate the edoc reference, use `make docs` and then open the +`doc/index.html` file in your browser. Detailed discussion of the +internal APIs that you can use in developing new diagnostics is found +in the edocs. + +## Contributing + +Have an idea for a diagnostic? Want to improve the way Riaknostic works? Fork the [github repository](https://github.com/basho/riaknostic) and send us a pull-request with your changes! The code is documented with `edoc`, so give the [API Docs](http://riaknostic.basho.com/edoc/index.html) a read before you contribute. + +### Developing for Riaknostic Without a Riak Instance + +If you want to run the `riaknostic` script while developing, and you don't have it hooked up to your local Riak, you can invoke it directly like so: + +```bash +./riaknostic --etc ~/code/riak/rel/riak/etc --base ~/code/riak/rel/riak --user `whoami` [other options] +``` + +The extra options are usually assigned by the `riak-admin` script for you, but here's how to set them: + +* `--etc`: Where your Riak configuration directory is, in the example above it's in the generated directory of a source checkout of Riak. +* `--base`: The "base" directory of Riak, usually the root of the generated directory or `/usr/lib/riak` on Linux, for example. Scan the `riak-admin` script for how the `RUNNER_BASE_DIR` variable is assigned on your platform. +* `--user`: What user/UID the Riak node runs as. In a source checkout, it's the current user, on most systems, it's `riak`. \ No newline at end of file diff --git a/src/weatherreport/LICENSE b/src/weatherreport/LICENSE new file mode 100644 index 00000000000..e454a52586f --- /dev/null +++ b/src/weatherreport/LICENSE @@ -0,0 +1,178 @@ + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + diff --git a/src/weatherreport/Makefile b/src/weatherreport/Makefile new file mode 100644 index 00000000000..546f13c629e --- /dev/null +++ b/src/weatherreport/Makefile @@ -0,0 +1,85 @@ +.PHONY: rel stagedevrel deps test + +all: deps compile + +compile: + ./rebar compile + +deps: + ./rebar get-deps + +clean: + ./rebar clean + +distclean: clean + ./rebar delete-deps + +test: + ./rebar compile eunit + +escriptize: + ./rebar escriptize + +## +## Doc targets +## +docs: + ./rebar doc skip_deps=true + +pages: docs + cp priv/index.html doc/_index.html + cp priv/ForkMe_Blk.png doc/ + cp priv/*.jpg doc/ + git checkout gh-pages + mv doc/_index.html ./index.html + mv doc/ForkMe_Blk.png . + mv doc/*.jpg . + rm -rf edoc/* + cp -R doc/* edoc/ + git add . + git add -u + git commit + git push origin gh-pages + git checkout master + +## +## Release targets +## +VSN = `grep vsn src/riaknostic.app.src | cut -f 2 -d "\""` + +package: all docs + @mkdir -p pkg/riaknostic + @rm -rf pkg/riaknostic/* + @cat .manifest | xargs -n 1 -I % cp -R % pkg/riaknostic/. + @tar -czf pkg/riaknostic-$(VSN).tar.gz -C pkg riaknostic + +## +## Dialyzer targets +## +APPS = kernel stdlib sasl erts ssl tools os_mon runtime_tools crypto inets \ + xmerl webtool snmp public_key mnesia eunit syntax_tools compiler +COMBO_PLT = $(HOME)/.riak_combo_dialyzer_plt + +check_plt: compile + dialyzer --check_plt --plt $(COMBO_PLT) --apps $(APPS) + +build_plt: compile + dialyzer --build_plt --output_plt $(COMBO_PLT) --apps $(APPS) + +dialyzer: compile + @echo + @echo Use "'make check_plt'" to check PLT prior to using this target. + @echo Use "'make build_plt'" to build PLT prior to using this target. + @echo + @sleep 1 + dialyzer -Wno_return --plt $(COMBO_PLT) ebin deps/*/ebin | \ + fgrep -v -f ./dialyzer.ignore-warnings + +cleanplt: + @echo + @echo "Are you sure? It takes about 1/2 hour to re-build." + @echo Deleting $(COMBO_PLT) in 5 seconds. + @echo + sleep 5 + rm $(COMBO_PLT) + diff --git a/src/weatherreport/README.md b/src/weatherreport/README.md new file mode 100644 index 00000000000..627c8826349 --- /dev/null +++ b/src/weatherreport/README.md @@ -0,0 +1,146 @@ +# Riaknostic [![Build Status](https://secure.travis-ci.org/basho/riaknostic.png?branch=master)](http://travis-ci.org/basho/riaknostic) + +`riaknostic` is an escript and set of tools that diagnoses common problems which could affect a Riak node or cluster. When experiencing any problem with Riak, `riaknostic` should be the first thing run during troubleshooting. The tool is integrated with Riak via the `riak-admin` script. + +## Overview + +To diagnose problems with Riak, Riaknostic uses a series of checks which are derived from the experience of the Basho Client Services Team as well as numerous public discussions on the mailing list, IRC room, and other online media. + +Here is a basic example of using `riaknostic` followed immediately by the command's output: + +```bash +$ riak-admin diag +15:34:52.736 [warning] Riak crashed at Wed, 07 Dec 2011 21:47:50 GMT, leaving +crash dump in /srv/riak/log/erl_crash.dump. Please inspect or remove the file. +15:34:52.736 [notice] Data directory /srv/riak/data/bitcask is not mounted with 'noatime'. Please remount its disk with the 'noatime' flag to improve +performance. +``` + +As shown in the above output, Riaknostic tells us about two problems right away. First, an Erlang crash dump is present, indicating that Riak has experienced a crash. Second, a performance problem is mentioned (disk mounted without `noatime` argument)along with a helpful tip to resolve the issue. + +## Installation + +**Important**: If you are running Riak v1.3.0 or greater, you already have Riaknostic, so you can skip to the **Usage** section below. + +Riaknostic depends on features introduced by Erlang version R14B04, so verify that you've installed this version of Erlang before proceeding with installation. + +To install `riaknostic`, download the latest package version, and extract it within the directory shown for your operating system in the following table: + + + + + + + + + + + + + + + + + + + + + + + + + + + +
PlatformDirectory
Linux (Redhat, CentOS, Debian, Ubuntu)/usr/lib/riak/lib
Linux (Fedora)/usr/lib64/riak/lib
Solaris, OpenSolaris/opt/riak/lib
SmartOS (Joyent)/opt/local/lib/riak/lib
Mac OS/X or Self-built$RIAK/lib + (where $RIAK=rel/riak for source installs, + or the directory where you unpacked the package)
+ +An example Riaknostic installation for Linux looks like this: + +```bash +wget https://github.com/basho/riaknostic/downloads/riaknostic-1.0.2.tar.gz -P /tmp +cd /usr/lib/riak/lib +sudo tar xzvf /tmp/riaknostic-1.0.2.tar.gz +``` + +The package will expand to a `riaknostic/` directory which contains the `riaknostic` script, source code in the `src/` directory, and documentation. + +Now try it out! + +## Usage + +For most cases, you can just run the `riak-admin diag` command as given at the top of this README. However, sometimes you might want to know some extra detail or run only specific checks. For that, there are command-line options. Execute `riaknostic --help` to learn more about these options: + +```bash +riak-admin diag --help +Usage: riak-admin diag [-d ] [-l] [-h] [check_name ...] + + -d, --level Minimum message severity level (default: notice) + -l, --list Describe available diagnostic tasks + -h, --help Display help/usage + check_name A specific check to run +``` + +To get an idea of what checks will be run, use the `--list` option: + +```bash +riak-admin diag --list +Available diagnostic checks: + + disk Data directory permissions and atime + dumps Find crash dumps + memory_use Measure memory usage + nodes_connected Cluster node liveness + ring_membership Cluster membership validity + ring_size Ring size valid +``` + +If you want all the gory details about what Riaknostic is doing, you can run the checks at a more verbose logging level with the --level option: + +```bash +riak-admin diag --level debug +18:34:19.708 [debug] Lager installed handler lager_console_backend into lager_event +18:34:19.720 [debug] Lager installed handler error_logger_lager_h into error_logger +18:34:19.720 [info] Application lager started on node nonode@nohost +18:34:20.736 [debug] Not connected to the local Riak node, trying to connect. alive:false connect_failed:undefined +18:34:20.737 [debug] Starting distributed Erlang. +18:34:20.740 [debug] Supervisor net_sup started erl_epmd:start_link() at pid <0.42.0> +18:34:20.742 [debug] Supervisor net_sup started auth:start_link() at pid <0.43.0> +18:34:20.771 [debug] Supervisor net_sup started net_kernel:start_link(['riak_diag87813@127.0.0.1',longnames]) at pid <0.44.0> +18:34:20.771 [debug] Supervisor kernel_sup started erl_distribution:start_link(['riak_diag87813@127.0.0.1',longnames]) at pid <0.41.0> +18:34:20.781 [debug] Supervisor inet_gethost_native_sup started undefined at pid <0.49.0> +18:34:20.782 [debug] Supervisor kernel_safe_sup started inet_gethost_native:start_link() at pid <0.48.0> +18:34:20.834 [debug] Connected to local Riak node 'riak@127.0.0.1'. +18:34:20.939 [debug] Local RPC: os:getpid([]) [5000] +18:34:20.939 [debug] Running shell command: ps -o pmem,rss,command -p 83144 +18:34:20.946 [debug] Shell command output: +%MEM RSS COMMAND + 0.4 31004 /srv/riak/erts-5.8.4/bin/beam.smp -K true -A 64 -W w -- -root /srv/riak/rel/riak -progname riak -- -home /Users/sean -- -boot /srv/riak/releases/1.0.2/riak -embedded -config /srv/riak/etc/app.config -name riak@127.0.0.1 -setcookie riak -- console + +18:34:20.960 [warning] Riak crashed at Wed, 07 Dec 2011 21:47:50 GMT, leaving crash dump in /srv/riak/log/erl_crash.dump. Please inspect or remove the file. +18:34:20.961 [notice] Data directory /srv/riak/data/bitcask is not mounted with 'noatime'. Please remount its disk with the 'noatime' flag to improve performance. +18:34:20.961 [info] Riak process is using 0.4% of available RAM, totalling 31004 KB of real memory. +``` + +Most times you'll want to use the defaults, but any Syslog severity name will do (from most to least verbose): `debug, info, notice, warning, error, critical, alert, emergency`. + +Finally, if you want to run just a single diagnostic or a list of specific ones, you can pass their name(s): + +```bash +riak-admin diag dumps +18:41:24.083 [warning] Riak crashed at Wed, 07 Dec 2011 21:47:50 GMT, leaving crash dump in /srv/riak/log/erl_crash.dump. Please inspect or remove the file. +``` + +## Contributing + +0. Read DEVELOPMENT.md +1. Fork the project on [Github](https://github.com/basho/riaknostic). +2. Make your changes or additions on a "topic" branch, test and + document them. If you are making a new diagnostic, make sure you + give some module-level information about the checks it + performs. *Note*: diagnostics _should not_ make modifications to + Riak, only inspect things. +3. Push to your fork and send a pull-request. +4. A Basho Developer Advocate or Engineer will review your + pull-request and get back to you. \ No newline at end of file diff --git a/src/weatherreport/dialyzer.ignore-warnings b/src/weatherreport/dialyzer.ignore-warnings new file mode 100644 index 00000000000..e69de29bb2d diff --git a/src/weatherreport/doc/overview.edoc b/src/weatherreport/doc/overview.edoc new file mode 100644 index 00000000000..30dbd18af81 --- /dev/null +++ b/src/weatherreport/doc/overview.edoc @@ -0,0 +1,23 @@ +@author Basho Technologies, Inc. +@copyright 2011 Basho Technologies, Inc. +@version 1.0.0 +@title riaknostic: Automated diagnostic tools for Riak +@doc

riaknostic is an escript and set of tools that diagnoses common problems which could affect a Riak node or cluster. When experiencing any problem with Riak, riaknostic should be the first thing run during troubleshooting. The tool is integrated with Riak via the riak-admin script.

+ +
$ riak-admin diag
+ +

This documentation describes the riaknostic API and user interface commands. The application's core consists of 5 modules:

+ + + +

All other included modules are generally prefixed with riaknostic_check_ and are individual diagnostics that can be run.

+ +

riaknostic is licensed under the Apache v2 license.

+@end \ No newline at end of file diff --git a/src/weatherreport/priv/ForkMe_Blk.png b/src/weatherreport/priv/ForkMe_Blk.png new file mode 100644 index 0000000000000000000000000000000000000000..d50902d2dac6bbf774fd703feafc1596a6c931d9 GIT binary patch literal 8149 zcmbVxby$?o+VIj1N=bvHNbD{x(y_2~NwcslwRB3CD2OaAu!KknNH>Te3rK@>hX_cA zgp`Qi`aADA-}lG4-ao$Qy7rl8X78A}bMBc~JsmX)QYKOW06+m%S24i0@qd5BgxL2h z*ws61!|1DO>TBrX;Oh_fwg)KMdDz->K;7Vu_6GKFyFjl2dl>)#&jD#<>T9a4C5`ZK z6NdkV5e{(k#9{*gGV%eQaDH>2Z103r5AwD* z4AL<|1i2s{*>TFtamWNnV+pv~`@%T_++5v#qyuC*|Dh|5{r)=)Qc{pZ1Ox^FK_Wn~h!9v(T2w?D1mgIghZD=q z+s;ASKn3zYys$l4PA6YqPiY{~-``)@UsTw`+YtzU^ytxF8X_V>SO_7XKzCnwfRMWn z*S{&K*!v*7k)FOt4|k5g6ydfWe!jAt*qQ!o32vU++W#T!?(;u_!io$S0QUrfg+V|! zx4-N92inKi!2bVE_5VGNIPi|SPBFNONxOcML-f#V6c=ZSVb8msVoIj1%Z{t!T+Z5 zUvX8XBt@iDRV5*RX{dsvBqYR?6d}rwBtef9MI`@&3w8JLg}Wo{|IHWqm+!xDRsL68 zX=QJFxUYw|k%x!tzb!z|$-~#f$H~K!L)lP_LmQ4jy8rF}RnNaxt77ksd}?n8@%C`z z_(#6d$p6A$0wOK~5*HT(W1IiJ?tkIh{r|`eh@A}ZuW|f8#`5nWtOfr4{Ez8lJO6PW zdv~nIcw=3oo-Dl%0AL}2swf%-EbLn$J&k75nrgwz#y@n^wQr zH!u3w<@rKQg{MLt{M#gY-Y$Wds^mO@(%Ef~_|*js4UKjtqXTtNV?&cHcy6&SE) zi#ME_mGK}ePOSMxTa!e_hSLbMWR{thhJ>|>C1hpG7mttYXJuuPl49%}vY^}IGGdn> zZ*xa;>%8eR6vm$oaKw5l;LzL`9JUgOPYVou#2d0`v9R#Ie9u|0?QLnP`rzo${d|+) z;6=^u-v@wU?Wdg{VeocdF%?rXURCofMu3*@(n)(}CS!3?k&1Niq0$(DFeNQhQ3*(h zZYKwg?qa^-Q@2ki=kQ`8%rc!5ZV#>gCX(P_8V zNECIX9r7zgKuvQEkno~=^5fbl%Y?*4L(q?&A3r=dQ--i$V3&nP=jm33Kte-*E8?Jg;<#S!s47+G9=hY8v)x}F;XiVS zurA;mdw$lNH1JwSsan9KikNa;(uVu)`DE9|<|ae(EBW(pB&~s`4r6U4H#aQQK}dBE z1pv)SUZ$|kp=mN~pv&3N*N#?PT%3r)#>NZ{<37tG!8~sW&Jwms=cy7Y__Pn^?$lgFdA3%#vXZn%#}HmnoHgOIfbMHP|IOKsvg!p|XpCrQI0XdE zF@i2Do#Y9A1NUZoSV@Pe+#u1h&tZ7KLnjGQYf*G0k2FQCFjzl17;3U()Zce;Im$~& z5MJ)6c=vh#fDtU^c2m)73VF?p=K@txm-Fci>xE0mJn3nW7f42l#u82X&I zkzuxv^Yvgl5>)MP!uXjC?HXmW!62yj84`f*7Zj_9LRp$=27yNo86YjHr! zZq?ea)t4n6lW%V?zwV!hQQlF_EU2S!LGGoGoc&0Tc_1MHc@anB=|0l6v0;kU0OID_ z4M(eAMfcVey;h*ab-T8+Ti+(OszXZ=u)E95_?0og@2SUDl9P*Dp$X-FZF|#N*jBDXDa$&p7Ck2V`yaeE zx{9Y-;&mP8)R3&=%r|jVQ^VlUOyoFR2cn;(xoB!?9cyNsP!*Sy%y$1NkTPZb<_vg! z4!5=v zZ;bq)i(x&Hr@KRGXM25NJ-M3sQ&Y6J6RCW2>LxAuJ|AtBM5trSZ@aY(ncyZ)Wu zGEFW-%_1)o?ieV;0uK2f0>5G^2iJ9P_`)f8nkvQZ~ukllIW0v#)FZr*Cb z;pG;Lmp8=2Mp?*)?84&Xz~x?hsY)qa2rVPKO~lpDL)M!ot3n>EVKEl>rdfZd$p2Z( z4Q;4(#pCBLklQUUh;yGY zb;wBwu=FxJ(Z67_(bLp`eVAaNV}(+<#~jlc&l`xXY?A#Fr8u61zhIMAmqZjFp|D^yav1ezXQ3 z3UlS(I|0=Xz&Ba%=3*lz@6r<_HY)Y?^iJ@|p~6R0``#cL7hv>yrB$Cgjp+e;31L6( zg^?v;gm6?IezVVUxBwnRCf&mbTDBx54wAM<6B0$eI{Ol+xjqWI-Mx{aQJ9#Rfmm5B zuh94x5WgSjA&%op@vR*G8FD3H-TeM+HR_=aM}IfVO?NgMP89s+*^LZ#Z_(UHq{im( z$&{(X87f$zqmC+BIQ5gp%fS}Uq6B^K6bDTpHa zY7^pkk5%R@nv@}`Tn42`W1J1$rW1_FluYk^Yi?lfT&CYh$~rYMvD+vJD2O90OGS;{ zZu2GTW%444BAK6QPNWkU)$C8Ta7OigW_5<{lp8>|{l2H_J|hMBtW03N6s;*@+x0_B zTc}Z*yzavTvN_AUUSayZf@FtW)X$ioVOsONDc(D>sF~)xA{9S-O2yE3VPu-5rc|x_ zo59Y|VTUiQGX!62jg>zHk1nhr8wh9k6J6Y{NX2jrE{#xpd6QNhmU{bbkq*_dF*eHn zo3Wm|+Go!eNVeha2Z2CbH|BJL283c_U%p&rE`1K0g{V1Jw!>kqB|B8>CB{|!{1Vza zIum{D*7=`qar(j-+%n$$1Yeu8g>LZOd@CNIIvuACp%cnl6>)yqo|=J+fdf)9&iB`o zC*p#`Mo&Tg4}vw8a6s=|Ua4o^Xf{~`JIuE zwo;xgOk64l#qaF&@@UeZ-lduCzHHTbe7+iWahow4Z3D+x(^BMdctR=L!Gh9VU!=KD z7Z_q?Z_Yo$oS|SliFc`EdxI>iAfK}Hjl>uHUu#xaXqdIt5(Alf{RI_lUl$c$E3fR~ zQm#8C(YkA|M*sNnM8Gqkr~#1?eI^|}aSbV}`|0x6CVqoXd4sZ^t_jb#02gh+Y7jL8 z#N;~*re1b7eG)+oCNAzg@ny5?+OxDs5so&%Z2Lu6Jk@+K9yz_VMV$zCr)UBYbu8cu z*T6$-YS?^p@%w4EqOq!P*>L+->gsMsXu!|~Yi{3Xzyd4a0f5Lk^eDfwVJAQ4jTDLv z^nl_u?Y*S2y{Fo8(d14y{BjO@{j9O*5kVHRS&}>ZywbwL88*oIM%2;S z%yd~7rj1%-5RWZ^Bf~n&CqC~%R>1{S(I4;gzAV$zJei@2f~_>Z{P+&jGk1VY5dw-=tQZz+zHu2ocu5_hc*-Y5{rj`z$B_OWw zg(6+&vPRO95Li~;b z7oU*zx!A)!A@W`=(A9&pEX@N7A6&6Xg5i^o6Q-tCc3C;-Jb|}ikaieW?5+1}Rn$>A zm{&Hp>hUkbdL!#&@k+)C?}Q3O31B$7hq^+kgRI(v{B-Eqqa;3T9a$O}*vd?UZ5P)4 zYU>e+s2Y%`c%s~i;of<4V!$myAj27E)dhI;72#)QH*~hpX**VgXrM6Sr4I7*^BZBt z;H&l@=nB0rt--L+uqroh?FP`a0O5^Kbx|#GxJ6EI%o*U?I^coZK?6;m_l9uLo^3S` z9sNe?n2TLN04#bs*G|929DUPf$>oqKFB3MgH4zb}OHO#)aU9>#O{Dp!uOiMZII1!6 zVzpCp=MGo5-swU2QR_j}xJKu$C3Vn9$%KO52J(inx6gK~p@FtFpmu&_qfp@U1Ij43 zg=8Ay{=;xnK0=jj-U?LcuKuLk7A^>&VeTwK((Zsb@q+mGkk^KDF{@ePbne zo0zaz%2Dv1R2FQ?iT?$+hU3DPSrz`7VbyE5X_5qDH?F{Q=Ir7^1|<_kGjy(rzVjG;1^w3huyrav^xFD{@a9&->r_GJ*M#&e5r*8jG}744hr6~ z5<(g;r^*-B%}suE8dqAGn_3y)5jCaF$ZzVhqKSwiCV77EWJ{iPE&r+44kTC}C1Egu zL--e5;LI6`Bq1U5SzM6)T??A0j{{@N)|JqsQr$-Gi|o#Wi?;zMjDv`LF?C=unT;QS z;rSA!A>KxApPi$H`nGt63sez_#9D4C&vIa&_w~jk{aa_KFdvE7PcET?G0^Qzz3xJP zO;&&KoVn%+DC4srIe_;pYp<~=Z;1HUV^rdwj;3>&cq{gc1*&!D&O7+YDW;mm_iBUa zPG%O5PG&DfP{1=LR#u8fl%j=s$0h5+-L4XmHU<5&%gez-v5%8uaQyGiTPCd>JbnrW z#5<==(c<+Zgh*KZ(M)%O{GR$oJFqzvOU26*xzbgA+3LQWZqr8wMRXqEQ7w();MG~c znj#&8!I)m=VlhCi1%15gI1V9^D;Qg%(UnDH)+Q}h*GP+Yzec}S-x8i0pDtg9VK}2t z=`1F6#w$|pX&Y2l9^A-L2h2&+-MgoJUeM@+#5@s-{aXqqoIfQ&D?Iy~XFGR8MAbL1 z$bR~+_iD3G0u!d4RcqP95nuB@%%(SV5B=FnBw`#L@Y@ui44-p>mY@1Sw_k)YkT8(u z1fTdb$P3D#%F1+tiBOY)cn{URPwTE0sA}U$?^#kil!GJ}9Aao@xEnP{e`bxqwVS|L>K{lhhz1OvTA!2RrnJB&GylgnB!?y|7y0<4YzD5o|*5hRBt#c=z}OI$<@hd z)OFUWc1A$isC^uHi*ZNH_8^}60{o2(0FMl7+c^ZrFMw)GRbPza^yjaWA=TZNW|`QS z0>$8?CodnP*oWk{FA`aN!Z!ki!CTc2 zj6Qw=Yd#(ByV}g9$EOCNLxu;M&|HO_JGO;e%1W%GRZpMj)=@7gJHKE$ozi4<>`Z_e zRemeZ4L{^}*KLa*v^@bYra=GEfJN30F0b%y)!;|T^kWL6Euh%g+#GtS=Zg-38lPdI z)WJ*r#Lrg=)2*)G7uob)F4g;YWUm!AiPc0%)b(RCnY0MY$!`!&d@eR2+SZEx(&j7I zG4$|wd*KaGchiwL+MFd_VE9aW=YVzt@zH-SqEJFNJ_$|#!zkpltz-V|bd@j?j=Ph4 zGf&kWbG$y4?>`&iRSf#*?_P*U1lzEiecDgI4qE8TYKHqf;eS7Hqb~0e5lh{DY240j zgKG)-QLQT4{jyuCR$(5YTYcDuLq6ACmZuS~1*A-k;OPuiJ!&O)o}6l8O&s4Gk+;kD zD~#*;l29ZmFsY?fJ!72NLiWH38%jrqM!<+mRzOP{h0!H#gQ(i>)in5NMiJ|jq*EHA z=)$%zW}e%RL0+;CYj}FBqM7;^?>>e!fi9M;++ht)ci%{dZq7s>yEPW^-}+y!InYBE zpB{@mLg6)+$*q+e$5cLIQ*+&;h+FPjYWKn;Y2WMyh3Qel1=K?bwYpAT4?ZcM(CRP4 z7IylM85ia$lb}zH>Cd%}Tw-8b*3>a>@W%mV#h;!ZWX>7)2%)lJ4C(;^QkCXu8cNko z5<4jlbtq4=p1y{`lr>IzW8EKC-VD7L8iB0ma&hy!3_%GmhWi_fa@3nD`SCn4KA)PA zWIaOh^@p+gr=TyItot6W$7{s4LCIre-IvrL(Y@TR5I^YERiHz?7u{%S0*v_-{ryQ! zms@b(@=PVwarc+|syy}*v)o_8X$2IRDzaI~VVfxcx%}C&u_XLqa*M1N#-0I{JoXlX z#ZC}mpw*|YX36bSV1k0z3VD_6%;`ue&az-nMTXqBUR z?Y()txq;48dXn~5J5$y>^B;UlOf+eBM!H^Sc!1Q@Amg>`*F9fy9g-U6t$wN!m(<3R zQmj_Gxqb2DHf4z>&jAG&?7p65^=Ht(8#M4>=bZNf-=?d=L<*UO5i-kkm~zh&{$RSl z@7qGJFZgTcYpcB|zunQ+_t=lii;s>~*@wruVs>II{$w^wmOApi{}|MX08q)=oPDFt z!X(Gs#X9T~Y#f(&DM)oL`j~O&4LYmHc~ht1er+mUZELP+Zbk68gaN1VXM`IgwSWJ} zl(-m3?o@O!l6JA;fP%Okte=-Fz8coY8hxCJJ@-mEpr}v1`?N-c90a`)qp2UDW$$>M zl`R~|-n+F$K8F+KEB~e1xMJ(UcW^WQ)!x9vpLt^=yWh+KlnatxGXTnlF+7NMWi;AU zUFa;O`?8*j3H?G zf6W>w;M{MITJrxo>nA+mhXhRGI*ro?eQV=J!X2o@mIj}{Q0wYiWw3>5mc5rfQl@En zSSm$k!_ka8#F^06NiZSMLS`;1SvQNJQ`{$d6U^H_x59CjqP}#6Koq7wJKcQ)|Syh?_QDH3}Nui78j?F zelh6@R|3u}(BY3i2~e48EuAYe>9~0p&v=e4cw8L=ait#rx3h zxi#!lZnNV_>eA(h=?y9C%6gD>Ye~*O1(%%=Ke3W~_X!>22=98CR?gjcr=pJ^`!2$* z;6OoK4BQ zCxyb-x?^5&`F#iHdUH2nV0#?~gf;CZ6JcE^bRL3%!~r zi$2gm{xKcskh#=Tia=1`*GO#iLLGx2p39rW@Ld5sCspU}mq~`nTd!&;LSt(~g(u3P zp`p~E&M!y@ObhZd{x@foOU7$>C6Bn6#OXxDaQE|^cjHlg?HrSF=Y%W`FFr3TbTojd z#bDf$i(P`lB0)#};16?V&wA_gydLt{^QdC0vhE&U5(W~cqkKy!G{5y}V{1Cqx+X@Q zbKkh89jE(}K_BV-NlQ5juP#!Y*X&li8fK?#qdZfQ(^sQEcJ*1AcSm zO^cr5t_DsGXjS9KQyCZ~jZ)2(Nk0*~_gM2{!>pzxW0VI>HaT?zwqj>TqAmno?FyPJtonjFJkP z!5v#eq<9QYOEZsbNHmq-_wy}bqJ_=fI1zVP+e{D-vQd}f0TfAUJ; z5#Br%mX}aP6Xm+7rs3?^0~~DE>J_PpXhuWCsBxhf2M$+kjfLW=q%(Bx>9p^TNIW^! zkXChr*dYPqNur$p7wW&LIbaJA^PLv zzK52q@d^F#5Pq0~VU2Xghhm4b#WIOwekh2)6jHLd4h)Z3&xqvg}>p|Zh5K^D2m^pIDYn%Hz*FLLSqx!!S9-`e`< z?@h|6dF1hn!<2~Ry!(SqcE=xzE~eo(U$HkD2!hYgH?iXd&w6bKinh@YTIc#>j;$GX zJ%R^GU-1hloUJBDkob*pc$tK@Mpwnh%MJlfiwHruEU8SZ>KOHW)ePZqEiNuDU8?g& zIrmVV@kmIx01nNXt!et}^Tp1U^si32lg}Vj97GtcaM9AWt54lKtqI|yi-=|Gj5SpV z;bRY0vHe%Pzp6x!D|06z5E>VzUdKpHlG<&g0TGi&20jX(Li@uHx+(=u9Il?ui54!_ zS46m+JkJ#iFEM&NHbqi*;N3#cr45Q&_DhmElGpTats`&%)6nD4h=S_bzZdOLRUMTo IC7Wme2m4(WlmGw# literal 0 HcmV?d00001 diff --git a/src/weatherreport/priv/doctorbasho.jpg b/src/weatherreport/priv/doctorbasho.jpg new file mode 100644 index 0000000000000000000000000000000000000000..7e0895f4914bb1f12d8f73362e1bcdabda2a6958 GIT binary patch literal 23178 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 ZhQaIr01Xj(z@;y+05nDV(Gj9A|Jg(0{8#`0 literal 0 HcmV?d00001 diff --git a/src/weatherreport/priv/edoc.css b/src/weatherreport/priv/edoc.css new file mode 100644 index 00000000000..1d50defeb96 --- /dev/null +++ b/src/weatherreport/priv/edoc.css @@ -0,0 +1,130 @@ +/* Baseline rhythm */ +body { + font-size: 16px; + font-family: Helvetica, sans-serif; + margin: 8px; +} + +p { + font-size: 1em; /* 16px */ + line-height: 1.5em; /* 24px */ + margin: 0 0 1.5em 0; +} + +h1 { + font-size: 1.5em; /* 24px */ + line-height: 1em; /* 24px */ + margin-top: 1em; + margin-bottom: 0em; +} + +h2 { + font-size: 1.375em; /* 22px */ + line-height: 1.0909em; /* 24px */ + margin-top: 1.0909em; + margin-bottom: 0em; +} + +h3 { + font-size: 1.25em; /* 20px */ + line-height: 1.2em; /* 24px */ + margin-top: 1.2em; + margin-bottom: 0em; +} + +h4 { + font-size: 1.125em; /* 18px */ + line-height: 1.3333em; /* 24px */ + margin-top: 1.3333em; + margin-bottom: 0em; +} + +.class-for-16px { + font-size: 1em; /* 16px */ + line-height: 1.5em; /* 24px */ + margin-top: 1.5em; + margin-bottom: 0em; +} + +.class-for-14px { + font-size: 0.875em; /* 14px */ + line-height: 1.7143em; /* 24px */ + margin-top: 1.7143em; + margin-bottom: 0em; +} + +ul { + margin: 0 0 1.5em 0; +} + +/* Customizations */ +body { + color: #333; +} + +tt, code, pre { + font-family: "Andale Mono", "Inconsolata", "Monaco", "DejaVu Sans Mono", monospaced; +} + +tt, code { font-size: 0.875em } + +pre { + font-size: 0.875em; /* 14px */ + line-height: 1.7143em; /* 24px */ + margin: 0 1em 1.7143em; + padding: 0 1em; + background: #eee; +} + +.navbar img, hr { display: none } + +table { + border-collapse: collapse; +} + +h1 { + border-left: 0.5em solid #fa0; + padding-left: 0.5em; +} + +h2.indextitle { + font-size: 1.25em; /* 20px */ + line-height: 1.2em; /* 24px */ + margin: -8px -8px 0.6em; + background-color: #fa0; + color: white; + padding: 0.3em; +} + +ul.index { + list-style: none; + margin-left: 0em; + padding-left: 0; +} + +ul.index li { + display: inline; + padding-right: 0.75em +} + +div.spec p { + margin-bottom: 0; + padding-left: 1.25em; + background-color: #eee; +} + +h3.function { + border-left: 0.5em solid #fa0; + padding-left: 0.5em; + background: #fc9; +} +a, a:visited, a:hover, a:active { color: #C60 } +h2 a, h3 a { color: #333 } + +i { + font-size: 0.875em; /* 14px */ + line-height: 1.7143em; /* 24px */ + margin-top: 1.7143em; + margin-bottom: 0em; + font-style: normal; +} diff --git a/src/weatherreport/priv/index.html b/src/weatherreport/priv/index.html new file mode 100644 index 00000000000..13fda2e0d79 --- /dev/null +++ b/src/weatherreport/priv/index.html @@ -0,0 +1,183 @@ + + + + + Riaknostic + + + + +
+ +
+
+
+

Overview

+

Sometimes, things go wrong in Riak. How can you know what's + wrong? Riaknostic is here to help.

+
$ riak-admin diag
+15:34:52.736 [warning] Riak crashed at Wed, 07 Dec 2011 21:47:50 GMT, leaving crash dump in /srv/riak/log/erl_crash.dump. Please inspect or remove the file.
+15:34:52.736 [notice] Data directory /srv/riak/data/bitcask is not mounted with 'noatime'. Please remount its disk with the 'noatime' flag to improve performance.
+

Riaknostic, which is invoked via the above command, is a + small suite of diagnostic checks that can be run against + your Riak node to discover common problems and recommend how + to resolve them. These checks are derived from the experience + of the Basho Client Services Team as well as numerous + public discussions on the mailing list, IRC room, and other + online media.

+
+
+
+
+

Installation

+

After downloading the package, expand it in the directory below according to + your platform:

+ + + + + + + + + + + + + + + + + + + + + + +
PlatformDirectory
Linux (Redhat, CentOS, Debian, Ubuntu)/usr/lib/riak/lib
Linux (Fedora)/usr/lib64/riak/lib
Solaris, OpenSolaris/opt/riak/lib
Mac OS/X or Self-built$RIAK/lib + (where $RIAK=rel/riak for source installs, + or the directory where you unpacked the package)
+

For example, on Linux, I might do this:

+
$ wget https://github.com/basho/riaknostic/downloads/riaknostic-1.0.1.tar.gz -P /tmp
+$ cd /usr/lib/riak/lib
+$ sudo tar xzvf /tmp/riaknostic-1.0.1.tar.gz
+

The package will expand to a riaknostic/ + directory which contains the riaknostic script, + source code in the src/ directory and + documentation. Now try it out!

+
+
+

Usage

+

For most cases, you can just run the riak-admin + diag command as given at the top of the + page. However, sometimes you might want to know some extra + detail or run only specific checks. For that, there are + command-line options. Add --help to get the options:

+
$ riak-admin diag --help
+Usage: riak-admin diag [-d <level>] [-l] [-h] [check_name ...]
+
+  -d, --level		Minimum message severity level (default: notice)
+  -l, --list		Describe available diagnostic tasks
+  -h, --help		Display help/usage
+  check_name		A specific check to run
+

To get an idea of what checks will be run, use + the --list option:

+
$ riak-admin diag --list
+Available diagnostic checks:
+
+  disk                 Data directory permissions and atime
+  dumps                Find crash dumps
+  memory_use           Measure memory usage
+  nodes_connected      Cluster node liveness
+  ring_membership      Cluster membership validity
+  ring_size            Ring size valid
+

If you want all the gory details about what Riaknostic is + doing, you can run the checks at a more verbose logging + level with the --level option:

+
$ riak-admin diag --level debug
+18:34:19.708 [debug] Lager installed handler lager_console_backend into lager_event
+18:34:19.720 [debug] Lager installed handler error_logger_lager_h into error_logger
+18:34:19.720 [info] Application lager started on node nonode@nohost
+18:34:20.736 [debug] Not connected to the local Riak node, trying to connect. alive:false connect_failed:undefined
+18:34:20.737 [debug] Starting distributed Erlang.
+18:34:20.740 [debug] Supervisor net_sup started erl_epmd:start_link() at pid <0.42.0>
+18:34:20.742 [debug] Supervisor net_sup started auth:start_link() at pid <0.43.0>
+18:34:20.771 [debug] Supervisor net_sup started net_kernel:start_link(['riak_diag87813@127.0.0.1',longnames]) at pid <0.44.0>
+18:34:20.771 [debug] Supervisor kernel_sup started erl_distribution:start_link(['riak_diag87813@127.0.0.1',longnames]) at pid <0.41.0>
+18:34:20.781 [debug] Supervisor inet_gethost_native_sup started undefined at pid <0.49.0>
+18:34:20.782 [debug] Supervisor kernel_safe_sup started inet_gethost_native:start_link() at pid <0.48.0>
+18:34:20.834 [debug] Connected to local Riak node 'riak@127.0.0.1'.
+18:34:20.939 [debug] Local RPC: os:getpid([]) [5000]
+18:34:20.939 [debug] Running shell command: ps -o pmem,rss,command -p 83144
+18:34:20.946 [debug] Shell command output: 
+%MEM    RSS COMMAND
+ 0.4  31004 /srv/riak/erts-5.8.4/bin/beam.smp -K true -A 64 -W w -- -root /srv/riak/rel/riak -progname riak -- -home /Users/sean -- -boot /srv/riak/releases/1.0.2/riak -embedded -config /srv/riak/etc/app.config -name riak@127.0.0.1 -setcookie riak -- console
+
+18:34:20.960 [warning] Riak crashed at Wed, 07 Dec 2011 21:47:50 GMT, leaving crash dump in /srv/riak/log/erl_crash.dump. Please inspect or remove the file.
+18:34:20.961 [notice] Data directory /srv/riak/data/bitcask is not mounted with 'noatime'. Please remount its disk with the 'noatime' flag to improve performance.
+18:34:20.961 [info] Riak process is using 0.4% of available RAM, totalling 31004 KB of real memory.
+

Most times you'll want to use the defaults, but any + Syslog severity name will do (from most to least + verbose): debug, info, notice, warning, error, + critical, alert, emergency.

+

Finally, if you want to run just a single diagnostic or a list + of specific ones, you can pass their name(s):

+
$ riak-admin diag dumps
+18:41:24.083 [warning] Riak crashed at Wed, 07 Dec 2011 21:47:50 GMT, leaving crash dump in /srv/riak/log/erl_crash.dump. Please inspect or remove the file.
+
+
+
+

Contributing

+

Have an idea for a diagnostic? Want to improve the way + Riaknostic works? Fork + the github + repository and send us a pull-request with your + changes! The code is documented with edoc, + so give the API Docs a + read before you contribute.

+

If you want to run the riaknostic script + while developing and you don't have it hooked up to your + local Riak, you can invoke it directly like so:

+
$ ./riaknostic --etc ~/code/riak/rel/riak/etc --base ~/code/riak/rel/riak --user `whoami` [other options]
+

Those extra options are usually assigned by + the riak-admin script for you, but here's + how to set them:

+ + + + + + + + + + + + + +
--etcWhere your Riak configuration directory is, in the + example above it's in the generated directory of a + source checkout of Riak.
--baseThe "base" directory of Riak, usually the root of + the generated directory + or /usr/lib/riak on Linux, for + example. Scan the riak-admin script for + how the RUNNER_BASE_DIR variable is + assigned on your platform.
--userWhat user/UID the Riak node runs as. In a source + checkout, it's the current user, on most systems, + it's riak.
+
+
+
+ + diff --git a/src/weatherreport/rebar b/src/weatherreport/rebar new file mode 100755 index 0000000000000000000000000000000000000000..c66b3e6267299d0471624d1135cd403a6fd78152 GIT binary patch literal 148150 zcmZ^~Q;;S=vn|>-r)}HQwr$&A+t##g+wPvWZQHhO`^^6Lx%bA7drn0~Rz0l9hm3rv zTx%5x5rd19BZHBJErW@zE1`*#v7?2(GYlEozfD9)XKzU8XkujO$nf9MR!Z8~A_6!2Z9O|4PR2{{W%>ldR7Eau*yE2pu#v(KpH}p9c2dpDMi@Wj1Fjy~`*!gO4|V z&EQF-FSSTWO-d@SN7}{u=458^z|L~dVnt7x6Yi@y#MK@tJ02Kx))jE zkI$?`)s9RwZku0Du>tpW*#LNi_Hb}&<~+tmtNl%G9@~E=HrE>*=U zqk)DUw_9zRu6h&d#0 zah)noQBDr8n>)w-TPzMsg;rBBpP-eUMeXWwk!*7`W*ObvW8z$|q#AC$Hdg`f8W=@D zWoWt#MApv1&ZIcVqcbiWy_8N3%)zaPyQ7oE;ust<#w}K{5oYtI=p>7e+r+_z!0=Ew zAJ#ml)YRIH&N<4p~0zcv&Ew|E_#Kz=%t8 zLn+s6JB^m-+-DyA7^@(n;j8Z}ZbPda3ZYlT)Pxjh3Zzl4b2X?l<(pApIK_xfKU_s#$(4#aO{e%-mtpxY=IJ!-+6+crW48$@n`Y$R=DwYb2t$ zYRSgGA2mIx0v%(9tbC-WNvpd=<8Px4)`V)s(0qYMITsX>Pz2HezGSqog))+fUo+YW zIjNlqGTl)r^&mGlj6oedR3lHN>= zd=&oCWx1R)EkU>CRJm1wRl$f}OP#HFs_4n!+2TURa*Fs#h)2w>F|B8oZJWa5Tgt&q z=|`up*B`!Oj2RzMyE82d>SL_PD7l4A^mYx#Chw}5+5#&&Lq^q;(D%fMXm!yrbZN>M zb0#pS;PHUJ8QmT%ize7o@8=3dqx4d!(cJ}x2-tjPv$I@@mJB#oNpo52v0mq)2#1TR$9yhhvyUmL8k?zG+rk$xrOp6W!}FP!o>({vt)zeoU# z6nzv0>MUWR;UUiuOKa*rS%a+>4!54qv0PU^c(iniN_b&G1HO;OMp0Vb%U){^-AzUw zwve#W&>~6nT6Nb<0?^+d4Q(c&p=x2lPOB@Gl2N0rcxncmbX|dnFB84l7(UZF-aYb-z-*I~FaQv#7S5NpF-|W^SJO z$U;$-ImKX`1srA!&K?Dq|2G1!EcC(n==$$^+F~VS!oTa`?dft{DP)@&sLaE$?6tKZ z^Fo@U+UdJAzrA0j-DJ!H&kcsv@g`T%B7978PEU{~^}4Ay=dWLa;Yn8-r@U6VhP~Q{ zfNh%?_A5}QA|96Z{27^>gFQ4b)#%=kL<1u#@=6U&$yH_R042zd$wK%lfpxnI_8b~{ zo``O;1M{~_dwM> zC?i~rlF40&W;?RmX%Kg5Sy2>+>v~{QPG~;!r#%K< z^Rw$b6I0?pv!cuJrnk%nle1On$(EEz&aoZqIQqX?$Xnm2Lhc&1N5P^831Q;g3Vlji)PH3N$iWD zOgQKjxNsL}%4r?Ei=%-YA{PAWa)g;u>{?y8Dso~M$pC%0DhyDfL@j41t@68tW1EKw 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 literal 0 HcmV?d00001 diff --git a/src/weatherreport/rebar.config b/src/weatherreport/rebar.config new file mode 100644 index 00000000000..3f53c665ccb --- /dev/null +++ b/src/weatherreport/rebar.config @@ -0,0 +1,36 @@ +%% ------------------------------------------------------------------- +%% +%% riaknostic - automated diagnostic tools for Riak +%% +%% Copyright (c) 2011 Basho Technologies, Inc. All Rights Reserved. +%% +%% This file is provided to you under the Apache License, +%% Version 2.0 (the "License"); you may not use this file +%% except in compliance with the License. You may obtain +%% a copy of the License at +%% +%% http://www.apache.org/licenses/LICENSE-2.0 +%% +%% Unless required by applicable law or agreed to in writing, +%% software distributed under the License is distributed on an +%% "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +%% KIND, either express or implied. See the License for the +%% specific language governing permissions and limitations +%% under the License. +%% +%% ------------------------------------------------------------------- + +{escript_shebang, "#!/usr/bin/env escript\n"}. +{escript_comment, "%% -nocookie\n"}. + +{erl_opts, [debug_info, {parse_transform, lager_transform}]}. + +{escript_incl_apps, [lager, getopt, goldrush]}. + +{deps, [ + {lager, "2.0.3", {git, "git://github.com/basho/lager.git", {tag, "2.0.3"}}}, + {getopt, ".*", {git, "git://github.com/jcomellas/getopt.git", {tag, "v0.4.3"}}}, + {meck, "0.8.1", {git, "git://github.com/basho/meck.git", {tag, "0.8.1"}}} + ]}. + +{edoc_opts, [{stylesheet_file, "priv/edoc.css"}]}. diff --git a/src/weatherreport/riaknostic.sh b/src/weatherreport/riaknostic.sh deleted file mode 100644 index db4420a6eed..00000000000 --- a/src/weatherreport/riaknostic.sh +++ /dev/null @@ -1,47 +0,0 @@ -#/bin/bash - -echo "Running Riaknostic..." - -basedir=$1 - -release_dirs="$basedir/libexec/releases $basedir/releases $basedir" -for dir in $release_dirs -do - if [ -f "$dir/start_erl.data" ] - then - riak=$dir - echo "Riak node found: $dir" - break - fi -done - -if [ -z "$riak" ] -then - echo "Couldn't find a Riak installation" - exit 1 -fi - -admin_cmd=`which riak-admin riaksearch-admin | tail -n 1 2>/dev/null` - -if [ -z "$admin_cmd" ] -then - echo "Couldn't find Riak admin tool in \$PATH" - exit 1 -fi - -riak_version=`awk '{print $2}' $dir/start_erl.data` -riak_status=`$admin_cmd status` -riak_search=`echo "$riak_status" | grep riak_search_core_version | awk '{print $3}' | sed 's/<<"\(.*\)">>/\1/'` - -arch=`uname -p` -os=`uname -s` -os_version=`uname -r` - -if [ -z "$riak_search" ] -then - echo "Riak version: $riak_version" -else - echo "Riak Search version: $riak_version" -fi - -echo "Riak running on: $os $os_version (arch: $arch)" diff --git a/src/weatherreport/src/riaknostic.app.src b/src/weatherreport/src/riaknostic.app.src new file mode 100644 index 00000000000..096282bffe2 --- /dev/null +++ b/src/weatherreport/src/riaknostic.app.src @@ -0,0 +1,34 @@ +%% ------------------------------------------------------------------- +%% +%% riaknostic - automated diagnostic tools for Riak +%% +%% Copyright (c) 2011 Basho Technologies, Inc. All Rights Reserved. +%% +%% This file is provided to you under the Apache License, +%% Version 2.0 (the "License"); you may not use this file +%% except in compliance with the License. You may obtain +%% a copy of the License at +%% +%% http://www.apache.org/licenses/LICENSE-2.0 +%% +%% Unless required by applicable law or agreed to in writing, +%% software distributed under the License is distributed on an +%% "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +%% KIND, either express or implied. See the License for the +%% specific language governing permissions and limitations +%% under the License. +%% +%% ------------------------------------------------------------------- +{application, riaknostic, + [ + {description, "Diagnostic tools for Riak"}, + {vsn, "1.2.1"}, + {registered, []}, + {applications, [ + kernel, + stdlib, + inets, + lager + ]}, + {mod, { riaknostic_app, []}} + ]}. diff --git a/src/weatherreport/src/riaknostic.erl b/src/weatherreport/src/riaknostic.erl new file mode 100644 index 00000000000..ec05d4dc92b --- /dev/null +++ b/src/weatherreport/src/riaknostic.erl @@ -0,0 +1,172 @@ +%% ------------------------------------------------------------------- +%% +%% riaknostic - automated diagnostic tools for Riak +%% +%% Copyright (c) 2011 Basho Technologies, Inc. All Rights Reserved. +%% +%% This file is provided to you under the Apache License, +%% Version 2.0 (the "License"); you may not use this file +%% except in compliance with the License. You may obtain +%% a copy of the License at +%% +%% http://www.apache.org/licenses/LICENSE-2.0 +%% +%% Unless required by applicable law or agreed to in writing, +%% software distributed under the License is distributed on an +%% "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +%% KIND, either express or implied. See the License for the +%% specific language governing permissions and limitations +%% under the License. +%% +%% ------------------------------------------------------------------- + +%% @doc

The riaknostic module is the entry point for +%% the escript. It is responsible for parsing command-line arguments +%% and switches, printing the available checks, listing the help text, +%% or running all or the specified checks, depending on the command +%% line.

+%% +%%

The getopt application and module is used +%% for command-line parsing. The defined switches and arguments are:

+%%
$ ./riaknostic --etc etc --base base --user user [-d level] [-l] [-h] [check_name...]
+%% +%% +%% +%% +%% +%% +%% +%% +%% +%%
--etc etcthe location of the Riak +%% configuration directory (set automatically by +%% riak-admin)
--base basethe base directory of +%% Riak, aka RUNNER_BASE_DIR (set automatically by +%% riak-admin)
--user userthe user that Riak runs as +%% (set automatically by riak-admin)
-d, --level level  the severity of +%% messages you want to see, defaulting to 'notice'. Equivalent to +%% syslog/lager severity levels.
-l, --listlists available checks, +%% that is, modules that implement riaknostic_check. A +%% "short name" will be given for ease-of-use.
-h, --help - print command usage +%% ("help")
check_namewhen given, a specific +%% check or list of checks to run
+%% @end +-module(riaknostic). +-export([main/1]). + +-define(OPTS, [ + {etc, undefined, "etc", string, undefined }, + {base, undefined, "base", string, undefined }, + {user, undefined, "user", string, undefined }, + {level, $d, "level", {atom, notice}, "Minimum message severity level (default: notice)"}, + {list, $l, "list", undefined, "Describe available diagnostic tasks" }, + % should we calc and interpolate the actual cwd for the below? + {export,undefined, "export",undefined, "Package system info in '$CWD/export.zip'" } + ]). + +%% @doc The main entry point for the riaknostic escript. +-spec main(CommandLineArguments::[string()]) -> any(). +main(Args) -> + application:load(riaknostic), + + case getopt:parse(?OPTS, Args) of + {ok, {Opts, NonOptArgs}} -> + case process_opts(Opts) of + list -> list_checks(); + run -> run(NonOptArgs); + export -> riaknostic_export:export() + end; + {error, Error} -> + io:format("Invalid option sequence given: ~w~n", [Error]) + end. + +list_checks() -> + Descriptions = [ {riaknostic_util:short_name(Mod), Mod:description()} || + Mod <- riaknostic_check:modules() ], + io:format("Available diagnostic checks:~n~n"), + lists:foreach(fun({Mod, Desc}) -> + io:format(" ~.20s ~s~n", [Mod, Desc]) + end, lists:sort(Descriptions)). + +run(InputChecks) -> + case riaknostic_config:prepare() of + {error, Reason} -> + io:format("Fatal error: ~s~n", [Reason]); + _ -> + ok + end, + Checks = case InputChecks of + [] -> + riaknostic_check:modules(); + _ -> + ShortNames = [{riaknostic_util:short_name(Mod), Mod} || Mod <- riaknostic_check:modules() ], + element(1, lists:foldr(fun validate_checks/2, {[], ShortNames}, InputChecks)) + end, + Messages = lists:foldl(fun(Mod, Acc) -> + Acc ++ riaknostic_check:check(Mod) + end, [], Checks), + case Messages of + [] -> + io:format("No diagnostic messages to report.~n"); + _ -> + %% Print the most critical messages first + LogLevelNum = lists:foldl( + fun({mask, Mask}, Acc) -> + Mask bor Acc; + (Level, Acc) when is_integer(Level) -> + {mask, Mask} = lager_util:config_to_mask(lager_util:num_to_level(Level)), + Mask bor Acc; + (_, Acc) -> + Acc + end, 0, lager:get_loglevels()), + FilteredMessages = lists:filter(fun({Level,_,_}) -> + lager_util:level_to_num(Level) =< LogLevelNum + end, Messages), + SortedMessages = lists:sort(fun({ALevel, _, _}, {BLevel, _, _}) -> + lager_util:level_to_num(ALevel) =< lager_util:level_to_num(BLevel) + end, FilteredMessages), + case SortedMessages of + [] -> + io:format("No diagnostic messages to report.~n"); + _ -> + lists:foreach(fun riaknostic_check:print/1, SortedMessages) + end + end. + +validate_checks(Check, {Mods, SNames}) -> + case lists:keyfind(Check, 1, SNames) of + {Check, Mod} -> + {[Mod|Mods], lists:delete({Check, Mod}, SNames)}; + _ -> + io:format("Unknown check '~s' specified, skipping.~n", [Check]), + {Mods, SNames} + end. + +process_opts(Opts) -> + process_opts(Opts, run). + +process_opts([], Result) -> + Result; +process_opts([H|T], Result) -> + process_opts(T, process_option(H, Result)). + +process_option({etc,Path}, Result) -> + application:set_env(riaknostic, etc, filename:absname(Path)), + Result; +process_option({base, Path}, Result) -> + application:set_env(riaknostic, base, filename:absname(Path)), + Result; +process_option({user, User}, Result) -> + application:set_env(riaknostic, user, User), + Result; +process_option({level, Level}, Result) -> + application:set_env(riaknostic, log_level, Level), + Result; +process_option(list, usage) -> %% Help should have precedence over listing checks + usage; +process_option(list, _) -> + list; +process_option(usage, _) -> + usage; +process_option(export, _) -> + export. diff --git a/src/weatherreport/src/riaknostic_check.erl b/src/weatherreport/src/riaknostic_check.erl new file mode 100644 index 00000000000..396ad568bf8 --- /dev/null +++ b/src/weatherreport/src/riaknostic_check.erl @@ -0,0 +1,102 @@ +%% ------------------------------------------------------------------- +%% +%% riaknostic - automated diagnostic tools for Riak +%% +%% Copyright (c) 2011 Basho Technologies, Inc. All Rights Reserved. +%% +%% This file is provided to you under the Apache License, +%% Version 2.0 (the "License"); you may not use this file +%% except in compliance with the License. You may obtain +%% a copy of the License at +%% +%% http://www.apache.org/licenses/LICENSE-2.0 +%% +%% Unless required by applicable law or agreed to in writing, +%% software distributed under the License is distributed on an +%% "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +%% KIND, either express or implied. See the License for the +%% specific language governing permissions and limitations +%% under the License. +%% +%% ------------------------------------------------------------------- + +%% @doc

Enforces a common API among all diagnostic modules and +%% provides some automation around their execution.

+%%

Behaviour Specification

+%% +%%

description/0

+%%
-spec description() -> iodata().
+%%

A short description of what the diagnostic does, which will be +%% printed when the script is given the -l flag.

+%% +%%

valid/0

+%%
-spec valid() -> boolean().
+%%

Whether the diagnostic is valid to run. For example, some checks +%% require connectivity to the Riak node and hence call {@link +%% riaknostic_node:can_connect/0. riaknostic_node:can_connect()}.

+%% +%%

check/0

+%%
-spec check() -> [{lager:log_level(), term()}].
+%%

Runs the diagnostic, returning a list of pairs, where the first +%% is a severity level and the second is any term that is understood +%% by the format/1 callback.

+%% +%%

format/1

+%%
-spec format(term()) -> iodata() | {io:format(), [term()]}.
+%%

Formats terms that were returned from check/0 for +%% output to the console. Valid return values are an iolist (string, +%% binary, etc) or a pair of a format string and a list of terms, as +%% you would pass to {@link io:format/2. io:format/2}.

+%% @end + +-module(riaknostic_check). +-export([behaviour_info/1]). +-export([check/1, + modules/0, + print/1]). + +%% @doc The behaviour definition for diagnostic modules. +-spec behaviour_info(atom()) -> 'undefined' | [{atom(), arity()}]. +behaviour_info(callbacks) -> + [{description, 0}, + {valid, 0}, + {check, 0}, + {format, 1}]; +behaviour_info(_) -> + undefined. + +%% @doc Runs the diagnostic in the given module, if it is valid. Returns a +%% list of messages that will be printed later using print/1. +-spec check(Module::module()) -> [{lager:log_level(), module(), term()}]. +check(Module) -> + case Module:valid() of + true -> + [ {Level, Module, Message} || {Level, Message} <- Module:check() ]; + _ -> + [] + end. + +%% @doc Collects a list of diagnostic modules included in the +%% riaknostic application. +-spec modules() -> [module()]. +modules() -> + {ok, Mods} = application:get_key(riaknostic, modules), + [ M || M <- Mods, + Attr <- M:module_info(attributes), + {behaviour, [?MODULE]} =:= Attr orelse {behavior, [?MODULE]} =:= Attr ]. + + +%% @doc Formats and prints the given message via lager:log/3,4. The diagnostic +%% module's format/1 function will be called to provide a +%% human-readable message. It should return an iolist() or a 2-tuple +%% consisting of a format string and a list of terms. +-spec print({Level::lager:log_level(), Module::module(), Data::term()}) -> ok. +print({Level, Mod, Data}) -> + case Mod:format(Data) of + {Format, Terms} -> + riaknostic_util:log(Level, Format, Terms); + String -> + riaknostic_util:log(Level, String) + end. + + diff --git a/src/weatherreport/src/riaknostic_check_disk.erl b/src/weatherreport/src/riaknostic_check_disk.erl new file mode 100644 index 00000000000..3b3e14ffccf --- /dev/null +++ b/src/weatherreport/src/riaknostic_check_disk.erl @@ -0,0 +1,165 @@ +%% ------------------------------------------------------------------- +%% +%% riaknostic - automated diagnostic tools for Riak +%% +%% Copyright (c) 2011 Basho Technologies, Inc. All Rights Reserved. +%% +%% This file is provided to you under the Apache License, +%% Version 2.0 (the "License"); you may not use this file +%% except in compliance with the License. You may obtain +%% a copy of the License at +%% +%% http://www.apache.org/licenses/LICENSE-2.0 +%% +%% Unless required by applicable law or agreed to in writing, +%% software distributed under the License is distributed on an +%% "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +%% KIND, either express or implied. See the License for the +%% specific language governing permissions and limitations +%% under the License. +%% +%% ------------------------------------------------------------------- + +%% @doc Diagnostic that checks permissions on data directories and +%% whether noatime is set. It will only check data directories of +%% known storage backends. +-module(riaknostic_check_disk). +-behaviour(riaknostic_check). + +%% The file that we will attempt to create and read under each data directory. +-define(TEST_FILE, "riaknostic.tmp"). + +%% A dependent chain of permissions checking functions. +-define(CHECKPERMFUNS, [fun check_is_dir/1, + fun check_is_writeable/1, + fun check_is_readable/1, + fun check_is_file_readable/1, + fun check_atime/1]). + +-include_lib("kernel/include/file.hrl"). + +-export([description/0, + valid/0, + check/0, + format/1]). + +-spec description() -> string(). +description() -> + "Data directory permissions and atime". + +-spec valid() -> true. +valid() -> + true. + +-spec check() -> [{lager:log_level(), term()}]. +check() -> + DataDirs = riaknostic_config:data_directories(), + %% Add additional disk checks in the function below + lists:flatmap(fun(Dir) -> + check_directory_permissions(Dir) + end, + DataDirs). + +-spec format(term()) -> {io:format(), [term()]}. +format({disk_full, DataDir}) -> + {"Disk containing data directory ~s is full! " + "Please check that it is set to the correct location and that there are not " + "other files using up space intended for Riak.", [DataDir]}; +format({no_data_dir, DataDir}) -> + {"Data directory ~s does not exist. Please create it.", [DataDir]}; +format({no_write, DataDir}) -> + User = riaknostic_config:user(), + {"No write access to data directory ~s. Please make it writeable by the '~s' user.", [DataDir, User]}; +format({no_read, DataDir}) -> + User = riaknostic_config:user(), + {"No read access to data directory ~s. Please make it readable by the '~s' user.", [DataDir, User]}; +format({write_check, File}) -> + {"Write-test file ~s is a directory! Please remove it so this test can continue.", [File]}; +format({atime, Dir}) -> + {"Data directory ~s is not mounted with 'noatime'. " + "Please remount its disk with the 'noatime' flag to improve performance.", [Dir]}. + +%%% Private functions + +check_directory_permissions(Directory) -> + check_directory(Directory, ?CHECKPERMFUNS). + +%% Run a list of check functions against the given directory, +%% returning the first non-ok result. +check_directory(_, []) -> + []; +check_directory(Directory, [Check|Checks]) -> + case Check(Directory) of + ok -> + check_directory(Directory, Checks); + Message -> + [ Message ] + end. + +%% Check if the path is actually a directory +check_is_dir(Directory) -> + case filelib:is_dir(Directory) of + true -> + ok; + _ -> + {error, {no_data_dir, Directory}} + end. + +%% Check if the directory is writeable +check_is_writeable(Directory) -> + File = filename:join([Directory, ?TEST_FILE]), + case file:write_file(File, <<"ok">>) of + ok -> + ok; + {error, Error} when Error == enoent orelse Error == eacces -> + {error, {no_write, Directory}}; + {error, enospc} -> + {critical, {disk_full, Directory}}; + {error, eisdir} -> + {error, {write_check, File}} + end. + +%% Check if the directory is readable +check_is_readable(Directory) -> + case file:read_file_info(Directory) of + {ok, #file_info{access=Access}} when Access == read orelse + Access == read_write -> + ok; + {error, eacces} -> + {error, {no_read, Directory}}; + {error, Error} when Error == enoent orelse + Error == enotdir -> + {error, {no_data_dir, Directory}}; + _ -> + {error, {no_read, Directory}} + end. + +%% Check if the file we created is readable +check_is_file_readable(Directory) -> + File = filename:join([Directory, ?TEST_FILE]), + case file:read_file(File) of + {error, Error} when Error == eacces orelse + Error == enotdir -> + {error, {no_read, Directory}}; + {error, enoent} -> + {error, {write_check, File}}; + _ -> ok + end. + +%% Check if the directory is mounted with 'noatime' +check_atime(Directory) -> + File = filename:join([Directory, ?TEST_FILE]), + {ok, FileInfo1} = file:read_file_info(File), + timer:sleep(1001), + {ok, S} = file:open(File, [read]), + io:get_line(S, ''), + file:close(S), + {ok, FileInfo2} = file:read_file_info(File), + file:delete(File), + case (FileInfo1#file_info.atime =/= FileInfo2#file_info.atime) of + true -> + {notice, {atime, Directory}}; + _ -> + ok + end. + diff --git a/src/weatherreport/src/riaknostic_check_dumps.erl b/src/weatherreport/src/riaknostic_check_dumps.erl new file mode 100644 index 00000000000..a9a2bb775b7 --- /dev/null +++ b/src/weatherreport/src/riaknostic_check_dumps.erl @@ -0,0 +1,79 @@ +%% ------------------------------------------------------------------- +%% +%% riaknostic - automated diagnostic tools for Riak +%% +%% Copyright (c) 2011 Basho Technologies, Inc. All Rights Reserved. +%% +%% This file is provided to you under the Apache License, +%% Version 2.0 (the "License"); you may not use this file +%% except in compliance with the License. You may obtain +%% a copy of the License at +%% +%% http://www.apache.org/licenses/LICENSE-2.0 +%% +%% Unless required by applicable law or agreed to in writing, +%% software distributed under the License is distributed on an +%% "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +%% KIND, either express or implied. See the License for the +%% specific language governing permissions and limitations +%% under the License. +%% +%% ------------------------------------------------------------------- + +%% @doc Diagnostic that detects the existence of Erlang-generated +%% crash dumps. It will also check whether the location that the crash +%% dump is written to has correct permissions. +-module(riaknostic_check_dumps). +-behaviour(riaknostic_check). + +-include_lib("kernel/include/file.hrl"). + +-export([description/0, + valid/0, + check/0, + format/1]). + +-spec description() -> string(). +description() -> + "Find crash dumps". + +-spec valid() -> true. +valid() -> + true. + +-spec check() -> [{lager:log_level(), term()}]. +check() -> + CrashDumpConfig = riaknostic_config:get_vm_env("ERL_CRASH_DUMP"), + {DumpDir, DumpFile} = case CrashDumpConfig of + undefined -> + Cwd = riaknostic_config:base_dir(), + {Cwd, filename:absname([Cwd, "erl_crash.dump"])}; + File -> + AbsFile = filename:absname(File, riaknostic_config:base_dir()), + {filename:dirname(AbsFile), AbsFile} + end, + Messages = case file:read_file_info(DumpDir) of + {error, enoent} -> + [{error, {enoent, DumpDir}}]; + {error, _} -> + [{error, {eacces, DumpDir}}]; + {ok, #file_info{access=Access}} when Access =/= read_write -> + [{error, {eacces, DumpDir}}]; + _ -> + [] + end, + case filelib:is_file(DumpFile) of + true -> + [{warning, {crash_dump, DumpFile}}|Messages]; + _ -> + Messages + end. + +-spec format(term()) -> {io:format(), [term()]}. +format({eacces, Dir}) -> + {"Crash dump directory ~s is not writeable by Riak. Please set -env ERL_CRASH_DUMP /erl_crash.dump in vm.args to a writeable path.", [Dir]}; +format({enoent, Dir}) -> + {"Crash dump directory ~s does not exist. Please set -env ERL_CRASH_DUMP /erl_crash.dump in vm.args to a writeable path.", [Dir]}; +format({crash_dump, File}) -> + {ok, #file_info{mtime=MTime}} = file:read_file_info(File), + {"Riak crashed at ~s, leaving crash dump in ~s. Please inspect or remove the file.", [httpd_util:rfc1123_date(MTime), File]}. diff --git a/src/weatherreport/src/riaknostic_check_memory_use.erl b/src/weatherreport/src/riaknostic_check_memory_use.erl new file mode 100644 index 00000000000..9ea57a19d8d --- /dev/null +++ b/src/weatherreport/src/riaknostic_check_memory_use.erl @@ -0,0 +1,61 @@ +%% ------------------------------------------------------------------- +%% +%% riaknostic - automated diagnostic tools for Riak +%% +%% Copyright (c) 2011 Basho Technologies, Inc. All Rights Reserved. +%% +%% This file is provided to you under the Apache License, +%% Version 2.0 (the "License"); you may not use this file +%% except in compliance with the License. You may obtain +%% a copy of the License at +%% +%% http://www.apache.org/licenses/LICENSE-2.0 +%% +%% Unless required by applicable law or agreed to in writing, +%% software distributed under the License is distributed on an +%% "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +%% KIND, either express or implied. See the License for the +%% specific language governing permissions and limitations +%% under the License. +%% +%% ------------------------------------------------------------------- + +%% @doc Diagnostic that checks Riak's current memory usage. If memory +%% usage is high, a warning message will be sent, otherwise only +%% informational messages. +-module(riaknostic_check_memory_use). +-behaviour(riaknostic_check). + +-export([description/0, + valid/0, + check/0, + format/1]). + +-spec description() -> string(). +description() -> + "Measure memory usage". + +-spec valid() -> boolean(). +valid() -> + riaknostic_node:can_connect(). + +-spec check() -> [{lager:log_level(), term()}]. +check() -> + Pid = riaknostic_node:pid(), + Output = riaknostic_util:run_command("ps -o pmem,rss -p " ++ Pid), + [_,_,Percent, RealSize| _] = string:tokens(Output, "/n \n"), + Messages = [ + {info, {process_usage, Percent, RealSize}} + ], + case riaknostic_util:binary_to_float(list_to_binary(Percent)) >= 90 of + false -> + Messages; + true -> + [{critical, {high_memory, Percent}} | Messages] + end. + +-spec format(term()) -> {io:format(), [term()]}. +format({high_memory, Percent}) -> + {"Riak memory usage is HIGH: ~s% of available RAM", [Percent]}; +format({process_usage, Percent, Real}) -> + {"Riak process is using ~s% of available RAM, totalling ~s KB of real memory.", [Percent, Real]}. diff --git a/src/weatherreport/src/riaknostic_check_nodes_connected.erl b/src/weatherreport/src/riaknostic_check_nodes_connected.erl new file mode 100644 index 00000000000..3301d881430 --- /dev/null +++ b/src/weatherreport/src/riaknostic_check_nodes_connected.erl @@ -0,0 +1,53 @@ +%% ------------------------------------------------------------------- +%% +%% riaknostic - automated diagnostic tools for Riak +%% +%% Copyright (c) 2011 Basho Technologies, Inc. All Rights Reserved. +%% +%% This file is provided to you under the Apache License, +%% Version 2.0 (the "License"); you may not use this file +%% except in compliance with the License. You may obtain +%% a copy of the License at +%% +%% http://www.apache.org/licenses/LICENSE-2.0 +%% +%% Unless required by applicable law or agreed to in writing, +%% software distributed under the License is distributed on an +%% "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +%% KIND, either express or implied. See the License for the +%% specific language governing permissions and limitations +%% under the License. +%% +%% ------------------------------------------------------------------- + +%% @doc Diagnostic check that detects cluster members that are down. +-module(riaknostic_check_nodes_connected). +-behaviour(riaknostic_check). + +-export([description/0, + valid/0, + check/0, + format/1]). + +-spec description() -> string(). +description() -> + "Cluster node liveness". + +-spec valid() -> boolean(). +valid() -> + riaknostic_node:can_connect(). + +-spec check() -> [{lager:log_level(), term()}]. +check() -> + Stats = riaknostic_node:stats(), + {connected_nodes, ConnectedNodes} = lists:keyfind(connected_nodes, 1, Stats), + {ring_members, RingMembers} = lists:keyfind(ring_members, 1, Stats), + {nodename, NodeName} = lists:keyfind(nodename, 1, Stats), + + [ {warning, {node_disconnected, N}} || N <- RingMembers, + N =/= NodeName, + lists:member(N, ConnectedNodes) == false]. + +-spec format(term()) -> {io:format(), [term()]}. +format({node_disconnected, Node}) -> + {"Cluster member ~s is not connected to this node. Please check whether it is down.", [Node]}. diff --git a/src/weatherreport/src/riaknostic_check_ring_membership.erl b/src/weatherreport/src/riaknostic_check_ring_membership.erl new file mode 100644 index 00000000000..8993fb1f991 --- /dev/null +++ b/src/weatherreport/src/riaknostic_check_ring_membership.erl @@ -0,0 +1,66 @@ +%% ------------------------------------------------------------------- +%% +%% riaknostic - automated diagnostic tools for Riak +%% +%% Copyright (c) 2011 Basho Technologies, Inc. All Rights Reserved. +%% +%% This file is provided to you under the Apache License, +%% Version 2.0 (the "License"); you may not use this file +%% except in compliance with the License. You may obtain +%% a copy of the License at +%% +%% http://www.apache.org/licenses/LICENSE-2.0 +%% +%% Unless required by applicable law or agreed to in writing, +%% software distributed under the License is distributed on an +%% "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +%% KIND, either express or implied. See the License for the +%% specific language governing permissions and limitations +%% under the License. +%% +%% ------------------------------------------------------------------- + +%% @doc Diagnostic that checks whether the local node is a member of +%% the ring. This might arise when the node name in vm.args has +%% changed but the node has not been renamed in the ring. +-module(riaknostic_check_ring_membership). +-behaviour(riaknostic_check). + +-export([description/0, + valid/0, + check/0, + format/1]). + +-include_lib("eunit/include/eunit.hrl"). + +-spec description() -> string(). +description() -> + "Cluster membership validity". + +-spec valid() -> boolean(). +valid() -> + riaknostic_node:can_connect(). + +-spec check() -> [{lager:log_level(), term()}]. +check() -> + Stats = riaknostic_node:stats(), + {ring_members, RingMembers} = lists:keyfind(ring_members, 1, Stats), + {nodename, NodeName} = lists:keyfind(nodename, 1, Stats), + case lists:member(NodeName, RingMembers) of + true -> + []; + false -> + [{warning, {not_ring_member, NodeName}}] + end. + +check_test() -> + meck:new(riaknostic_node, [passthrough]), + meck:expect(riaknostic_node, stats, fun() -> [{ring_members, ["riak@127.0.0.1"]}, {nodename, ["notmember@127.0.0.1"]}] end), + ?assert(meck:validate(riaknostic_node)), + ?assertEqual([{warning, {not_ring_member, ["notmember@127.0.0.1"]}}], check()), + ?assertNotEqual([{warning, {not_ring_member, ["notequal@127.0.0.1"]}}], check()), + meck:unload(riaknostic_node). + +-spec format(term()) -> {io:format(), [term()]}. +format({not_ring_member, Nodename}) -> + {"Local node ~w is not a member of the ring. Please check that the -name setting in vm.args is correct.", [Nodename]}. diff --git a/src/weatherreport/src/riaknostic_check_ring_preflists.erl b/src/weatherreport/src/riaknostic_check_ring_preflists.erl new file mode 100644 index 00000000000..df09e403007 --- /dev/null +++ b/src/weatherreport/src/riaknostic_check_ring_preflists.erl @@ -0,0 +1,51 @@ +%% ------------------------------------------------------------------- +%% +%% riaknostic - automated diagnostic tools for Riak +%% +%% Copyright (c) 2011 Basho Technologies, Inc. All Rights Reserved. +%% +%% This file is provided to you under the Apache License, +%% Version 2.0 (the "License"); you may not use this file +%% except in compliance with the License. You may obtain +%% a copy of the License at +%% +%% http://www.apache.org/licenses/LICENSE-2.0 +%% +%% Unless required by applicable law or agreed to in writing, +%% software distributed under the License is distributed on an +%% "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +%% KIND, either express or implied. See the License for the +%% specific language governing permissions and limitations +%% under the License. +%% +%% ------------------------------------------------------------------- + +%% @doc Diagnostic that checks the local ring for any preflists that do +%% not satisfy n_val +-module(riaknostic_check_ring_preflists). +-behaviour(riaknostic_check). + +-export([description/0, + valid/0, + check/0, + format/1]). + +-spec description() -> string(). +description() -> + "Check ring satisfies n_val". + +-spec valid() -> boolean(). +valid() -> + riaknostic_node:can_connect(). + +-spec check() -> [{lager:log_level(), term()}]. +check() -> + case riaknostic_node:local_command(riak_core_ring_util, check_ring) of + [] -> []; + PrefLists -> [ {warning, {n_val_not_satisfied, PrefLists}} ] + end. + +-spec format(term()) -> {io:format(), [term()]}. +format({n_val_not_satisfied, PrefLists}) -> + {"The following preflists do not satisfy the n_val: ~p", [PrefLists]}. + diff --git a/src/weatherreport/src/riaknostic_check_ring_size.erl b/src/weatherreport/src/riaknostic_check_ring_size.erl new file mode 100644 index 00000000000..e796b7b23ef --- /dev/null +++ b/src/weatherreport/src/riaknostic_check_ring_size.erl @@ -0,0 +1,76 @@ +%% ------------------------------------------------------------------- +%% +%% riaknostic - automated diagnostic tools for Riak +%% +%% Copyright (c) 2011 Basho Technologies, Inc. All Rights Reserved. +%% +%% This file is provided to you under the Apache License, +%% Version 2.0 (the "License"); you may not use this file +%% except in compliance with the License. You may obtain +%% a copy of the License at +%% +%% http://www.apache.org/licenses/LICENSE-2.0 +%% +%% Unless required by applicable law or agreed to in writing, +%% software distributed under the License is distributed on an +%% "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +%% KIND, either express or implied. See the License for the +%% specific language governing permissions and limitations +%% under the License. +%% +%% ------------------------------------------------------------------- + +%% @doc Diagnostic that compares the configured +%% ring_creation_size to the actual size of the ring. +-module(riaknostic_check_ring_size). +-behaviour(riaknostic_check). + +-export([description/0, + valid/0, + check/0, + format/1]). + +-spec description() -> string(). +description() -> + "Ring size valid". + +-spec valid() -> boolean(). +valid() -> + riaknostic_node:can_connect(). + +-spec check() -> [{lager:log_level(), term()}]. +check() -> + Stats = riaknostic_node:stats(), + {ring_creation_size, RingSize} = lists:keyfind(ring_creation_size, 1, Stats), + {ring_num_partitions, NumPartitions} = lists:keyfind(ring_num_partitions, 1, Stats), +% {ring_members, RingMembers} = lists:keyfind(ring_members, 1, Stats), +% NumRingMembers = length(RingMembers), +% VnodesPerNode = erlang:round(RingSize / NumRingMembers), +% MinAcceptableVnodesPerNode = erlang:round(RingSize * 0.03), +% MaxRecommendedVnodesPerNode = erlang:round(RingSize * 0.7), + + lists:append([ + [ {notice, {ring_size_unequal, RingSize, NumPartitions}} || RingSize /= NumPartitions ], + [ {critical, {ring_size_not_exp2, RingSize}} || (RingSize band -(bnot RingSize)) /= RingSize] +% [ {notice, {ring_size_too_small, RingSize, NumRingMembers}} || VnodesPerNode =< MinAcceptableVnodesPerNode ], +% [ {notice, {too_few_nodes_for_ring, RingSize, NumRingMembers}} || VnodesPerNode >= MaxRecommendedVnodesPerNode ] + ]). + +-spec format(term()) -> {io:format(), [term()]}. +format({ring_size_unequal, S, P}) -> + {"The configured ring_creation_size (~B) is not equal to the number of partitions in the ring (~B). " + "Please verify that the ring_creation_size in app.config is correct.", [S, P]}; + +format({ring_size_not_exp2, S}) -> + {"The configured ring_creation_size (~B) should always be a power of 2. " + "Please reconfigure the ring_creation_size in app.config.", [S]}. + +%format({ring_size_too_small, S, N}) -> +% {"With a ring_creation_size (~B) and ~B nodes participating in the cluster, each node is responsible for less than 3% of the data. " +% " You have too many nodes for this size ring. " +% "Please consider migrating data to a cluster with 2 or 4x your current ring size.", [S, N]}; + +%format({too_few_nodes_for_ring, S, N}) -> +% {"With a ring_creation_size (~B) and ~B nodes participating in the cluster, each node is responsible for more than 70% of the data. " +% " You have too few nodes for this size ring. " +% "Please consider joining more nodes to your cluster.", [S, N]}. diff --git a/src/weatherreport/src/riaknostic_check_search.erl b/src/weatherreport/src/riaknostic_check_search.erl new file mode 100644 index 00000000000..509a642c50a --- /dev/null +++ b/src/weatherreport/src/riaknostic_check_search.erl @@ -0,0 +1,57 @@ +%% ------------------------------------------------------------------- +%% +%% riaknostic - automated diagnostic tools for Riak +%% +%% Copyright (c) 2011 Basho Technologies, Inc. All Rights Reserved. +%% +%% This file is provided to you under the Apache License, +%% Version 2.0 (the "License"); you may not use this file +%% except in compliance with the License. You may obtain +%% a copy of the License at +%% +%% http://www.apache.org/licenses/LICENSE-2.0 +%% +%% Unless required by applicable law or agreed to in writing, +%% software distributed under the License is distributed on an +%% "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +%% KIND, either express or implied. See the License for the +%% specific language governing permissions and limitations +%% under the License. +%% +%% ------------------------------------------------------------------- + +%% @doc Diagnostic that checks if riak_search +%% is enabled on every node +-module(riaknostic_check_search). +-behaviour(riaknostic_check). + +-export([description/0, + valid/0, + check/0, + format/1]). + +-spec description() -> string(). +description() -> + "Check whether search is enabled on all nodes". + +-spec valid() -> boolean(). +valid() -> + riaknostic_node:can_connect_all(). + +-spec check() -> [{lager:log_level(), term()}]. +check() -> + Stats = riaknostic_node:stats(), + {ring_members, RingMembers} = lists:keyfind(ring_members, 1, Stats), + + {SearchEnabled, _} = riaknostic_node:cluster_command(application, get_env, [riak_search, enabled]), + + {_, X} = lists:unzip(SearchEnabled), + NodesSearchEnabled = lists:zip(RingMembers, X), + + lists:append([ + [ {warning, {riak_search, NodesSearchEnabled}} || length(lists:usort(SearchEnabled)) > 1 ] + ]). + +-spec format(term()) -> {io:format(), [term()]}. +format({riak_search, Services}) -> + {"Search is not enabled on all nodes: ~p", [Services]}. diff --git a/src/weatherreport/src/riaknostic_check_strong_consistency.erl b/src/weatherreport/src/riaknostic_check_strong_consistency.erl new file mode 100644 index 00000000000..e5f0569bfc9 --- /dev/null +++ b/src/weatherreport/src/riaknostic_check_strong_consistency.erl @@ -0,0 +1,58 @@ +%% ------------------------------------------------------------------- +%% +%% riaknostic - automated diagnostic tools for Riak +%% +%% Copyright (c) 2011 Basho Technologies, Inc. All Rights Reserved. +%% +%% This file is provided to you under the Apache License, +%% Version 2.0 (the "License"); you may not use this file +%% except in compliance with the License. You may obtain +%% a copy of the License at +%% +%% http://www.apache.org/licenses/LICENSE-2.0 +%% +%% Unless required by applicable law or agreed to in writing, +%% software distributed under the License is distributed on an +%% "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +%% KIND, either express or implied. See the License for the +%% specific language governing permissions and limitations +%% under the License. +%% +%% ------------------------------------------------------------------- + +%% @doc Diagnostic that checks Riak's current memory usage. If memory +%% usage is high, a warning message will be sent, otherwise only +%% informational messages. +-module(riaknostic_check_strong_consistency). +-behaviour(riaknostic_check). + +-export([description/0, + valid/0, + check/0, + format/1]). + +-spec description() -> string(). +description() -> + "Strong consistency configuration valid". + +-spec valid() -> boolean(). +valid() -> + riaknostic_node:can_connect(). + +-spec check() -> [{lager:log_level(), term()}]. +check() -> + StrongConsistencyOption = riaknostic_config:get_app_env([riak_core, enable_consensus]), + { AAEOption, _ } = riaknostic_config:get_app_env([riak_kv, anti_entropy]), + maybe_strong_consistency_aae_misconfigured(StrongConsistencyOption, AAEOption). + +-spec maybe_strong_consistency_aae_misconfigured(boolean, on | off | any()) -> [ { term(), term() } ] | []. +maybe_strong_consistency_aae_misconfigured(true, off) -> + [ { critical, { strong_consistency_aae_misconfigured } } ]; +maybe_strong_consistency_aae_misconfigured(false, _) -> + []; +maybe_strong_consistency_aae_misconfigured(true, on) -> + []. + +-spec format(term()) -> {io:format(), [term()]}. +format({ strong_consistency_aae_misconfigured }) -> + { "Strong consistency has been enabled without AAE -- all consistent operations will timeout until AAE is enabled.", [] }. diff --git a/src/weatherreport/src/riaknostic_config.erl b/src/weatherreport/src/riaknostic_config.erl new file mode 100644 index 00000000000..43cd2148cfc --- /dev/null +++ b/src/weatherreport/src/riaknostic_config.erl @@ -0,0 +1,282 @@ +%% ------------------------------------------------------------------- +%% +%% riaknostic - automated diagnostic tools for Riak +%% +%% Copyright (c) 2011 Basho Technologies, Inc. All Rights Reserved. +%% +%% This file is provided to you under the Apache License, +%% Version 2.0 (the "License"); you may not use this file +%% except in compliance with the License. You may obtain +%% a copy of the License at +%% +%% http://www.apache.org/licenses/LICENSE-2.0 +%% +%% Unless required by applicable law or agreed to in writing, +%% software distributed under the License is distributed on an +%% "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +%% KIND, either express or implied. See the License for the +%% specific language governing permissions and limitations +%% under the License. +%% +%% ------------------------------------------------------------------- + +%% @doc Provides convenient access to Riak configuration values. When +%% the {@link riaknostic. riaknostic} module calls {@link +%% prepare/0. prepare/0}, Riak's app.config and +%% vm.args files will be parsed and memoized, and lager +%% will be started on the console at the configured severity level. +%% @end + +-module(riaknostic_config). + +-export([prepare/0, + data_directories/0, + get_app_env/1, + get_app_env/2, + get_vm_env/1, + base_dir/0, + etc_dir/0, + node_name/0, + cookie/0, + user/0]). + +%% @doc Prepares appropriate configuration so the riaknostic script +%% can run. This is called by the riaknostic module and you do +%% not need to invoke it. +-spec prepare() -> ok | {error, iodata()}. +prepare() -> + prepare([fun start_lager/0, fun load_app_config/0, fun load_vm_args/0]). + +prepare([]) -> + ok; +prepare([Fun|T]) -> + case Fun() of + {error, Reason} -> + {error, Reason}; + _ -> + prepare(T) + end. + +%% @doc Determines where Riak is configured to store data. Returns a +%% list of paths to directories defined by storage backends. +-spec data_directories() -> [ file:filename() ]. +data_directories() -> + KVBackend = get_app_env([riak_kv, storage_backend]), + SearchBackend = get_app_env([riak_search, storage_backend], merge_index_backend), + Dirs = case get_app_env([riak_search, enabled]) of + true -> + data_directory(KVBackend) ++ data_directory(SearchBackend); + _ -> + data_directory(KVBackend) + end, + [ filename:absname(Dir, base_dir()) || Dir <- Dirs, Dir =/= undefined ]. + +%% @doc Get a key out of the app.config file, or if it doesn't exist, +%% return the Default. You specify a nested key using a list of atoms, +%% e.g. [riak_kv, storage_backend]. +%% @see get_app_env/1 +-spec get_app_env([atom()], term()) -> term(). +get_app_env(Keys, Default) -> + case get_app_env(Keys) of + undefined -> + Default; + Value -> + Value + end. + +%% @doc Get a key out of the app.config file. You specify a nested +%% key using a list of atoms, e.g. [riak_kv, storage_backend]. +%% @equiv get_app_env(Keys, undefined) +-spec get_app_env([atom()]) -> undefined | term(). +get_app_env(Keys) -> + {ok, Env} = application:get_env(riaknostic, app_config), + find_nested_key(Keys, Env). + +%% @doc Get an -env flag out of the vm.args file. +-spec get_vm_env(string()) -> string() | undefined. +get_vm_env(Key) -> + case application:get_env(riaknostic, vm_env) of + undefined -> + undefined; + {ok, PList} -> + proplists:get_value(Key, PList) + end. + +%% @doc Determines the user/uid that the installed Riak runs as. +-spec user() -> string(). +user() -> + case application:get_env(riaknostic, user) of + {ok, Value} -> + Value; + _ -> undefined + end. + +%% @doc The base directory from which the Riak script runs. +-spec base_dir() -> file:filename(). +base_dir() -> + case application:get_env(riaknostic, base) of + undefined -> + filename:absname("."); + {ok, Path} -> + filename:absname(Path) + end. + +%% @doc The Riak configuration directory. +-spec etc_dir() -> file:filename(). +etc_dir() -> + case application:get_env(riaknostic, etc) of + undefined -> + filename:absname("./etc", base_dir()); + {ok, Path} -> + filename:absname(Path, base_dir()) + end. + +%% @doc The local Riak node name. Includes whether the node uses short +%% or long nodenames for distributed Erlang. +-spec node_name() -> {shortnames | longnames, Name::string()}. +node_name() -> + case application:get_env(riaknostic, node_name) of + undefined -> + undefined; + {ok, Node} -> + Node + end. + +%% @doc The Riak node's distributed Erlang cookie. +-spec cookie() -> atom(). +cookie() -> + case application:get_env(riaknostic, cookie) of + undefined -> + undefined; + {ok, Cookie} -> + list_to_atom(Cookie) + end. + +%% Private functions +start_lager() -> + application:load(lager), + case application:get_env(riaknostic, log_level) of + undefined -> + {error, "Log level not set!"}; + {ok, Level} -> + application:set_env(lager, crash_log, undefined), + application:set_env(lager, handlers, [{lager_console_backend, Level}]), + lager:start() + end. + +load_app_config() -> + {ok, [[AppConfig]]} = init:get_argument(config), + case file:consult(AppConfig) of + {ok, [Config]} -> + application:set_env(riaknostic, app_config, Config); + _ -> + {error, io_lib:format("Riak config file ~s is malformed!", [AppConfig])} + end. + +load_vm_args() -> + VmArgs = case init:get_argument(vm_args) of + {ok, [[X]]} -> X; + _ -> + %% This is a backup. If for some reason -vm_args isn't specified + %% then assume it lives in the same dir as app.config + {ok, [[AppConfig]]} = init:get_argument(config), + AppIndex = string:str(AppConfig, "app"), + ConfigIndex = string:rstr(AppConfig, "config"), + string:sub_string(AppConfig, 1, AppIndex - 1) ++ "vm" ++ + string:sub_string(AppConfig, AppIndex + 3, ConfigIndex-1) ++ "args" + end, + + case file:read_file(VmArgs) of + {error, Reason} -> + {error, io_lib:format("Could not read ~s, received error ~w!", [VmArgs, Reason])}; + {ok, Binary} -> + load_vm_args(Binary) + end. + +load_vm_args(Bin) when is_binary(Bin) -> + load_vm_args(re:split(Bin, "\s*\r?\n\s*", [{return, list}, trim])); +load_vm_args([]) -> + ok; +load_vm_args([[$#|_]|T]) -> + load_vm_args(T); +load_vm_args([""|T]) -> + load_vm_args(T); +load_vm_args(["-sname " ++ NodeName|T]) -> + application:set_env(riaknostic, node_name, {shortnames, string:strip(NodeName)}), + load_vm_args(T); +load_vm_args(["-name " ++ NodeName|T]) -> + application:set_env(riaknostic, node_name, {longnames, string:strip(NodeName)}), + load_vm_args(T); +load_vm_args(["-setcookie " ++ Cookie|T]) -> + application:set_env(riaknostic, cookie, string:strip(Cookie)), + load_vm_args(T); +load_vm_args(["-env " ++ Env|T]) -> + [Key, Value] = re:split(Env, "\s+", [{return, list}, trim]), + add_or_insert_env(vm_env, {Key, Value}), + load_vm_args(T); +load_vm_args([[$+|EmuFlags]|T]) -> + [Flag|Rest] = re:split(EmuFlags, "\s+", [{return,list}, trim]), + add_or_insert_env(emu_flags, {[$+|Flag], Rest}), + load_vm_args(T); +load_vm_args([[$-|InitFlags]|T]) -> + [Flag|Rest] = re:split(InitFlags, "\s+", [{return,list}, trim]), + add_or_insert_env(init_flags, {[$-|Flag], Rest}), + load_vm_args(T); +load_vm_args([Line|_]) -> + {error, io_lib:format("Erroneous line in vm.args: ~s", [Line])}. + +add_or_insert_env(Key, Value) -> + case application:get_env(riaknostic, Key) of + undefined -> + application:set_env(riaknostic, Key, [Value]); + {ok, List} -> + application:set_env(riaknostic, Key, [Value|List]) + end. + +find_nested_key(_, undefined) -> + undefined; +find_nested_key([], Val) -> + Val; +find_nested_key([Key|T], PList) -> + find_nested_key(T, proplists:get_value(Key, PList)). + +%% Determine the data directory(ies) for the configured storage backend +-spec data_directory(atom()) -> [ file:filename() ]. +data_directory(riak_kv_bitcask_backend) -> + [ get_app_env([bitcask, data_root]) ]; +data_directory(riak_kv_eleveldb_backend) -> + [ get_app_env([eleveldb, data_root]) ]; +data_directory(merge_index_backend) -> + [ get_app_env([merge_index, data_root]) ]; +data_directory(riak_kv_innostore_backend) -> + [ get_app_env([innostore, data_home_dir]), + get_app_env([innostore, log_group_home_dir]) ]; +data_directory(riak_kv_multi_backend) -> + [ multi_data_directory(Backend) || + Backend <- get_app_env([riak_kv, multi_backend]), + element(2, Backend) =/= riak_kv_memory_backend ]; +data_directory(_) -> %% Memory or unknown backend + []. + +%% Extracts data paths from multi_backend config +multi_data_directory({_, riak_kv_bitcask_backend, Props}) -> + case proplists:get_value(data_root, Props) of + undefined -> + get_app_env([bitcask, data_root]); + Path when is_list(Path) -> + Path + end; +multi_data_directory({_, riak_kv_eleveldb_backend, Props}) -> + case proplists:get_value(data_root, Props) of + undefined -> + get_app_env([eleveldb, data_root]); + Path when is_list(Path) -> + Path + end; +multi_data_directory({_, riak_kv_innostore_backend, Props}) -> + case proplists:get_value(data_home_dir, Props) of + undefined -> + get_app_env([innostore, data_home_dir]); + Path when is_list(Path) -> + Path + end. diff --git a/src/weatherreport/src/riaknostic_export.erl b/src/weatherreport/src/riaknostic_export.erl new file mode 100644 index 00000000000..65371c7ada0 --- /dev/null +++ b/src/weatherreport/src/riaknostic_export.erl @@ -0,0 +1,176 @@ +%% ------------------------------------------------------------------- +%% +%% riaknostic - automated diagnostic tools for Riak +%% +%% Copyright (c) 2011 Basho Technologies, Inc. All Rights Reserved. +%% +%% This file is provided to you under the Apache License, +%% Version 2.0 (the "License"); you may not use this file +%% except in compliance with the License. You may obtain +%% a copy of the License at +%% +%% http://www.apache.org/licenses/LICENSE-2.0 +%% +%% Unless required by applicable law or agreed to in writing, +%% software distributed under the License is distributed on an +%% "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +%% KIND, either express or implied. See the License for the +%% specific language governing permissions and limitations +%% under the License. +%% +%% ------------------------------------------------------------------- + +%% @doc Gather and export system stats and other +%% diagnostic output. +%% @end + +-module(riaknostic_export). +-export([export/0]). + +%% @doc wrapper for all the moving parts of the export function +%% XXX/evan terrible first draft error-handling +-spec export() -> ok | {error, string()}. +export() -> + TmpDir = prep_tmp_dir(), + CmdList = get_cmd_list(), + FileList = get_file_list(), + copy_to_dir(FileList, TmpDir), + Outputs = run_commands(CmdList), + write_to_file(Outputs, TmpDir), + package_files(TmpDir), + cleanup_tmp_dir(TmpDir). + +get_cmd_list() -> + List = [ + {"df", "df"} + ], + Stats = [ + {"iostat", "iostat 1 4"}, + {"vmstat", "vmstat 1 4"}, + {"sysctl-net", "sysctl net"} + ], + PerOS = case os:type() of + {unix, linux} -> + [ + {"swappiness", "sysctl vm.swappiness"} + ] ++ Stats; + {unix, darwin} -> + [ + ]; % unsupported for production + {unix, freebsd} -> + [ + ] ++ Stats; + {unix, openbsd} -> + [ + ] ++ Stats; + {unix, sunos} -> + [ + ]; + _ -> [] %maybe explicitly error here? + end, + List ++ PerOS. + +get_file_list() -> + List = [ + "/etc/hosts", % might want to omit + "/etc/hostname", + "/etc/riak/", %TODO get from ENV variable + "/etc/fstab", + "/var/log/riak/" %TODO get from ENV variable + ], + PerOS = case os:type() of + {unix, linux} -> + [ + "/etc/fstab" + ]; + {unix, darwin} -> []; % unsupported for production + {unix, freebsd} -> + [ + ]; + {unix, openbsd} -> + [ + ]; + {unix, sunos} -> + [ + ]; + _ -> [] %maybe explicitly error here? + end, + List ++ PerOS. + +run_commands(CmdList) -> + [{Name, run_command(Cmd)} || + {Name, Cmd} <- CmdList]. + +run_command(Cmd) -> + try + riaknostic_util:run_command(Cmd) + catch + Error:Reason -> + io:format("when running ~s got ~s:~s~n ~w~n", + [Cmd, Error, Reason, erlang:get_stacktrace()]), + "got error\n" + end. + + +copy_to_dir([], _Dir) -> + ok; +copy_to_dir(FileList, Dir) -> + [FileName|Tail] = FileList, + case lists:last(FileName) of + $/ -> + %%add files in this dir to tail + {ok, Files} = file:list_dir(FileName), + copy_to_dir([FileName ++ File || File <- Files] ++ Tail, + Dir); + _ -> + Res = file:copy(FileName, + Dir ++ filename:basename(FileName)), + case Res of + {ok, _} -> ok; + {error, Reason} -> + %TODO: Something better than a hard stop + io:format("Couldn't copy ~s. Error: ~s", + [FileName, Reason]) + end, + copy_to_dir(Tail, Dir) + end. + +write_to_file([], _Dir) -> + ok; +write_to_file(Outputs, Dir) -> + [H|T] = Outputs, + {Filename, Output} = H, + file:write_file(Dir ++ "/" ++ Filename, Output), + write_to_file(T, Dir). + +package_files(Dir) -> + % gathered everything, now package it; + {ok, NameList} = file:list_dir(Dir), + FileList = ["export/" ++ File || File <- NameList], + PrefLen = string:len(Dir) - string:len("export/"), + Prefix = string:sub_string(Dir, 1, PrefLen), + {ok, Cwd} = file:get_cwd(), + io:format("Writing export file: ~s~n", + [Cwd ++ "/export.zip"]), + zip:zip("export.zip", FileList, [{cwd, Prefix}]). + +prep_tmp_dir() -> + % this may not be a good idea + {A, B, C} = now(), + {ok, Cwd} = file:get_cwd(), + DirPrefix = Cwd ++ "/export" ++ integer_to_list(A+B+C), + file:make_dir(DirPrefix), + DirName = DirPrefix ++ "/export/", + file:make_dir(DirName), + DirName. + +cleanup_tmp_dir(DirName) -> + {ok, FileNames} = file:list_dir(DirName), + lists:map(fun(File) -> file:delete(DirName ++ File) end, + FileNames), + ok = file:del_dir(DirName), + PrefLen = string:len(DirName) - string:len("export/"), + Prefix = string:sub_string(DirName, 1, PrefLen), + ok = file:del_dir(Prefix). + + diff --git a/src/weatherreport/src/riaknostic_node.erl b/src/weatherreport/src/riaknostic_node.erl new file mode 100644 index 00000000000..7e0ee0e6cff --- /dev/null +++ b/src/weatherreport/src/riaknostic_node.erl @@ -0,0 +1,208 @@ +%% ------------------------------------------------------------------- +%% +%% riaknostic - automated diagnostic tools for Riak +%% +%% Copyright (c) 2011 Basho Technologies, Inc. All Rights Reserved. +%% +%% This file is provided to you under the Apache License, +%% Version 2.0 (the "License"); you may not use this file +%% except in compliance with the License. You may obtain +%% a copy of the License at +%% +%% http://www.apache.org/licenses/LICENSE-2.0 +%% +%% Unless required by applicable law or agreed to in writing, +%% software distributed under the License is distributed on an +%% "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +%% KIND, either express or implied. See the License for the +%% specific language governing permissions and limitations +%% under the License. +%% +%% ------------------------------------------------------------------- + +%% @doc Functions that help diagnostics interact with the local Riak +%% node or other members of the cluster. +-module(riaknostic_node). + +-export([can_connect/0, + can_connect_all/0, + stats/0, + pid/0, + local_command/2, + local_command/3, + local_command/4, + cluster_command/2, + cluster_command/3, + cluster_command/4 + ]). + +%% @doc Calls the given 0-arity module and function on the local Riak +%% node and returns the result of that call. +%% @equiv local_command(Module, Function, []) +%% @see can_connect/0. +-spec local_command(Module::atom(), Function::atom()) -> term(). +local_command(Module, Function) -> + local_command(Module, Function, []). + +%% @doc Calls the given module and function with the given arguments +%% on the local Riak node and returns the result of that call. +%% @equiv local_command(Module, Function, Args, 5000) +%% @see can_connect/0 +-spec local_command(Module::atom(), Function::atom(), Args::[term()]) -> term(). +local_command(Module, Function, Args) -> + local_command(Module, Function, Args, 5000). + +%% @doc Calls the given module and function with the given arguments +%% on the local Riak node and returns the result of that call, +%% returning an error if the call doesn't complete within the given +%% timeout. +%% @equiv rpc:call(RiakNodeName, Module, Function, Args, Timeout) +%% @see can_connect/0 +-spec local_command(Module::atom(), Function::atom(), Args::[term()], Timeout::integer()) -> term(). +local_command(Module, Function, Args, Timeout) -> + riaknostic_util:log(debug, "Local RPC: ~p:~p(~p) [~p]", [Module, Function, Args, Timeout]), + rpc:call(nodename(), Module, Function, Args, Timeout). + +%% @doc Calls the given 0-arity module and function on all members of +%% the Riak cluster. +%% @equiv cluster_command(Module, Function, []) +%% @see can_connect/0 +-spec cluster_command(Module::atom(), Function::atom()) -> term(). +cluster_command(Module, Function) -> + cluster_command(Module, Function, []). + +%% @doc Calls the given module and function with the given arguments +%% on all members of the Riak cluster. +%% @equiv cluster_command(Module, Function, Args, 5000) +%% @see can_connect/0 +-spec cluster_command(Module::atom(), Function::atom(), Args::[term()]) -> term(). +cluster_command(Module, Function, Args) -> + cluster_command(Module, Function, Args, 5000). + +%% @doc Calls the given module and function with the given arguments +%% on all members for the Riak cluster, returning an error if the call +%% doesn't complete within the given timeout. +%% @equiv rpc:multicall(RiakClusterMembers, Module, Function, Args, Timeout) +%% @see can_connect/0 +-spec cluster_command(Module::atom(), Function::atom(), Args::[term()], Timeout::integer()) -> term(). +cluster_command(Module, Function, Args, Timeout) -> + riaknostic_util:log(debug, "Cluster RPC: ~p:~p(~p) [~p]", [Module, Function, Args, Timeout]), + Stats = stats(), + {ring_members, RingMembers} = lists:keyfind(ring_members, 1, Stats), + rpc:multicall(RingMembers, Module, Function, Args, Timeout). + +%% @doc Retrieves the operating system's process ID of the local Riak +%% node. +%% @equiv local_command(os, getpid) +%% @see can_connect/0 +-spec pid() -> string(). +pid() -> + local_command(os, getpid). + +%% @doc Attempts to connect to the local Riak node if it is not +%% already, and returns whether connection was successful. +-spec can_connect() -> true | false. +can_connect() -> + case is_connected() of + true -> true; + false -> + riaknostic_util:log(debug, "Not connected to the local Riak node, trying to connect. alive:~p connect_failed:~p", [is_alive(), connect_failed()]), + maybe_connect() + end. + +-spec can_connect_all() -> true | false. +can_connect_all() -> + case is_connected() of + true -> + case riaknostic_check_nodes_connected:check() of + [] -> true; + _ -> false + end; + false -> false + end. + +%% @doc Fetches or returns previously fetched Riak statistics. +%% @see can_connect/0 +-spec stats() -> [proplists:property()]. +stats() -> + case has_stats() of + {ok, Stats} -> Stats; + _ -> fetch_stats() + end. + +%% Private functions +is_connected() -> + is_alive() andalso connect_failed() =/= true. + +maybe_connect() -> + case connect_failed() of + true -> false; + _ -> try_connect() + end. + +try_connect() -> + TargetNode = nodename(), + case is_alive() of + true -> ok; + _ -> start_net() + end, + case {net_kernel:hidden_connect_node(TargetNode), net_adm:ping(TargetNode)} of + {true, pong} -> + application:set_env(riaknostic, connect_failed, false), + riaknostic_util:log(debug, "Connected to local Riak node ~p.", [TargetNode]), + true; + _ -> + application:set_env(riaknostic, connect_failed, true), + lager:warning("Could not connect to the local Riak node ~p, some checks will not run.", [TargetNode]), + false + end. + +connect_failed() -> + case application:get_env(riaknostic, connect_failed) of + {ok, true} -> true; + undefined -> undefined; + _ -> false + end. + +start_net() -> + riaknostic_util:log(debug, "Starting distributed Erlang."), + {Type, RiakName} = riaknostic_config:node_name(), + ThisNode = append_node_suffix(RiakName, "_diag"), + {ok, _} = net_kernel:start([ThisNode, Type]), + erlang:set_cookie(node(), riaknostic_config:cookie()). + +nodename() -> + {_, Name} = riaknostic_config:node_name(), + case string:tokens(Name, "@") of + [_Node, _Host] -> + list_to_atom(Name); + [Node] -> + [_, Host] = string:tokens(atom_to_list(node()), "@"), + list_to_atom(lists:concat([Node, "@", Host])) + end. + +append_node_suffix(Name, Suffix) -> + case string:tokens(Name, "@") of + [Node, Host] -> + list_to_atom(lists:concat([Node, Suffix, os:getpid(), "@", Host])); + [Node] -> + list_to_atom(lists:concat([Node, Suffix, os:getpid()])) + end. + +has_stats() -> + case application:get_env(riaknostic, local_stats) of + {ok, Stats} -> + {ok, Stats}; + undefined -> + false + end. + +fetch_stats() -> + riaknostic_util:log(debug, "Fetching local riak_kv_status."), + case local_command(riak_kv_status, statistics) of + [] -> []; + PList -> + application:set_env(riaknostic, local_stats, PList), + PList + end. + diff --git a/src/weatherreport/src/riaknostic_util.erl b/src/weatherreport/src/riaknostic_util.erl new file mode 100644 index 00000000000..5d370862b6c --- /dev/null +++ b/src/weatherreport/src/riaknostic_util.erl @@ -0,0 +1,89 @@ +%% ------------------------------------------------------------------- +%% +%% riaknostic - automated diagnostic tools for Riak +%% +%% Copyright (c) 2011 Basho Technologies, Inc. All Rights Reserved. +%% +%% This file is provided to you under the Apache License, +%% Version 2.0 (the "License"); you may not use this file +%% except in compliance with the License. You may obtain +%% a copy of the License at +%% +%% http://www.apache.org/licenses/LICENSE-2.0 +%% +%% Unless required by applicable law or agreed to in writing, +%% software distributed under the License is distributed on an +%% "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +%% KIND, either express or implied. See the License for the +%% specific language governing permissions and limitations +%% under the License. +%% +%% ------------------------------------------------------------------- + +%% @doc Utility functions for riaknostic. +%% @end +-module(riaknostic_util). +-export([short_name/1, + run_command/1, + log/2,log/3, + binary_to_float/1]). + +%% @doc Converts a check module name into a short name that can be +%% used to refer to a check on the command line. For example, +%% riaknostic_check_disk becomes "disk". +-spec short_name(module()) -> iodata() | unicode:charlist(). +short_name(Mod) when is_atom(Mod) -> + re:replace(atom_to_list(Mod), "riaknostic_check_", "", [{return, list}]). + +%% @doc Runs a shell command and returns the output. stderr is +%% redirected to stdout so its output will be included. +-spec run_command(Command::iodata()) -> StdOut::iodata(). +run_command(Command) -> + riaknostic_util:log(debug, "Running shell command: ~s", [Command]), + Port = erlang:open_port({spawn,Command},[exit_status, stderr_to_stdout]), + do_read(Port, []). + +do_read(Port, Acc) -> + receive + {Port, {data, StdOut}} -> + riaknostic_util:log(debug, "Shell command output: ~n~s~n",[StdOut]), + do_read(Port, Acc ++ StdOut); + {Port, {exit_status, _}} -> + %%port_close(Port), + Acc; + Other -> + io:format("~w", [Other]), + do_read(Port, Acc) + end. + +%% @doc Converts a binary containing a text representation of a float +%% into a float type. +-spec binary_to_float(binary()) -> float(). +binary_to_float(Bin) -> + list_to_float(binary_to_list(Bin)). + +log(Level, Format, Terms) -> + case should_log(Level) of + true -> + io:format(lists:concat(["[", Level, "] ", Format, "~n"]), Terms); + false -> + ok + end, + lager:log(Level, self(), Format, Terms). + +log(Level, String) -> + case should_log(Level) of + true -> + io:format(lists:concat(["[", Level, "] ", String, "~n"])); + false -> + ok + end, + lager:log(Level, self(), String). + +should_log(Level) -> + AppLevel = case application:get_env(riaknostic, log_level) of + undefined -> info; + {ok, L0} -> L0 + end, + lager_util:level_to_num(AppLevel) >= lager_util:level_to_num(Level). +