diff options
Diffstat (limited to 'ansible_collections/gluster')
16 files changed, 2027 insertions, 0 deletions
diff --git a/ansible_collections/gluster/gluster/.github/workflows/ansible-test-plugins.yml b/ansible_collections/gluster/gluster/.github/workflows/ansible-test-plugins.yml new file mode 100644 index 000000000..8c6b612cc --- /dev/null +++ b/ansible_collections/gluster/gluster/.github/workflows/ansible-test-plugins.yml @@ -0,0 +1,47 @@ +--- +name: Plugins CI +on: + push: + paths: + - 'plugins/**' + - 'tests/**' + - '.github/workflows/ansible-test-plugins.yml' + - 'changelogs/*' + - 'galaxy.yml' + pull_request: + paths: + - 'plugins/**' + - 'tests/**' + - '.github/workflows/ansible-test-plugins.yml' + schedule: + - cron: '0 6 * * *' + +jobs: + sanity: + name: "Sanity (Ansible: ${{ matrix.ansible }})" + runs-on: ubuntu-latest + strategy: + matrix: + ansible: + - stable-2.9 + - stable-2.10 + - devel + + steps: + - name: Check out code + uses: actions/checkout@v2 + with: + path: ansible_collections/gluster/gluster + + # Just run once as anyway ansible-test sanity using container will run test for all python versions he supports + - name: Set up Python + uses: actions/setup-python@v2 + with: + python-version: 3.8 + + - name: Install ansible-base (${{ matrix.ansible }}) + run: pip install https://github.com/ansible/ansible/archive/${{ matrix.ansible }}.tar.gz --disable-pip-version-check + + - name: Run sanity tests + run: ansible-test sanity --docker -v --color + working-directory: ./ansible_collections/gluster/gluster diff --git a/ansible_collections/gluster/gluster/.gitignore b/ansible_collections/gluster/gluster/.gitignore new file mode 100644 index 000000000..17f16c032 --- /dev/null +++ b/ansible_collections/gluster/gluster/.gitignore @@ -0,0 +1,2 @@ +tests/output/ +changelogs/.plugin-cache.yaml diff --git a/ansible_collections/gluster/gluster/CHANGELOG.rst b/ansible_collections/gluster/gluster/CHANGELOG.rst new file mode 100644 index 000000000..fca0b161a --- /dev/null +++ b/ansible_collections/gluster/gluster/CHANGELOG.rst @@ -0,0 +1,33 @@ +======================================== +Gluster Ansible Collection Release Notes +======================================== + +.. contents:: Topics + + +v1.0.2 +====== + +Major Changes +------------- + +- enable client.ssl,server.ssl before starting the gluster volume (https://github.com/gluster/gluster-ansible-collection/pull/19) + +v1.0.1 +====== + +v1.0.0 +====== + +Major Changes +------------- + +- geo_rep - Added the independent module of geo rep with other gluster modules (https://github.com/gluster/gluster-ansible-collection/pull/2). + +New Modules +----------- + +- gluster.gluster.geo_rep - Manage geo-replication sessions +- gluster.gluster.gluster_heal_info - Gather facts about either self-heal or rebalance status +- gluster.gluster.gluster_peer - Attach/Detach peers to/from the cluster +- gluster.gluster.gluster_volume - Manage GlusterFS volumes diff --git a/ansible_collections/gluster/gluster/FILES.json b/ansible_collections/gluster/gluster/FILES.json new file mode 100644 index 000000000..3d69367d0 --- /dev/null +++ b/ansible_collections/gluster/gluster/FILES.json @@ -0,0 +1,166 @@ +{ + "files": [ + { + "name": ".", + "ftype": "dir", + "chksum_type": null, + "chksum_sha256": null, + "format": 1 + }, + { + "name": "changelogs", + "ftype": "dir", + "chksum_type": null, + "chksum_sha256": null, + "format": 1 + }, + { + "name": "changelogs/config.yaml", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "6e1e182c7b4c93f749e0c7db228bc12d402a644619141252c43df5ab431c536f", + "format": 1 + }, + { + "name": "changelogs/changelog.yaml", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "d0803d36de0cbf127c9d777632b3b4b89192b90e05b8345c0c3749a80e709e58", + "format": 1 + }, + { + "name": "meta", + "ftype": "dir", + "chksum_type": null, + "chksum_sha256": null, + "format": 1 + }, + { + "name": "meta/runtime.yml", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "df18179bb2f5447a56ac92261a911649b96821c0b2c08eea62d5cc6b0195203f", + "format": 1 + }, + { + "name": "tests", + "ftype": "dir", + "chksum_type": null, + "chksum_sha256": null, + "format": 1 + }, + { + "name": "tests/sanity", + "ftype": "dir", + "chksum_type": null, + "chksum_sha256": null, + "format": 1 + }, + { + "name": "tests/sanity/ignore-2.10.txt", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "20bbf9ce835ecf43b345c1c86a5eafecaec98737c0a00f3146f6e459fbb2121a", + "format": 1 + }, + { + "name": "tests/sanity/ignore-2.11.txt", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "20bbf9ce835ecf43b345c1c86a5eafecaec98737c0a00f3146f6e459fbb2121a", + "format": 1 + }, + { + "name": "CHANGELOG.rst", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "7f395483772bc670fd201b8aceecee35ae6b9249a1f10fab55ad945a6c2230e0", + "format": 1 + }, + { + "name": "LICENSE", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "77691ea688645a83845ebe7922c529777d99cc34ac18e98c1bb710c576cf6073", + "format": 1 + }, + { + "name": ".github", + "ftype": "dir", + "chksum_type": null, + "chksum_sha256": null, + "format": 1 + }, + { + "name": ".github/workflows", + "ftype": "dir", + "chksum_type": null, + "chksum_sha256": null, + "format": 1 + }, + { + "name": ".github/workflows/ansible-test-plugins.yml", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "8baecdfb5acea6041a2689fb0f3ae7f87bfaa87be9c7fdf2bdf14ece1ceac786", + "format": 1 + }, + { + "name": "plugins", + "ftype": "dir", + "chksum_type": null, + "chksum_sha256": null, + "format": 1 + }, + { + "name": "plugins/modules", + "ftype": "dir", + "chksum_type": null, + "chksum_sha256": null, + "format": 1 + }, + { + "name": "plugins/modules/gluster_volume.py", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "68d4cef121879d83711f137466e13d9f6af40c2abdc00b09ccba4f31fc8762a4", + "format": 1 + }, + { + "name": "plugins/modules/gluster_heal_info.py", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "4e96c378d5e33d8230868f65229dd73b8365e7b3da59170c1c82a7995c6b1ba2", + "format": 1 + }, + { + "name": "plugins/modules/geo_rep.py", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "7d030d566f4be86dd6fb83eaa1aa7b01738fbb27b3b0492e6b18df0026d38ef7", + "format": 1 + }, + { + "name": "plugins/modules/gluster_peer.py", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "3c6c49c79c57f1319f96808c07fd9f05ebc92322aa4f25d9a63608430f8ac010", + "format": 1 + }, + { + "name": ".gitignore", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "f38ba82ecf283574365b673d8b219740fb4a3d6f07e8d9f06012565d2b0d5d84", + "format": 1 + }, + { + "name": "README.md", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "124c8f6c3f4100b7aee5da0546b2da643d8d3ab58348453fbee48fe0f125d9a4", + "format": 1 + } + ], + "format": 1 +}
\ No newline at end of file diff --git a/ansible_collections/gluster/gluster/LICENSE b/ansible_collections/gluster/gluster/LICENSE new file mode 100644 index 000000000..85d49a071 --- /dev/null +++ b/ansible_collections/gluster/gluster/LICENSE @@ -0,0 +1,288 @@ + GNU GENERAL PUBLIC LICENSE + Version 2, June 1991 + + Copyright (C) 1989, 1991 Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +License is intended to guarantee your freedom to share and change free +software--to make sure the software is free for all its users. This +General Public License applies to most of the Free Software +Foundation's software and to any other program whose authors commit to +using it. (Some other Free Software Foundation software is covered by +the GNU Lesser General Public License instead.) You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +this service if you wish), that you receive source code or can get it +if you want it, that you can change the software or use pieces of it +in new free programs; and that you know you can do these things. + + To protect your rights, we need to make restrictions that forbid +anyone to deny you these rights or to ask you to surrender the rights. +These restrictions translate to certain responsibilities for you if you +distribute copies of the software, or if you modify it. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must give the recipients all the rights that +you have. You must make sure that they, too, receive or can get the +source code. And you must show them these terms so they know their +rights. + + We protect your rights with two steps: (1) copyright the software, and +(2) offer you this license which gives you legal permission to copy, +distribute and/or modify the software. + + Also, for each author's protection and ours, we want to make certain +that everyone understands that there is no warranty for this free +software. If the software is modified by someone else and passed on, we +want its recipients to know that what they have is not the original, so +that any problems introduced by others will not reflect on the original +authors' reputations. + + Finally, any free program is threatened constantly by software +patents. We wish to avoid the danger that redistributors of a free +program will individually obtain patent licenses, in effect making the +program proprietary. To prevent this, we have made it clear that any +patent must be licensed for everyone's free use or not licensed at all. + + The precise terms and conditions for copying, distribution and +modification follow. + + GNU GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License applies to any program or other work which contains +a notice placed by the copyright holder saying it may be distributed +under the terms of this General Public License. The "Program", below, +refers to any such program or work, and a "work based on the Program" +means either the Program or any derivative work under copyright law: +that is to say, a work containing the Program or a portion of it, +either verbatim or with modifications and/or translated into another +language. (Hereinafter, translation is included without limitation in +the term "modification".) Each licensee is addressed as "you". + +Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running the Program is not restricted, and the output from the Program +is covered only if its contents constitute a work based on the +Program (independent of having been made by running the Program). +Whether that is true depends on what the Program does. + + 1. You may copy and distribute verbatim copies of the Program's +source code as you receive it, in any medium, provided that you +conspicuously and appropriately publish on each copy an appropriate +copyright notice and disclaimer of warranty; keep intact all the +notices that refer to this License and to the absence of any warranty; +and give any other recipients of the Program a copy of this License +along with the Program. + +You may charge a fee for the physical act of transferring a copy, and +you may at your option offer warranty protection in exchange for a fee. + + 2. You may modify your copy or copies of the Program or any portion +of it, thus forming a work based on the Program, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) You must cause the modified files to carry prominent notices + stating that you changed the files and the date of any change. + + b) You must cause any work that you distribute or publish, that in + whole or in part contains or is derived from the Program or any + part thereof, to be licensed as a whole at no charge to all third + parties under the terms of this License. + + c) If the modified program normally reads commands interactively + when run, you must cause it, when started running for such + interactive use in the most ordinary way, to print or display an + announcement including an appropriate copyright notice and a + notice that there is no warranty (or else, saying that you provide + a warranty) and that users may redistribute the program under + these conditions, and telling the user how to view a copy of this + License. (Exception: if the Program itself is interactive but + does not normally print such an announcement, your work based on + the Program is not required to print an announcement.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Program, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Program, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Program. + +In addition, mere aggregation of another work not based on the Program +with the Program (or with a work based on the Program) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may copy and distribute the Program (or a work based on it, +under Section 2) in object code or executable form under the terms of +Sections 1 and 2 above provided that you also do one of the following: + + a) Accompany it with the complete corresponding machine-readable + source code, which must be distributed under the terms of Sections + 1 and 2 above on a medium customarily used for software interchange; or, + + b) Accompany it with a written offer, valid for at least three + years, to give any third party, for a charge no more than your + cost of physically performing source distribution, a complete + machine-readable copy of the corresponding source code, to be + distributed under the terms of Sections 1 and 2 above on a medium + customarily used for software interchange; or, + + c) Accompany it with the information you received as to the offer + to distribute corresponding source code. (This alternative is + allowed only for noncommercial distribution and only if you + received the program in object code or executable form with such + an offer, in accord with Subsection b above.) + +The source code for a work means the preferred form of the work for +making modifications to it. For an executable work, complete source +code means all the source code for all modules it contains, plus any +associated interface definition files, plus the scripts used to +control compilation and installation of the executable. However, as a +special exception, the source code distributed need not include +anything that is normally distributed (in either source or binary +form) with the major components (compiler, kernel, and so on) of the +operating system on which the executable runs, unless that component +itself accompanies the executable. + +If distribution of executable or object code is made by offering +access to copy from a designated place, then offering equivalent +access to copy the source code from the same place counts as +distribution of the source code, even though third parties are not +compelled to copy the source along with the object code. + + 4. You may not copy, modify, sublicense, or distribute the Program +except as expressly provided under this License. Any attempt +otherwise to copy, modify, sublicense or distribute the Program is +void, and will automatically terminate your rights under this License. +However, parties who have received copies, or rights, from you under +this License will not have their licenses terminated so long as such +parties remain in full compliance. + + 5. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Program or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Program (or any work based on the +Program), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Program or works based on it. + + 6. Each time you redistribute the Program (or any work based on the +Program), the recipient automatically receives a license from the +original licensor to copy, distribute or modify the Program subject to +these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties to +this License. + + 7. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Program at all. For example, if a patent +license would not permit royalty-free redistribution of the Program by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Program. + +If any portion of this section is held invalid or unenforceable under +any particular circumstance, the balance of the section is intended to +apply and the section as a whole is intended to apply in other +circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system, which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 8. If the distribution and/or use of the Program is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Program under this License +may add an explicit geographical distribution limitation excluding +those countries, so that distribution is permitted only in or among +countries not thus excluded. In such case, this License incorporates +the limitation as if written in the body of this License. + + 9. The Free Software Foundation may publish revised and/or new versions +of the General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + +Each version is given a distinguishing version number. If the Program +specifies a version number of this License which applies to it and "any +later version", you have the option of following the terms and conditions +either of that version or of any later version published by the Free +Software Foundation. If the Program does not specify a version number of +this License, you may choose any version ever published by the Free Software +Foundation. + + 10. If you wish to incorporate parts of the Program into other free +programs whose distribution conditions are different, write to the author +to ask for permission. For software which is copyrighted by the Free +Software Foundation, write to the Free Software Foundation; we sometimes +make exceptions for this. Our decision will be guided by the two goals +of preserving the free status of all derivatives of our free software and +of promoting the sharing and reuse of software generally. + + NO WARRANTY + + 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY +FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN +OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES +PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED +OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS +TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE +PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, +REPAIR OR CORRECTION. + + 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR +REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, +INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING +OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED +TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY +YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER +PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE +POSSIBILITY OF SUCH DAMAGES. + + END OF TERMS AND CONDITIONS + + Copyright 2014 Red Hat, Inc. + +This General Public License does not permit incorporating your program into +proprietary programs. If your program is a subroutine library, you may +consider it more useful to permit linking proprietary applications with the +library. If this is what you want to do, use the GNU Lesser General +Public License instead of this License. diff --git a/ansible_collections/gluster/gluster/MANIFEST.json b/ansible_collections/gluster/gluster/MANIFEST.json new file mode 100644 index 000000000..5bcf910e3 --- /dev/null +++ b/ansible_collections/gluster/gluster/MANIFEST.json @@ -0,0 +1,32 @@ +{ + "collection_info": { + "namespace": "gluster", + "name": "gluster", + "version": "1.0.2", + "authors": [ + "gluster (github.com/gluster)```", + "Prajith Kesava Prasad <pkesavap@redhat.com>" + ], + "readme": "README.md", + "tags": [ + "gluster", + "storage" + ], + "description": "Administrate a gluster cluster.", + "license": [], + "license_file": "./LICENSE", + "dependencies": {}, + "repository": "https://github.com/gluster/gluster-ansible-collection", + "documentation": "https://docs.ansible.com/ansible/latest/", + "homepage": "https://github.com/gluster/gluster-ansible-collection", + "issues": "https://github.com/gluster/gluster-ansible-collection/issues" + }, + "file_manifest_file": { + "name": "FILES.json", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "a89d1a6580a92a0bd5fcc7aead7fc08398db51a66b8f8a43e3a2b6a167d10acd", + "format": 1 + }, + "format": 1 +}
\ No newline at end of file diff --git a/ansible_collections/gluster/gluster/README.md b/ansible_collections/gluster/gluster/README.md new file mode 100644 index 000000000..5966dd7eb --- /dev/null +++ b/ansible_collections/gluster/gluster/README.md @@ -0,0 +1,37 @@ +gluster ansible collection +==================================== + +The `gluster.gluster` manages all ansible modules of gluster-ansible + +Note +---- +Please note that when installing this collection from Ansible Galaxy you are instructed to run following command: + +```bash +$ ansible-galaxy collection install gluster.gluster +``` + + +Requirements +------------ + + * Ansible version 2.9 or higher + * Python SDK version 4.3 or higher + +Modules documentation +-------------- + +Dependencies +------------ +None. + +Example Playbook +---------------- + +``` +playbook +``` +License +------- + +GNU General Public License v2.0 diff --git a/ansible_collections/gluster/gluster/changelogs/changelog.yaml b/ansible_collections/gluster/gluster/changelogs/changelog.yaml new file mode 100644 index 000000000..788fa6015 --- /dev/null +++ b/ansible_collections/gluster/gluster/changelogs/changelog.yaml @@ -0,0 +1,31 @@ +ancestor: null +releases: + 1.0.0: + changes: + major_changes: + - geo_rep - Added the independent module of geo rep with other gluster modules + (https://github.com/gluster/gluster-ansible-collection/pull/2). + fragments: + - 2-geo_rep.py + modules: + - description: Manage geo-replication sessions + name: geo_rep + namespace: '' + - description: Gather facts about either self-heal or rebalance status + name: gluster_heal_info + namespace: '' + - description: Attach/Detach peers to/from the cluster + name: gluster_peer + namespace: '' + - description: Manage GlusterFS volumes + name: gluster_volume + namespace: '' + release_date: '2020-06-29' + 1.0.1: + release_date: '2020-08-18' + 1.0.2: + changes: + major_changes: + - enable client.ssl,server.ssl before starting the gluster volume + (https://github.com/gluster/gluster-ansible-collection/pull/19) + release_date: '2021-09-17' diff --git a/ansible_collections/gluster/gluster/changelogs/config.yaml b/ansible_collections/gluster/gluster/changelogs/config.yaml new file mode 100644 index 000000000..bf477a77c --- /dev/null +++ b/ansible_collections/gluster/gluster/changelogs/config.yaml @@ -0,0 +1,31 @@ +changelog_filename_template: ../CHANGELOG.rst +changelog_filename_version_depth: 0 +changes_file: changelog.yaml +changes_format: combined +ignore_other_fragment_extensions: true +keep_fragments: false +mention_ancestor: true +new_plugins_after_name: removed_features +notesdir: fragments +prelude_section_name: release_summary +prelude_section_title: Release Summary +sections: +- - major_changes + - Major Changes +- - minor_changes + - Minor Changes +- - breaking_changes + - Breaking Changes / Porting Guide +- - deprecated_features + - Deprecated Features +- - removed_features + - Removed Features (previously deprecated) +- - security_fixes + - Security Fixes +- - bugfixes + - Bugfixes +- - known_issues + - Known Issues +title: Gluster Ansible Collection +trivial_section_name: trivial +use_fqcn: true diff --git a/ansible_collections/gluster/gluster/meta/runtime.yml b/ansible_collections/gluster/gluster/meta/runtime.yml new file mode 100644 index 000000000..2ee3c9fa9 --- /dev/null +++ b/ansible_collections/gluster/gluster/meta/runtime.yml @@ -0,0 +1,2 @@ +--- +requires_ansible: '>=2.9.10' diff --git a/ansible_collections/gluster/gluster/plugins/modules/geo_rep.py b/ansible_collections/gluster/gluster/plugins/modules/geo_rep.py new file mode 100644 index 000000000..801cbd39f --- /dev/null +++ b/ansible_collections/gluster/gluster/plugins/modules/geo_rep.py @@ -0,0 +1,347 @@ +#!/usr/bin/python +# -*- coding: utf-8 -*- +# +# Copyright 2015 Nandaja Varma <nvarma@redhat.com> +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Library General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, +# USA. + +from __future__ import absolute_import, division, print_function +__metaclass__ = type + +DOCUMENTATION = """ +module: geo_rep +short_description: Manage geo-replication sessions +description: + - Create, stop, delete and configure geo-replication session +author: Sachidananda Urs (@sac) +options: + action: + description: + - Action to be performed on geo-replication session. + required: true + choices: ['create', 'start', 'stop', 'delete', 'pause', 'resume', 'config'] + type: str + mastervol: + description: + - Master volume name. + type: str + slavevol: + description: + - Slave volume name. + type: str + force: + description: + - force the system to perform the action. + type: str + georepuser: + description: + - Username to be used for the action being performed. + type: str + gluster_log_file: + description: + - The path to the geo-replication glusterfs log file. + type: str + gluster_log_level: + description: + - The log level for glusterfs processes. + type: str + log_file: + description: + - The path to the geo-replication log file. + type: str + log_level: + description: + - The log level for geo-replication. + type: str + changelog_log_level: + description: + - The log level for the changelog. + type: str + ssh_command: + description: + - The SSH command to connect to the remote machine. + type: str + rsync_command: + description: + - The command to use for setting synchronizing method for the files. + type: str + use_tarssh: + description: + - To use tar over ssh. + type: str + volume_id: + description: + - deletes the existing master UID for the intermediate/slave node. + type: str + timeout: + description: + - timeout period. + type: str + sync_jobs: + description: + - number of sync-jobs . + type: str + ignore_deletes: + description: + - file deletion on the master will not trigger a delete operation on the slave. + type: str + checkpoint: + description: + - Sets a checkpoint with the given option. + type: str + sync_acls: + description: + - Syncs acls to the Slave cluster. + type: str + sync_xattrs: + description: + - Syncs extended attributes to the Slave cluster. + type: str + log_rsync_performance: + description: + - for recording the rsync performance in log files. + type: str + rsync_options: + description: + - Additional options to rsync. + type: str + use_meta_volume: + description: + - to use meta volume in Geo-replication. + type: str + meta_volume_mnt: + description: + - The path of the meta volume mount point. + type: str +""" + +EXAMPLES = """ +- name: Create the geo-rep session + gluster.gluster.geo_rep: + action: create + mastervol: 10.70.42.122:mastervolume + slavevol: 10.70.43.48:slavevolume + force: true + georepuser: staff +- name: Starts the geo-rep session + gluster.gluster.geo_rep: + action: start + mastervol: 10.70.42.122:mastervolume + slavevol: 10.70.43.48:slavevolume + force: true + georepuser: staff +- name: Pause the geo-rep session + gluster.gluster.geo_rep: + action: pause + mastervol: 10.70.42.122:mastervolume + slavevol: 10.70.43.48:slavevolume + force: true + georepuser: staff +- name: Resume the geo-rep session + gluster.gluster.geo_rep: + action: resume + mastervol: 10.70.42.122:mastervolume + slavevol: 10.70.43.48:slavevolume + force: true + georepuser: staff +- name: Stop the geo-rep session + gluster.gluster.geo_rep: + action: stop + mastervol: 10.70.42.122:mastervolume + slavevol: 10.70.43.48:slavevolume + force: true + georepuser: staff +- name: Configures the geo-rep session + gluster.gluster.geo_rep: + action: config + mastervol: 10.70.42.122:mastervolume + slavevol: 10.70.43.48:slavevolume + gluster_log_file: /var/log/glusterfs/geo-replication/gluster.log + gluster_log_level: INFO + log_file: /var/log/glusterfs/geo-replication/file.log + log_level: INFO + changelog_log_level: INFO + ssh_command: SSH + rsync_command: rsync + use_tarssh: true + volume_id: 6a071cfa-b150-4f0b-b1ed-96ab5d4bd671 + timeout: 60 + sync_jobs: 3 + ignore_deletes: 1 + checkpoint: now + sync_acls: true + sync_xattr: true + log_rsync_performance: true + rsync_options: --compress-level=0 + use_meta_volume: true + meta_volume_mnt: /var/run/gluster/shared_storage/ +- name: Delete the geo-rep session + gluster.gluster.geo_rep: + action: delete + mastervol: 10.70.42.122:mastervolume + slavevol: 10.70.43.48:slavevolume + georepuser: staff +""" + +import re +from ansible.module_utils.basic import AnsibleModule + + +class GeoRep(object): + def __init__(self, module): + self.module = module + self.action = self._validated_params('action') + self.gluster_georep_ops() + + def get_playbook_params(self, opt): + return self.module.params[opt] + + def _validated_params(self, opt): + value = self.get_playbook_params(opt) + if value is None: + msg = "Please provide %s option in the playbook!" % opt + self.module.fail_json(msg=msg) + return value + + def gluster_georep_ops(self): + mastervol = self._validated_params('mastervol') + slavevol = self._validated_params('slavevol') + slavevol = self.check_pool_exclusiveness(mastervol, slavevol) + if self.action in ['delete', 'config']: + force = '' + else: + force = self._validated_params('force') + force = 'force' if force == 'yes' else ' ' + options = 'no-verify' if self.action == 'create' \ + else self.config_georep() + if isinstance(options, list): + for opt in options: + rc, output, err = self.call_gluster_cmd('volume', + 'geo-replication', + mastervol, slavevol, + self.action, opt, + force) + else: + rc, output, err = self.call_gluster_cmd('volume', + 'geo-replication', + mastervol, slavevol, + self.action, options, + force) + self._get_output(rc, output, err) + if self.action in ['stop', 'delete'] and self.user == 'root': + self.user = 'geoaccount' + rc, output, err = self.call_gluster_cmd('volume', 'geo-replication', + mastervol, slavevol.replace( + 'root', 'geoaccount'), + self.action, options, force) + self._get_output(rc, output, err) + + def config_georep(self): + if self.action != 'config': + return '' + options = ['gluster_log_file', 'gluster_log_level', 'log_file', + 'log_level', 'changelog_log_level', 'ssh_command', + 'rsync_command', 'use_tarssh', 'volume_id', 'timeout', + 'sync_jobs', 'ignore_deletes', 'checkpoint', 'sync_acls', + 'sync_xattrs', 'log_rsync_performance', 'rsync_options', + 'use_meta_volume', 'meta_volume_mnt'] + configs = [] + for opt in options: + value = self._validated_params(opt) + if value: + if value == 'reset': + configs.append("'!" + opt.replace('_', '-') + "'") + configs.append(opt.replace('_', '-') + ' ' + value) + if configs: + return configs + value = self._validated_params('config') + op = self._validated_params('op') + return value + ' ' + op + + def check_pool_exclusiveness(self, mastervol, slavevol): + rc, output, err = self.module.run_command( + "gluster pool list") + peers_in_cluster = [line.split('\t')[1].strip() for + line in filter(None, output.split('\n')[1:])] + val_group = re.search("(.*):(.*)", slavevol) + if not val_group: + self.module.fail_json(msg="Slave volume in Unknown format. " + "Correct format: <hostname>:<volume name>") + if val_group.group(1) in peers_in_cluster: + self.module.fail_json(msg="slave volume is in the trusted " + "storage pool of master") + self.user = 'root' if self.module.params['georepuser'] is None \ + else self.module.params['georepuser'] + return self.user + '@' + val_group.group(1) + '::' + val_group.group(2) + + def call_gluster_cmd(self, *args, **kwargs): + params = ' '.join(opt for opt in args) + key_value_pair = ' '.join(' %s %s ' % (key, value) + for key, value in kwargs) + return self._run_command('gluster', ' ' + params + ' ' + key_value_pair) + + def _get_output(self, rc, output, err): + carryon = True if self.action in ['stop', + 'delete', 'resume'] else False + changed = 0 if (carryon and rc) else 1 + if self.action in ['stop', 'delete'] and ( + self.user == 'root' and changed == 0): + return + if not rc or carryon: + self.module.exit_json(stdout=output, changed=changed) + else: + self.module.fail_json(msg=err) + + def _run_command(self, op, opts): + cmd = self.module.get_bin_path(op, True) + opts + return self.module.run_command(cmd) + + +def main(): + module = AnsibleModule( + argument_spec=dict( + action=dict(required=True, choices=['create', 'start', + 'stop', 'delete', 'pause', 'resume', 'config']), + mastervol=dict(), + slavevol=dict(), + force=dict(), + georepuser=dict(), + gluster_log_file=dict(), + gluster_log_level=dict(), + log_file=dict(), + log_level=dict(), + changelog_log_level=dict(), + ssh_command=dict(), + rsync_command=dict(), + use_tarssh=dict(), + volume_id=dict(), + timeout=dict(), + sync_jobs=dict(), + ignore_deletes=dict(), + checkpoint=dict(), + sync_acls=dict(), + sync_xattrs=dict(), + log_rsync_performance=dict(), + rsync_options=dict(), + use_meta_volume=dict(), + meta_volume_mnt=dict() + ), + ) + GeoRep(module) + + +if __name__ == "__main__": + main() diff --git a/ansible_collections/gluster/gluster/plugins/modules/gluster_heal_info.py b/ansible_collections/gluster/gluster/plugins/modules/gluster_heal_info.py new file mode 100644 index 000000000..786e0c23f --- /dev/null +++ b/ansible_collections/gluster/gluster/plugins/modules/gluster_heal_info.py @@ -0,0 +1,205 @@ +#!/usr/bin/python +# -*- coding: utf-8 -*- +# +# Copyright: (c) 2016, Red Hat, Inc. +# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) + +from __future__ import absolute_import, division, print_function +__metaclass__ = type + +ANSIBLE_METADATA = {'metadata_version': '1.1', + 'status': ['preview'], + 'supported_by': 'community'} + + +DOCUMENTATION = ''' +--- +module: gluster_heal_info +short_description: Gather information on self-heal or rebalance status +author: "Devyani Kota (@devyanikota)" +description: + - Gather facts about either self-heal or rebalance status. + - This module was called C(gluster_heal_facts) before Ansible 2.9, returning C(ansible_facts). + Note that the M(gluster.gluster.gluster_heal_info) module no longer returns C(ansible_facts)! +options: + name: + description: + - The volume name. + required: true + aliases: ['volume'] + type: str + status_filter: + default: "self-heal" + choices: ["self-heal", "rebalance"] + type: str + description: + - Determines which facts are to be returned. + - If the C(status_filter) is C(self-heal), status of self-heal, along with the number of files still in process are returned. + - If the C(status_filter) is C(rebalance), rebalance status is returned. +requirements: + - GlusterFS > 3.2 +''' + +EXAMPLES = ''' +- name: Gather self-heal facts about all gluster hosts in the cluster + gluster.gluster.gluster_heal_info: + name: test_volume + status_filter: self-heal + register: self_heal_status +- debug: + var: self_heal_status + +- name: Gather rebalance facts about all gluster hosts in the cluster + gluster.gluster.gluster_heal_info: + name: test_volume + status_filter: rebalance + register: rebalance_status +- debug: + var: rebalance_status +''' + +RETURN = ''' +name: + description: GlusterFS volume name + returned: always + type: str +status_filter: + description: Whether self-heal or rebalance status is to be returned + returned: always + type: str +heal_info: + description: List of files that still need healing process + returned: On success + type: list +rebalance_status: + description: Status of rebalance operation + returned: On success + type: list +''' + +import traceback + +from ansible.module_utils.basic import AnsibleModule +from ansible.module_utils._text import to_native +from distutils.version import LooseVersion + +glusterbin = '' + + +def run_gluster(gargs, **kwargs): + global glusterbin + global module + args = [glusterbin, '--mode=script'] + args.extend(gargs) + try: + rc, out, err = module.run_command(args, **kwargs) + if rc != 0: + module.fail_json(msg='error running gluster (%s) command (rc=%d): %s' % + (' '.join(args), rc, out or err), exception=traceback.format_exc()) + except Exception as e: + module.fail_json(msg='error running gluster (%s) command: %s' % (' '.join(args), + to_native(e)), exception=traceback.format_exc()) + return out + + +def get_self_heal_status(name): + out = run_gluster(['volume', 'heal', name, 'info'], environ_update=dict(LANG='C', LC_ALL='C', LC_MESSAGES='C')) + raw_out = out.split("\n") + heal_info = [] + # return files that still need healing. + for line in raw_out: + if 'Brick' in line: + br_dict = {} + br_dict['brick'] = line.strip().strip("Brick") + elif 'Status' in line: + br_dict['status'] = line.split(":")[1].strip() + elif 'Number' in line: + br_dict['no_of_entries'] = line.split(":")[1].strip() + elif line.startswith('/') or '\n' in line: + continue + else: + br_dict and heal_info.append(br_dict) + br_dict = {} + return heal_info + + +def get_rebalance_status(name): + out = run_gluster(['volume', 'rebalance', name, 'status'], environ_update=dict(LANG='C', LC_ALL='C', LC_MESSAGES='C')) + raw_out = out.split("\n") + rebalance_status = [] + # return the files that are either still 'in progress' state or 'completed'. + for line in raw_out: + line = " ".join(line.split()) + line_vals = line.split(" ") + if line_vals[0].startswith('-') or line_vals[0].startswith('Node'): + continue + node_dict = {} + if len(line_vals) == 1 or len(line_vals) == 4: + continue + node_dict['node'] = line_vals[0] + node_dict['rebalanced_files'] = line_vals[1] + node_dict['failures'] = line_vals[4] + if 'in progress' in line: + node_dict['status'] = line_vals[5] + line_vals[6] + rebalance_status.append(node_dict) + elif 'completed' in line: + node_dict['status'] = line_vals[5] + rebalance_status.append(node_dict) + return rebalance_status + + +def is_invalid_gluster_version(module, required_version): + cmd = module.get_bin_path('gluster', True) + ' --version' + result = module.run_command(cmd) + ver_line = result[1].split('\n')[0] + version = ver_line.split(' ')[1] + # If the installed version is less than 3.2, it is an invalid version + # return True + return LooseVersion(version) < LooseVersion(required_version) + + +def main(): + global module + global glusterbin + module = AnsibleModule( + argument_spec=dict( + name=dict(type='str', required=True, aliases=['volume']), + status_filter=dict(type='str', default='self-heal', choices=['self-heal', 'rebalance']), + ), + ) + is_old_facts = module._name == 'gluster_heal_facts' + if is_old_facts: + module.deprecate("The 'gluster_heal_facts' module has been renamed to 'gluster_heal_info', " + "and the renamed one no longer returns ansible_facts", version='2.13') + + glusterbin = module.get_bin_path('gluster', True) + required_version = "3.2" + status_filter = module.params['status_filter'] + volume_name = module.params['name'] + heal_info = '' + rebalance_status = '' + + # Verify if required GlusterFS version is installed + if is_invalid_gluster_version(module, required_version): + module.fail_json(msg="GlusterFS version > %s is required" % + required_version) + + try: + if status_filter == "self-heal": + heal_info = get_self_heal_status(volume_name) + elif status_filter == "rebalance": + rebalance_status = get_rebalance_status(volume_name) + except Exception as e: + module.fail_json(msg='Error retrieving status: %s' % e, exception=traceback.format_exc()) + + facts = {} + facts['glusterfs'] = {'volume': volume_name, 'status_filter': status_filter, 'heal_info': heal_info, 'rebalance': rebalance_status} + + if is_old_facts: + module.exit_json(ansible_facts=facts) + else: + module.exit_json(**facts) + + +if __name__ == '__main__': + main() diff --git a/ansible_collections/gluster/gluster/plugins/modules/gluster_peer.py b/ansible_collections/gluster/gluster/plugins/modules/gluster_peer.py new file mode 100644 index 000000000..213f2b849 --- /dev/null +++ b/ansible_collections/gluster/gluster/plugins/modules/gluster_peer.py @@ -0,0 +1,177 @@ +#!/usr/bin/python +# -*- coding: utf-8 -*- +# +# Copyright 2015 Nandaja Varma <nvarma@redhat.com> +# Copyright 2018 Red Hat, Inc. +# +# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) +# + +from __future__ import absolute_import, division, print_function +__metaclass__ = type + +ANSIBLE_METADATA = {'metadata_version': '1.1', + 'status': ['preview'], + 'supported_by': 'community'} + +DOCUMENTATION = ''' +--- +module: gluster_peer +short_description: Attach/Detach peers to/from the cluster +description: + - Create or diminish a GlusterFS trusted storage pool. A set of nodes can be + added into an existing trusted storage pool or a new storage pool can be + formed. Or, nodes can be removed from an existing trusted storage pool. +author: Sachidananda Urs (@sac) +options: + state: + choices: ["present", "absent"] + default: "present" + description: + - Determines whether the nodes should be attached to the pool or + removed from the pool. If the state is present, nodes will be + attached to the pool. If state is absent, nodes will be detached + from the pool. + required: true + type: str + nodes: + description: + - List of nodes that have to be probed into the pool. + required: true + type: list + force: + type: bool + default: "false" + description: + - Applicable only while removing the nodes from the pool. gluster + will refuse to detach a node from the pool if any one of the node + is down, in such cases force can be used. +requirements: + - GlusterFS > 3.2 +notes: + - This module does not support check mode. +''' + +EXAMPLES = ''' +- name: Create a trusted storage pool + gluster.gluster.gluster_peer: + state: present + nodes: + - 10.0.1.5 + - 10.0.1.10 + +- name: Delete a node from the trusted storage pool + gluster.gluster.gluster_peer: + state: absent + nodes: + - 10.0.1.10 + +- name: Delete a node from the trusted storage pool by force + gluster.gluster.gluster_peer: + state: absent + nodes: + - 10.0.0.1 + force: true +''' + +RETURN = ''' +''' + +from ansible.module_utils.basic import AnsibleModule +from distutils.version import LooseVersion + + +class Peer(object): + def __init__(self, module): + self.module = module + self.state = self.module.params['state'] + self.nodes = self.module.params['nodes'] + self.glustercmd = self.module.get_bin_path('gluster', True) + self.lang = dict(LANG='C', LC_ALL='C', LC_MESSAGES='C') + self.action = '' + self.force = '' + + def gluster_peer_ops(self): + if not self.nodes: + self.module.fail_json(msg="nodes list cannot be empty") + self.force = 'force' if self.module.params.get('force') else '' + if self.state == 'present': + self.nodes = self.get_to_be_probed_hosts(self.nodes) + self.action = 'probe' + # In case of peer probe, we do not need `force' + self.force = '' + else: + self.action = 'detach' + self.call_peer_commands() + + def get_to_be_probed_hosts(self, hosts): + peercmd = [self.glustercmd, 'pool', 'list', '--mode=script'] + rc, output, err = self.module.run_command(peercmd, + environ_update=self.lang) + peers_in_cluster = [line.split('\t')[1].strip() for + line in filter(None, output.split('\n')[1:])] + try: + peers_in_cluster.remove('localhost') + except ValueError: + # It is ok not to have localhost in list + pass + hosts_to_be_probed = [host for host in hosts if host not in + peers_in_cluster] + return hosts_to_be_probed + + def call_peer_commands(self): + result = {} + result['msg'] = '' + result['changed'] = False + + for node in self.nodes: + peercmd = [self.glustercmd, 'peer', self.action, node, '--mode=script'] + if self.force: + peercmd.append(self.force) + rc, out, err = self.module.run_command(peercmd, + environ_update=self.lang) + if rc: + result['rc'] = rc + result['msg'] = err + # Fail early, do not wait for the loop to finish + self.module.fail_json(**result) + else: + if 'already in peer' in out or \ + 'localhost not needed' in out: + result['changed'] |= False + else: + result['changed'] = True + self.module.exit_json(**result) + + +def main(): + module = AnsibleModule( + argument_spec=dict( + force=dict(type='bool', required=False), + nodes=dict(type='list', required=True), + state=dict(type='str', choices=['absent', 'present'], + default='present'), + ), + supports_check_mode=False + ) + pops = Peer(module) + required_version = "3.2" + # Verify if required GlusterFS version is installed + if is_invalid_gluster_version(module, required_version): + module.fail_json(msg="GlusterFS version > %s is required" % + required_version) + pops.gluster_peer_ops() + + +def is_invalid_gluster_version(module, required_version): + cmd = module.get_bin_path('gluster', True) + ' --version' + result = module.run_command(cmd) + ver_line = result[1].split('\n')[0] + version = ver_line.split(' ')[1] + # If the installed version is less than 3.2, it is an invalid version + # return True + return LooseVersion(version) < LooseVersion(required_version) + + +if __name__ == "__main__": + main() diff --git a/ansible_collections/gluster/gluster/plugins/modules/gluster_volume.py b/ansible_collections/gluster/gluster/plugins/modules/gluster_volume.py new file mode 100644 index 000000000..703ad6d75 --- /dev/null +++ b/ansible_collections/gluster/gluster/plugins/modules/gluster_volume.py @@ -0,0 +1,621 @@ +#!/usr/bin/python +# -*- coding: utf-8 -*- + +# Copyright: (c) 2014, Taneli Leppä <taneli@crasman.fi> +# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) + +from __future__ import absolute_import, division, print_function +__metaclass__ = type + +ANSIBLE_METADATA = {'metadata_version': '1.1', + 'status': ['preview'], + 'supported_by': 'community'} + +DOCUMENTATION = """ +module: gluster_volume +short_description: Manage GlusterFS volumes +description: + - Create, remove, start, stop and tune GlusterFS volumes +options: + name: + description: + - The volume name. + required: true + aliases: ['volume'] + type: str + state: + description: + - Use present/absent ensure if a volume exists or not. + Use started/stopped to control its availability. + required: true + choices: ['absent', 'present', 'started', 'stopped'] + type: str + cluster: + description: + - List of hosts to use for probing and brick setup. + type: list + host: + description: + - Override local hostname (for peer probing purposes). + type: str + replicas: + description: + - Replica count for volume. + type: int + arbiters: + description: + - Arbiter count for volume. + type: int + stripes: + description: + - Stripe count for volume. + type: int + disperses: + description: + - Disperse count for volume. + type: int + redundancies: + description: + - Redundancy count for volume. + type: int + transport: + description: + - Transport type for volume. + default: tcp + choices: [ tcp, rdma, 'tcp,rdma' ] + type: str + bricks: + description: + - Brick paths on servers. Multiple brick paths can be separated by commas. + aliases: [ brick ] + type: str + start_on_create: + description: + - Controls whether the volume is started after creation or not. + type: bool + default: 'yes' + rebalance: + description: + - Controls whether the cluster is rebalanced after changes. + type: bool + default: 'no' + directory: + description: + - Directory for limit-usage. + type: str + options: + description: + - A dictionary/hash with options/settings for the volume. + type: dict + quota: + description: + - Quota value for limit-usage (be sure to use 10.0MB instead of 10MB, see quota list). + type: str + force: + description: + - If brick is being created in the root partition, module will fail. + Set force to true to override this behaviour. + type: bool +notes: + - Requires cli tools for GlusterFS on servers. + - Will add new bricks, but not remove them. +author: +- Taneli Leppä (@rosmo) +""" + +EXAMPLES = """ +- name: create gluster volume + gluster.gluster.gluster_volume: + state: present + name: test1 + bricks: /bricks/brick1/g1 + rebalance: yes + cluster: + - 192.0.2.10 + - 192.0.2.11 + run_once: true + +- name: tune + gluster.gluster.gluster_volume: + state: present + name: test1 + options: + performance.cache-size: 256MB + +- name: Set multiple options on GlusterFS volume + gluster.gluster.gluster_volume: + state: present + name: test1 + options: + { performance.cache-size: 128MB, + write-behind: 'off', + quick-read: 'on' + } + +- name: start gluster volume + gluster.gluster.gluster_volume: + state: started + name: test1 + +- name: limit usage + gluster.gluster.gluster_volume: + state: present + name: test1 + directory: /foo + quota: 20.0MB + +- name: stop gluster volume + gluster.gluster.gluster_volume: + state: stopped + name: test1 + +- name: remove gluster volume + gluster.gluster.gluster_volume: + state: absent + name: test1 + +- name: create gluster volume with multiple bricks + gluster.gluster.gluster_volume: + state: present + name: test2 + bricks: /bricks/brick1/g2,/bricks/brick2/g2 + cluster: + - 192.0.2.10 + - 192.0.2.11 + run_once: true + +- name: Remove the bricks from gluster volume + gluster.gluster.gluster_volume: + state: present + name: testvol + bricks: /bricks/brick1/b1,/bricks/brick2/b2 + cluster: + - 10.70.42.85 + force: true + run_once: true + +- name: Reduce cluster configuration + gluster.gluster.gluster_volume: + state: present + name: testvol + bricks: /bricks/brick3/b1,/bricks/brick4/b2 + replicas: 2 + cluster: + - 10.70.42.85 + force: true + run_once: true +""" + +import re +import socket +import time +import traceback + +from ansible.module_utils.basic import AnsibleModule +from ansible.module_utils._text import to_native + +glusterbin = '' + + +def run_gluster(gargs, **kwargs): + global glusterbin + global module + args = [glusterbin, '--mode=script'] + args.extend(gargs) + try: + rc, out, err = module.run_command(args, **kwargs) + if rc != 0: + module.fail_json(msg='error running gluster (%s) command (rc=%d): %s' % + (' '.join(args), rc, out or err), exception=traceback.format_exc()) + except Exception as e: + module.fail_json(msg='error running gluster (%s) command: %s' % (' '.join(args), + to_native(e)), exception=traceback.format_exc()) + return out + + +def run_gluster_nofail(gargs, **kwargs): + global glusterbin + global module + args = [glusterbin] + args.extend(gargs) + rc, out, err = module.run_command(args, **kwargs) + if rc != 0: + return None + return out + + +def get_peers(): + out = run_gluster(['peer', 'status']) + peers = {} + hostname = None + uuid = None + state = None + shortNames = False + for row in out.split('\n'): + if ': ' in row: + key, value = row.split(': ') + if key.lower() == 'hostname': + hostname = value + shortNames = False + if key.lower() == 'uuid': + uuid = value + if key.lower() == 'state': + state = value + peers[hostname] = [uuid, state] + elif row.lower() == 'other names:': + shortNames = True + elif row != '' and shortNames is True: + peers[row] = [uuid, state] + elif row == '': + shortNames = False + return peers + + +def get_volumes(): + out = run_gluster(['volume', 'info']) + + volumes = {} + volume = {} + for row in out.split('\n'): + if ': ' in row: + key, value = row.split(': ') + if key.lower() == 'volume name': + volume['name'] = value + volume['options'] = {} + volume['quota'] = False + if key.lower() == 'volume id': + volume['id'] = value + if key.lower() == 'status': + volume['status'] = value + if key.lower() == 'transport-type': + volume['transport'] = value + if value.lower().endswith(' (arbiter)'): + if 'arbiters' not in volume: + volume['arbiters'] = [] + value = value[:-10] + volume['arbiters'].append(value) + elif key.lower() == 'number of bricks': + volume['replicas'] = value[-1:] + if key.lower() != 'bricks' and key.lower()[:5] == 'brick': + if 'bricks' not in volume: + volume['bricks'] = [] + volume['bricks'].append(value) + # Volume options + if '.' in key: + if 'options' not in volume: + volume['options'] = {} + volume['options'][key] = value + if key == 'features.quota' and value == 'on': + volume['quota'] = True + else: + if row.lower() != 'bricks:' and row.lower() != 'options reconfigured:': + if len(volume) > 0: + volumes[volume['name']] = volume + volume = {} + return volumes + + +def get_quotas(name, nofail): + quotas = {} + if nofail: + out = run_gluster_nofail(['volume', 'quota', name, 'list']) + if not out: + return quotas + else: + out = run_gluster(['volume', 'quota', name, 'list']) + for row in out.split('\n'): + if row[:1] == '/': + q = re.split(r'\s+', row) + quotas[q[0]] = q[1] + return quotas + + +def wait_for_peer(host): + for x in range(0, 4): + peers = get_peers() + if host in peers and peers[host][1].lower().find('peer in cluster') != -1: + return True + time.sleep(1) + return False + + +def probe(host, myhostname): + global module + out = run_gluster(['peer', 'probe', host]) + if out.find('localhost') == -1 and not wait_for_peer(host): + module.fail_json(msg='failed to probe peer %s on %s' % (host, myhostname)) + + +def probe_all_peers(hosts, peers, myhostname): + for host in hosts: + host = host.strip() # Clean up any extra space for exact comparison + if host not in peers: + probe(host, myhostname) + + +def create_volume(name, stripe, replica, arbiter, disperse, redundancy, transport, hosts, bricks, force): + args = ['volume', 'create'] + args.append(name) + if stripe: + args.append('stripe') + args.append(str(stripe)) + if replica: + args.append('replica') + args.append(str(replica)) + if arbiter: + args.append('arbiter') + args.append(str(arbiter)) + if disperse: + args.append('disperse') + args.append(str(disperse)) + if redundancy: + args.append('redundancy') + args.append(str(redundancy)) + args.append('transport') + args.append(transport) + for brick in bricks: + for host in hosts: + args.append(('%s:%s' % (host, brick))) + if force: + args.append('force') + run_gluster(args) + + +def start_volume(name): + run_gluster(['volume', 'start', name]) + + +def stop_volume(name): + run_gluster(['volume', 'stop', name]) + + +def set_volume_option(name, option, parameter): + run_gluster(['volume', 'set', name, option, parameter]) + + +def add_bricks(name, new_bricks, stripe, replica, force): + args = ['volume', 'add-brick', name] + if stripe: + args.append('stripe') + args.append(str(stripe)) + if replica: + args.append('replica') + args.append(str(replica)) + args.extend(new_bricks) + if force: + args.append('force') + run_gluster(args) + + +def remove_bricks(name, removed_bricks, force): + # max-tries=12 with default_interval=10 secs + max_tries = 12 + retries = 0 + success = False + args = ['volume', 'remove-brick', name] + args.extend(removed_bricks) + # create a copy of args to use for commit operation + args_c = args[:] + args.append('start') + run_gluster(args) + # remove-brick operation needs to be followed by commit operation. + if not force: + module.fail_json(msg="Force option is mandatory.") + else: + while retries < max_tries: + last_brick = removed_bricks[-1] + out = run_gluster(['volume', 'remove-brick', name, last_brick, 'status']) + for row in out.split('\n')[1:]: + if 'completed' in row: + # remove-brick successful, call commit operation. + args_c.append('commit') + out = run_gluster(args_c) + success = True + break + else: + time.sleep(10) + if success: + break + retries += 1 + if not success: + # remove-brick still in process, needs to be committed after completion. + module.fail_json(msg="Exceeded number of tries, check remove-brick status.\n" + "Commit operation needs to be followed.") + + +def reduce_config(name, removed_bricks, replicas, force): + out = run_gluster(['volume', 'heal', name, 'info']) + summary = out.split("\n") + for line in summary: + if 'Number' in line and int(line.split(":")[1].strip()) != 0: + module.fail_json(msg="Operation aborted, self-heal in progress.") + args = ['volume', 'remove-brick', name, 'replica', replicas] + args.extend(removed_bricks) + if force: + args.append('force') + else: + module.fail_json(msg="Force option is mandatory") + run_gluster(args) + + +def do_rebalance(name): + run_gluster(['volume', 'rebalance', name, 'start']) + + +def enable_quota(name): + run_gluster(['volume', 'quota', name, 'enable']) + + +def set_quota(name, directory, value): + run_gluster(['volume', 'quota', name, 'limit-usage', directory, value]) + + +def main(): + # MAIN + + global module + module = AnsibleModule( + argument_spec=dict( + name=dict(type='str', required=True, aliases=['volume']), + state=dict(type='str', required=True, choices=['absent', 'started', 'stopped', 'present']), + cluster=dict(type='list'), + host=dict(type='str'), + stripes=dict(type='int'), + replicas=dict(type='int'), + arbiters=dict(type='int'), + disperses=dict(type='int'), + redundancies=dict(type='int'), + transport=dict(type='str', default='tcp', choices=['tcp', 'rdma', 'tcp,rdma']), + bricks=dict(type='str', aliases=['brick']), + start_on_create=dict(type='bool', default=True), + rebalance=dict(type='bool', default=False), + options=dict(type='dict', default={}), + quota=dict(type='str'), + directory=dict(type='str'), + force=dict(type='bool', default=False), + ), + ) + + global glusterbin + glusterbin = module.get_bin_path('gluster', True) + + changed = False + + action = module.params['state'] + volume_name = module.params['name'] + cluster = module.params['cluster'] + brick_paths = module.params['bricks'] + stripes = module.params['stripes'] + replicas = module.params['replicas'] + arbiters = module.params['arbiters'] + disperses = module.params['disperses'] + redundancies = module.params['redundancies'] + transport = module.params['transport'] + myhostname = module.params['host'] + start_on_create = module.boolean(module.params['start_on_create']) + rebalance = module.boolean(module.params['rebalance']) + force = module.boolean(module.params['force']) + + if not myhostname: + myhostname = socket.gethostname() + + # Clean up if last element is empty. Consider that yml can look like this: + # cluster="{% for host in groups['glusterfs'] %}{{ hostvars[host]['private_ip'] }},{% endfor %}" + if cluster is not None and len(cluster) > 1 and cluster[-1] == '': + cluster = cluster[0:-1] + + if cluster is None: + cluster = [] + + if brick_paths is not None and "," in brick_paths: + brick_paths = brick_paths.split(",") + else: + brick_paths = [brick_paths] + + options = module.params['options'] + quota = module.params['quota'] + directory = module.params['directory'] + + # get current state info + peers = get_peers() + volumes = get_volumes() + quotas = {} + if volume_name in volumes and volumes[volume_name]['quota'] and volumes[volume_name]['status'].lower() == 'started': + quotas = get_quotas(volume_name, True) + + # do the work! + if action == 'absent': + if volume_name in volumes: + if volumes[volume_name]['status'].lower() != 'stopped': + stop_volume(volume_name) + run_gluster(['volume', 'delete', volume_name]) + changed = True + + if action == 'present': + probe_all_peers(cluster, peers, myhostname) + + # create if it doesn't exist + if volume_name not in volumes: + create_volume(volume_name, stripes, replicas, arbiters, disperses, redundancies, transport, cluster, brick_paths, force) + volumes = get_volumes() + changed = True + + if volume_name in volumes: + # set options + for option in options.keys(): + if option not in volumes[volume_name]['options'] or volumes[volume_name]['options'][option] != options[option]: + set_volume_option(volume_name, option, options[option]) + changed = True + + if volumes[volume_name]['status'].lower() != 'started' and start_on_create: + start_volume(volume_name) + changed = True + + # switch bricks + new_bricks = [] + removed_bricks = [] + all_bricks = [] + bricks_in_volume = volumes[volume_name]['bricks'] + + for node in cluster: + for brick_path in brick_paths: + brick = '%s:%s' % (node, brick_path) + all_bricks.append(brick) + if brick not in bricks_in_volume: + new_bricks.append(brick) + + if not new_bricks and len(all_bricks) > 0 and \ + len(all_bricks) < len(bricks_in_volume): + for brick in bricks_in_volume: + if brick not in all_bricks: + removed_bricks.append(brick) + + if new_bricks: + add_bricks(volume_name, new_bricks, stripes, replicas, force) + changed = True + + if removed_bricks: + if replicas and int(replicas) < int(volumes[volume_name]['replicas']): + reduce_config(volume_name, removed_bricks, str(replicas), force) + else: + remove_bricks(volume_name, removed_bricks, force) + changed = True + + # handle quotas + if quota: + if not volumes[volume_name]['quota']: + enable_quota(volume_name) + quotas = get_quotas(volume_name, False) + if directory not in quotas or quotas[directory] != quota: + set_quota(volume_name, directory, quota) + changed = True + + else: + module.fail_json(msg='failed to create volume %s' % volume_name) + + if action != 'absent' and volume_name not in volumes: + module.fail_json(msg='volume not found %s' % volume_name) + + if action == 'started': + if volumes[volume_name]['status'].lower() != 'started': + start_volume(volume_name) + changed = True + + if action == 'stopped': + if volumes[volume_name]['status'].lower() != 'stopped': + stop_volume(volume_name) + changed = True + + if changed: + volumes = get_volumes() + if rebalance: + do_rebalance(volume_name) + + facts = {} + facts['glusterfs'] = {'peers': peers, 'volumes': volumes, 'quotas': quotas} + + module.exit_json(changed=changed, ansible_facts=facts) + + +if __name__ == '__main__': + main() diff --git a/ansible_collections/gluster/gluster/tests/sanity/ignore-2.10.txt b/ansible_collections/gluster/gluster/tests/sanity/ignore-2.10.txt new file mode 100644 index 000000000..504540c84 --- /dev/null +++ b/ansible_collections/gluster/gluster/tests/sanity/ignore-2.10.txt @@ -0,0 +1,4 @@ +plugins/modules/gluster_peer.py validate-modules:doc-required-mismatch +plugins/modules/gluster_peer.py validate-modules:parameter-list-no-elements +plugins/modules/gluster_volume.py validate-modules:parameter-list-no-elements +plugins/modules/gluster_heal_info.py pylint:ansible-deprecated-no-collection-name diff --git a/ansible_collections/gluster/gluster/tests/sanity/ignore-2.11.txt b/ansible_collections/gluster/gluster/tests/sanity/ignore-2.11.txt new file mode 100644 index 000000000..504540c84 --- /dev/null +++ b/ansible_collections/gluster/gluster/tests/sanity/ignore-2.11.txt @@ -0,0 +1,4 @@ +plugins/modules/gluster_peer.py validate-modules:doc-required-mismatch +plugins/modules/gluster_peer.py validate-modules:parameter-list-no-elements +plugins/modules/gluster_volume.py validate-modules:parameter-list-no-elements +plugins/modules/gluster_heal_info.py pylint:ansible-deprecated-no-collection-name |