diff --git a/ChangeLog.md b/ChangeLog.md index 9556936..7769c17 100644 --- a/ChangeLog.md +++ b/ChangeLog.md @@ -1,4 +1,13 @@ # ansible-powerstore Change Log +## Version 1.3.0 - released on 28/09/2021 +- Added dual licensing. +- Added CRUD operations for local user. +- Network module is added to get the details and modify attributes of network. +- Cluster module is added to get the details and modify attributes of cluster. +- Job module is added to get the details of given job ID. +- Role module is added to get the details of given role name or role ID. +- Gather facts module is enhanced to list users, roles, networks and appliances. + ## Version 1.2.0 - released on 25/06/2021 - Added CRUD operations for replication rule. - Replication session module is added to get the details and modify state of replication session. diff --git a/LICENSE b/LICENSE index 261eeb9..e72bfdd 100644 --- a/LICENSE +++ b/LICENSE @@ -1,201 +1,674 @@ - Apache License - Version 2.0, January 2004 - http://www.apache.org/licenses/ - - TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION - - 1. Definitions. - - "License" shall mean the terms and conditions for use, reproduction, - and distribution as defined by Sections 1 through 9 of this document. - - "Licensor" shall mean the copyright owner or entity authorized by - the copyright owner that is granting the License. - - "Legal Entity" shall mean the union of the acting entity and all - other entities that control, are controlled by, or are under common - control with that entity. For the purposes of this definition, - "control" means (i) the power, direct or indirect, to cause the - direction or management of such entity, whether by contract or - otherwise, or (ii) ownership of fifty percent (50%) or more of the - outstanding shares, or (iii) beneficial ownership of such entity. - - "You" (or "Your") shall mean an individual or Legal Entity - exercising permissions granted by this License. - - "Source" form shall mean the preferred form for making modifications, - including but not limited to software source code, documentation - source, and configuration files. - - "Object" form shall mean any form resulting from mechanical - transformation or translation of a Source form, including but - not limited to compiled object code, generated documentation, - and conversions to other media types. - - "Work" shall mean the work of authorship, whether in Source or - Object form, made available under the License, as indicated by a - copyright notice that is included in or attached to the work - (an example is provided in the Appendix below). - - "Derivative Works" shall mean any work, whether in Source or Object - form, that is based on (or derived from) the Work and for which the - editorial revisions, annotations, elaborations, or other modifications - represent, as a whole, an original work of authorship. For the purposes - of this License, Derivative Works shall not include works that remain - separable from, or merely link (or bind by name) to the interfaces of, - the Work and Derivative Works thereof. - - "Contribution" shall mean any work of authorship, including - the original version of the Work and any modifications or additions - to that Work or Derivative Works thereof, that is intentionally - submitted to Licensor for inclusion in the Work by the copyright owner - or by an individual or Legal Entity authorized to submit on behalf of - the copyright owner. For the purposes of this definition, "submitted" - means any form of electronic, verbal, or written communication sent - to the Licensor or its representatives, including but not limited to - communication on electronic mailing lists, source code control systems, - and issue tracking systems that are managed by, or on behalf of, the - Licensor for the purpose of discussing and improving the Work, but - excluding communication that is conspicuously marked or otherwise - designated in writing by the copyright owner as "Not a Contribution." - - "Contributor" shall mean Licensor and any individual or Legal Entity - on behalf of whom a Contribution has been received by Licensor and - subsequently incorporated within the Work. - - 2. Grant of Copyright License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - copyright license to reproduce, prepare Derivative Works of, - publicly display, publicly perform, sublicense, and distribute the - Work and such Derivative Works in Source or Object form. - - 3. Grant of Patent License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - (except as stated in this section) patent license to make, have made, - use, offer to sell, sell, import, and otherwise transfer the Work, - where such license applies only to those patent claims licensable - by such Contributor that are necessarily infringed by their - Contribution(s) alone or by combination of their Contribution(s) - with the Work to which such Contribution(s) was submitted. If You - institute patent litigation against any entity (including a - cross-claim or counterclaim in a lawsuit) alleging that the Work - or a Contribution incorporated within the Work constitutes direct - or contributory patent infringement, then any patent licenses - granted to You under this License for that Work shall terminate - as of the date such litigation is filed. - - 4. Redistribution. You may reproduce and distribute copies of the - Work or Derivative Works thereof in any medium, with or without - modifications, and in Source or Object form, provided that You - meet the following conditions: - - (a) You must give any other recipients of the Work or - Derivative Works a copy of this License; and - - (b) You must cause any modified files to carry prominent notices - stating that You changed the files; and - - (c) You must retain, in the Source form of any Derivative Works - that You distribute, all copyright, patent, trademark, and - attribution notices from the Source form of the Work, - excluding those notices that do not pertain to any part of - the Derivative Works; and - - (d) If the Work includes a "NOTICE" text file as part of its - distribution, then any Derivative Works that You distribute must - include a readable copy of the attribution notices contained - within such NOTICE file, excluding those notices that do not - pertain to any part of the Derivative Works, in at least one - of the following places: within a NOTICE text file distributed - as part of the Derivative Works; within the Source form or - documentation, if provided along with the Derivative Works; or, - within a display generated by the Derivative Works, if and - wherever such third-party notices normally appear. The contents - of the NOTICE file are for informational purposes only and - do not modify the License. You may add Your own attribution - notices within Derivative Works that You distribute, alongside - or as an addendum to the NOTICE text from the Work, provided - that such additional attribution notices cannot be construed - as modifying the License. - - You may add Your own copyright statement to Your modifications and - may provide additional or different license terms and conditions - for use, reproduction, or distribution of Your modifications, or - for any such Derivative Works as a whole, provided Your use, - reproduction, and distribution of the Work otherwise complies with - the conditions stated in this License. - - 5. Submission of Contributions. Unless You explicitly state otherwise, - any Contribution intentionally submitted for inclusion in the Work - by You to the Licensor shall be under the terms and conditions of - this License, without any additional terms or conditions. - Notwithstanding the above, nothing herein shall supersede or modify - the terms of any separate license agreement you may have executed - with Licensor regarding such Contributions. - - 6. Trademarks. This License does not grant permission to use the trade - names, trademarks, service marks, or product names of the Licensor, - except as required for reasonable and customary use in describing the - origin of the Work and reproducing the content of the NOTICE file. - - 7. Disclaimer of Warranty. Unless required by applicable law or - agreed to in writing, Licensor provides the Work (and each - Contributor provides its Contributions) on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or - implied, including, without limitation, any warranties or conditions - of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A - PARTICULAR PURPOSE. You are solely responsible for determining the - appropriateness of using or redistributing the Work and assume any - risks associated with Your exercise of permissions under this License. - - 8. Limitation of Liability. In no event and under no legal theory, - whether in tort (including negligence), contract, or otherwise, - unless required by applicable law (such as deliberate and grossly - negligent acts) or agreed to in writing, shall any Contributor be - liable to You for damages, including any direct, indirect, special, - incidental, or consequential damages of any character arising as a - result of this License or out of the use or inability to use the - Work (including but not limited to damages for loss of goodwill, - work stoppage, computer failure or malfunction, or any and all - other commercial damages or losses), even if such Contributor - has been advised of the possibility of such damages. - - 9. Accepting Warranty or Additional Liability. While redistributing - the Work or Derivative Works thereof, You may choose to offer, - and charge a fee for, acceptance of support, warranty, indemnity, - or other liability obligations and/or rights consistent with this - License. However, in accepting such obligations, You may act only - on Your own behalf and on Your sole responsibility, not on behalf - of any other Contributor, and only if You agree to indemnify, - defend, and hold each Contributor harmless for any liability - incurred by, or claims asserted against, such Contributor by reason - of your accepting any such warranty or additional liability. - - END OF TERMS AND CONDITIONS - - APPENDIX: How to apply the Apache License to your work. - - To apply the Apache License to your work, attach the following - boilerplate notice, with the fields enclosed by brackets "[]" - replaced with your own identifying information. (Don't include - the brackets!) The text should be enclosed in the appropriate - comment syntax for the file format. We also recommend that a - file or class name and description of purpose be included on the - same "printed page" as the copyright notice for easier - identification within third-party archives. - - Copyright [yyyy] [name of copyright owner] - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. + GNU GENERAL PUBLIC LICENSE + Version 3, 29 June 2007 + + Copyright (C) 2007 Free Software Foundation, Inc. + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The GNU General Public License is a free, copyleft license for +software and other kinds of works. + + The licenses for most software and other practical works are designed +to take away your freedom to share and change the works. By contrast, +the GNU General Public License is intended to guarantee your freedom to +share and change all versions of a program--to make sure it remains free +software for all its users. We, the Free Software Foundation, use the +GNU General Public License for most of our software; it applies also to +any other work released this way by its authors. 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 +them 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 prevent others from denying you +these rights or asking you to surrender the rights. Therefore, you have +certain responsibilities if you distribute copies of the software, or if +you modify it: responsibilities to respect the freedom of others. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must pass on to the recipients the same +freedoms that you received. 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. + + Developers that use the GNU GPL protect your rights with two steps: +(1) assert copyright on the software, and (2) offer you this License +giving you legal permission to copy, distribute and/or modify it. + + For the developers' and authors' protection, the GPL clearly explains +that there is no warranty for this free software. For both users' and +authors' sake, the GPL requires that modified versions be marked as +changed, so that their problems will not be attributed erroneously to +authors of previous versions. + + Some devices are designed to deny users access to install or run +modified versions of the software inside them, although the manufacturer +can do so. This is fundamentally incompatible with the aim of +protecting users' freedom to change the software. The systematic +pattern of such abuse occurs in the area of products for individuals to +use, which is precisely where it is most unacceptable. Therefore, we +have designed this version of the GPL to prohibit the practice for those +products. If such problems arise substantially in other domains, we +stand ready to extend this provision to those domains in future versions +of the GPL, as needed to protect the freedom of users. + + Finally, every program is threatened constantly by software patents. +States should not allow patents to restrict development and use of +software on general-purpose computers, but in those that do, we wish to +avoid the special danger that patents applied to a free program could +make it effectively proprietary. To prevent this, the GPL assures that +patents cannot be used to render the program non-free. + + The precise terms and conditions for copying, distribution and +modification follow. + + TERMS AND CONDITIONS + + 0. Definitions. + + "This License" refers to version 3 of the GNU General Public License. + + "Copyright" also means copyright-like laws that apply to other kinds of +works, such as semiconductor masks. + + "The Program" refers to any copyrightable work licensed under this +License. Each licensee is addressed as "you". "Licensees" and +"recipients" may be individuals or organizations. + + To "modify" a work means to copy from or adapt all or part of the work +in a fashion requiring copyright permission, other than the making of an +exact copy. The resulting work is called a "modified version" of the +earlier work or a work "based on" the earlier work. + + A "covered work" means either the unmodified Program or a work based +on the Program. + + To "propagate" a work means to do anything with it that, without +permission, would make you directly or secondarily liable for +infringement under applicable copyright law, except executing it on a +computer or modifying a private copy. Propagation includes copying, +distribution (with or without modification), making available to the +public, and in some countries other activities as well. + + To "convey" a work means any kind of propagation that enables other +parties to make or receive copies. Mere interaction with a user through +a computer network, with no transfer of a copy, is not conveying. + + An interactive user interface displays "Appropriate Legal Notices" +to the extent that it includes a convenient and prominently visible +feature that (1) displays an appropriate copyright notice, and (2) +tells the user that there is no warranty for the work (except to the +extent that warranties are provided), that licensees may convey the +work under this License, and how to view a copy of this License. If +the interface presents a list of user commands or options, such as a +menu, a prominent item in the list meets this criterion. + + 1. Source Code. + + The "source code" for a work means the preferred form of the work +for making modifications to it. "Object code" means any non-source +form of a work. + + A "Standard Interface" means an interface that either is an official +standard defined by a recognized standards body, or, in the case of +interfaces specified for a particular programming language, one that +is widely used among developers working in that language. + + The "System Libraries" of an executable work include anything, other +than the work as a whole, that (a) is included in the normal form of +packaging a Major Component, but which is not part of that Major +Component, and (b) serves only to enable use of the work with that +Major Component, or to implement a Standard Interface for which an +implementation is available to the public in source code form. A +"Major Component", in this context, means a major essential component +(kernel, window system, and so on) of the specific operating system +(if any) on which the executable work runs, or a compiler used to +produce the work, or an object code interpreter used to run it. + + The "Corresponding Source" for a work in object code form means all +the source code needed to generate, install, and (for an executable +work) run the object code and to modify the work, including scripts to +control those activities. However, it does not include the work's +System Libraries, or general-purpose tools or generally available free +programs which are used unmodified in performing those activities but +which are not part of the work. For example, Corresponding Source +includes interface definition files associated with source files for +the work, and the source code for shared libraries and dynamically +linked subprograms that the work is specifically designed to require, +such as by intimate data communication or control flow between those +subprograms and other parts of the work. + + The Corresponding Source need not include anything that users +can regenerate automatically from other parts of the Corresponding +Source. + + The Corresponding Source for a work in source code form is that +same work. + + 2. Basic Permissions. + + All rights granted under this License are granted for the term of +copyright on the Program, and are irrevocable provided the stated +conditions are met. This License explicitly affirms your unlimited +permission to run the unmodified Program. The output from running a +covered work is covered by this License only if the output, given its +content, constitutes a covered work. This License acknowledges your +rights of fair use or other equivalent, as provided by copyright law. + + You may make, run and propagate covered works that you do not +convey, without conditions so long as your license otherwise remains +in force. You may convey covered works to others for the sole purpose +of having them make modifications exclusively for you, or provide you +with facilities for running those works, provided that you comply with +the terms of this License in conveying all material for which you do +not control copyright. Those thus making or running the covered works +for you must do so exclusively on your behalf, under your direction +and control, on terms that prohibit them from making any copies of +your copyrighted material outside their relationship with you. + + Conveying under any other circumstances is permitted solely under +the conditions stated below. Sublicensing is not allowed; section 10 +makes it unnecessary. + + 3. Protecting Users' Legal Rights From Anti-Circumvention Law. + + No covered work shall be deemed part of an effective technological +measure under any applicable law fulfilling obligations under article +11 of the WIPO copyright treaty adopted on 20 December 1996, or +similar laws prohibiting or restricting circumvention of such +measures. + + When you convey a covered work, you waive any legal power to forbid +circumvention of technological measures to the extent such circumvention +is effected by exercising rights under this License with respect to +the covered work, and you disclaim any intention to limit operation or +modification of the work as a means of enforcing, against the work's +users, your or third parties' legal rights to forbid circumvention of +technological measures. + + 4. Conveying Verbatim Copies. + + You may convey 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; +keep intact all notices stating that this License and any +non-permissive terms added in accord with section 7 apply to the code; +keep intact all notices of the absence of any warranty; and give all +recipients a copy of this License along with the Program. + + You may charge any price or no price for each copy that you convey, +and you may offer support or warranty protection for a fee. + + 5. Conveying Modified Source Versions. + + You may convey a work based on the Program, or the modifications to +produce it from the Program, in the form of source code under the +terms of section 4, provided that you also meet all of these conditions: + + a) The work must carry prominent notices stating that you modified + it, and giving a relevant date. + + b) The work must carry prominent notices stating that it is + released under this License and any conditions added under section + 7. This requirement modifies the requirement in section 4 to + "keep intact all notices". + + c) You must license the entire work, as a whole, under this + License to anyone who comes into possession of a copy. This + License will therefore apply, along with any applicable section 7 + additional terms, to the whole of the work, and all its parts, + regardless of how they are packaged. This License gives no + permission to license the work in any other way, but it does not + invalidate such permission if you have separately received it. + + d) If the work has interactive user interfaces, each must display + Appropriate Legal Notices; however, if the Program has interactive + interfaces that do not display Appropriate Legal Notices, your + work need not make them do so. + + A compilation of a covered work with other separate and independent +works, which are not by their nature extensions of the covered work, +and which are not combined with it such as to form a larger program, +in or on a volume of a storage or distribution medium, is called an +"aggregate" if the compilation and its resulting copyright are not +used to limit the access or legal rights of the compilation's users +beyond what the individual works permit. Inclusion of a covered work +in an aggregate does not cause this License to apply to the other +parts of the aggregate. + + 6. Conveying Non-Source Forms. + + You may convey a covered work in object code form under the terms +of sections 4 and 5, provided that you also convey the +machine-readable Corresponding Source under the terms of this License, +in one of these ways: + + a) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by the + Corresponding Source fixed on a durable physical medium + customarily used for software interchange. + + b) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by a + written offer, valid for at least three years and valid for as + long as you offer spare parts or customer support for that product + model, to give anyone who possesses the object code either (1) a + copy of the Corresponding Source for all the software in the + product that is covered by this License, on a durable physical + medium customarily used for software interchange, for a price no + more than your reasonable cost of physically performing this + conveying of source, or (2) access to copy the + Corresponding Source from a network server at no charge. + + c) Convey individual copies of the object code with a copy of the + written offer to provide the Corresponding Source. This + alternative is allowed only occasionally and noncommercially, and + only if you received the object code with such an offer, in accord + with subsection 6b. + + d) Convey the object code by offering access from a designated + place (gratis or for a charge), and offer equivalent access to the + Corresponding Source in the same way through the same place at no + further charge. You need not require recipients to copy the + Corresponding Source along with the object code. If the place to + copy the object code is a network server, the Corresponding Source + may be on a different server (operated by you or a third party) + that supports equivalent copying facilities, provided you maintain + clear directions next to the object code saying where to find the + Corresponding Source. Regardless of what server hosts the + Corresponding Source, you remain obligated to ensure that it is + available for as long as needed to satisfy these requirements. + + e) Convey the object code using peer-to-peer transmission, provided + you inform other peers where the object code and Corresponding + Source of the work are being offered to the general public at no + charge under subsection 6d. + + A separable portion of the object code, whose source code is excluded +from the Corresponding Source as a System Library, need not be +included in conveying the object code work. + + A "User Product" is either (1) a "consumer product", which means any +tangible personal property which is normally used for personal, family, +or household purposes, or (2) anything designed or sold for incorporation +into a dwelling. In determining whether a product is a consumer product, +doubtful cases shall be resolved in favor of coverage. For a particular +product received by a particular user, "normally used" refers to a +typical or common use of that class of product, regardless of the status +of the particular user or of the way in which the particular user +actually uses, or expects or is expected to use, the product. A product +is a consumer product regardless of whether the product has substantial +commercial, industrial or non-consumer uses, unless such uses represent +the only significant mode of use of the product. + + "Installation Information" for a User Product means any methods, +procedures, authorization keys, or other information required to install +and execute modified versions of a covered work in that User Product from +a modified version of its Corresponding Source. The information must +suffice to ensure that the continued functioning of the modified object +code is in no case prevented or interfered with solely because +modification has been made. + + If you convey an object code work under this section in, or with, or +specifically for use in, a User Product, and the conveying occurs as +part of a transaction in which the right of possession and use of the +User Product is transferred to the recipient in perpetuity or for a +fixed term (regardless of how the transaction is characterized), the +Corresponding Source conveyed under this section must be accompanied +by the Installation Information. But this requirement does not apply +if neither you nor any third party retains the ability to install +modified object code on the User Product (for example, the work has +been installed in ROM). + + The requirement to provide Installation Information does not include a +requirement to continue to provide support service, warranty, or updates +for a work that has been modified or installed by the recipient, or for +the User Product in which it has been modified or installed. Access to a +network may be denied when the modification itself materially and +adversely affects the operation of the network or violates the rules and +protocols for communication across the network. + + Corresponding Source conveyed, and Installation Information provided, +in accord with this section must be in a format that is publicly +documented (and with an implementation available to the public in +source code form), and must require no special password or key for +unpacking, reading or copying. + + 7. Additional Terms. + + "Additional permissions" are terms that supplement the terms of this +License by making exceptions from one or more of its conditions. +Additional permissions that are applicable to the entire Program shall +be treated as though they were included in this License, to the extent +that they are valid under applicable law. If additional permissions +apply only to part of the Program, that part may be used separately +under those permissions, but the entire Program remains governed by +this License without regard to the additional permissions. + + When you convey a copy of a covered work, you may at your option +remove any additional permissions from that copy, or from any part of +it. (Additional permissions may be written to require their own +removal in certain cases when you modify the work.) You may place +additional permissions on material, added by you to a covered work, +for which you have or can give appropriate copyright permission. + + Notwithstanding any other provision of this License, for material you +add to a covered work, you may (if authorized by the copyright holders of +that material) supplement the terms of this License with terms: + + a) Disclaiming warranty or limiting liability differently from the + terms of sections 15 and 16 of this License; or + + b) Requiring preservation of specified reasonable legal notices or + author attributions in that material or in the Appropriate Legal + Notices displayed by works containing it; or + + c) Prohibiting misrepresentation of the origin of that material, or + requiring that modified versions of such material be marked in + reasonable ways as different from the original version; or + + d) Limiting the use for publicity purposes of names of licensors or + authors of the material; or + + e) Declining to grant rights under trademark law for use of some + trade names, trademarks, or service marks; or + + f) Requiring indemnification of licensors and authors of that + material by anyone who conveys the material (or modified versions of + it) with contractual assumptions of liability to the recipient, for + any liability that these contractual assumptions directly impose on + those licensors and authors. + + All other non-permissive additional terms are considered "further +restrictions" within the meaning of section 10. If the Program as you +received it, or any part of it, contains a notice stating that it is +governed by this License along with a term that is a further +restriction, you may remove that term. If a license document contains +a further restriction but permits relicensing or conveying under this +License, you may add to a covered work material governed by the terms +of that license document, provided that the further restriction does +not survive such relicensing or conveying. + + If you add terms to a covered work in accord with this section, you +must place, in the relevant source files, a statement of the +additional terms that apply to those files, or a notice indicating +where to find the applicable terms. + + Additional terms, permissive or non-permissive, may be stated in the +form of a separately written license, or stated as exceptions; +the above requirements apply either way. + + 8. Termination. + + You may not propagate or modify a covered work except as expressly +provided under this License. Any attempt otherwise to propagate or +modify it is void, and will automatically terminate your rights under +this License (including any patent licenses granted under the third +paragraph of section 11). + + However, if you cease all violation of this License, then your +license from a particular copyright holder is reinstated (a) +provisionally, unless and until the copyright holder explicitly and +finally terminates your license, and (b) permanently, if the copyright +holder fails to notify you of the violation by some reasonable means +prior to 60 days after the cessation. + + Moreover, your license from a particular copyright holder is +reinstated permanently if the copyright holder notifies you of the +violation by some reasonable means, this is the first time you have +received notice of violation of this License (for any work) from that +copyright holder, and you cure the violation prior to 30 days after +your receipt of the notice. + + Termination of your rights under this section does not terminate the +licenses of parties who have received copies or rights from you under +this License. If your rights have been terminated and not permanently +reinstated, you do not qualify to receive new licenses for the same +material under section 10. + + 9. Acceptance Not Required for Having Copies. + + You are not required to accept this License in order to receive or +run a copy of the Program. Ancillary propagation of a covered work +occurring solely as a consequence of using peer-to-peer transmission +to receive a copy likewise does not require acceptance. However, +nothing other than this License grants you permission to propagate or +modify any covered work. These actions infringe copyright if you do +not accept this License. Therefore, by modifying or propagating a +covered work, you indicate your acceptance of this License to do so. + + 10. Automatic Licensing of Downstream Recipients. + + Each time you convey a covered work, the recipient automatically +receives a license from the original licensors, to run, modify and +propagate that work, subject to this License. You are not responsible +for enforcing compliance by third parties with this License. + + An "entity transaction" is a transaction transferring control of an +organization, or substantially all assets of one, or subdividing an +organization, or merging organizations. If propagation of a covered +work results from an entity transaction, each party to that +transaction who receives a copy of the work also receives whatever +licenses to the work the party's predecessor in interest had or could +give under the previous paragraph, plus a right to possession of the +Corresponding Source of the work from the predecessor in interest, if +the predecessor has it or can get it with reasonable efforts. + + You may not impose any further restrictions on the exercise of the +rights granted or affirmed under this License. For example, you may +not impose a license fee, royalty, or other charge for exercise of +rights granted under this License, and you may not initiate litigation +(including a cross-claim or counterclaim in a lawsuit) alleging that +any patent claim is infringed by making, using, selling, offering for +sale, or importing the Program or any portion of it. + + 11. Patents. + + A "contributor" is a copyright holder who authorizes use under this +License of the Program or a work on which the Program is based. The +work thus licensed is called the contributor's "contributor version". + + A contributor's "essential patent claims" are all patent claims +owned or controlled by the contributor, whether already acquired or +hereafter acquired, that would be infringed by some manner, permitted +by this License, of making, using, or selling its contributor version, +but do not include claims that would be infringed only as a +consequence of further modification of the contributor version. For +purposes of this definition, "control" includes the right to grant +patent sublicenses in a manner consistent with the requirements of +this License. + + Each contributor grants you a non-exclusive, worldwide, royalty-free +patent license under the contributor's essential patent claims, to +make, use, sell, offer for sale, import and otherwise run, modify and +propagate the contents of its contributor version. + + In the following three paragraphs, a "patent license" is any express +agreement or commitment, however denominated, not to enforce a patent +(such as an express permission to practice a patent or covenant not to +sue for patent infringement). To "grant" such a patent license to a +party means to make such an agreement or commitment not to enforce a +patent against the party. + + If you convey a covered work, knowingly relying on a patent license, +and the Corresponding Source of the work is not available for anyone +to copy, free of charge and under the terms of this License, through a +publicly available network server or other readily accessible means, +then you must either (1) cause the Corresponding Source to be so +available, or (2) arrange to deprive yourself of the benefit of the +patent license for this particular work, or (3) arrange, in a manner +consistent with the requirements of this License, to extend the patent +license to downstream recipients. "Knowingly relying" means you have +actual knowledge that, but for the patent license, your conveying the +covered work in a country, or your recipient's use of the covered work +in a country, would infringe one or more identifiable patents in that +country that you have reason to believe are valid. + + If, pursuant to or in connection with a single transaction or +arrangement, you convey, or propagate by procuring conveyance of, a +covered work, and grant a patent license to some of the parties +receiving the covered work authorizing them to use, propagate, modify +or convey a specific copy of the covered work, then the patent license +you grant is automatically extended to all recipients of the covered +work and works based on it. + + A patent license is "discriminatory" if it does not include within +the scope of its coverage, prohibits the exercise of, or is +conditioned on the non-exercise of one or more of the rights that are +specifically granted under this License. You may not convey a covered +work if you are a party to an arrangement with a third party that is +in the business of distributing software, under which you make payment +to the third party based on the extent of your activity of conveying +the work, and under which the third party grants, to any of the +parties who would receive the covered work from you, a discriminatory +patent license (a) in connection with copies of the covered work +conveyed by you (or copies made from those copies), or (b) primarily +for and in connection with specific products or compilations that +contain the covered work, unless you entered into that arrangement, +or that patent license was granted, prior to 28 March 2007. + + Nothing in this License shall be construed as excluding or limiting +any implied license or other defenses to infringement that may +otherwise be available to you under applicable patent law. + + 12. No Surrender of Others' Freedom. + + If 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 convey a +covered work so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you may +not convey it at all. For example, if you agree to terms that obligate you +to collect a royalty for further conveying from those to whom you convey +the Program, the only way you could satisfy both those terms and this +License would be to refrain entirely from conveying the Program. + + 13. Use with the GNU Affero General Public License. + + Notwithstanding any other provision of this License, you have +permission to link or combine any covered work with a work licensed +under version 3 of the GNU Affero General Public License into a single +combined work, and to convey the resulting work. The terms of this +License will continue to apply to the part which is the covered work, +but the special requirements of the GNU Affero General Public License, +section 13, concerning interaction through a network will apply to the +combination as such. + + 14. Revised Versions of this License. + + The Free Software Foundation may publish revised and/or new versions of +the GNU 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 that a certain numbered version of the GNU General +Public License "or any later version" applies to it, you have the +option of following the terms and conditions either of that numbered +version or of any later version published by the Free Software +Foundation. If the Program does not specify a version number of the +GNU General Public License, you may choose any version ever published +by the Free Software Foundation. + + If the Program specifies that a proxy can decide which future +versions of the GNU General Public License can be used, that proxy's +public statement of acceptance of a version permanently authorizes you +to choose that version for the Program. + + Later license versions may give you additional or different +permissions. However, no additional obligations are imposed on any +author or copyright holder as a result of your choosing to follow a +later version. + + 15. Disclaimer of Warranty. + + 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. + + 16. Limitation of Liability. + + IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS +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. + + 17. Interpretation of Sections 15 and 16. + + If the disclaimer of warranty and limitation of liability provided +above cannot be given local legal effect according to their terms, +reviewing courts shall apply local law that most closely approximates +an absolute waiver of all civil liability in connection with the +Program, unless a warranty or assumption of liability accompanies a +copy of the Program in return for a fee. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +state the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + 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 General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + +Also add information on how to contact you by electronic and paper mail. + + If the program does terminal interaction, make it output a short +notice like this when it starts in an interactive mode: + + Copyright (C) + This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, your program's commands +might be different; for a GUI interface, you would use an "about box". + + You should also get your employer (if you work as a programmer) or school, +if any, to sign a "copyright disclaimer" for the program, if necessary. +For more information on this, and how to apply and follow the GNU GPL, see +. + + The GNU 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. But first, please read +. \ No newline at end of file diff --git a/MODULE-LICENSE b/MODULE-LICENSE new file mode 100644 index 0000000..d645695 --- /dev/null +++ b/MODULE-LICENSE @@ -0,0 +1,202 @@ + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/README.md b/README.md index 9feb114..d222d10 100644 --- a/README.md +++ b/README.md @@ -1,21 +1,20 @@ # Ansible Modules for Dell EMC PowerStore The Ansible Modules for Dell EMC PowerStore allow Data Center and IT administrators to use RedHat Ansible to automate and orchestrate the configuration and management of Dell EMC PowerStore arrays. -The capabilities of the Ansible modules are managing volumes, volume groups, hosts, host groups, snapshots, snapshot rules, replication rules, replication sessions, protection policies, file systems, NAS servers, SMB shares, user and tree quotas, file system snapshots and NFS exports. It also allows the capability to gather facts from the array. The options available for each are list, show, create, modify and delete. These tasks can be executed by running simple playbooks written in yaml syntax. The modules are written so that all the operations are idempotent, so making multiple identical requests has the same effect as making a single request. +The capabilities of the Ansible modules are managing volumes, volume groups, hosts, host groups, snapshots, snapshot rules, replication rules, replication sessions, protection policies, file systems, NAS servers, SMB shares, user and tree quotas, file system snapshots, NFS exports, Cluster, Networks, Local users, Job, and Roles. It also allows the capability to gather facts from the array. The options available for each are list, show, create, modify and delete. These tasks can be executed by running simple playbooks written in yaml syntax. The modules are written so that all the operations are idempotent, so making multiple identical requests has the same effect as making a single request. +## License +Ansible collection for PowerStore is released and licensed under the GPL-3.0 license. See [LICENSE](LICENSE) for the full terms. Ansible modules and modules utilities that are part of the Ansible collection for PowerStore are released and licensed under the Apache 2.0 license. See [MODULE-LICENSE](MODULE-LICENSE) for the full terms. ## Support -Ansible modules for PowerStore are supported by Dell EMC and are provided under the terms of the license attached to the source code. -Dell EMC does not provide support for any source code modifications. -For any Ansible module issues, questions or feedback, join the [Dell EMC Automation community](https://www.dell.com/community/Automation/bd-p/Automation). - -## Supported Platforms - * Dell EMC PowerStore Arrays version 1.0, 2.0 +Ansible collections for PowerStore are supported by Dell EMC and are provided under the terms of the license attached to the collection. Please see the [LICENSE](#license) for the full terms. +Dell EMC does not provide any support for the source code modifications. +For any Ansible modules issues, questions or feedback, join the [Dell EMC Automation Community](https://www.dell.com/community/Automation/bd-p/Automation). ## Prerequisites - * Ansible 2.9, 2.10 - * Python 3.5, 3.6, 3.7, 3.8 - * Red Hat Enterprise Linux 7.6, 7.7, 7.8, 8.2 - * Python Library for PowerStore version 1.3.0 or higher + | **Ansible Modules** | **PowerStore Version** | **Red Hat Enterprise Linux**| **SDK version**| **Python version** | **Ansible** | +|---------------------|-----------------------|------------------------------|--------------------|--------------------|-------------| +| v1.3.0 | 1.x
2.0 |7.8,
8.2 | 1.4.0 | 3.6.x
3.7.x
3.8.x | 2.9
2.10
2.11 | + * Please follow PyPowerStore installation instructions on [PyPowerStore Documentation](https://github.com/dell/python-powerstore) ## Idempotency The modules are written in such a way that all requests are idempotent and hence fault-tolerant. It essentially means that the result of a successfully performed request is independent of the number of times it is executed. @@ -37,45 +36,71 @@ The modules are written in such a way that all requests are idempotent and hence * Quota module * File system snapshot module * NFS export module + * Cluster module + * Network module + * Local user module + * Role module + * Job module ## Installation of SDK 1. Clone the repo using the command: - git clone https://github.com/dell/python-powerstore/tree/1.3.0 + git clone https://github.com/dell/python-powerstore/tree/1.4.0 2. Go to the root directory of setup. 3. Execute the following command:
pip install . +## Building Collections + * Use the following command to build the collection from source code: + + ansible-galaxy collection build + + For more details on how to build a tar ball, please refer: [Building the collection](https://docs.ansible.com/ansible/latest/dev_guide/developing_collections_distributing.html#building-your-collection-tarball) + ## Installing Collections - 1. Download the tar build and use the following command to install the collection anywhere in your system: - - ansible-galaxy collection install dellemc-powerstore-1.2.1.tar.gz -p +#### Online Installation of Collections + 1. Use the following command to install the latest collection hosted in galaxy: + + ansible-galaxy collection install dellemc.powerstore -p + +#### Offline Installation of Collections + 1. Download the latest tar build from any of the available distribution channel [Ansible Galaxy](https://galaxy.ansible.com/dellemc/powerstore) /[Automation Hub](https://console.redhat.com/ansible/automation-hub/repo/published/dellemc/powerstore) and use the following command to install the collection anywhere in your system: + + ansible-galaxy collection install dellemc-powerstore-1.3.0.tar.gz -p 2. Set the environemnt variable: - - export ANSIBLE_COLLECTIONS_PATHS=$ANSIBLE_COLLECTIONS_PATHS: + + export ANSIBLE_COLLECTIONS_PATHS=$ANSIBLE_COLLECTIONS_PATHS: ## Using Collections - 1. In order to use any Ansible module, ensure that the importing of a proper FQCN(Fully Qualified Collection Name) must be embedded in the playbook. Refer to the followig example: -
collections: -
   - dellemc.powerstore + 1. In order to use any Ansible module, ensure that the importing of a proper FQCN (Fully Qualified Collection Name) must be embedded in the playbook. Refer to the followig example: - 2. For generating Ansible documentaion for a specific module, embed the FQCN before the module name. Refer to the following example: - - ansible-doc dellemc.powerstore.dellemc_powerstore_gatherfacts + collections: + - dellemc.powerstore + + 2. In order to use an installed collection specific to the task use a proper FQCN (Fully Qualified Collection Name). Refer to the followig example: + + tasks: + - name: Get Volume details + dellemc.powerstore.dellemc_powerstore_volume + + 3. For generating Ansible documentaion for a specific module, embed the FQCN before the module name. Refer to the following example: + + ansible-doc dellemc.powerstore.dellemc_powerstore_gatherfacts ## Running Ansible Modules -The Ansible server must be configured with Python library for PowerStore to run the Ansible playbooks. The [Documents]( https://github.com/dell/ansible-powerstore/tree/1.2.1/docs ) provide information on different Ansible modules along with their functions and syntax. The parameters table in the Product Guide provides information on various parameters which needs to be configured before running the modules. +The Ansible server must be configured with Python library for PowerStore to run the Ansible playbooks. The [Documents]( https://github.com/dell/ansible-powerstore/tree/1.3.0/docs ) provide information on different Ansible modules along with their functions and syntax. The parameters table in the Product Guide provides information on various parameters which needs to be configured before running the modules. ## SSL Certificate Validation 1. Copy the CA certificate to this "/etc/pki/ca-trust/source/anchors" path of the host by any external means. - 2.Set the "REQUESTS_CA_BUNDLE" environment variable to the path of the SSL certificate using the following command: + 2. Set the "REQUESTS_CA_BUNDLE" environment variable to the path of the SSL certificate using the following command: export REQUESTS_CA_BUNDLE=/etc/pki/ca-trust/source/anchors/<> - 3. Import the SSL certificate to host using the following command: + 3. Import the SSL certificate to host using the following command: + update-ca-trust ## Results diff --git a/dellemc-powerstore-1.2.1.tar.gz b/dellemc-powerstore-1.2.1.tar.gz deleted file mode 100644 index dd49d93..0000000 Binary files a/dellemc-powerstore-1.2.1.tar.gz and /dev/null differ diff --git a/docs/Product Guide.md b/docs/Product Guide.md index 568c7a6..066af2f 100644 --- a/docs/Product Guide.md +++ b/docs/Product Guide.md @@ -1,5 +1,5 @@ # Ansible Modules for Dell EMC PowerStore -## Product Guide 1.2 +## Product Guide 1.3 © 2021 Dell Inc. or its subsidiaries. All rights reserved. Dell, EMC, and other trademarks are trademarks of Dell Inc. or its subsidiaries. Other trademarks may be trademarks of their respective owners. -------------- @@ -11,102 +11,137 @@ * [Examples](#examples) * [Return Values](#return-values) * [Authors](#authors) -* [Volume Module](#volume-module) +* [Local User Module](#local-user-module) * [Synopsis](#synopsis-1) * [Parameters](#parameters-1) - * [Notes](#notes-1) * [Examples](#examples-1) * [Return Values](#return-values-1) * [Authors](#authors-1) -* [Quota Module](#quota-module) +* [Volume Module](#volume-module) * [Synopsis](#synopsis-2) * [Parameters](#parameters-2) - * [Notes](#notes-2) + * [Notes](#notes-1) * [Examples](#examples-2) * [Return Values](#return-values-2) * [Authors](#authors-2) -* [Host Module](#host-module) +* [Quota Module](#quota-module) * [Synopsis](#synopsis-3) * [Parameters](#parameters-3) + * [Notes](#notes-2) * [Examples](#examples-3) * [Return Values](#return-values-3) * [Authors](#authors-3) -* [Snapshot Rule Module](#snapshot-rule-module) +* [Cluster Module](#cluster-module) * [Synopsis](#synopsis-4) * [Parameters](#parameters-4) + * [Notes](#notes-3) * [Examples](#examples-4) * [Return Values](#return-values-4) * [Authors](#authors-4) -* [Gatherfacts Module](#gatherfacts-module) +* [Snapshot Rule Module](#snapshot-rule-module) * [Synopsis](#synopsis-5) * [Parameters](#parameters-5) * [Examples](#examples-5) * [Return Values](#return-values-5) * [Authors](#authors-5) -* [Replication Session Module](#replication-session-module) +* [Gatherfacts Module](#gatherfacts-module) * [Synopsis](#synopsis-6) * [Parameters](#parameters-6) - * [Notes](#notes-3) + * [Notes](#notes-4) * [Examples](#examples-6) * [Return Values](#return-values-6) * [Authors](#authors-6) -* [Host Group Module](#host-group-module) +* [Job Module](#job-module) * [Synopsis](#synopsis-7) * [Parameters](#parameters-7) * [Examples](#examples-7) * [Return Values](#return-values-7) * [Authors](#authors-7) -* [NFS Module](#nfs-module) +* [Replication Session Module](#replication-session-module) * [Synopsis](#synopsis-8) * [Parameters](#parameters-8) + * [Notes](#notes-5) * [Examples](#examples-8) * [Return Values](#return-values-8) * [Authors](#authors-8) -* [Volume Group Module](#volume-group-module) +* [Host Group Module](#host-group-module) * [Synopsis](#synopsis-9) * [Parameters](#parameters-9) - * [Notes](#notes-4) * [Examples](#examples-9) * [Return Values](#return-values-9) * [Authors](#authors-9) -* [NAS Server Module](#nas-server-module) +* [NFS Module](#nfs-module) * [Synopsis](#synopsis-10) * [Parameters](#parameters-10) * [Examples](#examples-10) * [Return Values](#return-values-10) * [Authors](#authors-10) -* [SMB Share Module](#smb-share-module) +* [Role Module](#role-module) * [Synopsis](#synopsis-11) * [Parameters](#parameters-11) - * [Notes](#notes-5) + * [Notes](#notes-6) * [Examples](#examples-11) * [Return Values](#return-values-11) * [Authors](#authors-11) -* [Snapshot Module](#snapshot-module) +* [Volume Group Module](#volume-group-module) * [Synopsis](#synopsis-12) * [Parameters](#parameters-12) + * [Notes](#notes-7) * [Examples](#examples-12) * [Return Values](#return-values-12) * [Authors](#authors-12) -* [Replication Rule Module](#replication-rule-module) +* [NAS Server Module](#nas-server-module) * [Synopsis](#synopsis-13) * [Parameters](#parameters-13) * [Examples](#examples-13) * [Return Values](#return-values-13) * [Authors](#authors-13) -* [Protection Policy Module](#protection-policy-module) +* [SMB Share Module](#smb-share-module) * [Synopsis](#synopsis-14) * [Parameters](#parameters-14) - * [Notes](#notes-6) + * [Notes](#notes-8) * [Examples](#examples-14) * [Return Values](#return-values-14) * [Authors](#authors-14) -* [Filesystem Snapshot Module](#filesystem-snapshot-module) +* [Snapshot Module](#snapshot-module) * [Synopsis](#synopsis-15) * [Parameters](#parameters-15) * [Examples](#examples-15) * [Return Values](#return-values-15) * [Authors](#authors-15) +* [Replication Rule Module](#replication-rule-module) + * [Synopsis](#synopsis-16) + * [Parameters](#parameters-16) + * [Examples](#examples-16) + * [Return Values](#return-values-16) + * [Authors](#authors-16) +* [Network Module](#network-module) + * [Synopsis](#synopsis-17) + * [Parameters](#parameters-17) + * [Notes](#notes-9) + * [Examples](#examples-17) + * [Return Values](#return-values-17) + * [Authors](#authors-17) +* [Protection Policy Module](#protection-policy-module) + * [Synopsis](#synopsis-18) + * [Parameters](#parameters-18) + * [Notes](#notes-10) + * [Examples](#examples-18) + * [Return Values](#return-values-18) + * [Authors](#authors-18) +* [Filesystem Snapshot Module](#filesystem-snapshot-module) + * [Synopsis](#synopsis-19) + * [Parameters](#parameters-19) + * [Examples](#examples-19) + * [Return Values](#return-values-19) + * [Authors](#authors-19) +* [Host Module](#host-module) + * [Synopsis](#synopsis-20) + * [Parameters](#parameters-20) + * [Notes](#notes-11) + * [Examples](#examples-20) + * [Return Values](#return-values-20) + * [Authors](#authors-20) -------------- @@ -584,6 +619,254 @@ Filesystem operations on PowerStore Storage system ### Authors * Arindam Datta (@dattaarindam) +-------------------------------- +# Local User Module + +Local user operations on PowerStore Storage System + +### Synopsis + Supports the provisioning operations on a Local user such as create, modify, delete and get the details of a local user. + +### Parameters + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
ParameterTypeRequiredDefaultChoicesDescription
user_name str
Name of the local user account. Mutually exclusive with user_id.
Mandatory only for create operation.
user_id str
Unique identifier of the local user account.
Mutually exclusive with user_name.
user_password str
Password for the new local user account to be created.
Mandatory only for create operation.
new_password str
New password for the existing local user account.
role_name str
The name of the role to which the local user account will be mapped.
It is mutually exclusive with role_id.
role_id int
The unique identifier of the role to which the local user account will be mapped.
It is mutually exclusive with role_name.
is_locked bool
Whether the user account is locked or not.
Defaults to false at creation time.
state str True
  • absent
  • present

Define whether the local user should exist or not.
array_ip str True
IP or FQDN of the PowerStore management system.
verifycert bool True
  • True
  • False

Boolean variable to specify whether to validate SSL certificate or not.
True - indicates that the SSL certificate should be verified. Set the environment variable REQUESTS_CA_BUNDLE to the path of the SSL certificate.
False - indicates that the SSL certificate should not be verified.
user str True
The username of the PowerStore host.
password str True
The password of the PowerStore host.
+ + +### Examples +``` +- name: create local user + dellemc_powerstore_local_user: + array_ip: "{{array_ip}}" + verifycert: "{{verifycert}}" + user: "{{user}}" + password: "{{password}}" + user_name: "ansible_user_1" + user_password: "Password123#" + role_name: "role_1" + is_locked: False + state: "present" + +- name: get the details local user with user id + dellemc_powerstore_local_user: + array_ip: "{{array_ip}}" + verifycert: "{{verifycert}}" + user: "{{user}}" + password: "{{password}}" + user_id: "{{user_id}}" + state: "present" + +- name: get the details local user with user name + dellemc_powerstore_local_user: + array_ip: "{{array_ip}}" + verifycert: "{{verifycert}}" + user: "{{user}}" + password: "{{password}}" + user_name: "ansible_user_1" + state: "present" + +- name: Modify attributes of local user + dellemc_powerstore_local_user: + array_ip: "{{array_ip}}" + verifycert: "{{verifycert}}" + user: "{{user}}" + password: "{{password}}" + user_name: "ansible_user_1" + user_password: "Password123#" + new_password: "Ansible123#" + role_id: 4 + is_locked: True + state: "present" + +- name: delete local user + dellemc_powerstore_local_user: + array_ip: "{{array_ip}}" + verifycert: "{{verifycert}}" + user: "{{user}}" + password: "{{password}}" + user_name: "ansible_user_1" + state: "absent" +``` + +### Return Values + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
KeyTypeReturnedDescription
changed bool always Whether or not the resource has changed
local_user_details complex When local user exists Details of the local user
  id str success The system generated ID given to the local user.
  is_built_in bool success Whether the user account is built-in or not.
  is_default_password bool success Whether the user account has a default password or not. Only applies to default user accounts
  is_locked bool success Whether the user account is locked or not. Defaults to false at creation time.
  name str success Name of the local user.
  role_id str success Unique identifier of the role local user account is mapped to.
  role_name str success Name of the role to which local user account is mapped.
+ +### Authors +* Arindam Datta (@dattaarindam) + -------------------------------- # Volume Module @@ -884,122 +1167,184 @@ Manage volumes on a PowerStore storage system. ``` ### Return Values - + - + - - - - - - - + - - - - - - - - - - - - - + - - - - - - - - - - - - - - - - + + + + - + - + - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - + - - + + - + - + - + - + - + - - + + - + - + - + - + - + - + - + -
KeyKey Type Returned Description
add_vols_to_vg bool When value exists A boolean flag to indicate whether volume/s got added to volume group
changed changed bool always Whether or not the resource has changed
create_vg bool When value exists A boolean flag to indicate whether volume group got created
delete_vg bool When value exists A boolean flag to indicate whether volume group got deleted Whether or not the resource has changed.
modify_vg bool When value exists A boolean flag to indicate whether volume group got modified
remove_vols_from_vg bool When value exists A boolean flag to indicate whether volume/s got removed from volume group
volume_group_details complex When volume group exists Details of the volume group volume_details complex When volume exists Details of the volume.
  description description str success ['description about the volume group'] description about the volume.
  id hlu_details complex success HLU details for mapped host/host group.
   host_group_id str success The host group ID mapped to the volume.
   host_id str success The host ID mapped to the volume.
   id str success The HLU ID.
   logical_unit_number int success Logical unit number for the host/host group volume access.
  host complex success Hosts details mapped to the volume.
   id str success The host ID mapped to the volume.
   name str success Name of the Host mapped to the volume.
  host_group complex success Host groups details mapped to the volume.
   id str success The host group ID mapped to the volume.
   name str success Name of the Host group mapped to the volume.
  id str success ['The system generated ID given to the volume group'] The system generated ID given to the volume.
  is_write_order_consistent bool name str success ['A boolean flag to indicate whether snapshot sets of the volume group will be write-order consistent'] Name of the volume.
  name performance_policy_id str success ['Name of the volume group'] The performance policy for the volume.
  protection_policy_id protection_policy_id str success ['The protection policy of the volume group'] The protection policy of the volume.
  type str size int success ['The type of the volume group'] Size of the volume.
  volumes volume_groups complex success ['The volumes details of the volume group'] The volume group details of the volume.
    id id str success ['The system generated ID given to the volume associated with the volume group'] The system generated ID given to the volume group.
    name name str success ['The name of the volume associated with the volume group.'] Name of the volume group.
+ +   + wwn + str + success + The world wide name of the volume. + + ### Authors * Ambuj Dubey (@AmbujDube) @@ -1182,7 +1527,7 @@ Manage Tree Quotas and User Quotas on PowerStore. ### Notes -* Tree quota can not be created at the root of the filesystem. +* Tree quota can't be created at the root of the filesystem. * When the ID of the filesystem is passed then nas_server is not required. If passed, then filesystem should exist for the nas_server, else the task will fail. * If a primary directory of the current directory or a subordinate directory of the path is having a Tree Quota configured, then the quota for that path can't be created. Hierarchical tree quotas are not allowed. * When the first quota is created for a directory/user in a filesystem then the quotas will be enabled for that filesystem automatically. @@ -1296,7 +1641,7 @@ Manage Tree Quotas and User Quotas on PowerStore. description str success - ['Additional information about the tree quota.', 'Only applicable for Tree Quotas.'] + Additional information about the tree quota. Only applicable for Tree Quotas.   @@ -1377,7 +1722,7 @@ Manage Tree Quotas and User Quotas on PowerStore. state str success - ['State of the user quota or tree quota record period.', 'OK means No quota limits are exceeded.', 'Soft_Exceeded means Soft limit is exceeded, and grace period is not expired.', 'Soft_Exceeded_And_Expired means Soft limit is exceeded, and grace period is expired.', 'Hard_Reached means Hard limit is reached.'] + State of the user quota or tree quota record period. OK means No quota limits are exceeded. Soft_Exceeded means Soft limit is exceeded, and grace period is not expired. Soft_Exceeded_And_Expired means Soft limit is exceeded, and grace period is expired. Hard_Reached means Hard limit is reached.   @@ -1391,7 +1736,7 @@ Manage Tree Quotas and User Quotas on PowerStore. tree_quota_for_user_quota complex success - ['Additional Information of Tree Quota limits on which user quota exists.', 'Only applicable for User Quotas'] + Additional Information of Tree Quota limits on which user quota exists. Only applicable for User Quotas.   @@ -1422,35 +1767,35 @@ Manage Tree Quotas and User Quotas on PowerStore. tree_quota_id str success - ['ID of the Tree Quota on which the specific User Quota exists.', 'Only applicable for user quotas.'] + ID of the Tree Quota on which the specific User Quota exists. Only applicable for user quotas.   uid int success - ['The ID of the unix host for which user quota exists.', 'Only applicable for user quotas.'] + The ID of the unix host for which user quota exists. Only applicable for user quotas.   unix_name str success - ['The Name of the unix host for which user quota exists.', 'Only applicable for user quotas.'] + The Name of the unix host for which user quota exists. Only applicable for user quotas.   windows_name str success - ['The Name of the Windows host for which user quota exists.', 'Only applicable for user quotas.'] + The Name of the Windows host for which user quota exists. Only applicable for user quotas.   windows_sid str success - ['The SID of the windows host for which user quota exists.', 'Only applicable for user quotas.'] + The SID of the windows host for which user quota exists. Only applicable for user quotas. @@ -1458,15 +1803,15 @@ Manage Tree Quotas and User Quotas on PowerStore. * P Srinivas Rao (@srinivas-rao5) -------------------------------- -# Host Module +# Cluster Module -Manage host on PowerStore storage system. +Manage cluster related opeartions on PowerStore. ### Synopsis - Managing host on PowerStore storage system includes create host with a set of initiators, add/remove initiators from host, rename host and delete host. + Managing cluster on PowerStore storage system includes getting details and modifying cluster configuration parameters. ### Parameters - + @@ -1477,60 +1822,84 @@ Manage host on PowerStore storage system. - + - + - + - - + + - + - - + + - - + + - + - + - - - + + + - + - - + + - + - + + + + + + + + + + + + + + + + + + + + + + + + + @@ -1564,166 +1933,261 @@ Manage host on PowerStore storage system. -
ParameterDescription
host_name cluster_name str
The host name. This value must contain 128 or fewer printable Unicode characters.
Creation of an empty host is not allowed.
Required when creating a host.
Use either host_id or host_name for modify and delete tasks.

The Name of cluster.
host_id chap_mode str
The 36 character long host id automatically generated when a host is created.
Use either host_id or host_name for modify and delete tasks.
host_id cannot be used while creating host, as it is generated by the array after creation of host.
  • Disabled
  • Single
  • Mutual

The mode that describes or sets the iSCSI CHAP mode for the cluster.
os_type cluster_id str
  • Windows
  • Linux
  • ESXi
  • AIX
  • HP-UX
  • Solaris

Operating system of the host.
Required when creating a host
OS type cannot be modified for a given host.

Id of the cluster.
initiators list
elements: str
new_name str
List of Initiator WWN or IQN to be added or removed from the host.
Subordinate initiators in a host can only be of one type, either FC or iSCSI.
Required when creating a host.

The new name for the cluster.
state service_password str True
  • absent
  • present

Define whether the host should exist or not.
present - indicates that the host should exist in system.
absent - indicates that the host should not exist in system.

The password for the service user.
initiator_state appliance_id str
  • present-in-host
  • absent-in-host

Define whether the initiators should be present or absent in host.
present-in-host - indicates that the initiators should exist on host.
absent-in-host - indicates that the initiators should not exist on host.
Required when creating a host with initiators or adding/removing initiators to/from existing host.

ID of the appliance.
appliance_id and appliance_name are mutually exclusive.
is_ssh_enabled has to be passed along with appliance_id.
new_name appliance_name str
The new name of host for renaming function. This value must contain 128 or fewer printable Unicode characters.
Cannot be specified when creating a host.

Name of the appliance.
appliance_id and appliance_name are mutually exclusive.
is_ssh_enabled has to be passed along with appliance_name.
is_ssh_enabled bool
Whether SSH access is enabled for the cluster.
Either appliance_id or appliance_name is to be passed along with is_ssh_enabled.
physical_mtu int
MTU for ethernet ports in the cluster.
The MTU can be set between 1500 to 9000.
state str True
  • absent
  • present

Define whether the cluster should exist or not.
present indicates that the cluster should exist on the system.
absent indicates that the cluster should not exist on the system.
array_ip
The password of the PowerStore host.
+ +### Notes +* Creation and deletion of cluster is not supported by ansible modules. ### Examples ``` - - name: Create host - dellemc_powerstore_host: - array_ip: "{{array_ip}}" - verifycert: "{{verifycert}}" - user: "{{user}}" - password: "{{password}}" - host_name: "{{host_name}}" - os_type: 'Windows' - initiators: - -21:00:00:24:ff:31:e9:fc - state: 'present' - initiator_state: 'present-in-host' - - - name: Get host details by name - dellemc_powerstore_host: - array_ip: "{{array_ip}}" - verifycert: "{{verifycert}}" - user: "{{user}}" - password: "{{password}}" - host_name: "{{host_name}}" - state: 'present' - - - name: Get host details by id - dellemc_powerstore_host: - array_ip: "{{array_ip}}" - verifycert: "{{verifycert}}" - user: "{{user}}" - password: "{{password}}" - host_id: "{{host_id}}" - state: 'present' - - - name: Add initiators to host - dellemc_powerstore_host: - array_ip: "{{array_ip}}" - verifycert: "{{verifycert}}" - user: "{{user}}" - password: "{{password}}" - host_name: "{{host_name}}" - initiators: - -21:00:00:24:ff:31:e9:ee - initiator_state: 'present-in-host' - state: 'present' - - - name: Remove initiators from host - dellemc_powerstore_host: - array_ip: "{{array_ip}}" - verifycert: "{{verifycert}}" - user: "{{user}}" - password: "{{password}}" - host_name: "{{host_name}}" - initiators: - -21:00:00:24:ff:31:e9:ee - initiator_state: 'absent-in-host' - state: 'present' - - - name: Rename host - dellemc_powerstore_host: - array_ip: "{{array_ip}}" - verifycert: "{{verifycert}}" - user: "{{user}}" - password: "{{password}}" - host_name: "{{host_name}}" - new_name: "{{new_host_name}}" - state: 'present' +- name: get the details of cluster using id + dellemc_powerstore_cluster: + array_ip: "{{array_ip}}" + verifycert: "{{verifycert}}" + user: "{{user}}" + password: "{{password}}" + cluster_id: "0" + state: "present" - - name: Delete host - dellemc_powerstore_host: - array_ip: "{{array_ip}}" - verifycert: "{{verifycert}}" - user: "{{user}}" - password: "{{password}}" - host_name: "{{new_host_name}}" - state: 'absent' +- name: Modify details of cluster using the name + dellemc_powerstore_cluster: + array_ip: "{{array_ip}}" + verifycert: "{{verifycert}}" + user: "{{user}}" + password: "{{password}}" + cluster_name: "RT-D1320" + appliance_id: "A1" + is_ssh_enabled: True + service_password: "S@mple_password" + chap_mode: "Disabled" + new_name: "new_RT-D1320" + state: "present" ``` ### Return Values - + - + - - + + - + - - + + - - + + - + - + - + - + - + - + - + - + + + + + + + + - + - + - + -
KeyKey Type Returned Description
changed
changed bool always Whether or not the resource has changed
hostgroup_details cluster_details complex When host group exists Details of the host group When Cluster exists. The cluster details.
  description str appliance_count int success Description about the host group Number of appliances configured in this cluster.
  hosts appliance_details complex success The hosts details which are part of this host group Name and Id of the appliance for which is_ssh_enabled parameter is used.
    id id str success The ID of the host Id of the appliance.
    name name str success The name of the host Name of the appliance.
  id compatibility_level int success The behavioral version of the software version API, It is used to ensure the compatibility across potentially different software versions.
  global_id str success The system generated ID given to the host group The global unique identifier of the cluster.
  name id str success Name of the host group The ID of the cluster.
- -### Authors -* Manisha Agrawal (@agrawm3) - --------------------------------- -# Snapshot Rule Module - -SnapshotRule operations on a PowerStore storage system. + +   + is_encryption_enabled + bool + success + Whether or not Data at Rest Encryption is enabled on the cluster. + + +   + is_ssh_enabled + bool + success + Whether or not the ssh is enabled. + + +   + management_address + str + success + The floating management IP address for the cluster in IPv4 or IPv6 format. + + +   + master_appliance_id + str + success + The unique identifier of the appliance acting as primary. This parameter is deprecated in version 2.0.0.0. + + +   + name + str + success + Name of the cluster. + + +   + physical_mtu + int + success + MTU for the cluster. + + +   + primary_appliance_id + str + success + The unique identifier of the appliance acting as primary. This parameter was added in version 2.0.0.0. + + +   + service_config_details + complex + success + Details of the service config for the entered appliance. + + +   +   + appliance_id + str + success + Id of the appliance for which the service configuration exists. + + +   +   + id + str + success + Id of the service configuration. + + +   +   + is_ssh_enabled + bool + success + Whether the ssh is enabled for the appliance or not. + + +   + service_user_details + complex + success + Details of the service user for which the password can be updated. + + +   +   + id + str + success + Id of the service user. + + +   +   + is_built_in + bool + success + Whether the service user is built in or not. + + +   +   + is_default_password + bool + success + Whether the service user has default password or not. + + +   +   + name + str + success + Name of the service user. + + +   + state + str + success + Possible cluster states. + + +   + storage_discovery_address + str + success + The floating storage discovery IP address for the cluster in IPv4 or IPv6 format. + + +   + system_time + str + success + Current clock time for the system. System time and all the system reported times are in UTC (GMT+0:00) format. + + + +### Authors +* P Srinivas Rao (@srinivas-rao5) + +-------------------------------- +# Snapshot Rule Module + +SnapshotRule operations on a PowerStore storage system. ### Synopsis - Performs all snapshot rule operations on PowerStore Storage System. - This modules supports get details of an existing snapshot rule, create new Snapshot Rule with Interval, create new Snapshot Rule with specific time and days_of_week with all supported. parameters. - Modify Snapshot Rule with supported parameters. - Delete a specific Snapshot Rule. + Performs all snapshot rule operations on PowerStore Storage System. This modules supports get details of an existing snapshot rule, create new Snapshot Rule with Interval, create new Snapshot Rule with specific time and days_of_week with all supported parameters. Modify Snapshot Rule with supported parameters. Delete a specific Snapshot Rule. ### Parameters @@ -1940,55 +2404,55 @@ SnapshotRule operations on a PowerStore storage system. changed bool always - Whether or not the resource has changed + Whether or not the resource has changed. snapshotrule_details complex When snapshot rule exists - Details of the snapshot rule + Details of the snapshot rule.   days_of_week list success - List of string to specify days of the week on which the rule should be applied + List of string to specify days of the week on which the rule should be applied.   desired_retention int success - Desired snapshot retention period + Desired snapshot retention period.   id str success - The system generated ID given to the snapshot rule + The system generated ID given to the snapshot rule.   interval str success - The interval between snapshots + The interval between snapshots.   name str success - Name of the snapshot rule + Name of the snapshot rule.   policies complex success - The protection policies details of the snapshot rule + The protection policies details of the snapshot rule.   @@ -1996,7 +2460,7 @@ SnapshotRule operations on a PowerStore storage system. id str success - The protection policy ID in which the snapshot rule is selected + The protection policy ID in which the snapshot rule is selected.   @@ -2004,14 +2468,14 @@ SnapshotRule operations on a PowerStore storage system. name str success - Name of the protection policy in which the snapshot rule is selected + Name of the protection policy in which the snapshot rule is selected.   time_of_day str success - The time of the day to take a daily snapshot + The time of the day to take a daily snapshot. @@ -2024,10 +2488,10 @@ SnapshotRule operations on a PowerStore storage system. Gathers information about PowerStore Storage entities ### Synopsis - Gathers the list of specified PowerStore Storage System entities, such as the list of cluster nodes, volumes, volume groups, hosts, host groups, snapshot rules, protection policies, NAS servers, NFS exports, SMB shares, tree quotas, user quotas, and file systems. + Gathers the list of specified PowerStore Storage System entities, such as the list of cluster nodes, volumes, volume groups, hosts, host groups, snapshot rules, protection policies, NAS servers, NFS exports, SMB shares, tree quotas, user quotas, file systems etc. ### Parameters - + @@ -2042,8 +2506,8 @@ Gathers information about PowerStore Storage entities - - + + @@ -2120,8 +2584,10 @@ Gathers information about PowerStore Storage entities -
Parameter list
elements: str
True
  • vol
  • vg
  • host
  • hg
  • node
  • protection_policy
  • snapshot_rule
  • nas_server
  • nfs_export
  • smb_share
  • tree_quota
  • user_quota
  • file_system
  • replication_rule
  • replication_session
  • remote_system

A list of string variables which specify the PowerStore system entities requiring information.information.
vol - volumes
node - all the nodes
vg - volume groups
protection_policy - protection policy
host - hosts
hg - host groups
snapshot_rule - snapshot rule
nas_server - NAS servers
nfs_export - NFS exports
smb_share - SMB shares
tree_quota - tree quotas
user_quota - user quotas
file_system - file systems
replication_rule - replication rules
replication_session - replication sessions
remote_system - remote systems
  • vol
  • vg
  • host
  • hg
  • node
  • protection_policy
  • snapshot_rule
  • nas_server
  • nfs_export
  • smb_share
  • tree_quota
  • user_quota
  • file_system
  • replication_rule
  • replication_session
  • remote_system
  • network
  • role
  • user
  • appliance

A list of string variables which specify the PowerStore system entities requiring information.information.
vol - volumes
node - all the nodes
vg - volume groups
protection_policy - protection policy
host - hosts
hg - host groups
snapshot_rule - snapshot rule
nas_server - NAS servers
nfs_export - NFS exports
smb_share - SMB shares
tree_quota - tree quotas
user_quota - user quotas
file_system - file systems
replication_rule - replication rules
replication_session - replication sessions
remote_system - remote systems
network - various networks
role - roles
user - local users
appliance - appliances
filters
The password of the PowerStore host.
+ +### Notes +* Pagination is not supported for role and local user. If all_pages is passed, it will be ignored. ### Examples ``` @@ -2234,32 +2700,88 @@ Gathers information about PowerStore Storage entities - filter_key: "name" filter_operator: "like" filter_value: "*share*" + +- name: Get list of user, role, network and appliances + dellemc_powerstore_gatherfacts: + array_ip: "{{array_ip}}" + verifycert: "{{verifycert}}" + user: "{{user}}" + password: "{{password}}" + gather_subset: + - user + - role + - network + - appliance + +- name: Get list of networks whose name contains 'Management' + dellemc_powerstore_gatherfacts: + array_ip: "{{array_ip}}" + verifycert: "{{verifycert}}" + user: "{{user}}" + password: "{{password}}" + gather_subset: + - network + filters: + - filter_key: "name" + filter_operator: "like" + filter_value: "*Management*" ``` ### Return Values - + - + - - + + - + - + - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -2267,7 +2789,7 @@ Gathers information about PowerStore Storage entities - + @@ -2275,14 +2797,14 @@ Gathers information about PowerStore Storage entities - + - + @@ -2290,7 +2812,7 @@ Gathers information about PowerStore Storage entities - + @@ -2298,14 +2820,14 @@ Gathers information about PowerStore Storage entities - + - + @@ -2313,7 +2835,7 @@ Gathers information about PowerStore Storage entities - + @@ -2321,14 +2843,14 @@ Gathers information about PowerStore Storage entities - + - + @@ -2336,7 +2858,7 @@ Gathers information about PowerStore Storage entities - + @@ -2344,14 +2866,37 @@ Gathers information about PowerStore Storage entities - + - + + + + + + + + + + + + + + + + + + + + + + + + @@ -2359,7 +2904,7 @@ Gathers information about PowerStore Storage entities - + @@ -2367,14 +2912,14 @@ Gathers information about PowerStore Storage entities - + - + @@ -2382,7 +2927,7 @@ Gathers information about PowerStore Storage entities - + @@ -2390,14 +2935,37 @@ Gathers information about PowerStore Storage entities - + - + + + + + + + + + + + + + + + + + + + + + + + + @@ -2405,7 +2973,7 @@ Gathers information about PowerStore Storage entities - + @@ -2413,14 +2981,14 @@ Gathers information about PowerStore Storage entities - + - + @@ -2428,7 +2996,7 @@ Gathers information about PowerStore Storage entities - + @@ -2436,14 +3004,14 @@ Gathers information about PowerStore Storage entities - + - + @@ -2451,7 +3019,7 @@ Gathers information about PowerStore Storage entities - + @@ -2459,14 +3027,14 @@ Gathers information about PowerStore Storage entities - + - + @@ -2474,7 +3042,7 @@ Gathers information about PowerStore Storage entities - + @@ -2482,29 +3050,52 @@ Gathers information about PowerStore Storage entities - + - + - + - + - + + + + + + + + + + + + + + + + + + + + + + + + @@ -2512,7 +3103,7 @@ Gathers information about PowerStore Storage entities - + @@ -2520,14 +3111,14 @@ Gathers information about PowerStore Storage entities - + - + @@ -2535,7 +3126,7 @@ Gathers information about PowerStore Storage entities - + @@ -2543,14 +3134,14 @@ Gathers information about PowerStore Storage entities - + - + @@ -2558,7 +3149,7 @@ Gathers information about PowerStore Storage entities - + @@ -2566,29 +3157,29 @@ Gathers information about PowerStore Storage entities - + - + - + - + - + @@ -2596,7 +3187,7 @@ Gathers information about PowerStore Storage entities - + @@ -2604,14 +3195,14 @@ Gathers information about PowerStore Storage entities - + - + @@ -2619,7 +3210,7 @@ Gathers information about PowerStore Storage entities - + @@ -2627,7 +3218,7 @@ Gathers information about PowerStore Storage entities - + @@ -2639,17 +3230,15 @@ Gathers information about PowerStore Storage entities * Vivek Soni (@v-soni11) -------------------------------- -# Replication Session Module +# Job Module -Replication session operations on a PowerStore storage system. +Manage jobs on Dell EMC PowerStore. ### Synopsis - Performs all replication session state change operations on a PowerStore Storage System. - This module supports get details of an existing replication session. - Updating the state of the replication session. + Managing jobs on PowerStore Storage System includes getting details of job. ### Parameters - +
KeyKey Type Returned Description
changed
changed bool always Shows whether or not the resource has changed Shows whether or not the resource has changed.
subset_result subset_result complex always Provides details of all given subsets.
  Cluster Appliance list success Provides details of all appliances.
   id str success appliance id
   model str success Model type of the PowerStore
   name str success appliance name
  Cluster list success Provides details of all clusters.
    id id str success cluster id
    name name str success cluster name
  FileSystems FileSystems list success Provides details of all filesystems.
    id id str success filesystem id
    name name str success filesystem name
  HostGroups HostGroups list success Provides details of all hostgroups.
    id id str success hostgroup id
    name name str success hostgroup name
  Hosts Hosts list success Provides details of all hosts.
    id id str success host id
    name name str success host name
  NASServers LocalUsers list success Provides details of all local users.
   id str success user id
   name str success user name
  NASServers list success Provides details of all nas servers.
    id id str success nas server id
    name name str success nas server name
  NFSExports NFSExports list success Provides details of all nfs exports.
    id id str success nfs export id
    name name str success nfs export name
  Nodes Networks list success Provides details of all networks.
   id str success network id
   name str success network name
  Nodes list success Provides details of all nodes.
    id id str success node id
    name name str success node name
  ProtectionPolicies ProtectionPolicies list success Provides details of all protectionpolicies.
    id id str success protectionpolicy id
    name name str success protectionpolicy name
  RemoteSystems RemoteSystems list success Provides details of all remote systems.
    id id str success remote system id
    name name str success remote system name
  ReplicationRules ReplicationRules list success Provides details of all replication rules.
    id id str success replication rule id
    name name str success replication rule name
  ReplicationSession ReplicationSession list success details of all replication sessions details of all replication sessions.
    id id str success replication session id
  SMBShares Roles list success Provides details of all roles.
   id str success role id
   name str success role name
  SMBShares list success Provides details of all smb shares.
    id id str success smb share id
    name name str success smb share name
  SnapshotRules SnapshotRules list success Provides details of all snapshot rules.
    id id str success snapshot rule id
    name name str success snapshot rule name
  TreeQuotas TreeQuotas list success Provides details of all tree quotas.
    id id str success tree quota id
    path path str success tree quota path
  UserQuotas UserQuotas list success Provides details of all user quotas Provides details of all user quotas.
    id id str success user quota id
  VolumeGroups VolumeGroups list success Provides details of all volumegroups.
    id id str success volumegroup id
    name name str success volumegroup name
  Volumes Volumes list success Provides details of all volumes.
    id id str success volume id
    name name str success volume name
@@ -2660,36 +3249,12 @@ Replication session operations on a PowerStore storage system. - - - - - - - - - - - - - - - - - - - - - - - - - + + - - + @@ -2723,171 +3288,234 @@ Replication session operations on a PowerStore storage system. -
ParameterDescription
volume_group str
Name/ID of the volume group for which a replication session exists.
volume_group, volume, and session_id are mutually exclusive.
volume str
Name/ID of the volume for which replication session exists.
volume_group, volume, and session_id are mutually exclusive.
session_id str
ID of the replication session.
volume_group, volume, and session_id are mutually exclusive.
session_state job_id str True
  • failed_over
  • paused
  • synchronizing

State in which the replication session is present after performing the task.

The ID of the job.
array_ip
The password of the PowerStore host.
+ -### Notes -* Manual synchronization for a replication session is not supported through the Ansible module. -* When the current state of the replication session is 'OK' and in the playbook task 'synchronizing', then it will return "changed" as False. This is because there is a scheduled synchronization in place with the associated replication rule's RPO in the protection policy. ### Examples ``` -- name: Pause a replication session - dellemc_powerstore_replicationsession: - array_ip: "{{array_ip}}" - verifycert: "{{verifycert}}" - user: "{{user}}" - password: "{{password}}" - volume: "sample_volume_1" - session_state: "paused" - -- name: Synchronize a replication session - dellemc_powerstore_replicationsession: - array_ip: "{{array_ip}}" +- name: Get Job Details + dellemc_powerstore_job: + array_ip: "{{mgmt_ip}}" verifycert: "{{verifycert}}" user: "{{user}}" password: "{{password}}" - volume: "sample_volume_1" - session_state: "synchronizing" - -- name: Get details of a replication session - dellemc_powerstore_replicationsession: - array_ip: "{{array_ip}}" - verifycert: "{{verifycert}}" - user: "{{user}}" - password: "{{password}}" - volume: "sample_volume_1" - -- name: Fail over a replication session - dellemc_powerstore_replicationsession: - array_ip: "{{array_ip}}" - verifycert: "{{verifycert}}" - user: "{{user}}" - password: "{{password}}" - volume: "sample_volume_1" - session_state: "failed_over" -``` + job_id: "a544981c-e94a-40ab-9eae-e578e182d2bb" +``` ### Return Values - + - + - - + + - + - + - - + + - + - + - + - + - + - + - + - + - + - + - + + + + + + + + - + - + - + - + - + - + - + - + - + - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - + - + - + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
KeyKey Type Returned Description
changed
changed bool always Whether or not the resource has changed Whether or not the resource has changed.
replication_session_details job_details complex When replication session exists Details of the replication session When job exists. The job details.
  estimated_completion_timestamp description_l10n str success Estimated completion time of the current replication operation. Description of the job.
  id end_time str success ['The system generated ID of the replication session.', 'Unique across source and destination roles.'] Date and time when the job execution completed.
  last_sync_timestamp estimated_completion_time str success Time of last successful synchronization. Estimated completion date and time.
  local_resource_id id str success Unique identifier of the local storage resource for the replication session. Unique identifier of the job.
  name parent_id str success Name of the replication rule. Unique identifier of the parent job, if applicable.
  progress_percentage phase str success Current status of the job.
  progress_percentage int success Progress of the current replication operation. Percent complete of the job.
  remote_resource_id resource_action str success Unique identifier of the remote storage resource for the replication session. User-specified action to be performed on the given resource.
  remote_system_id resource_id str success Unique identifier of the remote system instance. Unique identifier of the resource on which the job is operating.
  replication_rule_id resource_name str success Associated replication rule instance if created by policy engine. Name of the resource on which the job is operating.
  resource_type resource_type str success ['Storage resource type eligible for replication protection.', 'volume - Replication session created on a volume.', 'volume_group - Replication session created on a volume group.'] Resource Type for the given resource.
  role response_body complex success Base response object.
   messages complex success The details of the error response.
    arguments list success Values involved in the error.
    code str success Hexadecimal code of the error.
    message_l10n str success The description of the error.
    severity str success Type of the severity.
   response_type str success Job error response.
  response_status str success ['Role of the replication session.', 'Source - The local resource is the source of the remote replication session.', 'Destination - The local resource is the destination of the remote replication session.'] Possible HTTP status values of completed or failed jobs.
  state root_id str success State of the replication session. Unique identifier of the root job, if applicable. The root job is the job at the top of the parent hierarchy.
  start_time str success Date and time when the job execution started.
  state str success Current status of the job.
  step_order int success Order of a given job step with respect to its siblings within the job hierarchy.
  user str success Name of the user associated with the job.
### Authors -* P Srinivas Rao (@srinivas-rao5) +* Akash Shendge (@shenda1) -------------------------------- -# Host Group Module +# Replication Session Module -Manage host group on PowerStore Storage System. +Replication session operations on a PowerStore storage system. ### Synopsis - Managing host group on PowerStore storage system includes create host group with a set of hosts, add/remove hosts from host group, rename host group, and delete host group. - Deletion of a host group results in deletion of the containing hosts as well. Remove hosts from the host group first to retain them. + Performs all replication session state change operations on a PowerStore Storage System. This module supports get details of an existing replication session. Updating the state of the replication session. ### Parameters - + @@ -2898,52 +3526,36 @@ Manage host group on PowerStore Storage System. - + - + - + - - - - - - - - - + - + - - - - - - - - - + - + - - + + @@ -2977,178 +3589,170 @@ Manage host group on PowerStore Storage System. -
ParameterDescription
hostgroup_name volume_group str
The host group name. This value must contain 128 or fewer printable Unicode characters.
Creation of an empty host group is not allowed.
Required when creating a host group.
Use either hostgroup_id or hostgroup_name for modify and delete tasks.

Name/ID of the volume group for which a replication session exists.
volume_group, volume, and session_id are mutually exclusive.
hostgroup_id volume str
The 36-character long host group id, automatically generated when a host group is created.
Use either hostgroup_id or hostgroup_name for modify and delete tasks.
hostgroup_id cannot be used while creating host group, as it is generated by the array after creation of host group.
hosts list
elements: str

List of hosts to be added or removed from the host group.
Subordinate hosts in a host group can only be of one type, either FC or iSCSI.
Required when creating a host group.
To represent host, both name or ID can be used interchangeably. The module will detect both.

Name/ID of the volume for which replication session exists.
volume_group, volume, and session_id are mutually exclusive.
state session_id str True
  • absent
  • present

Define whether the host group should exist or not.
present - indicates that the host group should exist on the system.
absent - indicates that the host group should not exist on the system.
Deletion of a host group results in deletion of the containing hosts as well. Remove hosts from the host group first to retain them.
host_state str
  • present-in-group
  • absent-in-group

Define whether the hosts should be present or absent in host group.
present-in-group - indicates that the hosts should exist on the host group.
absent-in-group - indicates that the hosts should not exist on the host group.
Required when creating a host group with hosts or adding/removing hosts from existing host group.

ID of the replication session.
volume_group, volume, and session_id are mutually exclusive.
new_name session_state str
The new name for host group renaming function. This value must contain 128 or fewer printable Unicode characters.
  • failed_over
  • paused
  • synchronizing

State in which the replication session is present after performing the task.
array_ip
The password of the PowerStore host.
+ +### Notes +* Manual synchronization for a replication session is not supported through the Ansible module. +* When the current state of the replication session is 'OK' and in the playbook task 'synchronizing', then it will return "changed" as False. This is because there is a scheduled synchronization in place with the associated replication rule's RPO in the protection policy. ### Examples ``` - - name: Create host group with hosts using host name - dellemc_powerstore_hostgroup: - array_ip: "{{array_ip}}" - verifycert: "{{verifycert}}" - user: "{{user}}" - password: "{{password}}" - hostgroup_name: "{{hostgroup_name}}" - hosts: - - host1 - - host2 - state: 'present' - host_state: 'present-in-group' - - - name: Create host group with hosts using host ID - dellemc_powerstore_hostgroup: - array_ip: "{{array_ip}}" - verifycert: "{{verifycert}}" - user: "{{user}}" - password: "{{password}}" - hostgroup_name: "{{hostgroup_name}}" - hosts: - - c17fc987-bf82-480c-af31-9307b89923c3 - state: 'present' - host_state: 'present-in-group' +- name: Pause a replication session + dellemc_powerstore_replicationsession: + array_ip: "{{array_ip}}" + verifycert: "{{verifycert}}" + user: "{{user}}" + password: "{{password}}" + volume: "sample_volume_1" + session_state: "paused" - - name: Get host group details - dellemc_powerstore_hostgroup: - array_ip: "{{array_ip}}" - verifycert: "{{verifycert}}" - user: "{{user}}" - password: "{{password}}" - hostgroup_name: "{{hostgroup_name}}" - state: 'present' +- name: Synchronize a replication session + dellemc_powerstore_replicationsession: + array_ip: "{{array_ip}}" + verifycert: "{{verifycert}}" + user: "{{user}}" + password: "{{password}}" + volume: "sample_volume_1" + session_state: "synchronizing" - - name: Get host group details using ID - dellemc_powerstore_hostgroup: - array_ip: "{{array_ip}}" - verifycert: "{{verifycert}}" - user: "{{user}}" - password: "{{password}}" - hostgroup_id: "{{host group_id}}" - state: 'present' +- name: Get details of a replication session + dellemc_powerstore_replicationsession: + array_ip: "{{array_ip}}" + verifycert: "{{verifycert}}" + user: "{{user}}" + password: "{{password}}" + volume: "sample_volume_1" - - name: Add hosts to host group - dellemc_powerstore_hostgroup: - array_ip: "{{array_ip}}" - verifycert: "{{verifycert}}" - user: "{{user}}" - password: "{{password}}" - hostgroup_name: "{{hostgroup_name}}" - hosts: - - host3 - host_state: 'present-in-group' - state: 'present' - - - name: Remove hosts from host group - dellemc_powerstore_hostgroup: - array_ip: "{{array_ip}}" - verifycert: "{{verifycert}}" - user: "{{user}}" - password: "{{password}}" - hostgroup_name: "{{hostgroup_name}}" - hosts: - - host3 - host_state: 'absent-in-group' - state: 'present' - - - name: Rename host group - dellemc_powerstore_hostgroup: - array_ip: "{{array_ip}}" - verifycert: "{{verifycert}}" - user: "{{user}}" - password: "{{password}}" - hostgroup_name: "{{hostgroup_name}}" - new_name: "{{new_hostgroup_name}}" - state: 'present' - - - name: Delete host group - dellemc_powerstore_hostgroup: - array_ip: "{{array_ip}}" - verifycert: "{{verifycert}}" - user: "{{user}}" - password: "{{password}}" - hostgroup_name: "{{hostgroup_name}}" - state: 'absent' -``` +- name: Fail over a replication session + dellemc_powerstore_replicationsession: + array_ip: "{{array_ip}}" + verifycert: "{{verifycert}}" + user: "{{user}}" + password: "{{password}}" + volume: "sample_volume_1" + session_state: "failed_over" +``` ### Return Values - + - + - - + + - + - - + + - + - + - - + + - + - - - - - - - - - - - - - - - - - + - + - + - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - +
KeyKey Type Returned Description
changed
changed bool always Whether or not the resource has changed
hostgroup_details replication_session_details complex When host group exists Details of the host group When replication session exists Details of the replication session
  description estimated_completion_timestamp str success Description about the host group Estimated completion time of the current replication operation.
  hosts complex id str success The hosts details which are part of this host group The system generated ID of the replication session. Unique across source and destination roles.
   id str success The ID of the host
   name str success The name of the host
  id last_sync_timestamp str success The system generated ID given to the host group Time of last successful synchronization.
  name local_resource_id str success Unique identifier of the local storage resource for the replication session.
  name str success Name of the replication rule.
  progress_percentage int success Progress of the current replication operation.
  remote_resource_id str success Unique identifier of the remote storage resource for the replication session.
  remote_system_id str success Unique identifier of the remote system instance.
  replication_rule_id str success Associated replication rule instance if created by policy engine.
  resource_type str success Storage resource type eligible for replication protection. volume - Replication session created on a volume. volume_group - Replication session created on a volume group.
  role str success Role of the replication session. Source - The local resource is the source of the remote replication session. Destination - The local resource is the destination of the remote replication session.
  state str success Name of the host group State of the replication session.
### Authors -* Manisha Agrawal (@agrawm3) +* P Srinivas Rao (@srinivas-rao5) -------------------------------- -# NFS Module +# Host Group Module -Manage NFS exports on Dell EMC PowerStore. +Manage host group on PowerStore Storage System. ### Synopsis - Managing NFS exports on PowerStore Storage System includes creating new NFS Export, getting details of NFS export, modifying attributes of NFS export, and deleting NFS export. + Managing host group on PowerStore storage system includes create host group with a set of hosts, add/remove hosts from host group, rename host group, and delete host group. Deletion of a host group results in deletion of the containing hosts as well. Remove hosts from the host group first to retain them. ### Parameters - + @@ -3159,156 +3763,982 @@ Manage NFS exports on Dell EMC PowerStore. - - - - - - - - - + - + - + - + - - + + - + - + + - - - + + - + - - + + - + - + - + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + - - - - + + + - + + - + + - - - - - - - - - - - - - - - - - - - - - - - - - - + - + + - - + - +
ParameterDescription
nfs_export_name str
The name of the NFS export.
Mandatory for create operation.
Specify either nfs_export_name or nfs_export_id(but not both) for any operation.
nfs_export_id hostgroup_name str
The ID of the NFS export.

The host group name. This value must contain 128 or fewer printable Unicode characters.
Creation of an empty host group is not allowed.
Required when creating a host group.
Use either hostgroup_id or hostgroup_name for modify and delete tasks.
filesystem hostgroup_id str
The ID/Name of the filesystem for which the NFS export will be created.
Either filesystem or snapshot is required for creation of the NFS Export.
If filesystem name is specified, then nas_server is required to uniquely identify the filesystem.
If filesystem parameter is provided, then snapshot cannot be specified.

The 36-character long host group id, automatically generated when a host group is created.
Use either hostgroup_id or hostgroup_name for modify and delete tasks.
hostgroup_id cannot be used while creating host group, as it is generated by the array after creation of host group.
snapshot str hosts list
elements: str

The ID/Name of the Snapshot for which NFS export will be created.
Either filesystem or snapshot is required for creation of the NFS Export.
If snapshot name is specified, then nas_server is required to uniquely identify the snapshot.
If snapshot parameter is provided, then filesystem cannot be specified.
NFS export can be created only if access type of snapshot is "protocol".

List of hosts to be added or removed from the host group.
Subordinate hosts in a host group can only be of one type, either FC or iSCSI.
Required when creating a host group.
To represent host, both name or ID can be used interchangeably. The module will detect both.
nas_server state str True
The NAS server. This could be the name or ID of the NAS server.
  • absent
  • present

Define whether the host group should exist or not.
present - indicates that the host group should exist on the system.
absent - indicates that the host group should not exist on the system.
Deletion of a host group results in deletion of the containing hosts as well. Remove hosts from the host group first to retain them.
path host_state str
Local path to export relative to the NAS server root.
With NFS, each export of a file_system or file_snap must have a unique local path.
Mandatory while creating NFS export.
  • present-in-group
  • absent-in-group

Define whether the hosts should be present or absent in host group.
present-in-group - indicates that the hosts should exist on the host group.
absent-in-group - indicates that the hosts should not exist on the host group.
Required when creating a host group with hosts or adding/removing hosts from existing host group.
description new_name str
The description for the NFS export.

The new name for host group renaming function. This value must contain 128 or fewer printable Unicode characters.
default_access array_ip str True
  • NO_ACCESS
  • READ_ONLY
  • READ_WRITE
  • ROOT
  • READ_ONLY_ROOT

Default access level for all hosts that can access the Export.
For hosts that need different access than the default, they can be configured by adding to the list.
If default_access is not mentioned during creation, then NFS export will be created with No_Access.
no_access_hosts list
elements: str

Hosts with no access to the NFS export.
read_only_hosts list
elements: str

Hosts with read-only access to the NFS export.
read_only_root_hosts list
elements: str

Hosts with read-only access for root user to the NFS export.
read_write_hosts list
elements: str

Hosts with read and write access to the NFS export.

IP or FQDN of the PowerStore management system.
read_write_root_hosts list
elements: str
verifycert bool True
Hosts with read and write access for root user to the NFS export.
  • True
  • False

Boolean variable to specify whether to validate SSL certificate or not.
True - indicates that the SSL certificate should be verified. Set the environment variable REQUESTS_CA_BUNDLE to the path of the SSL certificate.
False - indicates that the SSL certificate should not be verified.
min_security user str True
  • SYS
  • KERBEROS
  • KERBEROS_WITH_INTEGRITY
  • KERBEROS_WITH_ENCRYPTION

NFS enforced security type for users accessing an NFS export.
If not specified at the time of creation, it will be set to SYS.
anonymous_uid int
Specifies the user ID of the anonymous account.
If not specified at the time of creation, it will be set to -2.
anonymous_gid int
Specifies the group ID of the anonymous account.
If not specified at the time of creation, it will be set to -2.
is_no_suid bool
If set, do not allow access to set SUID. Otherwise, allow access.
If not specified at the time of creation, it will be set to False.

The username of the PowerStore host.
host_state password str True
  • present-in-export
  • absent-in-export

Define whether the hosts can access the NFS export.
Required when adding or removing host access from the export.

The password of the PowerStore host.
+ + +### Examples +``` + - name: Create host group with hosts using host name + dellemc_powerstore_hostgroup: + array_ip: "{{array_ip}}" + verifycert: "{{verifycert}}" + user: "{{user}}" + password: "{{password}}" + hostgroup_name: "{{hostgroup_name}}" + hosts: + - host1 + - host2 + state: 'present' + host_state: 'present-in-group' + + - name: Create host group with hosts using host ID + dellemc_powerstore_hostgroup: + array_ip: "{{array_ip}}" + verifycert: "{{verifycert}}" + user: "{{user}}" + password: "{{password}}" + hostgroup_name: "{{hostgroup_name}}" + hosts: + - c17fc987-bf82-480c-af31-9307b89923c3 + state: 'present' + host_state: 'present-in-group' + + - name: Get host group details + dellemc_powerstore_hostgroup: + array_ip: "{{array_ip}}" + verifycert: "{{verifycert}}" + user: "{{user}}" + password: "{{password}}" + hostgroup_name: "{{hostgroup_name}}" + state: 'present' + + - name: Get host group details using ID + dellemc_powerstore_hostgroup: + array_ip: "{{array_ip}}" + verifycert: "{{verifycert}}" + user: "{{user}}" + password: "{{password}}" + hostgroup_id: "{{host group_id}}" + state: 'present' + + - name: Add hosts to host group + dellemc_powerstore_hostgroup: + array_ip: "{{array_ip}}" + verifycert: "{{verifycert}}" + user: "{{user}}" + password: "{{password}}" + hostgroup_name: "{{hostgroup_name}}" + hosts: + - host3 + host_state: 'present-in-group' + state: 'present' + + - name: Remove hosts from host group + dellemc_powerstore_hostgroup: + array_ip: "{{array_ip}}" + verifycert: "{{verifycert}}" + user: "{{user}}" + password: "{{password}}" + hostgroup_name: "{{hostgroup_name}}" + hosts: + - host3 + host_state: 'absent-in-group' + state: 'present' + + - name: Rename host group + dellemc_powerstore_hostgroup: + array_ip: "{{array_ip}}" + verifycert: "{{verifycert}}" + user: "{{user}}" + password: "{{password}}" + hostgroup_name: "{{hostgroup_name}}" + new_name: "{{new_hostgroup_name}}" + state: 'present' + + - name: Delete host group + dellemc_powerstore_hostgroup: + array_ip: "{{array_ip}}" + verifycert: "{{verifycert}}" + user: "{{user}}" + password: "{{password}}" + hostgroup_name: "{{hostgroup_name}}" + state: 'absent' +``` + +### Return Values + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
KeyTypeReturnedDescription
changed bool always Whether or not the resource has changed.
hostgroup_details complex When host group exists Details of the host group.
  description str success Description about the host group.
  hosts complex success The hosts details which are part of this host group.
   id str success The ID of the host.
   name str success The name of the host.
  id str success The system generated ID given to the host group.
  name str success Name of the host group.
+ +### Authors +* Manisha Agrawal (@agrawm3) + +-------------------------------- +# NFS Module + +Manage NFS exports on Dell EMC PowerStore. + +### Synopsis + Managing NFS exports on PowerStore Storage System includes creating new NFS Export, getting details of NFS export, modifying attributes of NFS export, and deleting NFS export. + +### Parameters + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
ParameterTypeRequiredDefaultChoicesDescription
nfs_export_name str
The name of the NFS export.
Mandatory for create operation.
Specify either nfs_export_name or nfs_export_id(but not both) for any operation.
nfs_export_id str
The ID of the NFS export.
filesystem str
The ID/Name of the filesystem for which the NFS export will be created.
Either filesystem or snapshot is required for creation of the NFS Export.
If filesystem name is specified, then nas_server is required to uniquely identify the filesystem.
If filesystem parameter is provided, then snapshot cannot be specified.
snapshot str
The ID/Name of the Snapshot for which NFS export will be created.
Either filesystem or snapshot is required for creation of the NFS Export.
If snapshot name is specified, then nas_server is required to uniquely identify the snapshot.
If snapshot parameter is provided, then filesystem cannot be specified.
NFS export can be created only if access type of snapshot is "protocol".
nas_server str
The NAS server. This could be the name or ID of the NAS server.
path str
Local path to export relative to the NAS server root.
With NFS, each export of a file_system or file_snap must have a unique local path.
Mandatory while creating NFS export.
description str
The description for the NFS export.
default_access str
  • NO_ACCESS
  • READ_ONLY
  • READ_WRITE
  • ROOT
  • READ_ONLY_ROOT

Default access level for all hosts that can access the Export.
For hosts that need different access than the default, they can be configured by adding to the list.
If default_access is not mentioned during creation, then NFS export will be created with No_Access.
no_access_hosts list
elements: str

Hosts with no access to the NFS export.
read_only_hosts list
elements: str

Hosts with read-only access to the NFS export.
read_only_root_hosts list
elements: str

Hosts with read-only access for root user to the NFS export.
read_write_hosts list
elements: str

Hosts with read and write access to the NFS export.
read_write_root_hosts list
elements: str

Hosts with read and write access for root user to the NFS export.
min_security str
  • SYS
  • KERBEROS
  • KERBEROS_WITH_INTEGRITY
  • KERBEROS_WITH_ENCRYPTION

NFS enforced security type for users accessing an NFS export.
If not specified at the time of creation, it will be set to SYS.
anonymous_uid int
Specifies the user ID of the anonymous account.
If not specified at the time of creation, it will be set to -2.
anonymous_gid int
Specifies the group ID of the anonymous account.
If not specified at the time of creation, it will be set to -2.
is_no_suid bool
If set, do not allow access to set SUID. Otherwise, allow access.
If not specified at the time of creation, it will be set to False.
host_state str
  • present-in-export
  • absent-in-export

Define whether the hosts can access the NFS export.
Required when adding or removing host access from the export.
state str True
  • absent
  • present

Define whether the NFS export should exist or not.
array_ip str True
IP or FQDN of the PowerStore management system.
verifycert bool True
  • True
  • False

Boolean variable to specify whether to validate SSL certificate or not.
True - indicates that the SSL certificate should be verified. Set the environment variable REQUESTS_CA_BUNDLE to the path of the SSL certificate.
False - indicates that the SSL certificate should not be verified.
user str True
The username of the PowerStore host.
password str True
The password of the PowerStore host.
+ + +### Examples +``` +- name: Create NFS export (filesystem) + dellemc_powerstore_nfs: + array_ip: "{{array_ip}}" + verifycert: "{{verifycert}}" + user: "{{user}}" + password: "{{password}}" + nfs_export_name: "{{export_name1}}" + filesystem: "{{filesystem}}" + nas_server: "{{nas_server}}" + path: "{{path1}}" + description: "sample description" + default_access: "NO_ACCESS" + no_access_hosts: + - "{{host5}}" + read_only_hosts: + - "{{host1}}" + read_only_root_hosts: + - "{{host2}}" + read_write_hosts: + - "{{host3}}" + read_write_root_hosts: + - "{{host4}}" + min_security: "SYS" + anonymous_uid: 1000 + anonymous_gid: 1000 + is_no_suid: True + host_state: "present-in-export" + state: "present" + +- name: Create NFS export Create NFS export for filesystem snapshot with mandatory parameters + dellemc_powerstore_nfs: + array_ip: "{{array_ip}}" + verifycert: "{{verifycert}}" + user: "{{user}}" + password: "{{password}}" + nfs_export_name: "{{export_name2}}" + snapshot: "{{snapshot}}" + nas_server: "{{nas_server}}" + path: "{{path2}}" + state: "present" + +- name: Get NFS export details using ID + dellemc_powerstore_nfs: + array_ip: "{{array_ip}}" + verifycert: "{{verifycert}}" + user: "{{user}}" + password: "{{password}}" + nfs_export_id: "{{export_id}}" + state: "present" + +- name: Add Read-Only and Read-Write hosts to NFS export + dellemc_powerstore_nfs: + array_ip: "{{array_ip}}" + verifycert: "{{verifycert}}" + user: "{{user}}" + password: "{{password}}" + nfs_export_id: "{{export_id}}" + read_only_hosts: + - "{{host5}}" + read_write_hosts: + - "{{host6}}" + host_state: "present-in-export" + state: "present" + +- name: Remove Read-Only and Read-Write hosts from NFS export + dellemc_powerstore_nfs: + array_ip: "{{array_ip}}" + verifycert: "{{verifycert}}" + user: "{{user}}" + password: "{{password}}" + nfs_export_id: "{{export_id}}" + read_only_hosts: + - "{{host1}}" + read_write_hosts: + - "{{host3}}" + host_state: "absent-in-export" + state: "present" + +- name: Modify the attributes of NFS export + dellemc_powerstore_nfs: + array_ip: "{{array_ip}}" + verifycert: "{{verifycert}}" + user: "{{user}}" + password: "{{password}}" + nfs_export_id: "{{export_id}}" + description: "modify description" + default_access: "ROOT" + state: "present" + +- name: Delete NFS export using name + dellemc_powerstore_nfs: + array_ip: "{{array_ip}}" + verifycert: "{{verifycert}}" + user: "{{user}}" + password: "{{password}}" + nfs_export_name: "{{export_name}}" + nas_server: "{{nas_server}}" + state: "absent" +``` + +### Return Values + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
KeyTypeReturnedDescription
changed bool always Whether or not the resource has changed
nfs_export_details complex When NFS export exists. The NFS export details.
  anonymous_GID int success The group ID of the anonymous account.
  anonymous_UID int success The user ID of the anonymous account.
  default_access str success Default access level for all hosts that can access the export.
  description str success The description for the NFS export.
  file_system complex success Details of filesystem and NAS server on which NFS export is present.
   filesystem_type str success The type of the filesystem.
   id str success The ID of the filesystem.
   name str success The name of the filesystem.
   nas_server complex success Details of NAS server.
    id str success The ID of the NAS server.
    name str success The name of the NAS server.
  id str success The ID of the NFS export.
  is_no_SUID bool success If set, do not allow access to set SUID. Otherwise, allow access.
  min_security str success NFS enforced security type for users accessing an NFS export.
  name str success The name of the NFS export.
  no_access_hosts list success Hosts with no access to the NFS export.
  path str success Local path to a location within the file system.
  read_only_hosts list success Hosts with read-only access to the NFS export.
  read_only_root_hosts list success Hosts with read-only for root user access to the NFS export.
  read_write_hosts list success Hosts with read and write access to the NFS export.
  read_write_root_hosts list success Hosts with read and write for root user access to the NFS export.
+ +### Authors +* Akash Shendge (@shenda1) + +-------------------------------- +# Role Module + +Get details of the roles present on the PowerStore storage system. + +### Synopsis + Manage role in PowerStore storage system includes getting the details of a role. + +### Parameters + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
ParameterTypeRequiredDefaultChoicesDescription
role_name str
Name of the role.
role_id str
Id of the role.
state str True
  • absent
  • present

Define whether the role should exist or not.
present, indicates that the role should exist on the system.
absent, indicates that the role should not exist on the system.
array_ip str True
IP or FQDN of the PowerStore management system.
verifycert bool True
  • True
  • False

Boolean variable to specify whether to validate SSL certificate or not.
True - indicates that the SSL certificate should be verified. Set the environment variable REQUESTS_CA_BUNDLE to the path of the SSL certificate.
False - indicates that the SSL certificate should not be verified.
user str True
The username of the PowerStore host.
password str True
The password of the PowerStore host.
+ +### Notes +* Only getting the details of the role is supported by the ansible module. +* Creation, modification and deletion of roles is not supported by the ansible modules. + +### Examples +``` +- name: Get the details of role by name + dellemc_powerstore_role: + array_ip: "{{array_ip}}" + verifycert: "{{verify_cert}}" + user: "{{user}}" + password: "{{password}}" + role_name: "Administrator" + state: "present" + +- name: Get the details of role by id + dellemc_powerstore_role: + array_ip: "{{array_ip}}" + verifycert: "{{verify_cert}}" + user: "{{user}}" + password: "{{password}}" + role_id: "1" + state: "present" +``` + +### Return Values + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
KeyTypeReturnedDescription
changed bool always Whether or not the resource has changed.
role_details complex When role exists. The role details.
  description str success Description of the role.
  id str success The ID of the role.
  is_built_in bool success Indicates whether the role is built-in.
  name str success The name of the role.
+ +### Authors +* P Srinivas Rao (@srinivas-rao5) + +-------------------------------- +# Volume Group Module + +Manage volume groups on a PowerStore Storage System + +### Synopsis + Managing volume group on PowerStore Storage System includes creating new volume group, adding volumes to volume group, removing volumes from volume group, renaming volume group, modifying volume group, and deleting volume group. + +### Parameters + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - + @@ -3342,302 +4772,224 @@ Manage NFS exports on Dell EMC PowerStore. -
ParameterTypeRequiredDefaultChoicesDescription
vg_name str
The name of the volume group.
vg_id str
The id of the volume group.
It can be used only for Modify, Add/Remove, or Delete operation.
volumes list
elements: str

This is a list of volumes.
Either the volume ID or name must be provided for adding/removing existing volumes from a volume group.
If volumes are given, then vol_state should also be specified.
vol_state str
  • present-in-group
  • absent-in-group

String variable. Describes the state of volumes inside a volume group.
If volume is given, then vol_state should also be specified.
new_vg_name str
The new name of the volume group.
description str
Description about the volume group.
protection_policy str
String variable. Represents Protection policy id or name used for volume group.
Specifying an empty string or "" removes the existing protection policy from volume group.
is_write_order_consistent bool
A boolean flag to indicate whether Snapshot sets of the volume group will be write-order consistent.
If this parameter is not specified, the array by default sets it to true.
state str True
  • absent
  • present

Define whether the NFS export should exist or not.

Define whether the volume group should exist or not.
array_ip
The password of the PowerStore host.
+ +### Notes +* vol_state is mandatory if volumes are provided. +* A protection policy can be specified either for an volume group, or for the individual volumes inside the volume group. +* A volume can be a member of at most one volume group. +* Specifying "protection_policy" as empty string or "" removes the existing protection policy from a volume group. ### Examples ``` -- name: Create NFS export (filesystem) - dellemc_powerstore_nfs: +- name: Create volume group without protection policy + dellemc_powerstore_volumegroup: array_ip: "{{array_ip}}" verifycert: "{{verifycert}}" user: "{{user}}" password: "{{password}}" - nfs_export_name: "{{export_name1}}" - filesystem: "{{filesystem}}" - nas_server: "{{nas_server}}" - path: "{{path1}}" - description: "sample description" - default_access: "NO_ACCESS" - no_access_hosts: - - "{{host5}}" - read_only_hosts: - - "{{host1}}" - read_only_root_hosts: - - "{{host2}}" - read_write_hosts: - - "{{host3}}" - read_write_root_hosts: - - "{{host4}}" - min_security: "SYS" - anonymous_uid: 1000 - anonymous_gid: 1000 - is_no_suid: True - host_state: "present-in-export" + vg_name: "{{vg_name}}" + description: "This volume group is for ansible" state: "present" -- name: Create NFS export Create NFS export for filesystem snapshot with mandatory parameters - dellemc_powerstore_nfs: +- name: Get details of volume group + dellemc_powerstore_volumegroup: array_ip: "{{array_ip}}" verifycert: "{{verifycert}}" user: "{{user}}" password: "{{password}}" - nfs_export_name: "{{export_name2}}" - snapshot: "{{snapshot}}" - nas_server: "{{nas_server}}" - path: "{{path2}}" + vg_name: "{{vg_name}}" state: "present" -- name: Get NFS export details using ID - dellemc_powerstore_nfs: +- name: Add volumes to volume group + dellemc_powerstore_volumegroup: array_ip: "{{array_ip}}" verifycert: "{{verifycert}}" user: "{{user}}" password: "{{password}}" - nfs_export_id: "{{export_id}}" + vg_name: "{{vg_name}}" state: "present" + volumes: + - "7f879569-676c-4749-a06f-c2c30e09b295" + - "68e4dad5-5de5-4644-a98f-6d4fb916e169" + - "Ansible_Testing" + vol_state: "present-in-group" -- name: Add Read-Only and Read-Write hosts to NFS export - dellemc_powerstore_nfs: +- name: Remove volumes from volume group + dellemc_powerstore_volumegroup: array_ip: "{{array_ip}}" verifycert: "{{verifycert}}" user: "{{user}}" password: "{{password}}" - nfs_export_id: "{{export_id}}" - read_only_hosts: - - "{{host5}}" - read_write_hosts: - - "{{host6}}" - host_state: "present-in-export" + vg_name: "{{vg_name}}" state: "present" + volumes: + - "7f879569-676c-4749-a06f-c2c30e09b295" + - "Ansible_Testing" + vol_state: "absent-in-group" -- name: Remove Read-Only and Read-Write hosts from NFS export - dellemc_powerstore_nfs: +- name: Rename volume group and change is_write_order_consistent flag + dellemc_powerstore_volumegroup: array_ip: "{{array_ip}}" verifycert: "{{verifycert}}" user: "{{user}}" password: "{{password}}" - nfs_export_id: "{{export_id}}" - read_only_hosts: - - "{{host1}}" - read_write_hosts: - - "{{host3}}" - host_state: "absent-in-export" + vg_name: "{{vg_name}}" + new_vg_name: "{{new_vg_name}}" + is_write_order_consistent: False state: "present" -- name: Modify the attributes of NFS export - dellemc_powerstore_nfs: +- name: Get details of volume group by ID + dellemc_powerstore_volumegroup: array_ip: "{{array_ip}}" verifycert: "{{verifycert}}" user: "{{user}}" password: "{{password}}" - nfs_export_id: "{{export_id}}" - description: "modify description" - default_access: "ROOT" + vg_id: "{{vg_id}}" state: "present" -- name: Delete NFS export using name - dellemc_powerstore_nfs: +- name: Delete volume group + dellemc_powerstore_volumegroup: array_ip: "{{array_ip}}" verifycert: "{{verifycert}}" user: "{{user}}" password: "{{password}}" - nfs_export_name: "{{export_name}}" - nas_server: "{{nas_server}}" + name: "{{new_vg_name}}" state: "absent" ``` ### Return Values - + - + - - + + + + + + + + - + - + + + + + + + + + + + + + + + + + + + + + + + + + - - + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + - + - + - + - - + + - + - + - - - - - - - - + - - + + - + - - + + - + - - + + - + -
KeyKey Type Returned Description
changed
add_vols_to_vg bool When value exists A boolean flag to indicate whether volume/s got added to volume group.
changed bool always Whether or not the resource has changed Whether or not the resource has changed.
nfs_export_details create_vg bool When value exists A boolean flag to indicate whether volume group got created.
delete_vg bool When value exists A boolean flag to indicate whether volume group got deleted.
modify_vg bool When value exists A boolean flag to indicate whether volume group got modified.
remove_vols_from_vg bool When value exists A boolean flag to indicate whether volume/s got removed from volume group.
volume_group_details complex When NFS export exists. The NFS export details. When volume group exists Details of the volume group.
  anonymous_GID int success The group ID of the anonymous account.
  anonymous_UID int success The user ID of the anonymous account.
  default_access str success Default access level for all hosts that can access the export.
  description str success The description for the NFS export.
  file_system complex success Details of filesystem and NAS server on which NFS export is present.
   filesystem_type str success The type of the filesystem.
   id str success The ID of the filesystem.
   name str success The name of the filesystem.
   nas_server complex success Details of NAS server.
    id str success The ID of the NAS server.
    name str success The name of the NAS server.
  id str success The ID of the NFS export.
  is_no_SUID bool success If set, do not allow access to set SUID. Otherwise, allow access.
  min_security description str success NFS enforced security type for users accessing an NFS export. description about the volume group.
  name id str success The name of the NFS export. The system generated ID given to the volume group.
  no_access_hosts list is_write_order_consistent bool success Hosts with no access to the NFS export. A boolean flag to indicate whether snapshot sets of the volume group will be write-order consistent.
  path name str success Local path to a location within the file system.
  read_only_hosts list success Hosts with read-only access to the NFS export. Name of the volume group.
  read_only_root_hosts list protection_policy_id str success Hosts with read-only for root user access to the NFS export. The protection policy of the volume group.
  read_write_hosts list type str success Hosts with read and write access to the NFS export. The type of the volume group.
  read_write_root_hosts list volumes complex success Hosts with read and write for root user access to the NFS export. The volumes details of the volume group.
+ +   +   + id + str + success + The system generated ID given to the volume associated with the volume group. + + +   +   + name + str + success + The name of the volume associated with the volume group. + + ### Authors * Akash Shendge (@shenda1) +* Arindam Datta (@dattaarindam) -------------------------------- -# Volume Group Module +# NAS Server Module -Manage volume groups on a PowerStore Storage System +NAS Server operations on PowerStore Storage system. ### Synopsis - Managing volume group on PowerStore Storage System includes creating new volume group, adding volumes to volume group, removing volumes from volume group, renaming volume group, modifying volume group, and deleting volume group. + Supports getting the details and modifying the attributes of a NAS server. ### Parameters - + @@ -3648,68 +5000,76 @@ Manage volume groups on a PowerStore Storage System - + - + - + - + - - + + - + - + - - + + - + - + - + - + - + + + + + + + - + + + - - + + - + @@ -3717,7 +5077,7 @@ Manage volume groups on a PowerStore Storage System - + @@ -3751,224 +5111,214 @@ Manage volume groups on a PowerStore Storage System -
ParameterDescription
vg_name nas_server_name str
The name of the volume group.

Name of the NAS server. Mutually exclusive with nas_server_id.
vg_id nas_server_id str
The id of the volume group.
It can be used only for Modify, Add/Remove, or Delete operation.

Unique id of the NAS server. Mutually exclusive with nas_server_name.
volumes list
elements: str
description str
This is a list of volumes.
Either the volume ID or name must be provided for adding/removing existing volumes from a volume group.
If volumes are given, then vol_state should also be specified.

Description of the NAS server.
vol_state nas_server_new_name str
  • present-in-group
  • absent-in-group

String variable. Describes the state of volumes inside a volume group.
If volume is given, then vol_state should also be specified.

New name of the NAS server for a rename operation.
new_vg_name current_node str
The new name of the volume group.

Unique identifier or name of the node on which the NAS server is running.
description preferred_node str
Description about the volume group.

Unique identifier or name of the preferred node for the NAS server. The initial value (on NAS server create) is taken from the current node.
protection_policy current_unix_directory_service str
  • NIS
  • LDAP
  • LOCAL_FILES
  • LOCAL_THEN_NIS
  • LOCAL_THEN_LDAP

Define the Unix directory service used for looking up identity information for Unix such as UIDs, GIDs, net groups, and so on.
default_unix_user str
String variable. Represents Protection policy id or name used for volume group.
Specifying an empty string or "" removes the existing protection policy from volume group.

Default Unix user name used for granting access in case of Windows to Unix user mapping failure. When empty, access in such case is denied.
is_write_order_consistent bool default_windows_user str
A boolean flag to indicate whether Snapshot sets of the volume group will be write-order consistent.
If this parameter is not specified, the array by default sets it to true.

Default Windows user name used for granting access in case of Unix to Windows user mapping failure. When empty, access in such case is denied.
state True
  • absent
  • present

Define whether the volume group should exist or not.

Define whether the nas server should exist or not.
array_ip
The password of the PowerStore host.
+ -### Notes -* vol_state is mandatory if volumes are provided. -* A protection policy can be specified either for an volume group, or for the individual volumes inside the volume group. -* A volume can be a member of at most one volume group. -* Specifying "protection_policy" as empty string or "" removes the existing protection policy from a volume group. ### Examples ``` -- name: Create volume group without protection policy - dellemc_powerstore_volumegroup: - array_ip: "{{array_ip}}" - verifycert: "{{verifycert}}" - user: "{{user}}" - password: "{{password}}" - vg_name: "{{vg_name}}" - description: "This volume group is for ansible" - state: "present" - -- name: Get details of volume group - dellemc_powerstore_volumegroup: - array_ip: "{{array_ip}}" - verifycert: "{{verifycert}}" - user: "{{user}}" - password: "{{password}}" - vg_name: "{{vg_name}}" - state: "present" - -- name: Add volumes to volume group - dellemc_powerstore_volumegroup: - array_ip: "{{array_ip}}" - verifycert: "{{verifycert}}" - user: "{{user}}" - password: "{{password}}" - vg_name: "{{vg_name}}" - state: "present" - volumes: - - "7f879569-676c-4749-a06f-c2c30e09b295" - - "68e4dad5-5de5-4644-a98f-6d4fb916e169" - - "Ansible_Testing" - vol_state: "present-in-group" - -- name: Remove volumes from volume group - dellemc_powerstore_volumegroup: - array_ip: "{{array_ip}}" - verifycert: "{{verifycert}}" - user: "{{user}}" - password: "{{password}}" - vg_name: "{{vg_name}}" - state: "present" - volumes: - - "7f879569-676c-4749-a06f-c2c30e09b295" - - "Ansible_Testing" - vol_state: "absent-in-group" + - name: Get details of NAS Server by name + dellemc_powerstore_nasserver: + array_ip: "{{array_ip}}" + verifycert: "{{verifycert}}" + user: "{{user}}" + password: "{{password}}" + nas_server_name: "{{nas_server_name}}" + state: "present" -- name: Rename volume group and change is_write_order_consistent flag - dellemc_powerstore_volumegroup: - array_ip: "{{array_ip}}" - verifycert: "{{verifycert}}" - user: "{{user}}" - password: "{{password}}" - vg_name: "{{vg_name}}" - new_vg_name: "{{new_vg_name}}" - is_write_order_consistent: False - state: "present" + - name: Get Details of NAS Server by ID + dellemc_powerstore_nasserver: + array_ip: "{{array_ip}}" + verifycert: "{{verifycert}}" + user: "{{user}}" + password: "{{password}}" + nas_server_id: "{{nas_id}}" + state: "present" -- name: Get details of volume group by ID - dellemc_powerstore_volumegroup: - array_ip: "{{array_ip}}" - verifycert: "{{verifycert}}" - user: "{{user}}" - password: "{{password}}" - vg_id: "{{vg_id}}" - state: "present" + - name: Rename NAS Server by Name + dellemc_powerstore_nasserver: + array_ip: "{{array_ip}}" + verifycert: "{{verifycert}}" + user: "{{user}}" + password: "{{password}}" + nas_server_name: "{{nas_server_name}}" + nas_server_new_name : "{{nas_server_new_name}}" + state: "present" -- name: Delete volume group - dellemc_powerstore_volumegroup: - array_ip: "{{array_ip}}" - verifycert: "{{verifycert}}" - user: "{{user}}" - password: "{{password}}" - name: "{{new_vg_name}}" - state: "absent" + - name: Modify NAS Server attributes by ID + dellemc_powerstore_nasserver: + array_ip: "{{array_ip}}" + verifycert: "{{verifycert}}" + user: "{{user}}" + password: "{{password}}" + nas_server_id: "{{nas_id}}" + current_unix_directory_service: "LOCAL_FILES" + current_node: "{{cur_node_n1}}" + preferred_node: "{{prefered_node}}" + state: "present" ``` ### Return Values - + - + - - - - - - - - + + - - - - - - - - - - - - - - - - - - - - - - - - - + - - + + - + - + - + - + - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - + - + - + - + - + - + - + - - + + - + - - - - - - - - - - - - - - - - -
KeyKey Type Returned Description
add_vols_to_vg bool When value exists A boolean flag to indicate whether volume/s got added to volume group
changed
changed bool always Whether or not the resource has changed
create_vg bool When value exists A boolean flag to indicate whether volume group got created
delete_vg bool When value exists A boolean flag to indicate whether volume group got deleted
modify_vg bool When value exists A boolean flag to indicate whether volume group got modified
remove_vols_from_vg bool When value exists A boolean flag to indicate whether volume/s got removed from volume group
volume_group_details nasserver_details complex When volume group exists Details of the volume group When nas server exists Details about the nas server
  description backup_IPv4_interface_id str success ['description about the volume group'] Unique identifier of the preferred IPv4 backup interface.
  id backup_IPv6_interface_id str success ['The system generated ID given to the volume group'] Unique identifier of the preferred IPv6 backup interface.
  is_write_order_consistent current_node dict success Unique identifier and name of the node on which the NAS server is running.
  current_unix_directory_service str success Define the Unix directory service used for looking up identity information for Unix such as UIDs, GIDs, net groups, and so on.
  default_unix_user str success Default Unix user name used for granting access in case of Windows to Unix user mapping failure.
  description str success Additional information about the nas server.
  file_interfaces dict success This is the inverse of the resource type file_interface association. Will return the id,name & ip_address of the associated file interface.
  file_ldaps str success This is the inverse of the resource type file_ldap association.
  file_systems dict success This is the inverse of the resource type file_system association.
  id str success The system generated ID given to the nas server
  is_username_translation_enabled bool success ['A boolean flag to indicate whether snapshot sets of the volume group will be write-order consistent'] Enable the possibility to match a windows account to a Unix account with different names.
  name name str success ['Name of the volume group'] Name of the nas server
  protection_policy_id nfs_servers str success ['The protection policy of the volume group'] This is the inverse of the resource type nfs_server association.
  type operational_status str success ['The type of the volume group'] NAS server operational status.
  volumes complex preferred_node dict success ['The volumes details of the volume group'] Unique identifier and name of the preferred node for the NAS server.
   id str success ['The system generated ID given to the volume associated with the volume group']
   name str success ['The name of the volume associated with the volume group.']
+ +   + production_IPv4_interface_id + str + success + Unique identifier of the preferred IPv4 production interface. + + +   + production_IPv6_interface_id + str + success + Unique identifier of the preferred IPv6 production interface. + + +   + smb_servers + str + success + This is the inverse of the resource type smb_server association. + + ### Authors -* Akash Shendge (@shenda1) * Arindam Datta (@dattaarindam) -------------------------------- -# NAS Server Module +# SMB Share Module -NAS Server operations on PowerStore Storage system. +Manage SMB shares on a PowerStore storage system. ### Synopsis - Supports getting the details and modifying the attributes of a NAS server. + Managing SMB Shares on PowerStore storage system includes create, get, modify, and delete the SMB shares. ### Parameters - + @@ -3979,76 +5329,108 @@ NAS Server operations on PowerStore Storage system. - + - + - + - + - + - + - + - + - + - + - + - + - + - - + + - - + + - + - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - + @@ -4056,7 +5438,7 @@ NAS Server operations on PowerStore Storage system. - + @@ -4090,214 +5472,224 @@ NAS Server operations on PowerStore Storage system. -
ParameterDescription
nas_server_name share_name str
Name of the NAS server. Mutually exclusive with nas_server_id.

Name of the SMB share.
Required during creation of the SMB share.
For all other operations either share_name or share_id is required.
nas_server_id share_id str
Unique id of the NAS server. Mutually exclusive with nas_server_name.

ID of the SMB share.
Should not be specified during creation. ID is auto generated.
For all other operations either share_name or share_id is required.
If share_id is used then no need to pass nas_server/filesystem/snapshot/ path.
description path str
Description of the NAS server.

Local path to the file system/Snapshot or any existing sub-folder of the file system/Snapshot that is shared over the network.
Path is relative to the base of the NAS server and must start with the name of the filesystem.
Required for creation of the SMB share.
nas_server_new_name filesystem str
New name of the NAS server for a rename operation.

The ID/Name of the File System.
Either filesystem or snapshot is required for creation of the SMB share.
If filesystem name is specified, then nas_server is required to uniquely identify the filesystem.
If filesystem parameter is provided, then snapshot cannot be specified.
current_node snapshot str
Unique identifier or name of the node on which the NAS server is running.

The ID/Name of the Snapshot.
Either filesystem or snapshot is required for creation of the SMB share.
If snapshot name is specified, then nas_server is required to uniquely identify the snapshot.
If snapshot parameter is provided, then filesystem cannot be specified.
SMB share can be created only if access type of snapshot is "protocol".
preferred_node nas_server str
Unique identifier or name of the preferred node for the NAS server. The initial value (on NAS server create) is taken from the current node.

The ID/Name of the NAS Server.
It is not required if share_id is used.
current_unix_directory_service description str
  • NIS
  • LDAP
  • LOCAL_FILES
  • LOCAL_THEN_NIS
  • LOCAL_THEN_LDAP

Define the Unix directory service used for looking up identity information for Unix such as UIDs, GIDs, net groups, and so on.

Description for the SMB share.
Optional parameter when creating a share.
To modify, pass the new value in description field.
default_unix_user str is_abe_enabled bool
Default Unix user name used for granting access in case of Windows to Unix user mapping failure. When empty, access in such case is denied.

Indicates whether Access-based Enumeration (ABE) for SMB share is enabled.
During creation, if not mentioned, then the default is False.
default_windows_user is_branch_cache_enabled bool
Indicates whether Branch Cache optimization for SMB share is enabled.
During creation, if not mentioned then default is False.
is_continuous_availability_enabled bool
Indicates whether continuous availability for SMB 3.0 is enabled.
During creation, if not mentioned, then the default is False.
is_encryption_enabled bool
Indicates whether encryption for SMB 3.0 is enabled at the shared folder level.
During creation, if not mentioned then default is False.
offline_availability str
  • MANUAL
  • DOCUMENTS
  • PROGRAMS
  • NONE

Defines valid states of Offline Availability.
MANUAL- Only specified files will be available offline.
DOCUMENTS- All files that users open will be available offline.
PROGRAMS- Program will preferably run from the offline cache even when connected to the network. All files that users open will be available offline.
NONE- Prevents clients from storing documents and programs in offline cache.
umask str
Default Windows user name used for granting access in case of Unix to Windows user mapping failure. When empty, access in such case is denied.

The default UNIX umask for new files created on the SMB Share.
During creation, if not mentioned, then the default is "022".
For all other operations, the default is None.
state True
  • absent
  • present

Define whether the nas server should exist or not.

Define whether the SMB share should exist or not.
present indicates that the share should exist on the system.
absent indicates that the share should not exist on the system.
array_ip
The password of the PowerStore host.
+ +### Notes +* When the ID of the filesystem/snapshot is passed then nas_server is not required. If passed, then the filesystem/snapshot should exist for the nas_server, else the task will fail. +* Multiple SMB shares can be created for the same local path. ### Examples ``` - - name: Get details of NAS Server by name - dellemc_powerstore_nasserver: - array_ip: "{{array_ip}}" - verifycert: "{{verifycert}}" - user: "{{user}}" - password: "{{password}}" - nas_server_name: "{{nas_server_name}}" - state: "present" +- name: Create SMB share for a filesystem + dellemc_powerstore_smbshare: + array_ip: "{{array_ip}}" + verifycert: "{{verifycert}}" + user: "{{user}}" + password: "{{password}}" + share_name: "sample_smb_share" + filesystem: "sample_fs" + nas_server: "{{nas_server_id}}" + path: "{{path}}" + description: "Sample SMB share created" + is_abe_enabled: True + is_branch_cache_enabled: True + offline_availability: "DOCUMENTS" + is_continuous_availability_enabled: True + is_encryption_enabled: True + state: "present" + +- name: Modify Attributes of SMB share for a filesystem + dellemc_powerstore_smbshare: + array_ip: "{{array_ip}}" + verifycert: "{{verifycert}}" + user: "{{user}}" + password: "{{password}}" + share_name: "sample_smb_share" + nas_server: "sample_nas_server" + description: "Sample SMB share attributes updated" + is_abe_enabled: False + is_branch_cache_enabled: False + offline_availability: "MANUAL" + is_continuous_availability_enabled: False + is_encryption_enabled: False + umask: "022" + state: "present" + +- name: Create SMB share for a snapshot + dellemc_powerstore_smbshare: + array_ip: "{{array_ip}}" + verifycert: "{{verifycert}}" + user: "{{user}}" + password: "{{password}}" + share_name: "sample_snap_smb_share" + snapshot: "sample_snapshot" + nas_server: "{{nas_server_id}}" + path: "{{path}}" + description: "Sample SMB share created for snapshot" + is_abe_enabled: True + is_branch_cache_enabled: True + is_continuous_availability_enabled: True + state: "present" - - name: Get Details of NAS Server by ID - dellemc_powerstore_nasserver: - array_ip: "{{array_ip}}" - verifycert: "{{verifycert}}" - user: "{{user}}" - password: "{{password}}" - nas_server_id: "{{nas_id}}" - state: "present" +- name: Modify Attributes of SMB share for a snapshot + dellemc_powerstore_smbshare: + array_ip: "{{array_ip}}" + verifycert: "{{verifycert}}" + user: "{{user}}" + password: "{{password}}" + share_name: "sample_snap_smb_share" + nas_server: "sample_nas_server" + description: "Sample SMB share attributes updated for snapshot" + is_abe_enabled: False + is_branch_cache_enabled: False + offline_availability: "MANUAL" + is_continuous_availability_enabled: False + umask: "022" + state: "present" - - name: Rename NAS Server by Name - dellemc_powerstore_nasserver: - array_ip: "{{array_ip}}" - verifycert: "{{verifycert}}" - user: "{{user}}" - password: "{{password}}" - nas_server_name: "{{nas_server_name}}" - nas_server_new_name : "{{nas_server_new_name}}" - state: "present" +- name: Get details of SMB share + dellemc_powerstore_smbshare: + array_ip: "{{array_ip}}" + verifycert: "{{verifycert}}" + user: "{{user}}" + password: "{{password}}" + share_id: "{{smb_share_id}}" + state: "present" - - name: Modify NAS Server attributes by ID - dellemc_powerstore_nasserver: - array_ip: "{{array_ip}}" - verifycert: "{{verifycert}}" - user: "{{user}}" - password: "{{password}}" - nas_server_id: "{{nas_id}}" - current_unix_directory_service: "LOCAL_FILES" - current_node: "{{cur_node_n1}}" - preferred_node: "{{prefered_node}}" - state: "present" +- name: Delete SMB share + dellemc_powerstore_smbshare: + array_ip: "{{array_ip}}" + verifycert: "{{verifycert}}" + user: "{{user}}" + password: "{{password}}" + share_id: "{{smb_share_id}}" + state: "absent" ``` ### Return Values - + - + - - + + - + - - + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + - + - - + + - + - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - + - + - + - - - - - - - - - - - - - - - - - - - - - - + - - + + - + - - + + - + - - + + - + - + - +
KeyKey Type Returned Description
changed
changed bool always Whether or not the resource has changed
nasserver_details smb_share_details complex When nas server exists Details about the nas server When share exists. The SMB share details.
  backup_IPv4_interface_id str success Unique identifier of the preferred IPv4 backup interface.
  backup_IPv6_interface_id str success Unique identifier of the preferred IPv6 backup interface.
  current_node dict success Unique identifier and name of the node on which the NAS server is running.
  current_unix_directory_service str success Define the Unix directory service used for looking up identity information for Unix such as UIDs, GIDs, net groups, and so on.
  default_unix_user str success Default Unix user name used for granting access in case of Windows to Unix user mapping failure.
  description str success Additional information about the nas server.
  file_interfaces dict success This is the inverse of the resource type file_interface association.Will return the id,name & ip_address of the associated file interface
  file_ldaps description str success This is the inverse of the resource type file_ldap association. Additional information about the share.
  file_systems dict file_system complex success This is the inverse of the resource type file_system association. Includes ID and Name of filesystem and nas server for which smb share exists.
   filesystem_type str success Type of filesystem.
   id str success ID of filesystem.
   name str success Name of filesystem.
   nas_server dict success nas_server of filesystem.
  id id str success The system generated ID given to the nas server The ID of the SMB share.
  is_username_translation_enabled is_ABE_enabled bool success Enable the possibility to match a windows account to a Unix account with different names.
  name str success Name of the nas server
  nfs_servers str success This is the inverse of the resource type nfs_server association.
  operational_status str success NAS server operational status. Whether Access Based enumeration is enforced or not
  preferred_node dict is_branch_cache_enabled bool success Unique identifier and name of the preferred node for the NAS server. Whether branch cache is enabled or not.
  production_IPv4_interface_id str is_continuous_availability_enabled bool success Unique identifier of the preferred IPv4 production interface. Whether the share will be available continuously or not.
  production_IPv6_interface_id str is_encryption_enabled bool success Unique identifier of the preferred IPv6 production interface. Whether encryption is enabled or not.
  smb_servers name str success This is the inverse of the resource type smb_server association. Name of the SMB share.
### Authors -* Arindam Datta (@dattaarindam) +* P Srinivas Rao (@srinivas-rao5) -------------------------------- -# SMB Share Module +# Snapshot Module -Manage SMB shares on a PowerStore storage system. +Manage Snapshots on Dell EMC PowerStore. ### Synopsis - Managing SMB Shares on PowerStore storage system includes create, get, modify, and delete the SMB shares. + Managing Snapshots on PowerStore storage system, Create a new Volume Group Snapshot, Get details of Volume Group Snapshot, Modify Volume Group Snapshot, Delete an existing Volume Group Snapshot, Create a new Volume Snapshot, Get details of Volume Snapshot, Modify Volume Snapshot, Delete an existing Volume Snapshot. ### Parameters - + @@ -4308,108 +5700,76 @@ Manage SMB shares on a PowerStore storage system. - - - - - - - - - - - - - - - - - - - - - - - - - + - + - + - + - + - + - + - - - - - - - - - + - - + + - + - - + + - + - - - + + - + + - + - - + + - + - + @@ -4417,7 +5777,7 @@ Manage SMB shares on a PowerStore storage system. - + @@ -4451,232 +5811,277 @@ Manage SMB shares on a PowerStore storage system. -
ParameterDescription
share_name str
Name of the SMB share.
Required during creation of the SMB share.
For all other operations either share_name or share_id is required.
share_id str
ID of the SMB share.
Should not be specified during creation. ID is auto generated.
For all other operations either share_name or share_id is required.
If share_id is used then no need to pass nas_server/filesystem/snapshot/ path.
path str
Local path to the file system/Snapshot or any existing sub-folder of the file system/Snapshot that is shared over the network.
Path is relative to the base of the NAS server and must start with the name of the filesystem.
Required for creation of the SMB share.
filesystem snapshot_name str
The ID/Name of the File System.
Either filesystem or snapshot is required for creation of the SMB share.
If filesystem name is specified, then nas_server is required to uniquely identify the filesystem.
If filesystem parameter is provided, then snapshot cannot be specified.

The name of the Snapshot. Either snapshot name or ID is required.
snapshot snapshot_id str
The ID/Name of the Snapshot.
Either filesystem or snapshot is required for creation of the SMB share.
If snapshot name is specified, then nas_server is required to uniquely identify the snapshot.
If snapshot parameter is provided, then filesystem cannot be specified.
SMB share can be created only if access type of snapshot is "protocol".

The ID of the Snapshot. Either snapshot ID or Snapshot name is required.
nas_server volume str
The ID/Name of the NAS Server.
It is not required if share_id is used.

The volume. This could be the volume name or ID.
description volume_group str
Description for the SMB share.
Optional parameter when creating a share.
To modify, pass the new value in description field.
is_abe_enabled bool
Indicates whether Access-based Enumeration (ABE) for SMB share is enabled.
During creation, if not mentioned, then the default is False.

The volume group. This could be the volume group name or ID.
is_branch_cache_enabled bool new_snapshot_name str
Indicates whether Branch Cache optimization for SMB share is enabled.
During creation, if not mentioned then default is False.

The new name of the Snapshot.
is_continuous_availability_enabled bool desired_retention str
Indicates whether continuous availability for SMB 3.0 is enabled.
During creation, if not mentioned, then the default is False.

The retention value for the Snapshot.
If the retention value is not specified, the Snapshot details would be returned.
To create a Snapshot, either a retention or expiration timestamp must be given.
If the Snapshot does not have any retention value - specify it as 'None'.
is_encryption_enabled bool retention_unit str
Indicates whether encryption for SMB 3.0 is enabled at the shared folder level.
During creation, if not mentioned then default is False.
  • hours
  • days

The unit for retention.
If this unit is not specified, 'hours' is taken as default retention_unit.
If desired_retention is specified, expiration_timestamp cannot be specified.
offline_availability expiration_timestamp str
  • MANUAL
  • DOCUMENTS
  • PROGRAMS
  • NONE

Defines valid states of Offline Availability.
MANUAL- Only specified files will be available offline.
DOCUMENTS- All files that users open will be available offline.
PROGRAMS- Program will preferably run from the offline cache even when connected to the network. All files that users open will be available offline.
NONE- Prevents clients from storing documents and programs in offline cache.

The expiration timestamp of the Snapshot. This should be provided in UTC format, e.g 2019-07-24T10:54:54Z.
umask description str
The default UNIX umask for new files created on the SMB Share.
During creation, if not mentioned, then the default is "022".
For all other operations, the default is None.

The description for the Snapshot.
state True
  • absent
  • present

Define whether the SMB share should exist or not.
present indicates that the share should exist on the system.
absent indicates that the share should not exist on the system.

Defines whether the Snapshot should exist or not.
array_ip
The password of the PowerStore host.
+ -### Notes -* When the ID of the filesystem/snapshot is passed then nas_server is not required. If passed, then the filesystem/snapshot should exist for the nas_server, else the task will fail. -* Multiple SMB shares can be created for the same local path. ### Examples ``` -- name: Create SMB share for a filesystem - dellemc_powerstore_smbshare: - array_ip: "{{array_ip}}" - verifycert: "{{verifycert}}" - user: "{{user}}" - password: "{{password}}" - share_name: "sample_smb_share" - filesystem: "sample_fs" - nas_server: "{{nas_server_id}}" - path: "{{path}}" - description: "Sample SMB share created" - is_abe_enabled: True - is_branch_cache_enabled: True - offline_availability: "DOCUMENTS" - is_continuous_availability_enabled: True - is_encryption_enabled: True - state: "present" + - name: Create a volume snapshot on PowerStore + dellemc_powerstore_snapshot: + array_ip: "{{mgmt_ip}}" + verifycert: "{{verifycert}}" + user: "{{user}}" + password: "{{password}}" + snapshot_name: "{{snapshot_name}}" + volume: "{{volume}}" + description: "{{description}}" + desired_retention: "{{desired_retention}}" + retention_unit: "{{retention_unit_days}}" + state: "{{state_present}}" -- name: Modify Attributes of SMB share for a filesystem - dellemc_powerstore_smbshare: - array_ip: "{{array_ip}}" - verifycert: "{{verifycert}}" - user: "{{user}}" - password: "{{password}}" - share_name: "sample_smb_share" - nas_server: "sample_nas_server" - description: "Sample SMB share attributes updated" - is_abe_enabled: False - is_branch_cache_enabled: False - offline_availability: "MANUAL" - is_continuous_availability_enabled: False - is_encryption_enabled: False - umask: "022" - state: "present" + - name: Get details of a volume snapshot + dellemc_powerstore_snapshot: + array_ip: "{{mgmt_ip}}" + verifycert: "{{verifycert}}" + user: "{{user}}" + password: "{{password}}" + snapshot_name: "{{snapshot_name}}" + volume: "{{volume}}" + state: "{{state_present}}" -- name: Create SMB share for a snapshot - dellemc_powerstore_smbshare: - array_ip: "{{array_ip}}" - verifycert: "{{verifycert}}" - user: "{{user}}" - password: "{{password}}" - share_name: "sample_snap_smb_share" - snapshot: "sample_snapshot" - nas_server: "{{nas_server_id}}" - path: "{{path}}" - description: "Sample SMB share created for snapshot" - is_abe_enabled: True - is_branch_cache_enabled: True - is_continuous_availability_enabled: True - state: "present" + - name: Rename volume snapshot + dellemc_powerstore_snapshot: + array_ip: "{{mgmt_ip}}" + verifycert: "{{verifycert}}" + user: "{{user}}" + password: "{{password}}" + snapshot_name: "{{snapshot_name}}" + new_snapshot_name: "{{new_snapshot_name}}" + volume: "{{volume}}" + state: "{{state_present}}" -- name: Modify Attributes of SMB share for a snapshot - dellemc_powerstore_smbshare: - array_ip: "{{array_ip}}" - verifycert: "{{verifycert}}" - user: "{{user}}" - password: "{{password}}" - share_name: "sample_snap_smb_share" - nas_server: "sample_nas_server" - description: "Sample SMB share attributes updated for snapshot" - is_abe_enabled: False - is_branch_cache_enabled: False - offline_availability: "MANUAL" - is_continuous_availability_enabled: False - umask: "022" - state: "present" + - name: Delete volume snapshot + dellemc_powerstore_snapshot: + array_ip: "{{mgmt_ip}}" + verifycert: "{{verifycert}}" + user: "{{user}}" + password: "{{password}}" + snapshot_name: "{{new_snapshot_name}}" + volume: "{{volume}}" + state: "{{state_absent}}" -- name: Get details of SMB share - dellemc_powerstore_smbshare: - array_ip: "{{array_ip}}" - verifycert: "{{verifycert}}" - user: "{{user}}" - password: "{{password}}" - share_id: "{{smb_share_id}}" - state: "present" + - name: Create a volume group snapshot on PowerStore + dellemc_powerstore_snapshot: + array_ip: "{{mgmt_ip}}" + verifycert: "{{verifycert}}" + user: "{{user}}" + password: "{{password}}" + snapshot_name: "{{snapshot_name}}" + volume_group: "{{volume_group}}" + description: "{{description}}" + expiration_timestamp: "{{expiration_timestamp}}" + state: "{{state_present}}" -- name: Delete SMB share - dellemc_powerstore_smbshare: - array_ip: "{{array_ip}}" - verifycert: "{{verifycert}}" - user: "{{user}}" - password: "{{password}}" - share_id: "{{smb_share_id}}" - state: "absent" + - name: Get details of a volume group snapshot + dellemc_powerstore_snapshot: + array_ip: "{{mgmt_ip}}" + verifycert: "{{verifycert}}" + user: "{{user}}" + password: "{{password}}" + snapshot_name: "{{snapshot_name}}" + volume_group: "{{volume_group}}" + state: "{{state_present}}" + + - name: Modify volume group snapshot expiration timestamp + dellemc_powerstore_snapshot: + array_ip: "{{mgmt_ip}}" + verifycert: "{{verifycert}}" + user: "{{user}}" + password: "{{password}}" + snapshot_name: "{{snapshot_name}}" + volume_group: "{{volume_group}}" + description: "{{description}}" + expiration_timestamp: "{{expiration_timestamp_new}}" + state: "{{state_present}}" + + - name: Rename volume group snapshot + dellemc_powerstore_snapshot: + array_ip: "{{mgmt_ip}}" + verifycert: "{{verifycert}}" + user: "{{user}}" + password: "{{password}}" + snapshot_name: "{{snapshot_name}}" + new_snapshot_name: "{{new_snapshot_name}}" + volume_group: "{{volume_group}}" + state: "{{state_present}}" + + - name: Delete volume group snapshot + dellemc_powerstore_snapshot: + array_ip: "{{mgmt_ip}}" + verifycert: "{{verifycert}}" + user: "{{user}}" + password: "{{password}}" + snapshot_name: "{{new_snapshot_name}}" + volume_group: "{{volume_group}}" + state: "{{state_absent}}" ``` ### Return Values - + - + - - + + + + + + + + + + + + + + + + + + + + - - + + - + + + + + + + + + + + + + + + + + + + - - + + - + - + - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - + - - - - - - - - - - - - - - - - - + - - - - - - - - - + - + - - - - - - - - + - - + + - + - - + + - + - - + + - + - - + + - + -
KeyKey Type Returned Description
changed
changed bool always Whether or not the resource has changed
create_vg_snap bool When value exists A boolean flag to indicate whether volume group snapshot got created.
create_vol_snap bool When value exists A boolean flag to indicate whether volume snapshot got created.
delete_vg_snap bool always Whether or not the resource has changed When value exists A boolean flag to indicate whether volume group snapshot got deleted.
smb_share_details delete_vol_snap bool When value exists A boolean flag to indicate whether volume snapshot got deleted.
modify_vg_snap bool When value exists A boolean flag to indicate whether volume group snapshot got modified.
modify_vol_snap bool When value exists A boolean flag to indicate whether volume snapshot got modified.
snap_details complex When share exists. The SMB share details. When snapshot exists Details of the snapshot
  description creation_timestamp str success Additional information about the share. The creation timestamp of the snapshot.
  file_system description str success Description about the snapshot.
  id str success The system generated ID given to the snapshot.
  name str success Name of the snapshot.
  performance_policy_id str success The performance policy for the snapshot.
  protection_data complex success Includes ID and Name of filesystem and nas server for which smb share exists. The protection data of the snapshot.
    filesystem_type str success Type of filesystem.
   id str success ID of filesystem.
   name expiration_timestamp str success Name of filesystem.
   nas_server dict success nas_server of filesystem. The expiration timestamp of the snapshot.
  id protection_policy_id str success The ID of the SMB share.
  is_ABE_enabled bool success Whether Access Based enumeration is enforced or not The protection policy of the snapshot.
  is_branch_cache_enabled bool size int success Whether branch cache is enabled or not. Size of the snapshot.
  is_continuous_availability_enabled bool state str success Whether the share will be available continuously or not The state of the snapshot.
  is_encryption_enabled bool type str success Whether encryption is enabled or not The type of the snapshot.
  name str volumes complex success Name of the SMB share. The volumes details of the volume group snapshot.
+ +   +   + id + str + success + The system generated ID given to the volume associated with the volume group. + + ### Authors -* P Srinivas Rao (@srinivas-rao5) +* Rajshree Khare (@khareRajshree) +* Prashant Rakheja (@prashant-dell) -------------------------------- -# Snapshot Module +# Replication Rule Module -Manage Snapshots on Dell EMC PowerStore. +Replication rule operations on a PowerStore storage system. ### Synopsis - Managing Snapshots on PowerStore. - Create a new Volume Group Snapshot. - Get details of Volume Group Snapshot. - Modify Volume Group Snapshot - Delete an existing Volume Group Snapshot. - Create a new Volume Snapshot. - Get details of Volume Snapshot. - Modify Volume Snapshot. - Delete an existing Volume Snapshot. + Performs all replication rule operations on a PowerStore Storage System. This module supports get details of an existing replication rule. Create new replication rule for all supported parameters. Modify replication rule with supported parameters. Delete a specific replication rule. ### Parameters - + @@ -4687,84 +6092,60 @@ Manage Snapshots on Dell EMC PowerStore. - - - - - - - - - - - - - - - - - - - - - - - - - + - + - + - + - + - + - + - - + + - - + + - + - + - + - - + + @@ -4803,279 +6184,131 @@ Manage Snapshots on Dell EMC PowerStore. ### Examples ``` - - name: Create a volume snapshot on PowerStore - dellemc_powerstore_snapshot: - array_ip: "{{mgmt_ip}}" - verifycert: "{{verifycert}}" - user: "{{user}}" - password: "{{password}}" - snapshot_name: "{{snapshot_name}}" - volume: "{{volume}}" - description: "{{description}}" - desired_retention: "{{desired_retention}}" - retention_unit: "{{retention_unit_days}}" - state: "{{state_present}}" - - - name: Get details of a volume snapshot - dellemc_powerstore_snapshot: - array_ip: "{{mgmt_ip}}" - verifycert: "{{verifycert}}" - user: "{{user}}" - password: "{{password}}" - snapshot_name: "{{snapshot_name}}" - volume: "{{volume}}" - state: "{{state_present}}" - - - name: Rename volume snapshot - dellemc_powerstore_snapshot: - array_ip: "{{mgmt_ip}}" - verifycert: "{{verifycert}}" - user: "{{user}}" - password: "{{password}}" - snapshot_name: "{{snapshot_name}}" - new_snapshot_name: "{{new_snapshot_name}}" - volume: "{{volume}}" - state: "{{state_present}}" - - - name: Delete volume snapshot - dellemc_powerstore_snapshot: - array_ip: "{{mgmt_ip}}" - verifycert: "{{verifycert}}" - user: "{{user}}" - password: "{{password}}" - snapshot_name: "{{new_snapshot_name}}" - volume: "{{volume}}" - state: "{{state_absent}}" - - - name: Create a volume group snapshot on PowerStore - dellemc_powerstore_snapshot: - array_ip: "{{mgmt_ip}}" - verifycert: "{{verifycert}}" - user: "{{user}}" - password: "{{password}}" - snapshot_name: "{{snapshot_name}}" - volume_group: "{{volume_group}}" - description: "{{description}}" - expiration_timestamp: "{{expiration_timestamp}}" - state: "{{state_present}}" - - - name: Get details of a volume group snapshot - dellemc_powerstore_snapshot: - array_ip: "{{mgmt_ip}}" - verifycert: "{{verifycert}}" - user: "{{user}}" - password: "{{password}}" - snapshot_name: "{{snapshot_name}}" - volume_group: "{{volume_group}}" - state: "{{state_present}}" +- name: Create new replication rule + dellemc_powerstore_replicationrule: + array_ip: "{{array_ip}}" + verifycert: "{{verifycert}}" + user: "{{user}}" + password: "{{password}}" + replication_rule_name: "sample_replication_rule" + rpo: "Five_Minutes" + alert_threshold: "15" + remote_system: "WN-D8877" + state: "present" - - name: Modify volume group snapshot expiration timestamp - dellemc_powerstore_snapshot: - array_ip: "{{mgmt_ip}}" - verifycert: "{{verifycert}}" - user: "{{user}}" - password: "{{password}}" - snapshot_name: "{{snapshot_name}}" - volume_group: "{{volume_group}}" - description: "{{description}}" - expiration_timestamp: "{{expiration_timestamp_new}}" - state: "{{state_present}}" +- name: Modify existing replication rule + dellemc_powerstore_replicationrule: + array_ip: "{{array_ip}}" + verifycert: "{{verifycert}}" + user: "{{user}}" + password: "{{password}}" + replication_rule_name: "sample_replication_rule" + new_name: "new_sample_replication_rule" + rpo: "One_Hour" + alert_threshold: "60" + remote_system: "WN-D0517" + state: "present" - - name: Rename volume group snapshot - dellemc_powerstore_snapshot: - array_ip: "{{mgmt_ip}}" - verifycert: "{{verifycert}}" - user: "{{user}}" - password: "{{password}}" - snapshot_name: "{{snapshot_name}}" - new_snapshot_name: "{{new_snapshot_name}}" - volume_group: "{{volume_group}}" - state: "{{state_present}}" +- name: Get details of replication rule + dellemc_powerstore_replicationrule: + array_ip: "{{array_ip}}" + verifycert: "{{verifycert}}" + user: "{{user}}" + password: "{{password}}" + replication_rule_id: "{{id}}" + state: "present" - - name: Delete volume group snapshot - dellemc_powerstore_snapshot: - array_ip: "{{mgmt_ip}}" - verifycert: "{{verifycert}}" - user: "{{user}}" - password: "{{password}}" - snapshot_name: "{{new_snapshot_name}}" - volume_group: "{{volume_group}}" - state: "{{state_absent}}" +- name: Delete an existing replication rule + dellemc_powerstore_replicationrule: + array_ip: "{{array_ip}}" + verifycert: "{{verifycert}}" + user: "{{user}}" + password: "{{password}}" + replication_rule_name: "new_sample_replication_rule" + state: "absent" ``` ### Return Values - +
ParameterDescription
snapshot_name str
The name of the Snapshot. Either snapshot name or ID is required.
snapshot_id str
The ID of the Snapshot. Either snapshot ID or Snapshot name is required.
volume str
The volume. This could be the volume name or ID.
volume_group replication_rule_name str
The volume group. This could be the volume group name or ID.

Name of the replication rule.
Required during creation of a replication rule.
replication_rule_name and replication_rule_id are mutually exclusive.
new_snapshot_name replication_rule_id str
The new name of the Snapshot.

ID of the replication rule.
ID for the rule is autogenerated, cannot be passed during creation of a replication rule.
replication_rule_name and replication_rule_id are mutually exclusive.
desired_retention new_name str
The retention value for the Snapshot.
If the retention value is not specified, the Snapshot details would be returned.
To create a Snapshot, either a retention or expiration timestamp must be given.
If the Snapshot does not have any retention value - specify it as 'None'.

New name of the replication rule.
Used for renaming a replication rule.
retention_unit rpo str
  • hours
  • days

The unit for retention.
If this unit is not specified, 'hours' is taken as default retention_unit.
If desired_retention is specified, expiration_timestamp cannot be specified.
  • Five_Minutes
  • Fifteen_Minutes
  • Thirty_Minutes
  • One_Hour
  • Six_Hours
  • Twelve_Hours
  • One_Day

Recovery point objective (RPO), which is the acceptable amount of data, measured in units of time, that may be lost in case of a failure.
expiration_timestamp str alert_threshold int
The expiration timestamp of the Snapshot. This should be provided in UTC format, e.g 2019-07-24T10:54:54Z.

Acceptable delay between the expected and actual replication sync intervals. The system generates an alert if the delay between the expected and actual sync exceeds this threshold.
During creation, if not passed, then by default one RPO in minutes will be passed.
The range of integers supported are in between 0 and 1440 (inclusive of both).
description remote_system str
The description for the Snapshot.

ID or name of the remote system to which this rule will replicate the associated resources.
state str True
  • absent
  • present

Defines whether the Snapshot should exist or not.
  • present
  • absent

The state of the replication rule after the task is performed.
For Delete operation only, it should be set to "absent".
For all Create, Modify or Get details operations it should be set to "present".
array_ip
- + - + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + - + - - + + - - - - - - - - - - - - - - - - + + - + - + - + - + - + - - - - - - - - - - - - - - - - + - - - - - - - - + - + - + - + - - - - - - - - + - - - - - - - - -
KeyKey Type Returned Description
changed changed bool always Whether or not the resource has changed
create_vg_snap bool When value exists A boolean flag to indicate whether volume group snapshot got created
create_vol_snap bool When value exists A boolean flag to indicate whether volume snapshot got created
delete_vg_snap bool When value exists A boolean flag to indicate whether volume group snapshot got deleted
delete_vol_snap bool When value exists A boolean flag to indicate whether volume snapshot got deleted
modify_vg_snap bool When value exists A boolean flag to indicate whether volume group snapshot got modified
modify_vol_snap bool When value exists A boolean flag to indicate whether volume snapshot got modified Whether or not the resource has changed.
snap_details replication_rule_details complex When snapshot exists Details of the snapshot When replication rule exists Details of the replication rule.
  creation_timestamp str success The creation timestamp of the snapshot
  description str success Description about the snapshot
  id str alert_threshold int success The system generated ID given to the snapshot Acceptable delay in minutes between the expected and actual replication sync intervals.
  name id str success Name of the snapshot The system generated ID of the replication rule.
  performance_policy_id name str success The performance policy for the snapshot Name of the replication rule.
  protection_data complex success The protection data of the snapshot
   expiration_timestamp str success The expiration timestamp of the snapshot
  protection_policy_id remote_system_id str success The protection policy of the snapshot
  size int success Size of the snapshot Unique identifier of the remote system to which this rule will replicate the associated resources.
  state remote_system_name str success The state of the snapshot Name of the remote system to which this rule will replicate the associated resources.
  type rpo str success The type of the snapshot
  volumes complex success The volumes details of the volume group snapshot Recovery point objective (RPO), which is the acceptable amount of data, measured in units of time, that may be lost in case of a failure.
   id str success The system generated ID given to the volume associated with the volume group
+ ### Authors -* Rajshree Khare (@khareRajshree) -* Prashant Rakheja (@prashant-dell) +* P Srinivas Rao (@srinivas-rao5) -------------------------------- -# Replication Rule Module +# Network Module -Replication rule operations on a PowerStore storage system. +Manage networks on Dell EMC PowerStore. ### Synopsis - Performs all replication rule operations on a PowerStore Storage System. - This module supports get details of an existing replication rule. - Create new replication rule for all supported parameters. - Modify replication rule with supported parameters. - Delete a specific replication rule. + Managing networks on PowerStore Storage System includes getting details of network, modifying attributes of network and adding/removing IP ports to/from storage network. ### Parameters - + - + @@ -5083,63 +6316,189 @@ Replication rule operations on a PowerStore storage system. - + - + - + - + - + + + + + + + + + - + - + + + + + + + + + - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - + + + + + + + + + + + - + + + + + + + + + + - - - + + + + + + + + + + + + - + - + - - + + - + @@ -5147,7 +6506,7 @@ Replication rule operations on a PowerStore storage system. - + @@ -5155,7 +6514,7 @@ Replication rule operations on a PowerStore storage system. - + @@ -5163,141 +6522,404 @@ Replication rule operations on a PowerStore storage system. - + -
ParameterParameter Type Required DefaultDescription
replication_rule_name network_name str
Name of the replication rule.
Required during creation of a replication rule.
replication_rule_name and replication_rule_id are mutually exclusive.

The name of the network.
This parameter is added in 2.0.0.0.
Specify either network_name or network_id for any operation.
replication_rule_id network_id str
ID of the replication rule.
ID for the rule is autogenerated, cannot be passed during creation of a replication rule.
replication_rule_name and replication_rule_id are mutually exclusive.

The ID of the network.
new_name vlan_id int
The ID of the VLAN.
gateway str
New name of the replication rule.
Used for renaming a replication rule.

Network gateway in IPv4 or IPv6 format, corresponding to the network's IP version.
Specify empty string to remove the gateway.
rpo prefix_length int
Network prefix length.
new_cluster_mgmt_address str
  • Five_Minutes
  • Fifteen_Minutes
  • Thirty_Minutes
  • One_Hour
  • Six_Hours
  • Twelve_Hours
  • One_Day

Recovery point objective (RPO), which is the acceptable amount of data, measured in units of time, that may be lost in case of a failure.

New cluster management IP address in IPv4 or IPv6 format, corresponding to the network's IP version.
storage_discovery_address str
New storage discovery IP address in IPv4 or IPv6 format, corresponding to the network's IP version.
Specify empty string to remove the storage discovery IP address.
mtu int
Maximum Transmission Unit (MTU) packet size set on network interfaces, in bytes.
new_name str
New name of the network.
addresses list
elements: dict

IP addresses to add/remove in IPv4 or IPv6 format.
  current_address str
Existing IPv4/IPv6 address.
  new_address str
New IPv4/IPv6 address.
ports list
elements: str

Ports to be mapped/unmapped to/from the storage network.
port_state str
  • present-in-network
  • absent-in-network

Specifies whether port should mapped/unmapped from the storage network.
vasa_provider_credentials dict
Credentials required for re-registering the VASA vendor provider during the reconfiguration of the cluster management IP address.
  username str True
VASA vendor provider user name.
alert_threshold int   password str True
VASA vendor provider password.
esxi_credentials list
elements: dict

Acceptable delay between the expected and actual replication sync intervals. The system generates an alert if the delay between the expected and actual sync exceeds this threshold.
During creation, if not passed, then by default one RPO in minutes will be passed.
The range of integers supported are in between 0 and 1440 (inclusive of both).

Credentials required for re-registering the ESXi hosts in the vCenter.
It should be passed only when ESXi host addresses or management network VLAN / prefix / gateway are changed during the reconfiguration of the PowerStore X model appliances.
This parameter is applicable only for PowerStore X model.
This parameter will be ignored if passed for PowerStore T model.
  node_id str True
Node identifier corresponding to the ESXi host.
remote_system str   password str True
ESXi host root password.
wait_for_completion bool False
ID or name of the remote system to which this rule will replicate the associated resources.

Flag to indicate if the operation should be run synchronously or asynchronously. True signifies synchronous execution. By default, modify operation will run asynchronously.
state state str True
  • present
  • absent

The state of the replication rule after the task is performed.
For Delete operation only, it should be set to "absent".
For all Create, Modify or Get details operations it should be set to "present".
  • absent
  • present

Define whether the network exist or not.
array_ip array_ip str True
IP or FQDN of the PowerStore management system.
verifycert verifycert bool True
Boolean variable to specify whether to validate SSL certificate or not.
True - indicates that the SSL certificate should be verified. Set the environment variable REQUESTS_CA_BUNDLE to the path of the SSL certificate.
False - indicates that the SSL certificate should not be verified.
user user str True
The username of the PowerStore host.
password password str True
The password of the PowerStore host.
+ +### Notes +* It is recommended to perform task asynchronously while changing cluster management address. +* Idempotency is not supported for vasa_provider_credentials and esxi_credentials. +* For PowerStore X model, vasa_provider_credentials has to be specified along with new_cluster_mgmt_address. ### Examples ``` -- name: Create new replication rule - dellemc_powerstore_replicationrule: +- name: Get network details using ID + dellemc_powerstore_network: array_ip: "{{array_ip}}" verifycert: "{{verifycert}}" user: "{{user}}" password: "{{password}}" - replication_rule_name: "sample_replication_rule" - rpo: "Five_Minutes" - alert_threshold: "15" - remote_system: "WN-D8877" + network_id: "NW1" state: "present" -- name: Modify existing replication rule - dellemc_powerstore_replicationrule: +- name: Get network details using name + dellemc_powerstore_network: array_ip: "{{array_ip}}" verifycert: "{{verifycert}}" user: "{{user}}" password: "{{password}}" - replication_rule_name: "sample_replication_rule" - new_name: "new_sample_replication_rule" - rpo: "One_Hour" - alert_threshold: "60" - remote_system: "WN-D0517" + network_name: "Default Management Network" state: "present" -- name: Get details of replication rule - dellemc_powerstore_replicationrule: +- name: Rename the storage network + dellemc_powerstore_network: array_ip: "{{array_ip}}" verifycert: "{{verifycert}}" user: "{{user}}" password: "{{password}}" - replication_rule_id: "{{id}}" + network_name: "Default Storage Network" + new_name: "iSCSI Network" + wait_for_completion: True state: "present" -- name: Delete an existing replication rule - dellemc_powerstore_replicationrule: +- name: Replace the IP's in the management network and re-register VASA vendor provider + dellemc_powerstore_network: array_ip: "{{array_ip}}" verifycert: "{{verifycert}}" user: "{{user}}" password: "{{password}}" - replication_rule_name: "new_sample_replication_rule" - state: "absent" + network_id: "NW1" + addresses: + - current_address: "100.230.x.x" + new_address: "100.230.x.x" + - current_address: "100.230.x.x" + new_address: "100.230.x.x" + - current_address: "100.230.x.x" + new_address: "100.230.x.x" + new_cluster_mgmt_address: "100.230.x.x" + vasa_provider_credentials: + username: "vmadmin" + password: "{{vm_password}}" + state: "present" + +- name: Map port to the storage network + dellemc_powerstore_network: + array_ip: "{{array_ip}}" + verifycert: "{{verifycert}}" + user: "{{user}}" + password: "{{password}}" + network_id: "NW6" + ports: + - "IP1" + port_state: "present-in-network" + state: "present" + +- name: Unmap port from the storage network + dellemc_powerstore_network: + array_ip: "{{array_ip}}" + verifycert: "{{verifycert}}" + user: "{{user}}" + password: "{{password}}" + network_id: "NW6" + ports: + - "IP1" + port_state: "absent-in-network" + state: "present" + +- name: Replace the IP's in the management network and re-register VASA vendor + provider for X model + dellemc_powerstore_network: + array_ip: "{{array_ip1}}" + verifycert: "{{verifycert}}" + user: "{{user}}" + password: "{{password}}" + network_id: "NW1" + vlan_id: 0 + gateway: "100.231.x.x" + mtu: 1500 + prefix_length: 24 + addresses: + - current_address: "100.230.x.x" + new_address: "100.231.x.x" + - current_address: "100.230.x.x" + new_address: "100.231.x.x" + - current_address: "100.230.x.x" + new_address: "100.231.x.x" + - current_address: "100.230.x.x" + new_address: "100.231.x.x" + - current_address: "100.230.x.x" + new_address: "100.231.x.x" + new_cluster_mgmt_address: "100.231.x.x" + vasa_provider_credentials: + username: "vmadmin" + password: "{{vm_password}}" + esxi_credentials: + - "node_id": "N1" + "password": "{{node_password}}" + - "node_id": "N2" + "password": "{{node_password}}" + state: "present" ``` ### Return Values - + - + - - + + - + - + - - + + - - + + - + - + + + + + + + - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - + - + - + - + - + - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - + - + + + + + + + + + + + + + + + - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
KeyKey Type Returned Description
changed
changed bool always Whether or not the resource has changed Whether or not the resource has changed.
replication_rule_details job_details complex When replication rule exists Details of the replication rule When asynchronous task is performed. The job details.
  alert_threshold int id str success Acceptable delay in minutes between the expected and actual replication sync intervals. The ID of the job.
network_details complex When network exists. The network details.
  id cluster_details complex success The details of the cluster.
   appliance_count int success Number of appliances configured in this cluster.
   id str success The unique identifier of the cluster.
   management_address str success The floating management IP address for the cluster in IPv4 or IPv6 format.
   name str success The name of the cluster.
   storage_discovery_address str success The floating storage discovery IP address for the cluster in IPv4 or IPv6 format.
  gateway str success The system generated ID of the replication rule The gateway of the network.
  name id str success Name of the replication rule The ID of the network.
  remote_system_id ip_version str success Unique identifier of the remote system to which this rule will replicate the associated resources. IP protocol version
  remote_system_name member_ips complex success Properties of the IP pool address.
   address str success IP address value, in IPv4 or IPv6 format.
   appliance_id str success Unique identifier of the appliance to which the IP address belongs.
   id str success Unique identifier of the IP address.
   ip_port_id str success Unique identifier of the port that uses this IP address to provide access to storage network services, such as iSCSI. This attribute can be set only for an IP address used by networks of type Storage.
   name str success Name of the IP address.
   network_id str success Unique identifier of the network to which the IP address belongs.
   node_id str success Unique identifier of the cluster node to which the IP address belongs.
   purposes list success IP address purposes.
  mtu int success Maximum Transmission Unit (MTU) packet size set on network interfaces, in bytes.
  name str success Name of the remote system to which this rule will replicate the associated resources. The name of the network.
  rpo prefix_length int success Network prefix length.
  purposes list success Purposes of the network.
  type str success Recovery point objective (RPO), which is the acceptable amount of data, measured in units of time, that may be lost in case of a failure. Network type
  vcenter_details complex success Details of the vcenter.
   address str success IP address of vCenter host, in IPv4, IPv6, or hostname format.
   id str success Unique identifier of the vCenter instance.
   instance_uuid str success UUID instance of the vCenter.
   username str success User name to login to vCenter.
   vendor_provider_status str success General status of the VASA vendor provider in vCenter.
  vlan_id int success VLAN identifier.
### Authors -* P Srinivas Rao (@srinivas-rao5) +* Akash Shendge (@shenda1) -------------------------------- # Protection Policy Module -Perform Protection policy operations on PowerStore storage system +Perform Protection policy operations on PowerStore storage system. ### Synopsis - Performs all protection policy operations on PowerStore Storage System. - This modules supports get details of an existing protection policy. - Create new protection policy with existing Snapshot Rule or replication rule. - Modify protection policy to change the name and description, and add or remove existing snapshot rules/ replication rule. - Delete an existing protection policy. + Performs all protection policy operations on PowerStore Storage System. This modules supports get details of an existing protection policy. Create new protection policy with existing Snapshot Rule or replication rule. Modify protection policy to change the name and description, and add or remove existing snapshot rules/ replication rule. Delete an existing protection policy. ### Parameters @@ -5505,7 +7127,7 @@ Perform Protection policy operations on PowerStore storage system changed bool always - Whether or not the resource has changed + Whether or not the resource has changed. protectionpolicy_details @@ -5518,28 +7140,28 @@ Perform Protection policy operations on PowerStore storage system description str success - description about the protection policy + description about the protection policy.   id str success - The system generated ID given to the protection policy + The system generated ID given to the protection policy.   name str success - Name of the protection policy + Name of the protection policy.   replication_rules complex success - The replication rule details of the protection policy + The replication rule details of the protection policy.   @@ -5547,7 +7169,7 @@ Perform Protection policy operations on PowerStore storage system id str success - The replication rule ID of the protection policy + The replication rule ID of the protection policy.   @@ -5555,14 +7177,14 @@ Perform Protection policy operations on PowerStore storage system name str success - The replication rule name of the protection policy + The replication rule name of the protection policy.   snapshot_rules complex success - The snapshot rules details of the protection policy + The snapshot rules details of the protection policy.   @@ -5570,7 +7192,7 @@ Perform Protection policy operations on PowerStore storage system id str success - The snapshot rule ID of the protection policy + The snapshot rule ID of the protection policy.   @@ -5578,7 +7200,7 @@ Perform Protection policy operations on PowerStore storage system name str success - The snapshot rule name of the protection policy + The snapshot rule name of the protection policy.   @@ -5897,3 +7519,276 @@ Manage Filesystem Snapshots on Dell EMC PowerStore * Akash Shendge (@shenda1) -------------------------------- +# Host Module + +Manage host on PowerStore storage system. + +### Synopsis + Managing host on PowerStore storage system includes create host with a set of initiators, add/remove initiators from host, rename host and delete host. + +### Parameters + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
ParameterTypeRequiredDefaultChoicesDescription
host_name str
The host name. This value must contain 128 or fewer printable Unicode characters.
Creation of an empty host is not allowed.
Required when creating a host.
Use either host_id or host_name for modify and delete tasks.
host_id str
The 36 character long host id automatically generated when a host is created.
Use either host_id or host_name for modify and delete tasks.
host_id cannot be used while creating host, as it is generated by the array after creation of host.
os_type str
  • Windows
  • Linux
  • ESXi
  • AIX
  • HP-UX
  • Solaris

Operating system of the host.
Required when creating a host
OS type cannot be modified for a given host.
initiators list
elements: str

List of Initiator WWN or IQN to be added or removed from the host.
Subordinate initiators in a host can only be of one type, either FC or iSCSI.
Required when creating a host.
state str True
  • absent
  • present

Define whether the host should exist or not.
present - indicates that the host should exist in system.
absent - indicates that the host should not exist in system.
initiator_state str
  • present-in-host
  • absent-in-host

Define whether the initiators should be present or absent in host.
present-in-host - indicates that the initiators should exist on host.
absent-in-host - indicates that the initiators should not exist on host.
Required when creating a host with initiators or adding/removing initiators to/from existing host.
new_name str
The new name of host for renaming function. This value must contain 128 or fewer printable Unicode characters.
Cannot be specified when creating a host.
array_ip str True
IP or FQDN of the PowerStore management system.
verifycert bool True
  • True
  • False

Boolean variable to specify whether to validate SSL certificate or not.
True - indicates that the SSL certificate should be verified. Set the environment variable REQUESTS_CA_BUNDLE to the path of the SSL certificate.
False - indicates that the SSL certificate should not be verified.
user str True
The username of the PowerStore host.
password str True
The password of the PowerStore host.
+ +### Notes +* Only completely and correctly configured iSCSI initiators can be associated with a host. + +### Examples +``` + - name: Create host + dellemc_powerstore_host: + array_ip: "{{array_ip}}" + verifycert: "{{verifycert}}" + user: "{{user}}" + password: "{{password}}" + host_name: "{{host_name}}" + os_type: 'Windows' + initiators: + -21:00:00:24:ff:31:e9:fc + state: 'present' + initiator_state: 'present-in-host' + + - name: Get host details by name + dellemc_powerstore_host: + array_ip: "{{array_ip}}" + verifycert: "{{verifycert}}" + user: "{{user}}" + password: "{{password}}" + host_name: "{{host_name}}" + state: 'present' + + - name: Get host details by id + dellemc_powerstore_host: + array_ip: "{{array_ip}}" + verifycert: "{{verifycert}}" + user: "{{user}}" + password: "{{password}}" + host_id: "{{host_id}}" + state: 'present' + + - name: Add initiators to host + dellemc_powerstore_host: + array_ip: "{{array_ip}}" + verifycert: "{{verifycert}}" + user: "{{user}}" + password: "{{password}}" + host_name: "{{host_name}}" + initiators: + -21:00:00:24:ff:31:e9:ee + initiator_state: 'present-in-host' + state: 'present' + + - name: Remove initiators from host + dellemc_powerstore_host: + array_ip: "{{array_ip}}" + verifycert: "{{verifycert}}" + user: "{{user}}" + password: "{{password}}" + host_name: "{{host_name}}" + initiators: + -21:00:00:24:ff:31:e9:ee + initiator_state: 'absent-in-host' + state: 'present' + + - name: Rename host + dellemc_powerstore_host: + array_ip: "{{array_ip}}" + verifycert: "{{verifycert}}" + user: "{{user}}" + password: "{{password}}" + host_name: "{{host_name}}" + new_name: "{{new_host_name}}" + state: 'present' + + - name: Delete host + dellemc_powerstore_host: + array_ip: "{{array_ip}}" + verifycert: "{{verifycert}}" + user: "{{user}}" + password: "{{password}}" + host_name: "{{new_host_name}}" + state: 'absent' +``` + +### Return Values + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
KeyTypeReturnedDescription
changed bool always Whether or not the resource has changed.
host_details complex When host exists Details of the host.
  description str success Description about the host.
  host_group_id str success The host group ID of host.
  host_initiators complex success The initiator details of this host.
   port_name str success Name of the port.
   port_type str success The type of the port.
  id str success The system generated ID given to the host.
  name str success Name of the host.
  os_type str success The os type of the host.
+ +### Authors +* Manisha Agrawal (@agrawm3) + +-------------------------------- diff --git a/docs/Release Notes.md b/docs/Release Notes.md index 51289db..7d028ef 100644 --- a/docs/Release Notes.md +++ b/docs/Release Notes.md @@ -1,6 +1,6 @@ **Ansible Modules for Dell EMC PowerStore** ========================================= -### Release Notes 1.2.0 +### Release Notes 1.3.0 > © 2021 Dell Inc. or its subsidiaries. All rights reserved. Dell, > EMC, and other trademarks are trademarks of Dell Inc. or its @@ -28,43 +28,49 @@ Table 1. Revision history | Revision | Date | Description | |----------|-----------|-----------------------------------------------------------| -| 01 | June 2021 | Current release of Ansible Modules for Dell EMC PowerStore 1.2.0 | +| 01 | September 2021 | Current release of Ansible Modules for Dell EMC PowerStore 1.3.0 | Product Description ------------------- -The Ansible modules for Dell EMC PowerStore are used to automate and orchestrate the deployment, configuration, and management of Dell EMC PowerStore storage systems. The capabilities of Ansible modules are managing Volumes, Volume groups, Hosts, Host groups, Protection policies, Replication rules, Replication sessions, NFS exports, SMB shares, NAS server, File systems, File system snapshots, Quota tree, Quotas for filesystem and obtaining PowerStore system information. The options available for each capability are list, show, create, delete, and modify. The only exception is for NAS server for which the options available are list & modify. +The Ansible modules for Dell EMC PowerStore are used to automate and orchestrate the deployment, configuration, and management of Dell EMC PowerStore storage systems. The capabilities of Ansible modules are managing Volumes, Volume groups, Hosts, Host groups, Protection policies, Replication rules, Replication sessions, NFS exports, SMB shares, NAS server, File systems, File system snapshots, Quota tree, Quotas for filesystem, Cluster, Networks, Local users, Roles, Job, and obtaining PowerStore system information. The options available for each capability are list, show, create, delete, and modify. The only exception is for NAS server for which the options available are list & modify. New features & enhancements --------------------------- Along with the previous release deliverables, this release supports the following features - -- Replication rule module supports the following functionalities: - - Create a replication rule - - Get replication rule details - - Modify attributes of replication rule - - Delete replication rule +- Cluster module supports the following functionalities: + - Get cluster details + - Modify attributes of cluster -- Replication session module supports the following functionalities: - - Get replication session details - - Modify the state of the replication session +- Network module supports the following functionalities: + - Get network details + - Modify attributes of the network -- Protection policy module has the following enhancements: - - Add a replication rule to protection policy - - Remove a replication rule from protection policy +- Local user module has the following functionalities: + - Create a local user + - Get local user details + - Modify attributes of local user + - Delete a local user + +- Role module has the following functionalities: + - Get role details + +- Job module has the following functionalities: + - Get job details for a given job ID - Gather Facts Module has the following enhancements: - - List of remote systems - - List of replication sessions - - List of replication rules + - List of users + - List of roles + - List of networks + - List of appliances Known issues ------------ There are no known issues. - Limitations ----------- -There are no known limitations. +- All occurrences of Password and related parameters do not support Idempotency. Distribution ---------------- diff --git a/galaxy.yml b/galaxy.yml index 7af3ba5..3302c66 100644 --- a/galaxy.yml +++ b/galaxy.yml @@ -8,7 +8,7 @@ namespace: dellemc name: powerstore # The version of the collection. Must be compatible with semantic versioning -version: 1.2.1 +version: 1.3.0 # The path to the Markdown (.md) readme file. This path is relative to the root of the collection readme: README.md @@ -32,10 +32,12 @@ description: Ansible modules for Dell EMC Powerstore # Either a single license or a list of licenses for content inside of a collection. Ansible Galaxy currently only # accepts L(SPDX,https://spdx.org/licenses/) licenses. This key is mutually exclusive with 'license_file' license: +- GPL-3.0-or-later +- Apache-2.0 # The path to the license file for the collection. This path is relative to the root of the collection. This key is # mutually exclusive with 'license' -license_file: 'LICENSE' +license_file: # A list of tags you want to associate with the collection for indexing/searching. A tag name has the same character # requirements as 'namespace' and 'name' @@ -48,13 +50,13 @@ tags: [storage] dependencies: {} # The URL of the originating SCM repository -repository: https://github.com/dell/ansible-powerstore/tree/1.2.1 +repository: https://github.com/dell/ansible-powerstore/tree/1.3.0 # The URL to any online docs -documentation: https://github.com/dell/ansible-powerstore/tree/1.2.1/docs +documentation: https://github.com/dell/ansible-powerstore/tree/1.3.0/docs # The URL to the homepage of the collection/project -homepage: https://github.com/dell/ansible-powerstore/tree/1.2.1 +homepage: https://github.com/dell/ansible-powerstore/tree/1.3.0 # The URL to the collection issue tracker issues: https://www.dell.com/community/Automation/bd-p/Automation @@ -64,4 +66,3 @@ issues: https://www.dell.com/community/Automation/bd-p/Automation # uses 'fnmatch' to match the files or directories. Some directories and files like 'galaxy.yml', '*.pyc', '*.retry', # and '.git' are always filtered build_ignore: [] - diff --git a/plugins/module_utils/storage/dell/dellemc_ansible_powerstore_utils.py b/plugins/module_utils/storage/dell/dellemc_ansible_powerstore_utils.py index ecb1c12..765b93a 100644 --- a/plugins/module_utils/storage/dell/dellemc_ansible_powerstore_utils.py +++ b/plugins/module_utils/storage/dell/dellemc_ansible_powerstore_utils.py @@ -1,4 +1,7 @@ -'''import PyPowerStore library for PowerStore Storage''' +# Apache License version 2.0 (see MODULE-LICENSE or http://www.apache.org/licenses/LICENSE-2.0.txt) +""" +import PyPowerStore library for PowerStore Storage +""" from __future__ import (absolute_import, division, print_function) __metaclass__ = type @@ -10,7 +13,9 @@ except ImportError: HAS_Py4PS = False -'''import pkg_resources''' +''' +check if pkg_resources can be imported or not +''' try: from pkg_resources import parse_version import pkg_resources diff --git a/plugins/modules/dellemc_powerstore_cluster.py b/plugins/modules/dellemc_powerstore_cluster.py new file mode 100644 index 0000000..85a25ae --- /dev/null +++ b/plugins/modules/dellemc_powerstore_cluster.py @@ -0,0 +1,569 @@ +#!/usr/bin/python +# Copyright: (c) 2021, DellEMC +# Apache License version 2.0 (see MODULE-LICENSE or http://www.apache.org/licenses/LICENSE-2.0.txt) +""" Ansible module for managing cluster related operations on PowerStore""" +from __future__ import (absolute_import, division, print_function) + +__metaclass__ = type +ANSIBLE_METADATA = {'metadata_version': '1.1', + 'status': ['preview'], + 'supported_by': 'community'} + +DOCUMENTATION = r''' +--- +module: dellemc_powerstore_cluster + +version_added: '1.3.0' + +short_description: Manage cluster related opeartions on PowerStore. + +description: +- Managing cluster on PowerStore storage system includes getting details and + modifying cluster configuration parameters. + +extends_documentation_fragment: + - dellemc.powerstore.dellemc_powerstore.powerstore + +author: +- P Srinivas Rao (@srinivas-rao5) + +options: + cluster_name: + description: + - The Name of cluster. + type: str + chap_mode: + description: + - The mode that describes or sets the iSCSI CHAP mode for the cluster. + type: str + choices: ['Disabled', 'Single', 'Mutual'] + cluster_id: + description: + - Id of the cluster. + type: str + new_name: + description: + - The new name for the cluster. + type: str + service_password: + description: + - The password for the service user. + type: str + appliance_id: + description: + - ID of the appliance. + - appliance_id and appliance_name are mutually exclusive. + - is_ssh_enabled has to be passed along with appliance_id. + type: str + appliance_name: + description: + - Name of the appliance. + - appliance_id and appliance_name are mutually exclusive. + - is_ssh_enabled has to be passed along with appliance_name. + type: str + is_ssh_enabled: + description: + - Whether SSH access is enabled for the cluster. + - Either appliance_id or appliance_name is to be passed along with + is_ssh_enabled. + type: bool + physical_mtu: + description: + - MTU for ethernet ports in the cluster. + - The MTU can be set between 1500 to 9000. + type: int + state: + description: + - Define whether the cluster should exist or not. + - present indicates that the cluster should exist on the system. + - absent indicates that the cluster should not exist on the system. + type: str + required: true + choices: ['absent', 'present'] + +notes: +- Creation and deletion of cluster is not supported by ansible modules. + +''' +EXAMPLES = r''' +- name: get the details of cluster using id + dellemc_powerstore_cluster: + array_ip: "{{array_ip}}" + verifycert: "{{verifycert}}" + user: "{{user}}" + password: "{{password}}" + cluster_id: "0" + state: "present" + +- name: Modify details of cluster using the name + dellemc_powerstore_cluster: + array_ip: "{{array_ip}}" + verifycert: "{{verifycert}}" + user: "{{user}}" + password: "{{password}}" + cluster_name: "RT-D1320" + appliance_id: "A1" + is_ssh_enabled: True + service_password: "S@mple_password" + chap_mode: "Disabled" + new_name: "new_RT-D1320" + state: "present" + +''' +RETURN = r''' +changed: + description: Whether or not the resource has changed + returned: always + type: bool + sample: True +cluster_details: + description: The cluster details. + type: complex + returned: When Cluster exists. + contains: + id: + description: The ID of the cluster. + type: str + name: + description: Name of the cluster. + type: str + is_ssh_enabled: + description: Whether or not the ssh is enabled. + type: bool + physical_mtu: + description: MTU for the cluster. + type: int + global_id: + description: The global unique identifier of the cluster. + type: str + management_address: + description: The floating management IP address for the cluster in + IPv4 or IPv6 format. + type: str + storage_discovery_address: + description: The floating storage discovery IP address for the + cluster in IPv4 or IPv6 format. + type: str + master_appliance_id: + description: The unique identifier of the appliance acting as + primary. This parameter is deprecated in version + 2.0.0.0. + type: str + primary_appliance_id: + description: The unique identifier of the appliance acting as + primary. This parameter was added in version 2.0.0.0. + type: str + appliance_count: + description: Number of appliances configured in this cluster. + type: int + is_encryption_enabled: + description: Whether or not Data at Rest Encryption is enabled on + the cluster. + type: bool + compatibility_level: + description: The behavioral version of the software version API, + It is used to ensure the compatibility across + potentially different software versions. + type: int + state: + description: Possible cluster states. + type: str + system_time: + description: Current clock time for the system. System time and + all the system reported times are in UTC (GMT+0:00) + format. + type: str + service_config_details: + description: Details of the service config for the entered + appliance. + type: complex + returned: When is_ssh_enabled is passed in the playbook task + contains: + id: + description: Id of the service configuration. + type: str + appliance_id: + description: Id of the appliance for which the service + configuration exists. + type: str + is_ssh_enabled: + description: Whether the ssh is enabled for the appliance + or not. + type: bool + service_user_details: + description: Details of the service user for which the password + can be updated. + type: complex + returned: when the cluster exists. + contains: + id: + description: Id of the service user. + type: str + name: + description: Name of the service user. + type: str + is_default_password: + description: Whether the service user has default password + or not. + type: bool + is_built_in: + description: Whether the service user is built in or not. + type: bool + appliance_details: + description: Name and Id of the appliance for which is_ssh_enabled + parameter is used. + type: complex + returned: When appliance name or id is passed in the playbook + task. + contains: + id: + description: Id of the appliance. + type: str + name: + description: Name of the appliance. + type: str + +''' + +from ansible_collections.dellemc.powerstore.plugins.module_utils.storage.dell\ + import dellemc_ansible_powerstore_utils as utils +from ansible.module_utils.basic import AnsibleModule + +LOG = utils.get_logger('dellemc_powerstore_cluster') + +py4ps_sdk = utils.has_pyu4ps_sdk() +HAS_PY4PS = py4ps_sdk['HAS_Py4PS'] +IMPORT_ERROR = py4ps_sdk['Error_message'] + +py4ps_version = utils.py4ps_version_check() +IS_SUPPORTED_PY4PS_VERSION = py4ps_version['supported_version'] +VERSION_ERROR = py4ps_version['unsupported_version_message'] + +# Application type +APPLICATION_TYPE = 'Ansible/1.3.0' + + +class PowerStoreCluster(object): + """Class with cluster configuration operations""" + + def __init__(self): + """ Define all parameters required by this module""" + + self.module_params = utils.get_powerstore_management_host_parameters() + self.module_params.update(get_powerstore_cluster_parameters()) + + # initialize the ansible module + mut_ex_args = [ + ['cluster_name', 'cluster_id'], + ['appliance_name', 'appliance_id'] + ] + required_one_of = [['cluster_name', 'cluster_id']] + self.module = AnsibleModule( + argument_spec=self.module_params, + supports_check_mode=False, + mutually_exclusive=mut_ex_args, + required_one_of=required_one_of + ) + + msg = 'HAS_PY4PS = {0} , IMPORT_ERROR = {1}'.format(HAS_PY4PS, + IMPORT_ERROR) + LOG.info(msg) + + if not HAS_PY4PS: + self.module.fail_json(msg=IMPORT_ERROR) + msg = 'IS_SUPPORTED_PY4PS_VERSION = {0} , VERSION_ERROR = {1}' \ + .format(IS_SUPPORTED_PY4PS_VERSION, VERSION_ERROR) + LOG.info(msg) + + if not IS_SUPPORTED_PY4PS_VERSION: + self.module.fail_json(msg=VERSION_ERROR) + + # result is a dictionary that contains changed status and + # cluster details + self.result = {"changed": False, "cluster_details": {}} + + self.conn = utils.get_powerstore_connection( + self.module.params, application_type=APPLICATION_TYPE) + self.configuration = self.conn.config_mgmt + msg = 'Got Py4Ps instance for configuring cluster on' \ + ' PowerStore {0}'.format(self.conn) + LOG.info(msg) + + def get_cluster_details(self, cluster_name=None, cluster_id=None): + """ + Get the cluster details + """ + try: + cluster_details = None + if cluster_name: + cluster_details = \ + self.configuration.get_cluster_by_name(cluster_name) + if cluster_details: + cluster_details = cluster_details[0] + + if cluster_id: + cluster_details =\ + self.configuration.get_cluster_details( + cluster_id=cluster_id) + return cluster_details + except Exception as e: + name_or_id = cluster_name if cluster_name else cluster_id + msg = 'Get details of cluster {0} failed' \ + ' with error: {1}'.format(name_or_id, str(e)) + if isinstance(e, utils.PowerStoreException) and \ + e.err_code == utils.PowerStoreException.HTTP_ERR and \ + e.status_code == "404": + LOG.info(msg) + return None + LOG.error(msg) + self.module.fail_json(msg=msg) + + def show_cluster_details(self, cluster_id, appliance_id): + """ + Show the details of the cluster. + """ + cluster_details = self.get_cluster_details( + cluster_id=cluster_id) + chap_config_details = self.get_chap_config_details() + if cluster_details: + cluster_details['chap_mode'] = chap_config_details['mode'] + cluster_details['service_config_details'] = None + if appliance_id: + cluster_details['service_config_details'] = \ + self.configuration.get_service_config_details(appliance_id) + cluster_details['service_user_details'] = \ + self.get_service_user_details() + appliance_details = self.get_appliance_details( + appliance_id, None) + cluster_details['appliance_details'] = {} + cluster_details['appliance_details']['id'] =\ + appliance_details['id'] + cluster_details['appliance_details']['name'] =\ + appliance_details['name'] + + return cluster_details + + def modify_cluster(self, cluster_id, service_config_id, + modify_dict=None): + """ + Update the parameters for clusters. + """ + try: + if 'name' in modify_dict.keys(): + self.configuration.modify_cluster(cluster_id, + name=modify_dict['name']) + if 'physical_mtu' in modify_dict.keys(): + self.configuration.modify_cluster( + cluster_id, physical_mtu=modify_dict['physical_mtu']) + if 'service_password' in modify_dict.keys(): + self.configuration.modify_service_user( + '1', password=modify_dict['service_password']) + if 'is_ssh_enabled' in modify_dict.keys() and service_config_id: + self.configuration.modify_service_config( + service_config_id, + is_ssh_enabled=modify_dict['is_ssh_enabled']) + if 'chap_mode' in modify_dict.keys(): + self.configuration.modify_chap_config( + '0', mode=modify_dict['chap_mode']) + except Exception as e: + msg = 'Modify operation failed' \ + ' with error: {0}'.format(str(e)) + LOG.error(msg) + self.module.fail_json(msg=msg) + + def get_chap_config_details(self): + """ + Get the details of chap config. + """ + try: + # The ID of the CHAP config will always be 0 + return \ + self.configuration.get_chap_config_details(chap_config_id='0') + + except Exception as e: + msg = 'Get details of chap config with id = 0 failed' \ + ' with error: {0}'.format(str(e)) + LOG.error(msg) + self.module.fail_json(msg=msg) + + def get_service_user_details(self): + """ + Get the details of the service user + """ + try: + # There can be only one service user with name as + # 'service' and id as '1'. This can't be changed or updated. + return self.configuration.get_service_user_details( + service_user_id='1') + except Exception as e: + msg = 'Get details of service user with id: 1 and name: service' \ + ' failed with error: {0}'.format(str(e)) + LOG.error(msg) + self.module.fail_json(msg=msg) + + def get_service_config_details(self, appliance_id): + """ + Get the service config details + """ + try: + return self.configuration.get_service_config_by_appliance_id( + appliance_id)[0] + except Exception as e: + msg = 'Get details of service config for appliance with id: {0}' \ + ' failed with error: {1}'.format(appliance_id, str(e)) + LOG.error(msg) + self.module.fail_json(msg=msg) + + def get_appliance_details(self, appliance_id, appliance_name): + """ + Get the appliance details + """ + try: + if appliance_name: + appliance_details = self.configuration.get_appliance_by_name( + appliance_name) + if appliance_details: + return appliance_details[0] + if appliance_id: + return self.configuration.get_appliance_details(appliance_id) + return None + + except Exception as e: + msg = 'Get appliance details failed with error: {0}'.format(str(e)) + LOG.error(msg) + self.module.fail_json(msg=msg) + + def perform_module_operation(self): + """ + Perform different actions on cluster module based on parameters + chosen in playbook + """ + + cluster_id = self.module.params['cluster_id'] + cluster_name = self.module.params['cluster_name'] + is_ssh_enabled = self.module.params['is_ssh_enabled'] + chap_mode = self.module.params['chap_mode'] + service_password = self.module.params['service_password'] + physical_mtu = self.module.params['physical_mtu'] + new_name = self.module.params['new_name'] + state = self.module.params['state'] + appliance_name = self.module.params['appliance_name'] + appliance_id = self.module.params['appliance_id'] + + changed = False + # if is_ssh_enabled is passed and appliance name/id is not passed. + if not (appliance_id or appliance_name) and is_ssh_enabled is not None: + self.module.fail_json( + msg='Either appliance id or appliance name is needed along' + ' with is_ssh_enabled parameter. Please enter a valid' + ' appliance id/name') + # if appliance name/id is passed and is_ssh_enabled is not passed. + if is_ssh_enabled is None and (appliance_id or appliance_name): + self.module.fail_json( + msg='is_ssh_enabled parameter is also needed along with ' + 'appliance id/name. Please enter a valid is_ssh_enabled ' + 'value.') + # Getting the service_config_id from the appliance details + # for updating is_ssh_enabled. + service_config_id = None + if appliance_id or appliance_name: + appliance_details = self.get_appliance_details( + appliance_id, appliance_name) + if not appliance_details: + self.module.fail_json( + msg="Unable to fetch the appliance details. Please provide" + " a valid appliance_name.") + service_config_id = appliance_details['id'] + appliance_id = appliance_details['id'] + + cluster_details = self.get_cluster_details(cluster_name, cluster_id) + if cluster_details and cluster_name: + cluster_id = cluster_details['id'] + + if state == 'present' and not cluster_details: + self.module.fail_json( + msg="Creation of cluster is currently not supported by the " + "module. Please enter the cluster_name/cluster_id of the " + "existing cluster.") + + if state == 'absent' and cluster_details: + self.module.fail_json( + msg="Invalid operation. Deletion of the existing cluster is " + "currently not supported by the module.") + + # cluster details with all the parameters needed for modify + cluster_details = self.show_cluster_details(cluster_id, appliance_id) + + if state == 'present' and cluster_details: + update_params_dict = modify_payload( + cluster_details, new_name, physical_mtu, service_password, + chap_mode, is_ssh_enabled) + if update_params_dict: + self.modify_cluster(cluster_id, service_config_id, + update_params_dict) + changed = True + + cluster_details = self.show_cluster_details(cluster_id, appliance_id) + self.result["changed"] = changed + self.result["cluster_details"] = cluster_details + self.module.exit_json(**self.result) + + +def modify_payload(cluster_details, new_name=None, physical_mtu=None, + service_password=None, chap_mode=None, is_ssh_enabled=None): + """ + Dictionary containing all the parameters which are to be updated + :param cluster_details: The details of the cluster + :param physical_mtu: MTU for the cluster + :param new_name: New name for the cluster + :param service_password: new password for the service user + :param chap_mode: CHAP mode + :param is_ssh_enabled: To enable ssh for the service users or not + :return: Returns payload_dict, includes parameters which are to be updated + """ + payload = {} + if service_password is not None: + payload['service_password'] = service_password + if physical_mtu is not None and\ + physical_mtu != int(cluster_details['physical_mtu']): + payload['physical_mtu'] = physical_mtu + if new_name is not None and new_name != "" and\ + cluster_details['name'].lower() != new_name.lower(): + payload['name'] = new_name + if chap_mode and \ + (not cluster_details['chap_mode'] or + (chap_mode.lower() != cluster_details['chap_mode'].lower())): + payload['chap_mode'] = chap_mode + if cluster_details['service_config_details'] is not None: + service_config_ssh = \ + cluster_details['service_config_details']['is_ssh_enabled'] + if is_ssh_enabled is not None and service_config_ssh != is_ssh_enabled: + payload['is_ssh_enabled'] = is_ssh_enabled + + return payload + + +def get_powerstore_cluster_parameters(): + """This method provides parameters required for the ansible cluster + module on PowerStore""" + return dict( + cluster_name=dict(), cluster_id=dict(), + appliance_name=dict(), appliance_id=dict(), + service_password=dict(no_log=True), + chap_mode=dict(choices=['Disabled', 'Single', 'Mutual']), + is_ssh_enabled=dict(type='bool'), new_name=dict(), + physical_mtu=dict(type='int'), + state=dict(required=True, choices=['present', 'absent']) + ) + + +def main(): + """ Create cluster config object and perform actions on it + based on user input from playbook""" + + obj = PowerStoreCluster() + obj.perform_module_operation() + + +if __name__ == '__main__': + main() diff --git a/plugins/modules/dellemc_powerstore_filesystem.py b/plugins/modules/dellemc_powerstore_filesystem.py index b8f6b14..aab7292 100644 --- a/plugins/modules/dellemc_powerstore_filesystem.py +++ b/plugins/modules/dellemc_powerstore_filesystem.py @@ -1,6 +1,8 @@ #!/usr/bin/python # Copyright: (c) 2020-2021, Dell EMC -""" Ansible module for managing NAS server on PowerStore""" +# Apache License version 2.0 (see MODULE-LICENSE or http://www.apache.org/licenses/LICENSE-2.0.txt) + +""" Ansible module for managing filesystems on PowerStore""" from __future__ import (absolute_import, division, print_function) __metaclass__ = type @@ -258,10 +260,12 @@ description: The description about the filesystem. type: str protection_policy: - description: Id and name of the protection policy associated with the filesystem. + description: Id and name of the protection policy associated with + the filesystem. type: dict nas_server: - description: Id and name of the nas server to which the filesystem belongs. + description: Id and name of the nas server to which the filesystem + belongs. type: dict size_total: description: Total size of the filesystem in bytes. @@ -282,7 +286,8 @@ description: Locking policy about the filesystem. type: str is_smb_no_notify_enabled: - description: Whether smb notify policy is enabled for a filesystem. + description: Whether smb notify policy is enabled for a + filesystem. type: bool is_smb_notify_on_access_enabled: description: Whether smb on access notify policy is enabled. @@ -291,13 +296,16 @@ description: Whether smb op lock is enabled. type: bool grace_period: - description: Default grace period for a filesystem quota in second. + description: Default grace period for a filesystem quota in + second. type: int default_hard_limit: - description: Default hard limit period for a filesystem quota in byte. + description: Default hard limit period for a filesystem quota in + byte. type: int default_soft_limit: - description: Default soft limit period for a filesystem quota in byte. + description: Default soft limit period for a filesystem quota in + byte. type: int snapshots: description: Id and name of the snapshots of a filesystem. @@ -321,7 +329,7 @@ VERSION_ERROR = py4ps_version['unsupported_version_message'] # Application type -APPLICATION_TYPE = 'Ansible/1.2.0' +APPLICATION_TYPE = 'Ansible/1.3.0' class PowerStoreFileSystem(object): diff --git a/plugins/modules/dellemc_powerstore_filesystem_snapshot.py b/plugins/modules/dellemc_powerstore_filesystem_snapshot.py index 093e225..1156838 100644 --- a/plugins/modules/dellemc_powerstore_filesystem_snapshot.py +++ b/plugins/modules/dellemc_powerstore_filesystem_snapshot.py @@ -1,5 +1,6 @@ #!/usr/bin/python # Copyright: (c) 2020-2021, DellEMC +# Apache License version 2.0 (see MODULE-LICENSE or http://www.apache.org/licenses/LICENSE-2.0.txt) """Ansible module for managing filesystem snapshots on PowerStore""" @@ -165,11 +166,12 @@ description: Description of the filesystem snapshot. type: str expiration_timestamp: - description: The date and time the snapshot is due to be automatically - deleted by the system. + description: The date and time the snapshot is due to be + automatically deleted by the system. type: str id: - description: Unique identifier of the filesystem snapshot instance. + description: Unique identifier of the filesystem snapshot + instance. type: str name: description: The name of the snapshot. @@ -192,7 +194,6 @@ type: str """ -from uuid import UUID from datetime import datetime, timedelta from ansible.module_utils.basic import AnsibleModule from ansible_collections.dellemc.powerstore.plugins.module_utils.storage.dell\ @@ -210,7 +211,7 @@ VERSION_ERROR = py4ps_version['unsupported_version_message'] # Application type -APPLICATION_TYPE = 'Ansible/1.2.0' +APPLICATION_TYPE = 'Ansible/1.3.0' class PowerStoreFilesystemSnapshot(object): @@ -371,7 +372,7 @@ def get_fs_name(self, filesystem_id): fs = self.provisioning.get_filesystem_details( filesystem_id=filesystem_id) return fs['name'] - except Exception as e: + except Exception: error_msg = "Filesystem {0} not found on the array.".format( filesystem_id) LOG.error(error_msg) @@ -415,14 +416,14 @@ def get_fs_snapshot(self, snapshot_name, snapshot_id, filesystem_id, # Check if given filesystem_id matches with the parent_id # of the snapshot - if snapshot and filesystem_id: - if snapshot[0]['parent_id'] != filesystem_id: - error_msg = "Given filesystem {0} does not " \ - "match with the filesystem of the " \ - "snapshot. Please provide valid " \ - "filesystem.".format(filesystem_id) - LOG.error(error_msg) - self.module.fail_json(msg=error_msg) + if snapshot and filesystem_id and \ + snapshot[0]['parent_id'] != filesystem_id: + error_msg = "Given filesystem {0} does not match" \ + " with the filesystem of the snapshot. " \ + "Please provide valid filesystem.".\ + format(filesystem_id) + LOG.error(error_msg) + self.module.fail_json(msg=error_msg) else: snapshot = self.protection.\ get_filesystem_snapshot_details_by_name( @@ -463,8 +464,7 @@ def get_fs_snapshot(self, snapshot_name, snapshot_id, filesystem_id, self.module.fail_json(msg=msg) def create_filesystem_snapshot(self, filesystem_id, snapshot_name, - description, desired_retention, - retention_unit, expiration_timestamp, + description, expiration_timestamp, access_type, nas_server): """Create a snapshot for a filesystem on PowerStore""" @@ -477,28 +477,6 @@ def create_filesystem_snapshot(self, filesystem_id, snapshot_name, "new snapshot.".format(snapshot_name, nas_server) self.module.fail_json(msg=error_msg) - if expiration_timestamp is not None: - self.validate_expiration_timestamp(expiration_timestamp) - current_timestamp = datetime.utcnow().isoformat()[0:19] + 'Z' - if expiration_timestamp < current_timestamp: - self.module.fail_json(msg="The given timestamp {0} is already" - " expired. Current timestamp is" - " {1}. Please provide valid " - "expiration timestamp".format( - expiration_timestamp, - current_timestamp)) - - if desired_retention is None and expiration_timestamp is None: - expiration_timestamp = None - - if desired_retention: - if retention_unit == 'days': - expiration_timestamp = (datetime.utcnow() + timedelta( - days=desired_retention)).isoformat() + 'Z' - else: - expiration_timestamp = (datetime.utcnow() + timedelta( - hours=desired_retention)).isoformat() + 'Z' - if access_type: access_type = access_type.title() @@ -524,6 +502,7 @@ def check_fs_snapshot_modified(self, snapshot, filesystem_id, description, nas_server): """Determines whether the snapshot has been modified""" LOG.info("Determining if the filesystem snapshot has been modified..") + datetime_format = "%Y-%m-%dT%H:%MZ" if access_type and access_type.title() != snapshot['access_type']: error_message = "Modification of access type is not allowed." @@ -543,12 +522,12 @@ def check_fs_snapshot_modified(self, snapshot, filesystem_id, description, if retention_unit == "days": expiration_timestamp = \ (datetime.strptime(creation_timestamp, - '%Y-%m-%dT%H:%MZ') + timedelta( + datetime_format) + timedelta( days=int(desired_retention))).isoformat() + 'Z' else: expiration_timestamp = \ (datetime.strptime(creation_timestamp, - '%Y-%m-%dT%H:%MZ') + timedelta( + datetime_format) + timedelta( hours=int(desired_retention))).isoformat() + 'Z' LOG.info("The new expiration timestamp is %s", expiration_timestamp) @@ -564,9 +543,9 @@ def check_fs_snapshot_modified(self, snapshot, filesystem_id, description, new_timestamp = expiration_timestamp[0:16] + 'Z' existing_time_obj = datetime.strptime(existing_timestamp, - '%Y-%m-%dT%H:%MZ') + datetime_format) new_time_obj = datetime.strptime(new_timestamp, - '%Y-%m-%dT%H:%MZ') + datetime_format) if existing_time_obj > new_time_obj: td = existing_time_obj - new_time_obj @@ -587,11 +566,11 @@ def check_fs_snapshot_modified(self, snapshot, filesystem_id, description, snap_modify_dict[ 'expiration_timestamp'] = "1970-01-01T00:00:00.000Z" - if description is not None and snapshot['description'] != description: - if (snapshot['description'] is None and description != "") or \ - (snapshot['description'] is not None): - snap_modify_dict['description'] = \ - description + if (description is not None and + snapshot['description'] != description) and \ + ((snapshot['description'] is None and description != "") or + (snapshot['description'] is not None)): + snap_modify_dict['description'] = description LOG.info("Snapshot modification details: %s", snap_modify_dict) @@ -629,10 +608,10 @@ def delete_filesystem_snapshot(self, snapshot): return True except Exception as e: e_msg = str(e) - if isinstance(e, utils.PowerStoreException): - if e.status_code == "422" and "not found in the " \ - "system" in e_msg: - return False + if isinstance(e, utils.PowerStoreException) and \ + e.status_code == "422" and \ + "not found in the system" in e_msg: + return False errormsg = "Delete operation of filesystem snapshot with " \ "name: {0}, id: {1} failed with error {2}".\ format(snapshot['name'], snapshot['id'], e_msg) @@ -691,8 +670,17 @@ def perform_module_operation(self): result['create_fs_snap'] =\ self.create_filesystem_snapshot( filesystem_id, snapshot_name, description, - desired_retention, retention_unit, expiration_timestamp, - access_type, nas_server) + expiration_timestamp, access_type, nas_server) + + snapshot = self.get_fs_snapshot( + snapshot_name, snapshot_id, filesystem_id, nas_server) + + fs_snap_modify_dict = \ + self.check_fs_snapshot_modified( + snapshot, filesystem_id, description, desired_retention, + retention_unit, expiration_timestamp, access_type, + nas_server) + elif state == 'absent' and snapshot: result['delete_fs_snap'] = self.delete_filesystem_snapshot( snapshot) diff --git a/plugins/modules/dellemc_powerstore_gatherfacts.py b/plugins/modules/dellemc_powerstore_gatherfacts.py index 8652cb4..0da4e98 100644 --- a/plugins/modules/dellemc_powerstore_gatherfacts.py +++ b/plugins/modules/dellemc_powerstore_gatherfacts.py @@ -1,5 +1,6 @@ #!/usr/bin/python # Copyright: (c) 2019-2021, DellEMC +# Apache License version 2.0 (see MODULE-LICENSE or http://www.apache.org/licenses/LICENSE-2.0.txt) from __future__ import absolute_import, division, print_function @@ -18,7 +19,7 @@ - Gathers the list of specified PowerStore Storage System entities, such as the list of cluster nodes, volumes, volume groups, hosts, host groups, snapshot rules, protection policies, NAS servers, NFS exports, SMB shares, - tree quotas, user quotas, and file systems. + tree quotas, user quotas, file systems etc. author: - Arindam Datta (@dattaarindam) - Vivek Soni (@v-soni11) @@ -45,12 +46,16 @@ - replication_rule - replication rules - replication_session - replication sessions - remote_system - remote systems + - network - various networks + - role - roles + - user - local users + - appliance - appliances required: True elements: str choices: [vol, vg, host, hg, node, protection_policy, snapshot_rule, nas_server, nfs_export, smb_share, tree_quota, user_quota, file_system, replication_rule, replication_session, - remote_system] + remote_system, network, role, user, appliance] type: list filters: description: @@ -85,6 +90,9 @@ will be returned. type: bool default: False +notes: +- Pagination is not supported for role and local user. If all_pages is passed, + it will be ignored. ''' EXAMPLES = r''' @@ -199,11 +207,35 @@ filter_operator: "like" filter_value: "*share*" +- name: Get list of user, role, network and appliances + dellemc_powerstore_gatherfacts: + array_ip: "{{array_ip}}" + verifycert: "{{verifycert}}" + user: "{{user}}" + password: "{{password}}" + gather_subset: + - user + - role + - network + - appliance + +- name: Get list of networks whose name contains 'Management' + dellemc_powerstore_gatherfacts: + array_ip: "{{array_ip}}" + verifycert: "{{verifycert}}" + user: "{{user}}" + password: "{{password}}" + gather_subset: + - network + filters: + - filter_key: "name" + filter_operator: "like" + filter_value: "*Management*" ''' RETURN = r''' changed: - description: Shows whether or not the resource has changed + description: Shows whether or not the resource has changed. returned: always type: bool subset_result: @@ -211,6 +243,19 @@ returned: always type: complex contains: + Appliance: + description: Provides details of all appliances. + type: list + contains: + id: + description: appliance id + type: str + name: + description: appliance name + type: str + model: + description: Model type of the PowerStore + type: str Cluster: description: Provides details of all clusters. type: list @@ -227,7 +272,7 @@ FileSystems: description: Provides details of all filesystems. type: list - returned: When file_system is in a given gather_subset. + returned: When file_system is in a given gather_subset contains: id: description: filesystem id @@ -257,6 +302,17 @@ name: description: host name type: str + LocalUsers: + description: Provides details of all local users. + type: list + returned: When user is in a given gather_subset + contains: + id: + description: user id + type: str + name: + description: user name + type: str NASServers: description: Provides details of all nas servers. type: list @@ -268,10 +324,21 @@ name: description: nas server name type: str + Networks: + description: Provides details of all networks. + type: list + returned: When network is in a given gather_subset + contains: + id: + description: network id + type: str + name: + description: network name + type: str NFSExports: description: Provides details of all nfs exports. type: list - returned: When nfs_export isin a given gather_subset + returned: When nfs_export is in a given gather_subset contains: id: description: nfs export id @@ -313,7 +380,7 @@ description: replication rule name type: str ReplicationSession: - description: details of all replication sessions + description: details of all replication sessions. type: list returned: when replication_session given in gather_subset contains: @@ -331,6 +398,17 @@ name: description: remote system name type: str + Roles: + description: Provides details of all roles. + type: list + returned: When role is in a given gather_subset + contains: + id: + description: role id + type: str + name: + description: role name + type: str SMBShares: description: Provides details of all smb shares. type: list @@ -387,7 +465,7 @@ description: tree quota path type: str UserQuotas: - description: Provides details of all user quotas + description: Provides details of all user quotas. type: list returned: When user_quota is in a given gather_subset contains: @@ -401,8 +479,7 @@ import dellemc_ansible_powerstore_utils as utils import logging -LOG = utils.get_logger('dellemc_powerstore_gatherfacts', - log_devel=logging.INFO) +LOG = utils.get_logger('dellemc_powerstore_gatherfacts') py4ps_sdk = utils.has_pyu4ps_sdk() HAS_PY4PS = py4ps_sdk['HAS_Py4PS'] @@ -413,7 +490,7 @@ VERSION_ERROR = py4ps_version['unsupported_version_message'] # Application type -APPLICATION_TYPE = 'Ansible/1.2.0' +APPLICATION_TYPE = 'Ansible/1.3.0' class PowerstoreGatherFacts(object): @@ -453,6 +530,7 @@ def __init__(self): application_type=APPLICATION_TYPE) self.provisioning = self.conn.provisioning self.protection = self.conn.protection + self.configuration = self.conn.config_mgmt self.subset_mapping = { 'vol': { @@ -518,10 +596,26 @@ def __init__(self): 'file_system': { 'func': self.provisioning.get_file_systems, 'display_as': 'FileSystems' + }, + 'network': { + 'func': self.configuration.get_networks, + 'display_as': 'Networks' + }, + 'role': { + 'func': self.configuration.get_roles, + 'display_as': 'Roles' + }, + 'user': { + 'func': self.configuration.get_local_users, + 'display_as': 'LocalUsers' + }, + 'appliance': { + 'func': self.configuration.get_appliances, + 'display_as': 'Appliance' } } - LOG.info('Got Py4ps instance for provisioning on PowerStore %s', + LOG.info('Got Py4ps connection object %s', self.conn) def update_result_with_item_list(self, item, filter_dict=None, @@ -531,8 +625,12 @@ def update_result_with_item_list(self, item, filter_dict=None, try: LOG.info('Getting %s list', item) - item_list = self.subset_mapping[item]['func']( - filter_dict=filter_dict, all_pages=all_pages) + if item not in ['role', 'user']: + item_list = self.subset_mapping[item]['func']( + filter_dict=filter_dict, all_pages=all_pages) + else: + item_list = self.subset_mapping[item]['func']( + filter_dict=filter_dict) LOG.info('Successfully listed %s %s from powerstore array name: ' '%s , global id : %s', len(item_list), self. subset_mapping[item]['display_as'], self.cluster_name, @@ -607,6 +705,20 @@ def get_clusters(self): LOG.error(msg) self.module.fail_json(msg=msg) + def get_array_software_version(self): + """Get array software version""" + try: + soft_ver = self.provisioning.get_array_version() + msg = 'Got array software version as {0}'.format(soft_ver) + LOG.info(msg) + return soft_ver + + except Exception as e: + msg = 'Failed to get the array software version with ' \ + 'error {0}'.format(str(e)) + LOG.error(msg) + self.module.fail_json(msg=msg) + def perform_module_operation(self): clusters = self.get_clusters() if len(clusters) > 0: @@ -616,7 +728,10 @@ def perform_module_operation(self): self.module.fail_json(msg="Unable to find any active cluster on" " this array ") - self.result.update(Cluster=clusters) + array_soft_ver = self.get_array_software_version() + + self.result.update(Cluster=clusters, + Array_Software_Version=array_soft_ver) subset = self.module.params['gather_subset'] filters = self.module.params['filters'] all_pages = self.module.params['all_pages'] @@ -628,9 +743,8 @@ def perform_module_operation(self): if subset is not None: for item in subset: if item in self.subset_mapping: - self.update_result_with_item_list(item, - filter_dict=filter_dict, - all_pages=all_pages) + self.update_result_with_item_list( + item, filter_dict=filter_dict, all_pages=all_pages) else: self.module.fail_json( msg="subset_mapping do not have details for '{0}'" @@ -652,7 +766,8 @@ def get_powerstore_gatherfacts_parameters(): 'nas_server', 'nfs_export', 'smb_share', 'tree_quota', 'user_quota', 'file_system', 'replication_rule', 'replication_session', - 'remote_system' + 'remote_system', 'network', 'role', + 'user', 'appliance' ]), filters=dict(type='list', required=False, elements='dict', options=dict(filter_key=dict(type='str', required=True, @@ -670,7 +785,7 @@ def get_powerstore_gatherfacts_parameters(): def main(): - """ Create PowerStoreGatherFacts object and perform action on it + """ Create PowerStore gather facts object and perform action on it based on user input from playbook """ obj = PowerstoreGatherFacts() obj.perform_module_operation() diff --git a/plugins/modules/dellemc_powerstore_host.py b/plugins/modules/dellemc_powerstore_host.py index 4dc9c6c..0bdddfd 100644 --- a/plugins/modules/dellemc_powerstore_host.py +++ b/plugins/modules/dellemc_powerstore_host.py @@ -1,5 +1,6 @@ #!/usr/bin/python # Copyright: (c) 2019-2021, DellEMC +# Apache License version 2.0 (see MODULE-LICENSE or http://www.apache.org/licenses/LICENSE-2.0.txt) from __future__ import absolute_import, division, print_function @@ -78,9 +79,11 @@ or fewer printable Unicode characters. - Cannot be specified when creating a host. type: str - ''' +notes: +- Only completely and correctly configured iSCSI initiators can be associated with a host. +''' EXAMPLES = r''' - name: Create host dellemc_powerstore_host: @@ -160,39 +163,39 @@ RETURN = r''' changed: - description: Whether or not the resource has changed + description: Whether or not the resource has changed. returned: always type: bool host_details: - description: Details of the host + description: Details of the host. returned: When host exists type: complex contains: id: - description: The system generated ID given to the host + description: The system generated ID given to the host. type: str name: - description: Name of the host + description: Name of the host. type: str description: - description: Description about the host + description: Description about the host. type: str host_group_id: - description: The host group ID of host + description: The host group ID of host. type: str os_type: - description: The os type of the host + description: The os type of the host. type: str host_initiators: - description: The initiator details of this host + description: The initiator details of this host. type: complex contains: port_name: - description: Name of the port + description: Name of the port. type: str port_type: - description: The type of the port + description: The type of the port. type: str ''' @@ -212,7 +215,7 @@ VERSION_ERROR = py4ps_version['unsupported_version_message'] # Application type -APPLICATION_TYPE = 'Ansible/1.2.0' +APPLICATION_TYPE = 'Ansible/1.3.0' class PowerStoreHost(object): @@ -427,7 +430,7 @@ def remove_host_initiators(self, host, initiators): for initiator in current_initiators: existing_inits.append(initiator['port_name']) - if existing_inits is None or not len(existing_inits): + if len(existing_inits) == 0: LOG.info( 'No initiators are present in host %s', host['name']) return False @@ -477,18 +480,12 @@ def delete_host(self, host): LOG.error(error_msg) self.module.fail_json(msg=error_msg) - def _create_result_dict(self, changed): + def _create_result_dict(self, changed, host_id): self.result['changed'] = changed if self.module.params['state'] == 'absent': self.result['host_details'] = {} else: - if self.module.params['host_name']: - host_id = self.get_host_id_by_name( - self.module.params['host_name']) - self.result['host_details'] = self.get_host(host_id) - if self.module.params['host_id']: - self.result['host_details'] = self.get_host( - self.module.params['host_id']) + self.result['host_details'] = self.get_host(host_id) def perform_module_operation(self): ''' @@ -548,18 +545,15 @@ def perform_module_operation(self): remove_host_initiators(host, initiators=initiators) or changed) - if state == 'present' and host and new_name: - if host_name != new_name: - LOG.info('Renaming host %s to %s', host_name, new_name) - changed = self.rename_host(host, new_name) - if changed: - self.module.params['host_name'] = new_name + if state == 'present' and host and new_name and host_name != new_name: + LOG.info('Renaming host %s to %s', host_name, new_name) + changed = self.rename_host(host, new_name) if state == 'absent' and host: LOG.info('Delete host %s ', host['name']) changed = self.delete_host(host) or changed - self._create_result_dict(changed) + self._create_result_dict(changed, host_id) # Update the module's final state LOG.info('changed %s', changed) self.module.exit_json(**self.result) diff --git a/plugins/modules/dellemc_powerstore_hostgroup.py b/plugins/modules/dellemc_powerstore_hostgroup.py index 1266ba4..a28ae9c 100644 --- a/plugins/modules/dellemc_powerstore_hostgroup.py +++ b/plugins/modules/dellemc_powerstore_hostgroup.py @@ -1,5 +1,6 @@ #!/usr/bin/python # Copyright: (c) 2019-2021, DellEMC +# Apache License version 2.0 (see MODULE-LICENSE or http://www.apache.org/licenses/LICENSE-2.0.txt) from __future__ import absolute_import, division, print_function @@ -17,9 +18,9 @@ description: - Managing host group on PowerStore storage system includes create host group with a set of hosts, add/remove hosts from host group, rename - host group, and delete host group. -- Deletion of a host group results in deletion of the containing hosts as - well. Remove hosts from the host group first to retain them. + host group, and delete host group. Deletion of a host group results in + deletion of the containing hosts as well. Remove hosts from the host group + first to retain them. author: - Manisha Agrawal (@agrawm3) extends_documentation_fragment: @@ -169,33 +170,33 @@ RETURN = r''' changed: - description: Whether or not the resource has changed + description: Whether or not the resource has changed. returned: always type: bool hostgroup_details: - description: Details of the host group + description: Details of the host group. returned: When host group exists type: complex contains: id: - description: The system generated ID given to the host group + description: The system generated ID given to the host group. type: str name: - description: Name of the host group + description: Name of the host group. type: str description: - description: Description about the host group + description: Description about the host group. type: str hosts: - description: The hosts details which are part of this host group + description: The hosts details which are part of this host group. type: complex contains: id: - description: The ID of the host + description: The ID of the host. type: str name: - description: The name of the host + description: The name of the host. type: str ''' @@ -215,7 +216,7 @@ VERSION_ERROR = py4ps_version['unsupported_version_message'] # Application type -APPLICATION_TYPE = 'Ansible/1.2.0' +APPLICATION_TYPE = 'Ansible/1.3.0' class PowerStoreHostgroup(object): @@ -331,9 +332,9 @@ def create_host_list(self, hosts): host_list.append(host) else: # check if host is host_name - id = self.get_host_id_by_name(host) - if id: - host_list.append(id) + host_id = self.get_host_id_by_name(host) + if host_id: + host_list.append(host_id) else: error_msg = ("Host {0} not found".format(host)) LOG.error(error_msg) @@ -369,8 +370,8 @@ def create_hostgroup(self, hostgroup_name, hosts): try: if hosts is None or not len(hosts): - error_msg = ("Create host group {0} failed as no hosts or invalid" - " hosts specified".format(hostgroup_name)) + error_msg = ("Create host group {0} failed as no hosts or " + "invalid hosts specified".format(hostgroup_name)) LOG.error(error_msg) self.module.fail_json(msg=error_msg) @@ -448,7 +449,7 @@ def remove_hostgroup_hosts(self, hostgroup, hosts): for host in current_hosts: existing_hosts.append(host['id']) - if existing_hosts is None or not len(existing_hosts): + if len(existing_hosts) == 0: LOG.info('No hosts are present in host group %s', hostgroup['name']) return False @@ -500,19 +501,13 @@ def delete_hostgroup(self, hostgroup): LOG.error(error_msg) self.module.fail_json(msg=error_msg) - def _create_result_dict(self, changed): + def _create_result_dict(self, changed, hostgroup_id): self.result['changed'] = changed if self.module.params['state'] == 'absent': self.result['hostgroup_details'] = {} else: - if self.module.params['hostgroup_name']: - hostgroup_id = self.get_hostgroup_id_by_name( - self.module.params['hostgroup_name']) - self.result['hostgroup_details'] = self.get_hostgroup( - hostgroup_id) - elif self.module.params['hostgroup_id']: - self.result['hostgroup_details'] = self.get_hostgroup( - self.module.params['hostgroup_id']) + self.result['hostgroup_details'] = self.get_hostgroup( + hostgroup_id) def perform_module_operation(self): ''' @@ -526,14 +521,6 @@ def perform_module_operation(self): hosts = self.module.params['hosts'] new_name = self.module.params['new_name'] - if hostgroup_name and hostgroup_id: - error_msg = ( - "Operation on host group failed as both hostgroup_id and " - "hostgroup_name are specified. Please specify either of " - "them") - LOG.error(error_msg) - self.module.fail_json(msg=error_msg) - if hostgroup_name: hostgroup_id = self.get_hostgroup_id_by_name(hostgroup_name) if hostgroup_id: @@ -552,7 +539,8 @@ def perform_module_operation(self): hostgroup_id = self.get_hostgroup_id_by_name(hostgroup_name) if (state == 'present' and hostgroup and host_state == - 'present-in-group' and host_ids_list and len(host_ids_list) > 0): + 'present-in-group' and host_ids_list and + len(host_ids_list) > 0): LOG.info('Adding hosts to host group %s', hostgroup['name']) changed = ( self.add_hostgroup_hosts( @@ -560,26 +548,25 @@ def perform_module_operation(self): hosts=host_ids_list) or changed) if (state == 'present' and hostgroup and host_state == - 'absent-in-group' and host_ids_list and len(host_ids_list) > 0): + 'absent-in-group' and host_ids_list and + len(host_ids_list) > 0): LOG.info('Removing hosts from host group %s', hostgroup['name']) changed = ( self.remove_hostgroup_hosts( hostgroup, hosts=host_ids_list) or changed) - if state == 'present' and hostgroup and new_name: - if hostgroup['name'] != new_name: - LOG.info('Renaming host group %s to %s', hostgroup['name'], - new_name) - changed = self.rename_hostgroup(hostgroup, new_name) - if changed: - self.module.params['hostgroup_name'] = new_name + if state == 'present' and hostgroup and new_name and \ + hostgroup['name'] != new_name: + LOG.info('Renaming host group %s to %s', hostgroup['name'], + new_name) + changed = self.rename_hostgroup(hostgroup, new_name) if state == 'absent' and hostgroup: LOG.info('Deleting host group %s ', hostgroup['name']) changed = self.delete_hostgroup(hostgroup) or changed - self._create_result_dict(changed) + self._create_result_dict(changed, hostgroup_id) # Update the module's final state LOG.info('changed %s', changed) self.module.exit_json(**self.result) diff --git a/plugins/modules/dellemc_powerstore_job.py b/plugins/modules/dellemc_powerstore_job.py new file mode 100644 index 0000000..e7db54b --- /dev/null +++ b/plugins/modules/dellemc_powerstore_job.py @@ -0,0 +1,233 @@ +#!/usr/bin/python +# Copyright: (c) 2021, DellEMC +# Apache License version 2.0 (see MODULE-LICENSE or http://www.apache.org/licenses/LICENSE-2.0.txt) + +from __future__ import absolute_import, division, print_function + +__metaclass__ = type +ANSIBLE_METADATA = {'metadata_version': '1.1', + 'status': ['preview'], + 'supported_by': 'community' + } + +DOCUMENTATION = r''' +module: dellemc_powerstore_job +version_added: '1.3.0' +short_description: Manage jobs on Dell EMC PowerStore. +description: +- Managing jobs on PowerStore Storage System includes getting details of job. + +author: +- Akash Shendge (@shenda1) + +extends_documentation_fragment: + - dellemc.powerstore.dellemc_powerstore.powerstore + +options: + job_id: + description: + - The ID of the job. + type: str + required: true +''' + +EXAMPLES = r''' +- name: Get Job Details + dellemc_powerstore_job: + array_ip: "{{mgmt_ip}}" + verifycert: "{{verifycert}}" + user: "{{user}}" + password: "{{password}}" + job_id: "a544981c-e94a-40ab-9eae-e578e182d2bb" +''' + +RETURN = r''' +changed: + description: Whether or not the resource has changed. + returned: always + type: bool + +job_details: + description: The job details. + type: complex + returned: When job exists. + contains: + id: + description: Unique identifier of the job. + type: str + resource_action: + description: User-specified action to be performed on the + given resource. + type: str + resource_type: + description: Resource Type for the given resource. + type: str + resource_id: + description: Unique identifier of the resource on which the job + is operating. + type: str + resource_name: + description: Name of the resource on which the job is operating. + type: str + description_l10n: + description: Description of the job. + type: str + state: + description: Current status of the job. + type: str + start_time: + description: Date and time when the job execution started. + type: str + phase: + description: Current status of the job. + type: str + end_time: + description: Date and time when the job execution completed. + type: str + estimated_completion_time: + description: Estimated completion date and time. + type: str + progress_percentage: + description: Percent complete of the job. + type: int + parent_id: + description: Unique identifier of the parent job, if applicable. + type: str + root_id: + description: Unique identifier of the root job, if applicable. + The root job is the job at the top of the parent + hierarchy. + type: str + response_body: + description: Base response object. + type: complex + contains: + response_type: + description: Job error response. + type: str + messages: + description: The details of the error response. + type: complex + contains: + code: + description: Hexadecimal code of the error. + type: str + severity: + description: Type of the severity. + type: str + arguments: + description: Values involved in the error. + type: list + message_l10n: + description: The description of the error. + type: str + user: + description: Name of the user associated with the job. + type: str + response_status: + description: Possible HTTP status values of completed or failed + jobs. + type: str + step_order: + description: Order of a given job step with respect to its + siblings within the job hierarchy. + type: int +''' + +from ansible.module_utils.basic import AnsibleModule +from ansible_collections.dellemc.powerstore.plugins.module_utils.storage.dell\ + import dellemc_ansible_powerstore_utils as utils + +LOG = utils.get_logger('dellemc_powerstore_job') + +py4ps_sdk = utils.has_pyu4ps_sdk() +HAS_PY4PS = py4ps_sdk['HAS_Py4PS'] +IMPORT_ERROR = py4ps_sdk['Error_message'] + +py4ps_version = utils.py4ps_version_check() +IS_SUPPORTED_PY4PS_VERSION = py4ps_version['supported_version'] +VERSION_ERROR = py4ps_version['unsupported_version_message'] + +# Application type +APPLICATION_TYPE = 'Ansible/1.3.0' + + +class PowerStoreJob(object): + """Class with job operations""" + + def __init__(self): + """Define all the parameters required by this module""" + self.module_params = utils.get_powerstore_management_host_parameters() + self.module_params.update(get_powerstore_job_parameters()) + + # initialize the Ansible module + self.module = AnsibleModule( + argument_spec=self.module_params, + supports_check_mode=False + ) + + LOG.info('HAS_PY4PS = %s , IMPORT_ERROR = %s', HAS_PY4PS, + IMPORT_ERROR) + if HAS_PY4PS is False: + self.module.fail_json(msg=IMPORT_ERROR) + LOG.info('IS_SUPPORTED_PY4PS_VERSION = %s , VERSION_ERROR = ' + '%s', IS_SUPPORTED_PY4PS_VERSION, VERSION_ERROR) + if IS_SUPPORTED_PY4PS_VERSION is False: + self.module.fail_json(msg=VERSION_ERROR) + + self.conn = utils.get_powerstore_connection( + self.module.params, application_type=APPLICATION_TYPE) + self.provisioning = self.conn.provisioning + LOG.info('Got Py4ps instance for provisioning on PowerStore %s', + self.provisioning) + + def get_job_details(self, job_id): + """Get job details""" + + try: + LOG.info('Getting the details of job ID: %s', job_id) + return self.provisioning.get_job_details(job_id) + + except Exception as e: + msg = "Get details of job {0} failed with error {1}".format( + job_id, str(e)) + LOG.error(msg) + self.module.fail_json(msg=msg) + + def perform_module_operation(self): + """ Accept the parameters and get the job details """ + + job_id = self.module.params['job_id'] + + # Check for valid UUID + is_valid_uuid = utils.name_or_id(job_id) + if is_valid_uuid != "ID": + error_msg = "Please provide valid job id." + LOG.error(error_msg) + self.module.fail_json(msg=error_msg) + + job_details = self.get_job_details(job_id) + + self.module.exit_json( + changed=False, + job_details=job_details + ) + + +def get_powerstore_job_parameters(): + """This method provides the parameters required for the ansible job module + of PowerStore. """ + return dict( + job_id=dict(type='str', required=True) + ) + + +def main(): + """ Create PowerStore Job object and perform action on it + based on user input from playbook """ + obj = PowerStoreJob() + obj.perform_module_operation() + + +if __name__ == '__main__': + main() diff --git a/plugins/modules/dellemc_powerstore_local_user.py b/plugins/modules/dellemc_powerstore_local_user.py new file mode 100644 index 0000000..33540f4 --- /dev/null +++ b/plugins/modules/dellemc_powerstore_local_user.py @@ -0,0 +1,492 @@ +#!/usr/bin/python +# Copyright: (c) 2021, Dell EMC +# Apache License version 2.0 (see MODULE-LICENSE or http://www.apache.org/licenses/LICENSE-2.0.txt) + +""" Ansible module for managing local users on PowerStore""" +from __future__ import (absolute_import, division, print_function) + +__metaclass__ = type +ANSIBLE_METADATA = {'metadata_version': '1.1', + 'status': ['preview'], + 'supported_by': 'community' + } + +DOCUMENTATION = r''' +--- +module: dellemc_powerstore_local_user +version_added: '1.3.0' +short_description: Local user operations on PowerStore Storage System +description: +- Supports the provisioning operations on a Local user such as create, modify, + delete and get the details of a local user. + +extends_documentation_fragment: + - dellemc.powerstore.dellemc_powerstore.powerstore + +author: +- Arindam Datta (@dattaarindam) + +options: + user_name: + description: + - Name of the local user account. Mutually exclusive with user_id. + - Mandatory only for create operation. + type: str + user_id: + description: + - Unique identifier of the local user account. + - Mutually exclusive with user_name. + type: str + user_password: + description: + - Password for the new local user account to be created. + - Mandatory only for create operation. + type: str + new_password: + description: + - New password for the existing local user account. + type: str + role_name: + description: + - The name of the role to which the local user account will be mapped. + - It is mutually exclusive with role_id. + type: str + role_id: + description: + - The unique identifier of the role to which the local user account will be + mapped. + - It is mutually exclusive with role_name. + type: int + is_locked: + description: + - Whether the user account is locked or not. + - Defaults to false at creation time. + type: bool + state: + description: + - Define whether the local user should exist or not. + choices: ['absent', 'present'] + required: True + type: str +''' + +EXAMPLES = r''' +- name: create local user + dellemc_powerstore_local_user: + array_ip: "{{array_ip}}" + verifycert: "{{verifycert}}" + user: "{{user}}" + password: "{{password}}" + user_name: "ansible_user_1" + user_password: "Password123#" + role_name: "role_1" + is_locked: False + state: "present" + +- name: get the details local user with user id + dellemc_powerstore_local_user: + array_ip: "{{array_ip}}" + verifycert: "{{verifycert}}" + user: "{{user}}" + password: "{{password}}" + user_id: "{{user_id}}" + state: "present" + +- name: get the details local user with user name + dellemc_powerstore_local_user: + array_ip: "{{array_ip}}" + verifycert: "{{verifycert}}" + user: "{{user}}" + password: "{{password}}" + user_name: "ansible_user_1" + state: "present" + +- name: Modify attributes of local user + dellemc_powerstore_local_user: + array_ip: "{{array_ip}}" + verifycert: "{{verifycert}}" + user: "{{user}}" + password: "{{password}}" + user_name: "ansible_user_1" + user_password: "Password123#" + new_password: "Ansible123#" + role_id: 4 + is_locked: True + state: "present" + +- name: delete local user + dellemc_powerstore_local_user: + array_ip: "{{array_ip}}" + verifycert: "{{verifycert}}" + user: "{{user}}" + password: "{{password}}" + user_name: "ansible_user_1" + state: "absent" +''' + +RETURN = r''' +changed: + description: Whether or not the resource has changed + returned: always + type: bool +local_user_details: + description: Details of the local user + returned: When local user exists + type: complex + contains: + id: + description: The system generated ID given to the local user. + type: str + name: + description: Name of the local user. + type: str + is_built_in: + description: Whether the user account is built-in or not. + type: bool + is_locked: + description: Whether the user account is locked or not. Defaults + to false at creation time. + type: bool + is_default_password: + description: Whether the user account has a default password or + not. Only applies to default user accounts + type: bool + role_id: + description: Unique identifier of the role local user account is + mapped to. + type: str + role_name: + description: Name of the role to which local user account is + mapped. + type: str +''' + +from ansible.module_utils.basic import AnsibleModule +from ansible_collections.dellemc.powerstore.plugins.module_utils.storage.dell\ + import dellemc_ansible_powerstore_utils as utils + +LOG = utils.get_logger('dellemc_powerstore_local_user') + +py4ps_sdk = utils.has_pyu4ps_sdk() +HAS_PY4PS = py4ps_sdk['HAS_Py4PS'] +IMPORT_ERROR = py4ps_sdk['Error_message'] + +py4ps_version = utils.py4ps_version_check() +IS_SUPPORTED_PY4PS_VERSION = py4ps_version['supported_version'] +VERSION_ERROR = py4ps_version['unsupported_version_message'] + +# Application type +APPLICATION_TYPE = 'Ansible/1.3.0' + + +class PowerStoreLocalUser(object): + """Local user operations""" + cluster_name = None + cluster_global_id = None + valid_role_id = None + + def __init__(self): + """Define all the parameters required by this module""" + self.module_params = utils.get_powerstore_management_host_parameters() + self.module_params.update(get_powerstore_local_user_parameters()) + + mutually_exclusive = [['user_name', 'user_id'], + ['role_id', 'role_name']] + required_one_of = [['user_name', 'user_id']] + + # initialize the Ansible module + self.module = AnsibleModule( + argument_spec=self.module_params, + supports_check_mode=False, + mutually_exclusive=mutually_exclusive, + required_one_of=required_one_of + ) + msg = 'HAS_PY4PS = {0} , IMPORT_ERROR = ' \ + '{1}'.format(HAS_PY4PS, IMPORT_ERROR) + LOG.info(msg) + if HAS_PY4PS is False: + self.module.fail_json(msg=IMPORT_ERROR) + msg = 'IS_SUPPORTED_PY4PS_VERSION = {0} , ' \ + 'VERSION_ERROR = {1}'.format(IS_SUPPORTED_PY4PS_VERSION, + VERSION_ERROR) + LOG.info(msg) + if IS_SUPPORTED_PY4PS_VERSION is False: + self.module.fail_json(msg=VERSION_ERROR) + + self.conn = utils.get_powerstore_connection( + self.module.params, + application_type=APPLICATION_TYPE) + self.provisioning = self.conn.provisioning + msg = 'Got Py4ps instance for provisioning on ' \ + 'PowerStore {0}'.format(self.conn) + LOG.info(msg) + self.configuration = self.conn.config_mgmt + msg = 'Got Py4ps instance for configuration on' \ + ' PowerStore {0}'.format(self.configuration) + LOG.info(msg) + + def get_role(self, role_id=None, role_name=None): + """Get the details of a role on a PowerStore storage system""" + try: + role_details = dict() + role_dtls = None + + if role_id: + role_dtls = self.configuration.get_role_details(role_id) + + if role_name: + role_dtls = self.configuration.get_role_by_name(role_name) + + if role_dtls: + role_details['id'] = role_dtls['id'] + role_details['name'] = role_dtls['name'] + + if not role_details: + raise NameError("Specified role does not exist") + + return role_details + except Exception as e: + msg = "Failed to get the role with error {0} ".format(str(e)) + LOG.error(msg) + self.module.fail_json(msg=msg) + + def get_local_user_details(self, user_id=None, user_name=None): + """Get the details of a local user on a PowerStore storage system""" + + try: + msg = 'Getting Local User Details with user_id {0}, ' \ + 'user_name {1}'.format(user_id, user_name) + LOG.info(msg) + usr_details = None + if user_id: + usr_details = \ + self.configuration.get_local_user_details(user_id) + elif user_name: + usr_details = \ + self.configuration.get_local_user_by_name(user_name) + if usr_details: + return usr_details + + msg = 'Successfully Got Local User Details' \ + ' {0}'.format(usr_details) + LOG.info(msg) + return usr_details + + except Exception as e: + msg = 'Get local user details for PowerStore array name : ' \ + '{0} , global id : {1} failed with error' \ + ' {2} '.format(self.cluster_name, self.cluster_global_id, + str(e)) + if isinstance(e, utils.PowerStoreException) and \ + e.err_code == utils.PowerStoreException.HTTP_ERR \ + and e.status_code == "404": + LOG.info(msg) + return None + LOG.error(msg) + self.module.fail_json(msg=msg) + + def create_local_user(self, user_name): + """Create a local user.""" + try: + LOG.info("Attempting to create local user with name " + "%s", user_name) + user_password = self.module.params['user_password'] + + create_params = dict() + if user_name: + create_params['name'] = user_name + if user_password: + create_params['password'] = user_password + if self.valid_role_id: + create_params['role_id'] = self.valid_role_id + resp = self.configuration.create_local_user( + create_params=create_params) + + usr_details = None + if resp: + usr_details = self.get_local_user_details(user_id=resp['id']) + + LOG.info("Successfully Created Local User with " + "details : %s", usr_details) + + return usr_details + + except Exception as e: + msg = 'Create local user with name {0} on PowerStore array ' \ + 'name : {1} , global id : {2} failed with ' \ + 'error {3} '.format(user_name, self.cluster_name, + self.cluster_global_id, str(e)) + LOG.error(msg) + self.module.fail_json(msg=msg) + + def delete_local_user(self, user_id): + """Deletes a local_user""" + try: + LOG.info("Attempting to delete local_user id " + "%s", user_id) + + self.configuration.delete_local_user(user_id=user_id) + return True + except Exception as e: + msg = 'Failed to delete local user id {0} with ' \ + 'error {1}'.format(user_id, str(e)) + LOG.error(msg) + self.module.fail_json(msg=msg) + + def get_clusters(self): + """Get the clusters""" + try: + clusters = self.provisioning.get_cluster_list() + return clusters + + except Exception as e: + msg = 'Failed to get the clusters with ' \ + 'error {0}'.format(str(e)) + LOG.error(msg) + self.module.fail_json(msg=msg) + + def is_modify_required(self, user_details): + """To get the details of the field to be modified.""" + + try: + LOG.info("User details: %s", user_details) + modify_dict = dict() + user_password = self.module.params['user_password'] + new_password = self.module.params['new_password'] + is_locked = self.module.params['is_locked'] + + if new_password: + if not user_password: + msg = 'Please provide a valid user_password. ' \ + 'user_password along with new_password is required' \ + ' to update password.' + LOG.error(msg) + self.module.fail_json(msg=msg) + if new_password != user_password: + modify_dict['current_password'] = user_password + modify_dict['password'] = new_password + if self.valid_role_id and \ + user_details['role_id'] != self.valid_role_id: + modify_dict['role_id'] = self.valid_role_id + if is_locked is not None\ + and user_details['is_locked'] != is_locked: + modify_dict['is_locked'] = is_locked + + if modify_dict: + return modify_dict + else: + return None + + except Exception as e: + msg = 'Failed to determine if local user instance need ' \ + 'to modified with error {0}'.format(str(e)) + LOG.error(msg) + self.module.fail_json(msg=msg) + + def modify_local_user(self, user_id, modify_params): + """Perform modify operations on a local user""" + + try: + self.configuration.modify_local_user( + local_user_id=user_id, modify_parameters=modify_params) + return True + except Exception as e: + msg = 'Failed to modify local user instance ' \ + 'with error {0}'.format(str(e)) + LOG.error(msg) + self.module.fail_json(msg=msg) + + def perform_module_operation(self): + """ Perform various module operations""" + clusters = self.get_clusters() + if len(clusters) > 0: + self.cluster_name = clusters[0]['name'] + self.cluster_global_id = clusters[0]['id'] + else: + self.module.fail_json( + msg="Unable to find any active cluster on this array ") + + user_id = self.module.params['user_id'] + user_name = self.module.params['user_name'] + user_password = self.module.params['user_password'] + new_password = self.module.params['new_password'] + role_name = self.module.params['role_name'] + role_id = self.module.params['role_id'] + state = self.module.params['state'] + + # result is a dictionary to contain end state and local user details + changed = False + result = dict( + changed=False, + local_user_details=None + ) + + modify_params = None + + if role_id or role_name: + role_details = self.get_role(role_id=role_id, role_name=role_name) + self.valid_role_id = role_details.get('id') + + usr_details = self.get_local_user_details( + user_id=user_id, user_name=user_name) + if usr_details: + modify_params = self.is_modify_required(user_details=usr_details) + + if not usr_details and state == 'present': + if not (user_name and user_password and self.valid_role_id): + msg = "user_name and user_password and role " \ + "details are required to create a local user" + LOG.error(msg) + self.module.fail_json(msg=msg) + if new_password is not None: + err_msg = "new_password is not allowed during creation." + LOG.error(err_msg) + self.module.fail_json(msg=err_msg) + usr_details = self.create_local_user(user_name=user_name) + changed = True + modify_params = self.is_modify_required(user_details=usr_details) + + if state == 'present' and usr_details and modify_params: + LOG.info('attempting to modify user with id %s', + usr_details.get("id")) + changed = self.modify_local_user( + user_id=usr_details.get("id"), + modify_params=modify_params) + usr_details = self.get_local_user_details(usr_details.get("id")) + + if state == 'absent' and usr_details: + changed = self.delete_local_user(user_id=usr_details.get("id")) + usr_details = None + + if state == 'present' and usr_details: + role_details = self.get_role(role_id=usr_details['role_id']) + usr_details['role_name'] = role_details.get('name') + result['local_user_details'] = usr_details + result['changed'] = changed + self.module.exit_json(**result) + + +def get_powerstore_local_user_parameters(): + """This method provides the parameters required for the ansible + local user modules on PowerStore""" + return dict( + user_name=dict(required=False, type='str'), + user_id=dict(required=False, type='str'), + user_password=dict(required=False, type='str', no_log=True), + new_password=dict(required=False, type='str', no_log=True), + role_name=dict(required=False, type='str'), + role_id=dict(required=False, type='int'), + is_locked=dict(required=False, type='bool'), + state=dict(required=True, type='str', choices=['present', 'absent']) + ) + + +def main(): + """ Create PowerStore local_user object and perform action on it + based on user input from playbook """ + obj = PowerStoreLocalUser() + obj.perform_module_operation() + + +if __name__ == '__main__': + main() diff --git a/plugins/modules/dellemc_powerstore_nasserver.py b/plugins/modules/dellemc_powerstore_nasserver.py index b5db534..7bf4300 100644 --- a/plugins/modules/dellemc_powerstore_nasserver.py +++ b/plugins/modules/dellemc_powerstore_nasserver.py @@ -1,5 +1,7 @@ #!/usr/bin/python # Copyright: (c) 2020-2021, DellEMC +# Apache License version 2.0 (see MODULE-LICENSE or http://www.apache.org/licenses/LICENSE-2.0.txt) + """ Ansible module for managing NAS server on PowerStore""" from __future__ import (absolute_import, division, print_function) @@ -142,59 +144,61 @@ type: str current_node: description: Unique identifier and name of the node on which the - NAS server is running. + NAS server is running. type: dict preferred_node: description: Unique identifier and name of the preferred node for - the NAS server. + the NAS server. type: dict default_unix_user: description: Default Unix user name used for granting access in - case of Windows to Unix user mapping failure. + case of Windows to Unix user mapping failure. type: str current_unix_directory_service: description: Define the Unix directory service used for looking up - identity information for Unix such as UIDs, GIDs, - net groups, and so on. + identity information for Unix such as UIDs, GIDs, net + groups, and so on. type: str is_username_translation_enabled: - description: Enable the possibility to match a windows account to a - Unix account with different names. + description: Enable the possibility to match a windows account to + a Unix account with different names. type: bool production_IPv4_interface_id: description: Unique identifier of the preferred IPv4 production - interface. + interface. type: str production_IPv6_interface_id: description: Unique identifier of the preferred IPv6 production - interface. + interface. type: str backup_IPv4_interface_id: - description: Unique identifier of the preferred IPv4 backup interface. + description: Unique identifier of the preferred IPv4 backup + interface. type: str backup_IPv6_interface_id: - description: Unique identifier of the preferred IPv6 backup interface. + description: Unique identifier of the preferred IPv6 backup + interface. type: str file_interfaces: - description: This is the inverse of the resource type file_interface - association.Will return the id,name & ip_address of the - associated file interface + description: This is the inverse of the resource type + file_interface association. Will return the id,name & + ip_address of the associated file interface. type: dict nfs_servers: description: This is the inverse of the resource type nfs_server - association. + association. type: str smb_servers: description: This is the inverse of the resource type smb_server - association. + association. type: str file_ldaps: description: This is the inverse of the resource type file_ldap - association. + association. type: str file_systems: description: This is the inverse of the resource type file_system - association. + association. type: dict ''' @@ -216,7 +220,7 @@ VERSION_ERROR = py4ps_version['unsupported_version_message'] # Application type -APPLICATION_TYPE = 'Ansible/1.2.0' +APPLICATION_TYPE = 'Ansible/1.3.0' class PowerStoreNasServer(object): diff --git a/plugins/modules/dellemc_powerstore_network.py b/plugins/modules/dellemc_powerstore_network.py new file mode 100644 index 0000000..70a4efc --- /dev/null +++ b/plugins/modules/dellemc_powerstore_network.py @@ -0,0 +1,874 @@ +#!/usr/bin/python +# Copyright: (c) 2021, DellEMC +# Apache License version 2.0 (see MODULE-LICENSE or http://www.apache.org/licenses/LICENSE-2.0.txt) + +from __future__ import absolute_import, division, print_function + +__metaclass__ = type +ANSIBLE_METADATA = {'metadata_version': '1.1', + 'status': ['preview'], + 'supported_by': 'community' + } + +DOCUMENTATION = r''' +module: dellemc_powerstore_network +version_added: '1.3.0' +short_description: Manage networks on Dell EMC PowerStore. +description: +- Managing networks on PowerStore Storage System includes getting details of + network, modifying attributes of network and adding/removing IP ports + to/from storage network. + +author: +- Akash Shendge (@shenda1) + +extends_documentation_fragment: + - dellemc.powerstore.dellemc_powerstore.powerstore + +options: + network_name: + description: + - The name of the network. + - This parameter is added in 2.0.0.0. + - Specify either network_name or network_id for any operation. + type: str + network_id: + description: + - The ID of the network. + type: str + vlan_id: + description: + - The ID of the VLAN. + type: int + gateway: + description: + - Network gateway in IPv4 or IPv6 format, corresponding to the network's + IP version. + - Specify empty string to remove the gateway. + type: str + prefix_length: + description: + - Network prefix length. + type: int + new_cluster_mgmt_address: + description: + - New cluster management IP address in IPv4 or IPv6 format, corresponding + to the network's IP version. + type: str + storage_discovery_address: + description: + - New storage discovery IP address in IPv4 or IPv6 format, corresponding + to the network's IP version. + - Specify empty string to remove the storage discovery IP address. + type: str + mtu: + description: + - Maximum Transmission Unit (MTU) packet size set on network interfaces, + in bytes. + type: int + new_name: + description: + - New name of the network. + type: str + addresses: + description: + - IP addresses to add/remove in IPv4 or IPv6 format. + type: list + elements: dict + suboptions: + current_address: + description: + - Existing IPv4/IPv6 address. + type: str + new_address: + description: + - New IPv4/IPv6 address. + type: str + ports: + description: + - Ports to be mapped/unmapped to/from the storage network. + type: list + elements: str + port_state: + description: + - Specifies whether port should mapped/unmapped from the storage network. + type: str + choices: ['present-in-network', 'absent-in-network'] + vasa_provider_credentials: + description: + - Credentials required for re-registering the VASA vendor provider during + the reconfiguration of the cluster management IP address. + type: dict + suboptions: + username: + description: + - VASA vendor provider user name. + type: str + required: True + password: + description: + - VASA vendor provider password. + type: str + required: True + esxi_credentials: + description: + - Credentials required for re-registering the ESXi hosts in the vCenter. + - It should be passed only when ESXi host addresses or management network + VLAN / prefix / gateway are changed during the reconfiguration of the + PowerStore X model appliances. + - This parameter is applicable only for PowerStore X model. + - This parameter will be ignored if passed for PowerStore T model. + type: list + elements: dict + suboptions: + node_id: + description: + - Node identifier corresponding to the ESXi host. + type: str + required: True + password: + description: + - ESXi host root password. + type: str + required: True + wait_for_completion: + description: + - Flag to indicate if the operation should be run synchronously or + asynchronously. True signifies synchronous execution. By default, + modify operation will run asynchronously. + default: False + type: bool + state: + description: + - Define whether the network exist or not. + required: true + choices: ['absent', 'present'] + type: str +notes: +- It is recommended to perform task asynchronously while changing cluster + management address. +- Idempotency is not supported for vasa_provider_credentials and + esxi_credentials. +- For PowerStore X model, vasa_provider_credentials has to be specified + along with new_cluster_mgmt_address. +''' + +EXAMPLES = r''' +- name: Get network details using ID + dellemc_powerstore_network: + array_ip: "{{array_ip}}" + verifycert: "{{verifycert}}" + user: "{{user}}" + password: "{{password}}" + network_id: "NW1" + state: "present" + +- name: Get network details using name + dellemc_powerstore_network: + array_ip: "{{array_ip}}" + verifycert: "{{verifycert}}" + user: "{{user}}" + password: "{{password}}" + network_name: "Default Management Network" + state: "present" + +- name: Rename the storage network + dellemc_powerstore_network: + array_ip: "{{array_ip}}" + verifycert: "{{verifycert}}" + user: "{{user}}" + password: "{{password}}" + network_name: "Default Storage Network" + new_name: "iSCSI Network" + wait_for_completion: True + state: "present" + +- name: Replace the IP's in the management network and re-register VASA vendor provider + dellemc_powerstore_network: + array_ip: "{{array_ip}}" + verifycert: "{{verifycert}}" + user: "{{user}}" + password: "{{password}}" + network_id: "NW1" + addresses: + - current_address: "100.230.x.x" + new_address: "100.230.x.x" + - current_address: "100.230.x.x" + new_address: "100.230.x.x" + - current_address: "100.230.x.x" + new_address: "100.230.x.x" + new_cluster_mgmt_address: "100.230.x.x" + vasa_provider_credentials: + username: "vmadmin" + password: "{{vm_password}}" + state: "present" + +- name: Map port to the storage network + dellemc_powerstore_network: + array_ip: "{{array_ip}}" + verifycert: "{{verifycert}}" + user: "{{user}}" + password: "{{password}}" + network_id: "NW6" + ports: + - "IP1" + port_state: "present-in-network" + state: "present" + +- name: Unmap port from the storage network + dellemc_powerstore_network: + array_ip: "{{array_ip}}" + verifycert: "{{verifycert}}" + user: "{{user}}" + password: "{{password}}" + network_id: "NW6" + ports: + - "IP1" + port_state: "absent-in-network" + state: "present" + +- name: Replace the IP's in the management network and re-register VASA vendor + provider for X model + dellemc_powerstore_network: + array_ip: "{{array_ip1}}" + verifycert: "{{verifycert}}" + user: "{{user}}" + password: "{{password}}" + network_id: "NW1" + vlan_id: 0 + gateway: "100.231.x.x" + mtu: 1500 + prefix_length: 24 + addresses: + - current_address: "100.230.x.x" + new_address: "100.231.x.x" + - current_address: "100.230.x.x" + new_address: "100.231.x.x" + - current_address: "100.230.x.x" + new_address: "100.231.x.x" + - current_address: "100.230.x.x" + new_address: "100.231.x.x" + - current_address: "100.230.x.x" + new_address: "100.231.x.x" + new_cluster_mgmt_address: "100.231.x.x" + vasa_provider_credentials: + username: "vmadmin" + password: "{{vm_password}}" + esxi_credentials: + - "node_id": "N1" + "password": "{{node_password}}" + - "node_id": "N2" + "password": "{{node_password}}" + state: "present" +''' + +RETURN = r''' +changed: + description: Whether or not the resource has changed. + returned: always + type: bool + +job_details: + description: The job details. + type: complex + returned: When asynchronous task is performed. + contains: + id: + description: The ID of the job. + type: str + +network_details: + description: The network details. + type: complex + returned: When network exists. + contains: + name: + description: The name of the network. + type: str + id: + description: The ID of the network. + type: str + gateway: + description: The gateway of the network. + type: str + vlan_id: + description: VLAN identifier. + type: int + prefix_length: + description: Network prefix length. + type: int + mtu: + description: Maximum Transmission Unit (MTU) packet size set on + network interfaces, in bytes. + type: int + ip_version: + description: IP protocol version + type: str + type: + description: Network type + type: str + purposes: + description: Purposes of the network. + type: list + cluster_details: + description: The details of the cluster. + type: complex + contains: + id: + description: The unique identifier of the cluster. + type: str + name: + description: The name of the cluster. + type: str + management_address: + description: The floating management IP address for the + cluster in IPv4 or IPv6 format. + type: str + storage_discovery_address: + description: The floating storage discovery IP address for + the cluster in IPv4 or IPv6 format. + type: str + appliance_count: + description: Number of appliances configured in this + cluster. + type: int + member_ips: + description: Properties of the IP pool address. + type: complex + contains: + id: + description: Unique identifier of the IP address. + type: str + name: + description: Name of the IP address. + type: str + network_id: + description: Unique identifier of the network to which the + IP address belongs. + type: str + ip_port_id: + description: Unique identifier of the port that uses this + IP address to provide access to storage + network services, such as iSCSI. This + attribute can be set only for an IP address + used by networks of type Storage. + type: str + appliance_id: + description: Unique identifier of the appliance to which + the IP address belongs. + type: str + node_id: + description: Unique identifier of the cluster node to + which the IP address belongs. + type: str + address: + description: IP address value, in IPv4 or IPv6 format. + type: str + purposes: + description: IP address purposes. + type: list + vcenter_details: + description: Details of the vcenter. + type: complex + contains: + address: + description: IP address of vCenter host, in IPv4, IPv6, + or hostname format. + type: str + id: + description: Unique identifier of the vCenter instance. + type: str + instance_uuid: + description: UUID instance of the vCenter. + type: str + username: + description: User name to login to vCenter. + type: str + vendor_provider_status: + description: General status of the VASA vendor provider + in vCenter. + type: str +''' + +from ansible.module_utils.basic import AnsibleModule +from ansible_collections.dellemc.powerstore.plugins.module_utils.storage.dell\ + import dellemc_ansible_powerstore_utils as utils + +LOG = utils.get_logger('dellemc_powerstore_network') + +py4ps_sdk = utils.has_pyu4ps_sdk() +HAS_PY4PS = py4ps_sdk['HAS_Py4PS'] +IMPORT_ERROR = py4ps_sdk['Error_message'] + +py4ps_version = utils.py4ps_version_check() +IS_SUPPORTED_PY4PS_VERSION = py4ps_version['supported_version'] +VERSION_ERROR = py4ps_version['unsupported_version_message'] + +# Application type +APPLICATION_TYPE = 'Ansible/1.3.0' + + +class PowerStoreNetwork(object): + """Class with network operations""" + + def __init__(self): + """Define all the parameters required by this module""" + self.module_params = utils.get_powerstore_management_host_parameters() + self.module_params.update(get_powerstore_network_parameters()) + + # initialize the Ansible module + mut_ex_args = [['network_id', 'network_name']] + required_one_of = [['network_id', 'network_name']] + required_together = [['ports', 'port_state']] + + self.module = AnsibleModule( + argument_spec=self.module_params, + supports_check_mode=False, + mutually_exclusive=mut_ex_args, + required_one_of=required_one_of, + required_together=required_together + ) + + LOG.info('HAS_PY4PS = %s , IMPORT_ERROR = %s', HAS_PY4PS, + IMPORT_ERROR) + if HAS_PY4PS is False: + self.module.fail_json(msg=IMPORT_ERROR) + LOG.info('IS_SUPPORTED_PY4PS_VERSION = %s , VERSION_ERROR = ' + '%s', IS_SUPPORTED_PY4PS_VERSION, VERSION_ERROR) + if IS_SUPPORTED_PY4PS_VERSION is False: + self.module.fail_json(msg=VERSION_ERROR) + + self.conn = utils.get_powerstore_connection( + self.module.params, application_type=APPLICATION_TYPE) + self.configuration = self.conn.config_mgmt + self.provisioning = self.conn.provisioning + LOG.info('Got Py4ps instance for configuration on PowerStore %s', + self.configuration) + + def get_member_ips(self, network_id): + """ Get IP members of the network """ + + try: + filters = {'network_id': 'eq.' + network_id} + return self.configuration.get_ip_pool_address(filters) + except Exception as e: + msg = 'Failed to get the member IPs of {0} with ' \ + 'error {1}'.format(network_id, str(e)) + LOG.error(msg) + self.module.fail_json(msg=msg) + + def get_cluster_details(self): + """ Get cluster details """ + + try: + cluster_details = self.configuration.get_clusters() + if cluster_details: + return self.configuration.get_cluster_details( + cluster_details[0]['id']) + except Exception as e: + msg = 'Failed to get the cluster details with error {0}'.format( + str(e)) + LOG.error(msg) + self.module.fail_json(msg=msg) + + def get_vcenter_details(self): + """ Get vcenter details """ + + try: + vcenter_details = self.configuration.get_vcenters() + if vcenter_details: + return self.configuration.get_vcenter_details( + vcenter_details[0]['id']) + except Exception as e: + msg = 'Failed to get the vcenter details with error {0}'.format( + str(e)) + LOG.error(msg) + self.module.fail_json(msg=msg) + + def get_network_details(self, network_name=None, network_id=None): + """ Get network details by name or id""" + + try: + LOG.info('Getting the details of network , Name:%s ,' + ' ID:%s', network_name, network_id) + if network_id: + resp = self.configuration.get_network_details(network_id) + else: + resp = self.configuration.get_network_by_name( + name=network_name) + if resp and len(resp) > 0: + resp = resp[0] + + if resp: + resp['member_ips'] = self.get_member_ips(resp['id']) + resp['cluster_details'] = self.get_cluster_details() + resp['vcenter_details'] = self.get_vcenter_details() + return resp + return None + except Exception as e: + name_or_id = network_name if network_name else network_id + msg = "Get details of network {0} failed with error {1}".format( + name_or_id, str(e)) + if isinstance(e, utils.PowerStoreException) and \ + e.err_code == utils.PowerStoreException.HTTP_ERR and \ + e.status_code == "404": + LOG.info(msg) + return None + LOG.error(msg) + self.module.fail_json(msg=msg) + + def add_ports_to_network(self, network_details, ports): + """ Add IP ports to the storage network """ + + existing_ports = [] + + if network_details['member_ips']: + for ip in network_details['member_ips']: + if ip['ip_port_id']: + existing_ports.append(ip['ip_port_id']) + + ports_to_add = list(set(ports) - set(existing_ports)) + + if len(ports_to_add) == 0: + return False + + try: + LOG.info("Ports to add: %s", ports_to_add) + self.configuration.add_remove_ports(network_details['id'], + add_port_ids=ports_to_add) + return True + except Exception as e: + errormsg = "Add existing IP ports to storage network {0} failed" \ + " with error {1}".format(network_details['id'], str(e)) + LOG.error(errormsg) + self.module.fail_json(msg=errormsg) + + def remove_ports_from_network(self, network_details, ports): + """ Remove IP ports from the storage network """ + + existing_ports = [] + + if network_details['member_ips']: + for ip in network_details['member_ips']: + if ip['ip_port_id']: + existing_ports.append(ip['ip_port_id']) + + ports_to_remove = list(set(ports).intersection(set(existing_ports))) + + if len(ports_to_remove) == 0: + return False + + try: + LOG.info("Ports to remove: %s", ports_to_remove) + self.configuration.add_remove_ports( + network_details['id'], remove_port_ids=ports_to_remove) + return True + except Exception as e: + errormsg = "Remove existing IP ports from storage network {0} " \ + "failed with error {1}".format(network_details['id'], + str(e)) + LOG.error(errormsg) + self.module.fail_json(msg=errormsg) + + def modify_network(self, network_id, wait_for_completion, + network_modify_dict): + """Modify network properties""" + + if wait_for_completion: + is_async = False + else: + is_async = True + + try: + LOG.info("Modify network properties") + job_dict = self.configuration.modify_network( + network_id, network_modify_dict, is_async) + return True, job_dict + except Exception as e: + errormsg = "Modify operation of network with id: {0}, failed " \ + "with error {1}".format(network_id, str(e)) + LOG.error(errormsg) + self.module.fail_json(msg=errormsg) + + def register_vasa_provider(self, vcenter_id, vasa_provider_credentials): + """Register VASA provider""" + + try: + LOG.info("Register VASA provider") + modify_dict = dict() + modify_dict['vasa_provider_credentials'] = \ + vasa_provider_credentials + self.configuration.modify_vcenter(vcenter_id=vcenter_id, + modify_param_dict=modify_dict) + return True + except Exception as e: + errormsg = "VASA provider registration of vcenter with id: {0}," \ + " failed with error {1}".format(vcenter_id, str(e)) + LOG.error(errormsg) + self.module.fail_json(msg=errormsg) + + def check_array_version(self, network_name): + """Verify PowerStore array version""" + + try: + foot_hill_version = '2.0.0.0' + release_version = self.provisioning.get_array_version() + + if release_version and network_name and ( + utils.parse_version(release_version) < + utils.parse_version(foot_hill_version)): + error_message = 'Please provide network_id. Network name ' \ + 'can be used with PowerStore ' \ + 'release >= 2.0.0.0.' + LOG.error(error_message) + self.module.fail_json(msg=error_message) + except Exception as e: + error_msg = "Failed to get the array version with error " \ + "{0}".format(str(e)) + LOG.error(error_msg) + self.module.fail_json(msg=error_msg) + + def validate_parameters(self): + """Validate the input parameters""" + + param_list = ['network_name', 'new_name'] + param_list1 = ['network_id', 'gateway', 'storage_discovery_address'] + msg = "Please provide valid {0}" + + for param in param_list: + if self.module.params[param] is not None and len( + self.module.params[param].strip()) == 0: + error_msg = msg.format(param) + self.module.fail_json(msg=error_msg) + + # Check for spaces + for param in param_list1: + if self.module.params[param] is not None and \ + self.module.params[param].count(" ") > 0: + error_msg = msg.format(param) + self.module.fail_json(msg=error_msg) + + # Check for valid addresses + addresses = self.module.params['addresses'] + if addresses: + for address_dict in addresses: + if 'current_address' in address_dict and \ + address_dict['current_address'] is not None and \ + len(address_dict['current_address'].strip()) == 0: + error_msg = "Please provide valid current address." + self.module.fail_json(msg=error_msg) + + if 'new_address' in address_dict and \ + address_dict['new_address'] is not None and \ + len(address_dict['new_address'].strip()) == 0: + error_msg = "Please provide valid new address." + self.module.fail_json(msg=error_msg) + + def perform_module_operation(self): + """ + Perform different actions on network based on user parameters + chosen in playbook + """ + network_name = self.module.params['network_name'] + network_id = self.module.params['network_id'] + vlan_id = self.module.params['vlan_id'] + gateway = self.module.params['gateway'] + prefix_length = self.module.params['prefix_length'] + mtu = self.module.params['mtu'] + new_name = self.module.params['new_name'] + storage_discovery_address = self.module.params[ + 'storage_discovery_address'] + addresses = self.module.params['addresses'] + ports = self.module.params['ports'] + port_state = self.module.params['port_state'] + new_cluster_mgmt_address = self.module.params['new_cluster_mgmt_address'] + vasa_provider_credentials = self.module.params[ + 'vasa_provider_credentials'] + esxi_credentials = self.module.params['esxi_credentials'] + wait_for_completion = self.module.params['wait_for_completion'] + state = self.module.params['state'] + + # result is a dictionary to contain end state and network details + result = dict( + changed=False, + network_details=None + ) + + self.check_array_version(network_name) + + self.validate_parameters() + + # Get the details of Network + network_details = self.get_network_details(network_name, network_id) + if not network_id and network_details: + network_id = network_details['id'] + + if state == 'present' and not network_details: + error_message = 'Network not found - Creation of network is ' \ + 'not allowed through Ansible module.' + LOG.error(error_message) + self.module.fail_json(msg=error_message) + + if state == 'absent' and network_details: + error_message = 'Deletion of network is not allowed through ' \ + 'Ansible module.' + LOG.error(error_message) + self.module.fail_json(msg=error_message) + + if state == 'present' and not network_details['vcenter_details'] and \ + vasa_provider_credentials: + error_message = "Please configure the vCenter server." + LOG.error(error_message) + self.module.fail_json(msg=error_message) + + # Check if modification to the network is required + if state == 'present' and network_details: + new_network_param_dict = { + "vlan_id": vlan_id, + "gateway": gateway, + "prefix_length": prefix_length, + "mtu": mtu, + "name": new_name + } + + network_modify_dict = check_network_modified( + network_details, new_network_param_dict, + new_cluster_mgmt_address, storage_discovery_address, + addresses, vasa_provider_credentials, esxi_credentials) + + # Check if IP ports can be added to storage network + if state == 'present' and port_state == 'present-in-network' \ + and network_details and ports: + result['changed'] = self.add_ports_to_network(network_details, + ports) + # Check if IP ports can be removed from storage network + elif state == 'present' and port_state == 'absent-in-network' \ + and network_details and ports: + result['changed'] = self.remove_ports_from_network( + network_details, ports) + + # Register VASA provider + if state == 'present' and network_details['vcenter_details'] and \ + 'vendor_provider_status' in \ + network_details['vcenter_details'] and \ + network_details['vcenter_details']['vendor_provider_status'] \ + == 'Not_Registered': + result['changed'] = self.register_vasa_provider( + network_details['vcenter_details']['id'], + vasa_provider_credentials) + + # Modify Network Properties + if state == 'present' and network_details and network_modify_dict: + result['changed'], job_dict = self.modify_network( + network_id, wait_for_completion, network_modify_dict) + + # Finally update the module result! + if state == 'present' and result['changed'] and network_modify_dict \ + and not wait_for_completion: + result['job_details'] = job_dict + else: + result['network_details'] = self.get_network_details( + network_name, network_id) + self.module.exit_json(**result) + + +def check_network_modified(network_details, new_network_param_dict=None, + new_cluster_mgmt_address=None, + storage_discovery_address=None, + addresses=None, vasa_provider_credentials=None, + esxi_credentials=None): + """Check if modification is required for network""" + + LOG.info("Checking if modification is required for network") + modify_param = dict() + + for key in new_network_param_dict.keys(): + if key in network_details and \ + new_network_param_dict[key] is not None and \ + new_network_param_dict[key] != network_details[key]: + modify_param[key] = new_network_param_dict[key] + + existing_address = network_details['cluster_details'][ + 'storage_discovery_address'] + if (storage_discovery_address is not None and + existing_address != storage_discovery_address) and \ + ((existing_address is None and storage_discovery_address != "") + or (existing_address is not None)): + modify_param['storage_discovery_address'] = storage_discovery_address + + if new_cluster_mgmt_address is not None and \ + network_details['cluster_details']['management_address'] != \ + new_cluster_mgmt_address: + modify_param['cluster_mgmt_address'] = new_cluster_mgmt_address + + if addresses is not None: + existing_addresses = [ip['address'] for ip in + network_details['member_ips']] + addresses_to_add = [] + addresses_to_remove = [] + for address_dict in addresses: + if address_dict['current_address'] and address_dict['new_address']: + if address_dict['current_address'] in existing_addresses: + addresses_to_add.append(address_dict['new_address']) + addresses_to_remove.append(address_dict['current_address'] + ) + elif address_dict['current_address'] and \ + address_dict['current_address'] in existing_addresses: + addresses_to_remove.append(address_dict['current_address']) + elif address_dict['new_address'] and \ + address_dict['new_address'] not in existing_addresses: + addresses_to_add.append(address_dict['new_address']) + + if addresses_to_add: + modify_param['add_addresses'] = addresses_to_add + if addresses_to_remove: + modify_param['remove_addresses'] = addresses_to_remove + + if vasa_provider_credentials is not None: + modify_param['vasa_provider_credentials'] = vasa_provider_credentials + + if esxi_credentials is not None: + modify_param['esxi_credentials'] = esxi_credentials + + return modify_param + + +def get_powerstore_network_parameters(): + """This method provide the parameters required for the network operations + on PowerStore""" + + return dict( + network_name=dict(), network_id=dict(), vlan_id=dict(type='int'), + gateway=dict(), prefix_length=dict(type='int'), new_name=dict(), + mtu=dict(type='int'), new_cluster_mgmt_address=dict(), + addresses=dict(type='list', elements='dict', + options=dict(current_address=dict(type='str'), + new_address=dict(type='str'))), + vasa_provider_credentials=dict( + type='dict', options=dict( + username=dict(type='str', required=True), + password=dict(type='str', required=True, no_log=True))), + esxi_credentials=dict(type='list', elements='dict', + options=dict(node_id=dict(type='str', + required=True), + password=dict(type='str', + required=True, + no_log=True))), + storage_discovery_address=dict(), + ports=dict(type='list', elements='str'), + port_state=dict(choices=['present-in-network', + 'absent-in-network']), + wait_for_completion=dict(type='bool', default=False), + state=dict(required=True, type='str', choices=['present', 'absent']) + ) + + +def main(): + """ Create PowerStore network object and perform action on it based on + user input from playbook """ + obj = PowerStoreNetwork() + obj.perform_module_operation() + + +if __name__ == '__main__': + main() diff --git a/plugins/modules/dellemc_powerstore_nfs.py b/plugins/modules/dellemc_powerstore_nfs.py index f5d2f31..3a29673 100644 --- a/plugins/modules/dellemc_powerstore_nfs.py +++ b/plugins/modules/dellemc_powerstore_nfs.py @@ -1,5 +1,6 @@ #!/usr/bin/python # Copyright: (c) 2020-2021, DellEMC +# Apache License version 2.0 (see MODULE-LICENSE or http://www.apache.org/licenses/LICENSE-2.0.txt) """Ansible module for managing NFS exports on PowerStore""" @@ -262,14 +263,15 @@ description: The user ID of the anonymous account. type: int default_access: - description: Default access level for all hosts that can access the export. + description: Default access level for all hosts that can access + the export. type: str description: description: The description for the NFS export. type: str file_system: description: Details of filesystem and NAS server on which NFS - export is present. + export is present. type: complex contains: id: @@ -295,11 +297,12 @@ description: The ID of the NFS export. type: str is_no_SUID: - description: If set, do not allow access to set SUID. Otherwise, allow - access. + description: If set, do not allow access to set SUID. Otherwise, + allow access. type: bool min_security: - description: NFS enforced security type for users accessing an NFS export. + description: NFS enforced security type for users accessing an NFS + export. type: str name: description: The name of the NFS export. @@ -314,14 +317,15 @@ description: Hosts with read-only access to the NFS export. type: list read_only_root_hosts: - description: Hosts with read-only for root user access to the NFS export. + description: Hosts with read-only for root user access to the NFS + export. type: list read_write_hosts: description: Hosts with read and write access to the NFS export. type: list read_write_root_hosts: - description: Hosts with read and write for root user access to the NFS - export. + description: Hosts with read and write for root user access to the + NFS export. type: list """ @@ -348,7 +352,7 @@ VERSION_ERROR = py4ps_version['unsupported_version_message'] # Application type -APPLICATION_TYPE = 'Ansible/1.2.0' +APPLICATION_TYPE = 'Ansible/1.3.0' class PowerStoreNfsExport(object): @@ -686,22 +690,22 @@ def check_add_hosts(self, export_details): ipv4_host = self.get_ipv4_host(host) # Check if given host is member of already added # network - if ipv4_host not in ipv4_hosts: - if str(ipv4_host) not in hosts_to_add: - hosts_to_add.append(str(ipv4_host)) + if ipv4_host not in ipv4_hosts and \ + str(ipv4_host) not in hosts_to_add: + hosts_to_add.append(str(ipv4_host)) else: # IPv6 host is provided ipv6_host = self.get_ipv6_host(host) # Check if given host is member of already added # network - if ipv6_host not in ipv6_hosts: - if str(ipv6_host) not in hosts_to_add: - hosts_to_add.append(str(ipv6_host)) + if ipv6_host not in ipv6_hosts and \ + str(ipv6_host) not in hosts_to_add: + hosts_to_add.append(str(ipv6_host)) else: # FQDN/Netgroup is provided - if host not in fqdn_hosts: - if host not in hosts_to_add: - hosts_to_add.append(host) + if host not in fqdn_hosts and \ + host not in hosts_to_add: + hosts_to_add.append(host) if hosts_to_add: if host_type == "read_only_root_hosts": export_details[host_type].extend(hosts_to_add) @@ -737,26 +741,24 @@ def check_remove_hosts(self, export_details): ipv4_host = self.get_ipv4_host(host) # Check if given host is member of already added # network - if ipv4_host in ipv4_hosts: - if str(ipv4_host.with_netmask) not in \ - hosts_to_remove: - hosts_to_remove.append( - str(ipv4_host.with_netmask)) + if ipv4_host in ipv4_hosts and \ + str(ipv4_host.with_netmask) not in \ + hosts_to_remove: + hosts_to_remove.append(str(ipv4_host.with_netmask)) else: # IPv6 host is provided ipv6_host = self.get_ipv6_host(host) # Check if given host is member of already added # network - if ipv6_host in ipv6_hosts: - if str(ipv6_host.with_prefixlen) not in \ - hosts_to_remove: - hosts_to_remove.append( - str(ipv6_host.with_prefixlen)) + if ipv6_host in ipv6_hosts and \ + str(ipv6_host.with_prefixlen) not in \ + hosts_to_remove: + hosts_to_remove.append( + str(ipv6_host.with_prefixlen)) else: # FQDN/Netgroup is provided - if host in fqdn_hosts: - if host not in hosts_to_remove: - hosts_to_remove.append(host) + if host in fqdn_hosts and host not in hosts_to_remove: + hosts_to_remove.append(host) if hosts_to_remove: remove_host_dict['remove_' + host_type] = hosts_to_remove @@ -777,11 +779,12 @@ def check_nfs_export_modified(self, export_details, description, elif self.module.params['host_state'] == 'absent-in-export': modify_param = self.check_remove_hosts(export_details) - if description is not None and export_details['description'] != \ - description: - if (export_details['description'] is None and description != "") \ - or (export_details['description'] is not None): - modify_param['description'] = description + if (description is not None and export_details['description'] + != description) and \ + ((export_details['description'] is None + and description != "") or (export_details['description'] + is not None)): + modify_param['description'] = description if default_access: access = self.get_enum_keys(default_access) @@ -793,17 +796,17 @@ def check_nfs_export_modified(self, export_details, description, if export_details['min_security'] != security: modify_param['min_security'] = security - if anonymous_uid is not None: - if anonymous_uid != export_details['anonymous_UID']: - modify_param['anonymous_UID'] = anonymous_uid + if anonymous_uid is not None and \ + anonymous_uid != export_details['anonymous_UID']: + modify_param['anonymous_UID'] = anonymous_uid - if anonymous_gid is not None: - if anonymous_gid != export_details['anonymous_GID']: - modify_param['anonymous_GID'] = anonymous_gid + if anonymous_gid is not None and \ + anonymous_gid != export_details['anonymous_GID']: + modify_param['anonymous_GID'] = anonymous_gid - if is_no_suid is not None: - if is_no_suid != export_details['is_no_SUID']: - modify_param['is_no_SUID'] = is_no_suid + if is_no_suid is not None and \ + is_no_suid != export_details['is_no_SUID']: + modify_param['is_no_SUID'] = is_no_suid LOG.info("NFS modification details: %s", modify_param) return modify_param @@ -904,16 +907,10 @@ def perform_module_operation(self): path = self.module.params['path'] description = self.module.params['description'] default_access = self.module.params['default_access'] - no_access_hosts = self.module.params['no_access_hosts'] - read_only_hosts = self.module.params['read_only_hosts'] - read_only_root_hosts = self.module.params['read_only_root_hosts'] - read_write_hosts = self.module.params['read_write_hosts'] - read_write_root_hosts = self.module.params['read_write_root_hosts'] min_security = self.module.params['min_security'] anonymous_uid = self.module.params['anonymous_uid'] anonymous_gid = self.module.params['anonymous_gid'] is_no_suid = self.module.params['is_no_suid'] - host_state = self.module.params['host_state'] state = self.module.params['state'] result = dict( @@ -984,12 +981,10 @@ def match_nfs_export(exports_list, export_parent, nas_server, path): for export in exports_list: flag = True - if nas_server and not is_nas_server_matched(nas_server, export): - flag = False - elif export_parent and not is_export_parent_matched(export_parent, - export): - flag = False - elif path and not is_path_matched(path, export): + if (nas_server and not is_nas_server_matched(nas_server, export)) or \ + (export_parent and not is_export_parent_matched( + export_parent, export)) or \ + (path and not is_path_matched(path, export)): flag = False if flag: return export diff --git a/plugins/modules/dellemc_powerstore_protectionpolicy.py b/plugins/modules/dellemc_powerstore_protectionpolicy.py index 03d4456..f05289f 100644 --- a/plugins/modules/dellemc_powerstore_protectionpolicy.py +++ b/plugins/modules/dellemc_powerstore_protectionpolicy.py @@ -1,5 +1,6 @@ #!/usr/bin/python # Copyright: (c) 2019-2021, DellEMC +# Apache License version 2.0 (see MODULE-LICENSE or http://www.apache.org/licenses/LICENSE-2.0.txt) from __future__ import absolute_import, division, print_function @@ -14,14 +15,15 @@ module: dellemc_powerstore_protectionpolicy version_added: '1.0.0' -short_description: Perform Protection policy operations on PowerStore storage system +short_description: Perform Protection policy operations on PowerStore storage + system. description: -- Performs all protection policy operations on PowerStore Storage System. -- This modules supports get details of an existing protection policy. -- Create new protection policy with existing Snapshot Rule or replication rule. -- Modify protection policy to change the name and description, and - add or remove existing snapshot rules/ replication rule. -- Delete an existing protection policy. +- Performs all protection policy operations on PowerStore Storage System. This + modules supports get details of an existing protection policy. Create new + protection policy with existing Snapshot Rule or replication rule. Modify + protection policy to change the name and description, and add or remove + existing snapshot rules/ replication rule. Delete an existing protection + policy. extends_documentation_fragment: - dellemc.powerstore.dellemc_powerstore.powerstore author: @@ -52,8 +54,8 @@ elements: str replicationrule: description: - - The name or ids of the replcation rule which is to be added to the protection - policy. + - The name or ids of the replcation rule which is to be added to the + protection policy. - To remove the replication rule, an empty string has to be passed. required: False type: str @@ -82,8 +84,8 @@ choices: [ present-in-policy, absent-in-policy] type: str notes: -- Before deleting a protection policy, the replication rule has to be removed from - the protection policy. +- Before deleting a protection policy, the replication rule has to be removed + from the protection policy. ''' EXAMPLES = r''' @@ -171,7 +173,7 @@ RETURN = r''' changed: - description: Whether or not the resource has changed + description: Whether or not the resource has changed. returned: always type: bool @@ -181,36 +183,42 @@ type: complex contains: id: - description: The system generated ID given to the protection policy + description: The system generated ID given to the protection + policy. type: str name: - description: Name of the protection policy + description: Name of the protection policy. type: str description: - description: description about the protection policy + description: description about the protection policy. type: str type: description: The type for the protection policy type: str replication_rules: - description: The replication rule details of the protection policy + description: The replication rule details of the protection + policy. type: complex contains: id: - description: The replication rule ID of the protection policy + description: The replication rule ID of the protection + policy. type: str name: - description: The replication rule name of the protection policy + description: The replication rule name of the protection + policy. type: str snapshot_rules: - description: The snapshot rules details of the protection policy + description: The snapshot rules details of the protection policy. type: complex contains: id: - description: The snapshot rule ID of the protection policy + description: The snapshot rule ID of the protection + policy. type: str name: - description: The snapshot rule name of the protection policy + description: The snapshot rule name of the protection + policy. type: str ''' @@ -232,7 +240,7 @@ VERSION_ERROR = py4ps_version['unsupported_version_message'] # Application type -APPLICATION_TYPE = 'Ansible/1.2.0' +APPLICATION_TYPE = 'Ansible/1.3.0' class PowerstoreProtectionpolicy(object): diff --git a/plugins/modules/dellemc_powerstore_quota.py b/plugins/modules/dellemc_powerstore_quota.py index 841e13a..da6eb8e 100644 --- a/plugins/modules/dellemc_powerstore_quota.py +++ b/plugins/modules/dellemc_powerstore_quota.py @@ -1,5 +1,7 @@ #!/usr/bin/python # Copyright: (c) 2020-2021, DellEMC +# Apache License version 2.0 (see MODULE-LICENSE or http://www.apache.org/licenses/LICENSE-2.0.txt) + """ Ansible module for managing Tree Quotas and User Quotas on PowerStore""" from __future__ import (absolute_import, division, print_function) @@ -123,17 +125,17 @@ choices: ['absent', 'present'] notes: -- Tree quota can not be created at the root of the filesystem. +- Tree quota can't be created at the root of the filesystem. - When the ID of the filesystem is passed then nas_server is not required. If passed, then filesystem should exist for the nas_server, else the task will fail. -- If a primary directory of the current directory or a subordinate directory of the - path is having a Tree Quota configured, then the quota for that path can't be - created. Hierarchical tree quotas are not allowed. -- When the first quota is created for a directory/user in a filesystem then the - quotas will be enabled for that filesystem automatically. -- If a user quota is to be created on a tree quota, then the user quotas will be - enabled automatically in a tree quota. +- If a primary directory of the current directory or a subordinate directory + of the path is having a Tree Quota configured, then the quota for that path + can't be created. Hierarchical tree quotas are not allowed. +- When the first quota is created for a directory/user in a filesystem then + the quotas will be enabled for that filesystem automatically. +- If a user quota is to be created on a tree quota, then the user quotas will + be enabled automatically in a tree quota. - Delete User Quota operation is not supported. ''' @@ -235,8 +237,8 @@ type: str sample: "2nQKAAEAAAAAAAAAAAAAQIMCAAAAAAAA" file_system: - description: Includes ID and Name of filesystem and nas server for which - smb share exists. + description: Includes ID and Name of filesystem and nas server for + which smb share exists. type: complex contains: filesystem_type: @@ -263,46 +265,38 @@ type: int sample: "2.0" remaining_grace_period: - description: The time period remaining after which the grace period will - expire. + description: The time period remaining after which the grace + period will expire. type: int sample: 86400 description: - description: - - Additional information about the tree quota. - - Only applicable for Tree Quotas. + description: Additional information about the tree quota. + Only applicable for Tree Quotas. type: str sample: "Sample Tree quota's description" uid: - description: - - The ID of the unix host for which user quota exists. - - Only applicable for user quotas. + description: The ID of the unix host for which user quota exists. + Only applicable for user quotas. type: int unix_name: - description: - - The Name of the unix host for which user quota exists. - - Only applicable for user quotas. + description: The Name of the unix host for which user quota + exists. Only applicable for user quotas. type: str windows_name: - description: - - The Name of the Windows host for which user quota exists. - - Only applicable for user quotas. + description: The Name of the Windows host for which user quota + exists. Only applicable for user quotas. type: str windows_sid: - description: - - The SID of the windows host for which user quota exists. - - Only applicable for user quotas. + description: The SID of the windows host for which user quota + exists. Only applicable for user quotas. type: str tree_quota_id: - description: - - ID of the Tree Quota on which the specific User Quota exists. - - Only applicable for user quotas. + description: ID of the Tree Quota on which the specific User Quota + exists. Only applicable for user quotas. type: str tree_quota_for_user_quota: - description: - - Additional Information of Tree Quota limits on which user - quota exists. - - Only applicable for User Quotas + description: Additional Information of Tree Quota limits on which + user quota exists. Only applicable for User Quotas. type: complex contains: description: @@ -318,17 +312,16 @@ type: str sample: "/sample_path" size_used: - description: Size currently consumed by Tree/User on the filesystem. + description: Size currently consumed by Tree/User on the + filesystem. type: int state: - description: - - State of the user quota or tree quota record period. - - OK means No quota limits are exceeded. - - Soft_Exceeded means Soft limit is exceeded, and grace period - is not expired. - - Soft_Exceeded_And_Expired means Soft limit is exceeded, and - grace period is expired. - - Hard_Reached means Hard limit is reached. + description: State of the user quota or tree quota record period. + OK means No quota limits are exceeded. Soft_Exceeded + means Soft limit is exceeded, and grace period is not + expired. Soft_Exceeded_And_Expired means Soft limit + is exceeded, and grace period is expired. + Hard_Reached means Hard limit is reached. type: str sample: "Ok" state_l10n: @@ -353,7 +346,7 @@ VERSION_ERROR = py4ps_version['unsupported_version_message'] # Application type -APPLICATION_TYPE = 'Ansible/1.2.0' +APPLICATION_TYPE = 'Ansible/1.3.0' class PowerStoreQuota(object): @@ -867,7 +860,7 @@ def show_quota_details(self, quota_id, quota_type, path, filesystem_id, """ quota_details = {} if state == "present": - quota_details, type = self.get_quota_details( + quota_details, user_or_tree = self.get_quota_details( quota_id, quota_type, path, filesystem_id, uid, unix_name, windows_name, windows_sid) if state == "present" and quota_type == "user" and \ @@ -954,12 +947,12 @@ def perform_module_operation(self): msg="Description parameter is not valid for User Quota.") # Check if valid combination of parameters are entered or not. - if quota_type == "tree": - if uid or unix_name or windows_sid or windows_name: - self.module.fail_json( - msg="uid/unix_name/windows_sid/windows_name are not " - "valid parameters for Tree Quota type. " - "Please enter correct quota_type") + if quota_type == "tree" and\ + (uid or unix_name or windows_sid or windows_name): + self.module.fail_json( + msg="uid/unix_name/windows_sid/windows_name are not " + "valid parameters for Tree Quota type. " + "Please enter correct quota_type") # Update TREE/USER Quota details if state == "present" and quota_details: diff --git a/plugins/modules/dellemc_powerstore_replicationrule.py b/plugins/modules/dellemc_powerstore_replicationrule.py index 74e7026..e8d86e6 100644 --- a/plugins/modules/dellemc_powerstore_replicationrule.py +++ b/plugins/modules/dellemc_powerstore_replicationrule.py @@ -1,5 +1,6 @@ #!/usr/bin/python # Copyright: (c) 2021, DellEMC +# Apache License version 2.0 (see MODULE-LICENSE or http://www.apache.org/licenses/LICENSE-2.0.txt) from __future__ import absolute_import, division, print_function @@ -16,10 +17,9 @@ short_description: Replication rule operations on a PowerStore storage system. description: - Performs all replication rule operations on a PowerStore Storage System. -- This module supports get details of an existing replication rule. -- Create new replication rule for all supported parameters. -- Modify replication rule with supported parameters. -- Delete a specific replication rule. + This module supports get details of an existing replication rule. Create new + replication rule for all supported parameters. Modify replication rule with + supported parameters. Delete a specific replication rule. extends_documentation_fragment: - dellemc.powerstore.dellemc_powerstore.powerstore author: @@ -133,37 +133,37 @@ RETURN = r''' changed: - description: Whether or not the resource has changed + description: Whether or not the resource has changed. returned: always type: bool replication_rule_details: - description: Details of the replication rule + description: Details of the replication rule. returned: When replication rule exists type: complex contains: id: - description: The system generated ID of the replication rule + description: The system generated ID of the replication rule. type: str name: - description: Name of the replication rule + description: Name of the replication rule. type: str alert_threshold: - description: Acceptable delay in minutes between the expected and actual - replication sync intervals. + description: Acceptable delay in minutes between the expected and + actual replication sync intervals. type: int rpo: - description: Recovery point objective (RPO), which is the acceptable - amount of data, measured in units of time, that may be lost - in case of a failure. + description: Recovery point objective (RPO), which is the + acceptable amount of data, measured in units of time, + that may be lost in case of a failure. type: str remote_system_id: - description: Unique identifier of the remote system to which this rule - will replicate the associated resources. + description: Unique identifier of the remote system to which this + rule will replicate the associated resources. type: str remote_system_name: - description: Name of the remote system to which this rule will replicate - the associated resources. + description: Name of the remote system to which this rule will + replicate the associated resources. type: str ''' @@ -184,7 +184,7 @@ VERSION_ERROR = py4ps_version['unsupported_version_message'] # Application type -APPLICATION_TYPE = 'Ansible/1.2.0' +APPLICATION_TYPE = 'Ansible/1.3.0' class PowerstoreReplicationRule(object): @@ -508,13 +508,13 @@ def show_output(self, rep_rule_id): def modify_replication_rule_required(rep_rule_details, passed_args): """ To check if modification is required or not""" for key in rep_rule_details.keys(): - if key in passed_args.keys(): - if passed_args[key] is not None: - if rep_rule_details[key] != passed_args[key]: - LOG.debug("Key %s in rep_rule_details=%s," - "passed_args=%s", key, - rep_rule_details[key], passed_args[key]) - return True + if key in passed_args.keys() and\ + passed_args[key] is not None and\ + rep_rule_details[key] != passed_args[key]: + LOG.debug("Key %s in rep_rule_details=%s," + "passed_args=%s", key, + rep_rule_details[key], passed_args[key]) + return True return False diff --git a/plugins/modules/dellemc_powerstore_replicationsession.py b/plugins/modules/dellemc_powerstore_replicationsession.py index f0a5f41..875d3f3 100644 --- a/plugins/modules/dellemc_powerstore_replicationsession.py +++ b/plugins/modules/dellemc_powerstore_replicationsession.py @@ -1,5 +1,6 @@ #!/usr/bin/python # Copyright: (c) 2021, DellEMC +# Apache License version 2.0 (see MODULE-LICENSE or http://www.apache.org/licenses/LICENSE-2.0.txt) from __future__ import absolute_import, division, print_function @@ -13,12 +14,12 @@ --- module: dellemc_powerstore_replicationsession version_added: '1.2.0' -short_description: Replication session operations on a PowerStore storage system. +short_description: Replication session operations on a PowerStore storage + system. description: -- Performs all replication session state change operations on a - PowerStore Storage System. -- This module supports get details of an existing replication session. -- Updating the state of the replication session. +- Performs all replication session state change operations on a PowerStore + Storage System. This module supports get details of an existing replication + session. Updating the state of the replication session. extends_documentation_fragment: - dellemc.powerstore.dellemc_powerstore.powerstore author: @@ -50,12 +51,12 @@ choices: [ 'failed_over', 'paused', 'synchronizing'] type: str notes: -- Manual synchronization for a replication session is not supported through the - Ansible module. -- When the current state of the replication session is 'OK' and in the playbook - task 'synchronizing', then it will return "changed" as False. This is because there is a - scheduled synchronization in place with the associated replication rule's RPO - in the protection policy. +- Manual synchronization for a replication session is not supported through + the Ansible module. +- When the current state of the replication session is 'OK' and in the + playbook task 'synchronizing', then it will return "changed" as False. This + is because there is a scheduled synchronization in place with the associated + replication rule's RPO in the protection policy. ''' EXAMPLES = r''' @@ -110,34 +111,31 @@ type: complex contains: id: - description: - - The system generated ID of the replication session. - - Unique across source and destination roles. + description: The system generated ID of the replication session. + Unique across source and destination roles. type: str name: description: Name of the replication rule. type: str role: - description: - - Role of the replication session. - - Source - The local resource is the source of the remote - replication session. - - Destination - The local resource is the destination of the - remote replication session. + description: Role of the replication session. Source - The local + resource is the source of the remote replication + session. Destination - The local resource is the + destination of the remote replication session. type: str resource_type: - description: - - Storage resource type eligible for replication protection. - - volume - Replication session created on a volume. - - volume_group - Replication session created on a volume group. + description: Storage resource type eligible for replication + protection. volume - Replication session created on a + volume. volume_group - Replication session created on + a volume group. type: str local_resource_id: - description: Unique identifier of the local storage resource for the - replication session. + description: Unique identifier of the local storage resource for + the replication session. type: str remote_resource_id: - description: Unique identifier of the remote storage resource for the - replication session. + description: Unique identifier of the remote storage resource for + the replication session. type: str remote_system_id: description: Unique identifier of the remote system instance. @@ -146,8 +144,8 @@ description: Progress of the current replication operation. type: int replication_rule_id: - description: Associated replication rule instance if created by policy - engine. + description: Associated replication rule instance if created by + policy engine. type: str state: description: State of the replication session. @@ -157,7 +155,7 @@ type: str estimated_completion_timestamp: description: Estimated completion time of the current replication - operation. + operation. type: str ''' @@ -176,7 +174,7 @@ VERSION_ERROR = py4ps_version['unsupported_version_message'] # Application type -APPLICATION_TYPE = 'Ansible/1.2.0' +APPLICATION_TYPE = 'Ansible/1.3.0' """ =============================================================================== Idempotency table for the replication session ansible module on the basis of @@ -383,15 +381,12 @@ def get_clusters(self): self.module.fail_json(msg=msg) def change_state_from_ok(self, session_state, current_state, - rep_session_details): + rep_session_details, err_msg): """ The operation will be performed when the current state of the replication session is OK. """ try: - msg = "Current state of the replication session: {0} and entered" \ - " session state: {1}".format(current_state, session_state) - LOG.info(msg) session_id = rep_session_details['id'] role = rep_session_details['role'].lower() if session_state == 'synchronizing': @@ -413,29 +408,23 @@ def change_state_from_ok(self, session_state, current_state, return True except Exception as e: - msg = '{0} call on replication session with id: {1} having' \ - ' current state: {2}, failed with error: {3}' \ - ''.format(session_state, rep_session_details['id'], - current_state, str(e)) - + err_msg = err_msg.format(session_state, rep_session_details['id'], + current_state, str(e)) if isinstance(e, utils.PowerStoreException) and \ e.err_code == utils.PowerStoreException.HTTP_ERR and \ e.status_code == "404": - LOG.info(msg) + LOG.info(err_msg) return None - LOG.error(msg) - self.module.fail_json(msg=msg) + LOG.error(err_msg) + self.module.fail_json(msg=err_msg) def change_state_from_sync(self, session_state, current_state, - rep_session_details): + rep_session_details, err_msg): """ The operation will be performed when the current state of the replication session is synchronizing. """ try: - msg = "Current state of the replication session: {0} and entered" \ - " session state: {1}".format(current_state, session_state) - LOG.info(msg) session_id = rep_session_details['id'] role = rep_session_details['role'].lower() @@ -464,29 +453,23 @@ def change_state_from_sync(self, session_state, current_state, return True except Exception as e: - msg = '{0} call on replication session with id: {1} having' \ - ' current state: {2}, failed with error: {3}' \ - ''.format(session_state, rep_session_details['id'], - current_state, str(e)) - + err_msg = err_msg.format(session_state, rep_session_details['id'], + current_state, str(e)) if isinstance(e, utils.PowerStoreException) and \ e.err_code == utils.PowerStoreException.HTTP_ERR and \ e.status_code == "404": - LOG.info(msg) + LOG.info(err_msg) return None - LOG.error(msg) - self.module.fail_json(msg=msg) + LOG.error(err_msg) + self.module.fail_json(msg=err_msg) def change_state_from_paused(self, session_state, current_state, - rep_session_details): + rep_session_details, err_msg): """ The operation will be performed when the current state of the replication session is paused. """ try: - msg = "Current state of the replication session: {0} and entered" \ - " session state: {1}".format(current_state, session_state) - LOG.info(msg) session_id = rep_session_details['id'] role = rep_session_details['role'].lower() @@ -524,29 +507,23 @@ def change_state_from_paused(self, session_state, current_state, return True except Exception as e: - msg = '{0} call on replication session with id: {1} having' \ - ' current state: {2}, failed with error: {3}' \ - ''.format(session_state, rep_session_details['id'], - current_state, str(e)) - + err_msg = err_msg.format(session_state, rep_session_details['id'], + current_state, str(e)) if isinstance(e, utils.PowerStoreException) and \ e.err_code == utils.PowerStoreException.HTTP_ERR and \ e.status_code == "404": - LOG.info(msg) + LOG.info(err_msg) return None - LOG.error(msg) - self.module.fail_json(msg=msg) + LOG.error(err_msg) + self.module.fail_json(msg=err_msg) def change_state_from_failing_over(self, session_state, current_state, - rep_session_details): + rep_session_details, err_msg): """ The operation will be performed when the current state of the replication session is failing_over. """ try: - msg = "Current state of the replication session: {0} and entered" \ - " session state: {1}".format(current_state, session_state) - LOG.info(msg) session_id = rep_session_details['id'] if session_state == 'synchronizing': @@ -565,31 +542,24 @@ def change_state_from_failing_over(self, session_state, current_state, return False except Exception as e: - msg = '{0} call on replication session with id: {1} having' \ - ' current state: {2}, failed with error: {3}' \ - ''.format(session_state, rep_session_details['id'], - current_state, str(e)) - + err_msg = err_msg.format(session_state, rep_session_details['id'], + current_state, str(e)) if isinstance(e, utils.PowerStoreException) and \ e.err_code == utils.PowerStoreException.HTTP_ERR and \ e.status_code == "404": - LOG.info(msg) + LOG.info(err_msg) return None - LOG.error(msg) - self.module.fail_json(msg=msg) + LOG.error(err_msg) + self.module.fail_json(msg=err_msg) def change_state_from_failed_over(self, session_state, current_state, - rep_session_details): + rep_session_details, err_msg): """ The operation will be performed when the current state of the replication session is failed_over. """ try: - current_state = rep_session_details['state'].lower() - msg = "Current state of the replication session: {0} and entered" \ - " session state: {1}".format(current_state, session_state) - LOG.info(msg) session_id = rep_session_details['id'] role = rep_session_details['role'].lower() @@ -625,18 +595,15 @@ def change_state_from_failed_over(self, session_state, current_state, return False except Exception as e: - msg = '{0} call on replication session with id: {1} having' \ - ' current state: {2}, failed with error: {3}' \ - ''.format(session_state, rep_session_details['id'], - current_state, str(e)) - + err_msg = err_msg.format(session_state, rep_session_details['id'], + current_state, str(e)) if isinstance(e, utils.PowerStoreException) and \ e.err_code == utils.PowerStoreException.HTTP_ERR and \ e.status_code == "404": - LOG.info(msg) + LOG.info(err_msg) return None - LOG.error(msg) - self.module.fail_json(msg=msg) + LOG.error(err_msg) + self.module.fail_json(msg=err_msg) def change_state_from_transitioning_states(self, session_state, current_state): @@ -691,37 +658,43 @@ def perform_module_operation(self): session_id = rep_session_details['id'] current_state = rep_session_details['state'].lower() - + err_msg = None + if current_state and session_state: + msg = "Current state of the replication session: {0} and entered" \ + " session state: {1}".format(current_state, session_state) + LOG.info(msg) + err_msg = '{0} call on replication session with id: {1} having'\ + ' current state: {2}, failed with error: {3}' # perform operation for the given session state parameter in playbook # task, if current replication state is OK if session_state and current_state == "ok": changed = self.change_state_from_ok( - session_state, current_state, rep_session_details) + session_state, current_state, rep_session_details, err_msg) # perform operation for the given session state parameter in playbook # task, if current replication state is synchronizing if session_state and current_state == "synchronizing": changed = self.change_state_from_sync(session_state, current_state, - rep_session_details) + rep_session_details, err_msg) # perform operation for the given session state parameter in playbook # task, if current replication state is paused if session_state and current_state == "paused": changed = self.change_state_from_paused( - session_state, current_state, rep_session_details) + session_state, current_state, rep_session_details, err_msg) # perform operation for the given session state parameter in playbook # task, if current replication state is failed_over if session_state and current_state == "failed_over": changed = self.change_state_from_failed_over( - session_state, current_state, rep_session_details) + session_state, current_state, rep_session_details, err_msg) # perform operation for the given session state parameter in playbook # task, if current replication state is failing_over if session_state and current_state == "failing_over" or \ current_state == "failing_over_for_dr": changed = self.change_state_from_failing_over( - session_state, current_state, rep_session_details) + session_state, current_state, rep_session_details, err_msg) transitioning_states = ['resuming', 'reprotecting', 'initializing'] if session_state and current_state in transitioning_states: diff --git a/plugins/modules/dellemc_powerstore_role.py b/plugins/modules/dellemc_powerstore_role.py new file mode 100644 index 0000000..35086a2 --- /dev/null +++ b/plugins/modules/dellemc_powerstore_role.py @@ -0,0 +1,244 @@ +#!/usr/bin/python +# Copyright: (c) 2021, DellEMC +# Apache License version 2.0 (see MODULE-LICENSE or http://www.apache.org/licenses/LICENSE-2.0.txt) + +""" Ansible module for managing roles on PowerStore""" +from __future__ import (absolute_import, division, print_function) + +__metaclass__ = type +ANSIBLE_METADATA = {'metadata_version': '1.1', + 'status': ['preview'], + 'supported_by': 'community'} + +DOCUMENTATION = r''' +--- +module: dellemc_powerstore_role + +version_added: '1.3.0' + +short_description: Get details of the roles present on the PowerStore storage + system. + +description: +- Manage role in PowerStore storage system includes getting the details of a + role. + +extends_documentation_fragment: + - dellemc.powerstore.dellemc_powerstore.powerstore + +author: +- P Srinivas Rao (@srinivas-rao5) +options: + role_name: + description: + - Name of the role. + type: str + role_id: + description: + - Id of the role. + type: str + state: + description: + - Define whether the role should exist or not. + - present, indicates that the role should exist on the system. + - absent, indicates that the role should not exist on the system. + type: str + required: true + choices: ['absent', 'present'] + +notes: +- Only getting the details of the role is supported by the ansible module. +- Creation, modification and deletion of roles is not supported by the ansible + modules. + +''' +EXAMPLES = r''' + +- name: Get the details of role by name + dellemc_powerstore_role: + array_ip: "{{array_ip}}" + verifycert: "{{verify_cert}}" + user: "{{user}}" + password: "{{password}}" + role_name: "Administrator" + state: "present" + +- name: Get the details of role by id + dellemc_powerstore_role: + array_ip: "{{array_ip}}" + verifycert: "{{verify_cert}}" + user: "{{user}}" + password: "{{password}}" + role_id: "1" + state: "present" +''' +RETURN = r''' +changed: + description: Whether or not the resource has changed. + returned: always + type: bool + sample: True + +role_details: + description: The role details. + type: complex + returned: When role exists. + contains: + id: + description: The ID of the role. + type: str + name: + description: The name of the role. + type: str + is_built_in: + description: Indicates whether the role is built-in. + type: bool + description: + description: Description of the role. + type: str +''' + +from ansible_collections.dellemc.powerstore.plugins.module_utils.storage.dell\ + import dellemc_ansible_powerstore_utils as utils +from ansible.module_utils.basic import AnsibleModule + +LOG = utils.get_logger('dellemc_powerstore_role') + +py4ps_sdk = utils.has_pyu4ps_sdk() +HAS_PY4PS = py4ps_sdk['HAS_Py4PS'] +IMPORT_ERROR = py4ps_sdk['Error_message'] + +py4ps_version = utils.py4ps_version_check() +IS_SUPPORTED_PY4PS_VERSION = py4ps_version['supported_version'] +VERSION_ERROR = py4ps_version['unsupported_version_message'] + +# Application type +APPLICATION_TYPE = 'Ansible/1.3.0' + + +class PowerStoreRole(object): + """Class with role operations""" + + def __init__(self): + """ Define all parameters required by this module""" + + self.module_params = utils.get_powerstore_management_host_parameters() + self.module_params.update(get_powerstore_role_parameters()) + + # initialize the ansible module + mut_ex_args = [ + ['role_id', 'role_name'] + ] + + required_one_of = [['role_id', 'role_name']] + self.module = AnsibleModule( + argument_spec=self.module_params, + supports_check_mode=False, + mutually_exclusive=mut_ex_args, + required_one_of=required_one_of + ) + + msg = 'HAS_PY4PS = {0} , IMPORT_ERROR = {1}'.format(HAS_PY4PS, + IMPORT_ERROR) + LOG.info(msg) + + if not HAS_PY4PS: + self.module.fail_json(msg=IMPORT_ERROR) + msg = 'IS_SUPPORTED_PY4PS_VERSION = {0} , VERSION_ERROR = {1}' \ + .format(IS_SUPPORTED_PY4PS_VERSION, VERSION_ERROR) + LOG.info(msg) + + if not IS_SUPPORTED_PY4PS_VERSION: + self.module.fail_json(msg=VERSION_ERROR) + + # result is a dictionary that contains changed status and + # role details + self.result = {"changed": False, "role_details": {}} + + self.conn = utils.get_powerstore_connection( + self.module.params, application_type=APPLICATION_TYPE) + self.provisioning = self.conn.provisioning + msg = 'Got Py4Ps instance for provisioning on' \ + ' PowerStore {0}'.format(self.conn) + LOG.info(msg) + self.configuration = self.conn.config_mgmt + msg = 'Got Py4Ps instance for configuring cluster on' \ + ' PowerStore {0}'.format(self.conn) + LOG.info(msg) + + def get_role_details(self, role_name, role_id): + """ + Get the details of role + :param role_name: Name of the role + :param role_id: Id of the role + :return: Details of role if exists else None. + """ + name_or_id = role_name if role_name else role_id + try: + if role_name: + role_details = self.configuration.get_role_by_name(role_name) + else: + role_details = self.configuration.get_role_details(role_id) + return role_details + except Exception as e: + err_msg = "Get details for role: {0} failed with error: " \ + "{1}".format(name_or_id, str(e)) + if isinstance(e, utils.PowerStoreException) and \ + e.err_code == utils.PowerStoreException.HTTP_ERR and \ + e.status_code == "404": + LOG.info(err_msg) + return None + LOG.error(err_msg) + self.module.fail_json(msg=err_msg) + + def perform_module_operation(self): + """ + Perform different actions on role module based on parameters + chosen in playbook + """ + role_id = self.module.params['role_id'] + role_name = self.module.params['role_name'] + state = self.module.params['state'] + changed = False + # Get the details of role + role_details = self.get_role_details(role_name, role_id) + + # Create role + if not role_details and state == 'present': + name_or_id = role_name if role_name else role_id + err_msg = "Role: {0} doesn't exist on the PowerStore storage " \ + "system. Creation of a role is not supported by " \ + "ansible module.".format(name_or_id) + self.module.fail_json(msg=err_msg) + + # Delete role + if state == "absent" and role_details: + self.module.fail_json( + msg="Deletion of role is not supported by ansible module. Only" + " get details of role is supported. Please enter a valid " + "operation.") + + self.result["changed"] = changed + self.result["role_details"] = role_details + self.module.exit_json(**self.result) + + +def get_powerstore_role_parameters(): + """This method provides parameters required for the ansible role + module on PowerStore""" + return dict( + role_name=dict(), role_id=dict(), + state=dict(required=True, choices=['present', 'absent']) + ) + + +def main(): + """ Create role object and perform actions on it + based on user input from playbook""" + + obj = PowerStoreRole() + obj.perform_module_operation() + + +if __name__ == '__main__': + main() diff --git a/plugins/modules/dellemc_powerstore_smbshare.py b/plugins/modules/dellemc_powerstore_smbshare.py index 31a6d2c..c741ceb 100644 --- a/plugins/modules/dellemc_powerstore_smbshare.py +++ b/plugins/modules/dellemc_powerstore_smbshare.py @@ -236,8 +236,8 @@ type: str sample: "sample_smb_share" file_system: - description: Includes ID and Name of filesystem and nas server for which - smb share exists. + description: Includes ID and Name of filesystem and nas server for + which smb share exists. type: complex contains: filesystem_type: @@ -268,11 +268,12 @@ type: bool sample: false is_continuous_availability_enabled: - description: Whether the share will be available continuously or not + description: Whether the share will be available continuously or + not. type: bool sample: false is_encryption_enabled: - description: Whether encryption is enabled or not + description: Whether encryption is enabled or not. type: bool sample: false ''' @@ -292,7 +293,7 @@ VERSION_ERROR = py4ps_version['unsupported_version_message'] # Application type -APPLICATION_TYPE = 'Ansible/1.2.0' +APPLICATION_TYPE = 'Ansible/1.3.0' class PowerStoreSMBShare(object): @@ -674,12 +675,9 @@ def match_smb_share(share_details, smb_parent, nas_server, path): for share in share_details: match_flag = True - if nas_server and not is_match_nas(nas_server, share): - match_flag = False - elif smb_parent and not is_match_smb_parent( - smb_parent, share): - match_flag = False - elif path and not is_match_path(path, share): + if (nas_server and not is_match_nas(nas_server, share)) or\ + (smb_parent and not is_match_smb_parent(smb_parent, share)) or\ + (path and not is_match_path(path, share)): match_flag = False if match_flag: return share diff --git a/plugins/modules/dellemc_powerstore_snapshot.py b/plugins/modules/dellemc_powerstore_snapshot.py index 268b721..cd8c413 100644 --- a/plugins/modules/dellemc_powerstore_snapshot.py +++ b/plugins/modules/dellemc_powerstore_snapshot.py @@ -1,5 +1,7 @@ #!/usr/bin/python # Copyright: (c) 2019-2021, DellEMC +# Apache License version 2.0 (see MODULE-LICENSE or http://www.apache.org/licenses/LICENSE-2.0.txt) + from __future__ import (absolute_import, division, print_function) __metaclass__ = type @@ -14,15 +16,11 @@ version_added: '1.0.0' short_description: Manage Snapshots on Dell EMC PowerStore. description: -- Managing Snapshots on PowerStore. -- Create a new Volume Group Snapshot. -- Get details of Volume Group Snapshot. -- Modify Volume Group Snapshot -- Delete an existing Volume Group Snapshot. -- Create a new Volume Snapshot. -- Get details of Volume Snapshot. -- Modify Volume Snapshot. -- Delete an existing Volume Snapshot. +- Managing Snapshots on PowerStore storage system, Create a new Volume Group + Snapshot, Get details of Volume Group Snapshot, Modify Volume Group + Snapshot, Delete an existing Volume Group Snapshot, Create a new Volume + Snapshot, Get details of Volume Snapshot, Modify Volume Snapshot, Delete an + existing Volume Snapshot. author: - Rajshree Khare (@khareRajshree) - Prashant Rakheja (@prashant-dell) @@ -63,8 +61,8 @@ - The unit for retention. - If this unit is not specified, 'hours' is taken as default retention_unit. - - If desired_retention is specified, - expiration_timestamp cannot be specified. + - If desired_retention is specified, expiration_timestamp cannot be + specified. choices: [hours, days] type: str expiration_timestamp: @@ -194,37 +192,37 @@ create_vg_snap: description: A boolean flag to indicate whether volume group snapshot got - created + created. returned: When value exists type: bool create_vol_snap: description: A boolean flag to indicate whether volume snapshot got - created + created. returned: When value exists type: bool delete_vg_snap: description: A boolean flag to indicate whether volume group snapshot got - deleted + deleted. returned: When value exists type: bool delete_vol_snap: description: A boolean flag to indicate whether volume snapshot got - deleted + deleted. returned: When value exists type: bool modify_vg_snap: description: A boolean flag to indicate whether volume group snapshot got - modified + modified. returned: When value exists type: bool modify_vol_snap: description: A boolean flag to indicate whether volume snapshot got - modified + modified. returned: When value exists type: bool @@ -234,46 +232,46 @@ type: complex contains: id: - description: The system generated ID given to the snapshot + description: The system generated ID given to the snapshot. type: str name: - description: Name of the snapshot + description: Name of the snapshot. type: str size: - description: Size of the snapshot + description: Size of the snapshot. type: int description: - description: Description about the snapshot + description: Description about the snapshot. type: str creation_timestamp: - description: The creation timestamp of the snapshot + description: The creation timestamp of the snapshot. type: str performance_policy_id: - description: The performance policy for the snapshot + description: The performance policy for the snapshot. type: str protection_policy_id: - description: The protection policy of the snapshot + description: The protection policy of the snapshot. type: str state: - description: The state of the snapshot + description: The state of the snapshot. type: str type: - description: The type of the snapshot + description: The type of the snapshot. type: str protection_data: - description: The protection data of the snapshot + description: The protection data of the snapshot. type: complex contains: expiration_timestamp: - description: The expiration timestamp of the snapshot + description: The expiration timestamp of the snapshot. type: str volumes: - description: The volumes details of the volume group snapshot + description: The volumes details of the volume group snapshot. type: complex contains: id: description: The system generated ID given to the volume - associated with the volume group + associated with the volume group. type: str ''' @@ -295,7 +293,7 @@ VERSION_ERROR = py4ps_version['unsupported_version_message'] # Application type -APPLICATION_TYPE = 'Ansible/1.2.0' +APPLICATION_TYPE = 'Ansible/1.3.0' class PowerStoreSnapshot(object): @@ -375,11 +373,10 @@ def get_vol_snapshot(self, volume_id, snapshot_name, snapshot_id): LOG.info("Found snapshot by name: %s", snapshot_name) snapshot = snap break - elif snapshot_id is not None: - if snap['id'] == snapshot_id: - LOG.info("Found snapshot by ID: %s", snapshot_id) - snapshot = snap - break + elif snapshot_id is not None and snap['id'] == snapshot_id: + LOG.info("Found snapshot by ID: %s", snapshot_id) + snapshot = snap + break return snapshot except Exception as e: if isinstance(e, utils.PowerStoreException) and \ @@ -405,11 +402,10 @@ def get_vol_group_snapshot(self, vg_id, snapshot_name, snapshot_id): LOG.info("Found snapshot by name: %s", snapshot_name) snapshot = snap break - elif snapshot_id is not None: - if snap['id'] == snapshot_id: - LOG.info("Found snapshot by ID: %s", snapshot_id) - snapshot = snap - break + elif snapshot_id is not None and snap['id'] == snapshot_id: + LOG.info("Found snapshot by ID: %s", snapshot_id) + snapshot = snap + break return snapshot except Exception as e: msg = ("Not able to get snapshot details for volume group: %s" @@ -433,7 +429,7 @@ def get_vol_id_from_volume(self, volume): except Exception as e: LOG.info("No volume found by ID: %s, looking it up by name. " "Error: %s", volume, str(e)) - pass + try: vol = \ self.provisioning.get_volume_by_name(volume) @@ -459,7 +455,7 @@ def get_vol_group_id_from_vg(self, volume_group): except Exception as e: LOG.info("No volume group found by ID: %s, looking it up by " "name. Error %s", volume_group, str(e)) - pass + try: vg = \ self.provisioning.get_volume_group_by_name(volume_group) @@ -478,7 +474,6 @@ def create_vol_snapshot(self, snapshot_name, description, volume_id, desired_retention, - retention_unit, expiration_timestamp, new_name): """Create a snap for a volume on PowerStore""" @@ -501,22 +496,6 @@ def create_vol_snapshot(self, snapshot_name, LOG.error("Snapshot: %s already exists", snapshot_name) return False - if desired_retention is not None and desired_retention != 'None': - if retention_unit is None: - expiration_timestamp = (datetime.utcnow() + - timedelta( - hours=int(desired_retention)) - ).isoformat() \ - + 'Z' - elif retention_unit == 'days': - expiration_timestamp = (datetime.utcnow() + timedelta( - days=int(desired_retention))).isoformat() + 'Z' - elif retention_unit == 'hours': - expiration_timestamp = (datetime.utcnow() + timedelta( - hours=int(desired_retention))).isoformat() + 'Z' - elif desired_retention == 'None': - expiration_timestamp = None - try: resp = \ self.protection.create_volume_snapshot( @@ -537,7 +516,6 @@ def create_vg_snapshot(self, snapshot_name, description, vg_id, desired_retention, - retention_unit, expiration_timestamp, new_name): """Create a snap for a VG on PowerStore""" @@ -555,22 +533,6 @@ def create_vg_snapshot(self, snapshot_name, self.module.fail_json(msg="Invalid param: new_name while " "creating a new snapshot.") - if desired_retention is not None and desired_retention != 'None': - if retention_unit is None: - expiration_timestamp = (datetime.utcnow() + - timedelta( - hours=int( - desired_retention))).isoformat() \ - + 'Z' - elif retention_unit == 'days': - expiration_timestamp = (datetime.utcnow() + timedelta( - days=int(desired_retention))).isoformat() + 'Z' - elif retention_unit == 'hours': - expiration_timestamp = (datetime.utcnow() + timedelta( - hours=int(desired_retention))).isoformat() + 'Z' - elif desired_retention == 'None': - expiration_timestamp = None - try: resp = \ self.protection.create_volume_group_snapshot( @@ -619,7 +581,7 @@ def rename_vol_snapshot(self, snapshot, new_name): self.module.fail_json(msg="Snapshot not found.") if snapshot['name'] == new_name: - return False + return False, None try: changed = False self.protection.modify_volume_snapshot( @@ -645,7 +607,7 @@ def rename_vol_group_snapshot(self, snapshot, new_name): self.module.fail_json(msg="Snapshot not found.") if snapshot['name'] == new_name: - return False + return False, None try: changed = False self.protection.modify_volume_group_snapshot( @@ -674,6 +636,7 @@ def check_snapshot_modified(self, snapshot, volume, volume_group, snapshot_modification_details['new_description_value'] = None snapshot_modification_details['is_timestamp_modified'] = False snapshot_modification_details['new_expiration_timestamp_value'] = None + datetime_format = "%Y-%m-%dT%H:%MZ" if desired_retention is None and expiration_timestamp is None: LOG.info("desired_retention and expiration_time are both " @@ -696,88 +659,74 @@ def check_snapshot_modified(self, snapshot, volume, volume_group, snap_details['creation_timestamp'][0:16] + 'Z' if desired_retention is not None and desired_retention != 'None': - if retention_unit is None: + if retention_unit is None or retention_unit == 'hours': expiration_timestamp = (datetime.strptime( - snap_creation_timestamp, '%Y-%m-%dT%H:%MZ') + + snap_creation_timestamp, datetime_format) + timedelta( hours=int(desired_retention)) ).isoformat() \ + 'Z' elif retention_unit == 'days': expiration_timestamp = (datetime.strptime( - snap_creation_timestamp, '%Y-%m-%dT%H:%MZ') + timedelta( + snap_creation_timestamp, datetime_format) + timedelta( days=int(desired_retention))).isoformat() + 'Z' - elif retention_unit == 'hours': - expiration_timestamp = (datetime.strptime( - snap_creation_timestamp, '%Y-%m-%dT%H:%MZ') + timedelta( - hours=int(desired_retention))).isoformat() + 'Z' elif desired_retention == 'None': expiration_timestamp = None LOG.info("The new expiration timestamp is %s", expiration_timestamp) modified = False - if 'expiration_timestamp' in snap_details['protection_data'] \ - and snap_details['protection_data']['expiration_timestamp'] \ - is not None and expiration_timestamp is not None: - # Only taking into account YYYY-MM-DDTHH-MM, ignoring - # seconds component. - if snap_details['protection_data']['expiration_timestamp'][0:16] \ - != expiration_timestamp[0:16]: - # We can tolerate a delta of two minutes. - existing_timestamp = \ - snap_details['protection_data']['expiration_timestamp'][ - 0:16] + 'Z' - new_timestamp = expiration_timestamp[0:16] + 'Z' - - existing_time_obj = datetime.strptime(existing_timestamp, - '%Y-%m-%dT%H:%MZ') - new_time_obj = datetime.strptime(new_timestamp, - '%Y-%m-%dT%H:%MZ') - - if existing_time_obj > new_time_obj: - td = existing_time_obj - new_time_obj - else: - td = new_time_obj - existing_time_obj - td_mins = int(round(td.total_seconds() / 60)) - - if td_mins > 2: - snapshot_modification_details[ - 'is_timestamp_modified'] = True - snapshot_modification_details[ - 'new_expiration_timestamp_value'] = \ - expiration_timestamp - modified = True + # Only taking into account YYYY-MM-DDTHH-MM, ignoring + # seconds component. + if snap_details['protection_data']['expiration_timestamp'] \ + and expiration_timestamp and \ + snap_details['protection_data']['expiration_timestamp'][0:16]\ + != expiration_timestamp[0:16]: + # We can tolerate a delta of two minutes. + existing_timestamp = \ + snap_details['protection_data']['expiration_timestamp'][ + 0:16] + 'Z' + new_timestamp = expiration_timestamp[0:16] + 'Z' + + existing_time_obj = datetime.strptime(existing_timestamp, + datetime_format) + new_time_obj = datetime.strptime(new_timestamp, + datetime_format) + + if existing_time_obj > new_time_obj: + td = existing_time_obj - new_time_obj + else: + td = new_time_obj - existing_time_obj + td_mins = int(round(td.total_seconds() / 60)) - elif 'expiration_timestamp' not in snap_details['protection_data'] \ - and expiration_timestamp is not None: - snapshot_modification_details['is_timestamp_modified'] = True - snapshot_modification_details[ - 'new_expiration_timestamp_value'] = expiration_timestamp - modified = True - elif 'expiration_timestamp' in snap_details['protection_data'] \ - and expiration_timestamp is None: - if snap_details['protection_data'][ - 'expiration_timestamp'] is not None: - snapshot_modification_details['is_timestamp_modified'] = True + if td_mins > 2: + snapshot_modification_details[ + 'is_timestamp_modified'] = True snapshot_modification_details[ - 'new_expiration_timestamp_value'] = expiration_timestamp + 'new_expiration_timestamp_value'] = \ + expiration_timestamp modified = True - elif 'expiration_timestamp' in snap_details['protection_data'] and \ - snap_details['protection_data']['expiration_timestamp'] is \ - None and expiration_timestamp is not None: + + if (not snap_details['protection_data']['expiration_timestamp']) \ + and expiration_timestamp: snapshot_modification_details['is_timestamp_modified'] = True snapshot_modification_details[ 'new_expiration_timestamp_value'] = expiration_timestamp modified = True - if 'description' in snap_details and description is not None: - if snap_details['description'] != description: - snapshot_modification_details['is_description_modified'] = \ - True - snapshot_modification_details['new_description_value'] \ - = description - modified = True + if (not expiration_timestamp) and \ + snap_details['protection_data']['expiration_timestamp']: + snapshot_modification_details['is_timestamp_modified'] = True + snapshot_modification_details['new_expiration_timestamp_value'] =\ + expiration_timestamp + modified = True + + if 'description' in snap_details and description is not None and \ + snap_details['description'] != description: + snapshot_modification_details['is_description_modified'] = True + snapshot_modification_details['new_description_value'] = \ + description + modified = True LOG.info("Snapshot modified %s, modification details: %s", modified, snapshot_modification_details) @@ -857,15 +806,27 @@ def validate_expiration_timestamp(self, expiration_timestamp): self.module.fail_json(msg='Incorrect date format, ' 'should be YYYY-MM-DDTHH:MM:SSZ') - def validate_desired_retention(self, desired_retention): + def validate_desired_retention(self, desired_retention, retention_unit): """Validates the specified desired retention""" + + if desired_retention == 'None': + LOG.info("Desired retention is set to 'None'") + return + try: - int(desired_retention) + desired_retention = int(desired_retention) + if (retention_unit == 'hours' or retention_unit is None) and \ + (desired_retention < 1 or desired_retention > 744): + self.module.fail_json(msg="Please provide a valid integer " + "as the desired retention between " + "1 and 744.") + elif retention_unit == 'days' and (desired_retention < 1 or + desired_retention > 31): + self.module.fail_json(msg="Please provide a valid integer as " + "the desired retention between 1 " + "and 31.") except ValueError: - if desired_retention == 'None': - LOG.info("Desired retention is set to 'None'") - else: - self.module.fail_json(msg="Please provide a valid integer" + self.module.fail_json(msg="Please provide a valid integer" " as the desired retention.") def perform_module_operation(self): @@ -903,7 +864,7 @@ def perform_module_operation(self): self.validate_expiration_timestamp(expiration_timestamp) if desired_retention is not None: - self.validate_desired_retention(desired_retention) + self.validate_desired_retention(desired_retention, retention_unit) if volume is not None: volume_id = self.get_vol_id_from_volume(volume) @@ -935,9 +896,15 @@ def perform_module_operation(self): description, volume_id, desired_retention, - retention_unit, expiration_timestamp, new_snapshot_name) + snapshot = result['snap_details'] + is_snap_modified, snapshot_modification_details = \ + self.check_snapshot_modified(snapshot, volume, + volume_group, description, + desired_retention, + retention_unit, + expiration_timestamp) elif state == 'absent' and (snapshot_name or snapshot_id) and \ volume and snapshot: LOG.info("Deleting snapshot %s for Volume %s", @@ -949,13 +916,17 @@ def perform_module_operation(self): LOG.info("Creating new snapshot: %s for VG: %s", snapshot_name, volume_group) result['create_vg_snap'], result['snap_details'] = \ - self.create_vg_snapshot(snapshot_name, - description, - volume_group_id, - desired_retention, - retention_unit, + self.create_vg_snapshot(snapshot_name, description, + volume_group_id, desired_retention, expiration_timestamp, new_snapshot_name) + snapshot = result['snap_details'] + is_snap_modified, snapshot_modification_details = \ + self.check_snapshot_modified(result['snap_details'], volume, + volume_group, description, + desired_retention, + retention_unit, + expiration_timestamp) elif state == 'absent' and ( snapshot_name or snapshot_id) and volume_group \ and snapshot: diff --git a/plugins/modules/dellemc_powerstore_snapshotrule.py b/plugins/modules/dellemc_powerstore_snapshotrule.py index 79acda3..27ce70c 100644 --- a/plugins/modules/dellemc_powerstore_snapshotrule.py +++ b/plugins/modules/dellemc_powerstore_snapshotrule.py @@ -1,5 +1,6 @@ #!/usr/bin/python # Copyright: (c) 2019-2021, DellEMC +# Apache License version 2.0 (see MODULE-LICENSE or http://www.apache.org/licenses/LICENSE-2.0.txt) from __future__ import absolute_import, division, print_function @@ -16,12 +17,10 @@ short_description: SnapshotRule operations on a PowerStore storage system. description: - Performs all snapshot rule operations on PowerStore Storage System. -- This modules supports get details of an existing snapshot rule, - create new Snapshot Rule with Interval, create new Snapshot Rule - with specific time and days_of_week with all supported. - parameters. -- Modify Snapshot Rule with supported parameters. -- Delete a specific Snapshot Rule. + This modules supports get details of an existing snapshot rule, create new + Snapshot Rule with Interval, create new Snapshot Rule with specific time and + days_of_week with all supported parameters. Modify Snapshot Rule with + supported parameters. Delete a specific Snapshot Rule. extends_documentation_fragment: - dellemc.powerstore.dellemc_powerstore.powerstore author: @@ -48,10 +47,10 @@ description: - List of strings to specify days of the week on which the Snapshot rule. should be applied. - Must be applied for Snapshot rules where the 'time_of_day' parameter is set. - Optional for the Snapshot rule created with an interval. - When 'days_of_week' is not specified for a new Snapshot rule, the rule - is applied on every day of the week. + Must be applied for Snapshot rules where the 'time_of_day' parameter is + set. Optional for the Snapshot rule created with an interval. When + 'days_of_week' is not specified for a new Snapshot rule, the rule is + applied on every day of the week. required: False choices: [ Monday, Tuesday, Wednesday, Thursday, Friday, Saturday, Sunday ] @@ -190,45 +189,45 @@ RETURN = r''' changed: - description: Whether or not the resource has changed + description: Whether or not the resource has changed. returned: always type: bool snapshotrule_details: - description: Details of the snapshot rule + description: Details of the snapshot rule. returned: When snapshot rule exists type: complex contains: id: - description: The system generated ID given to the snapshot rule + description: The system generated ID given to the snapshot rule. type: str name: - description: Name of the snapshot rule + description: Name of the snapshot rule. type: str days_of_week: - description: List of string to specify days of the week on which the - rule should be applied + description: List of string to specify days of the week on which + the rule should be applied. type: list time_of_day: - description: The time of the day to take a daily snapshot + description: The time of the day to take a daily snapshot. type: str interval: - description: The interval between snapshots + description: The interval between snapshots. type: str desired_retention: - description: Desired snapshot retention period + description: Desired snapshot retention period. type: int policies: - description: The protection policies details of the snapshot rule + description: The protection policies details of the snapshot rule. type: complex contains: id: - description: The protection policy ID in which the snapshot rule - is selected + description: The protection policy ID in which the + snapshot rule is selected. type: str name: - description: Name of the protection policy in which the snapshot - rule is selected + description: Name of the protection policy in which the + snapshot rule is selected. type: str ''' @@ -250,7 +249,7 @@ VERSION_ERROR = py4ps_version['unsupported_version_message'] # Application type -APPLICATION_TYPE = 'Ansible/1.2.0' +APPLICATION_TYPE = 'Ansible/1.3.0' class PowerstoreSnapshotrule(object): @@ -431,25 +430,6 @@ def delete_snapshot_rule(self, snapshot_rule_id, delete_snaps=False): LOG.error(msg) self.module.fail_json(msg=msg) - def modify_snapshotrule_required(self, snapruledict1, snapruledict2): - """to compare two snapshot rule""" - for key in snapruledict1.keys(): - if key in snapruledict2.keys(): - if snapruledict2[key] is not None: - if isinstance(snapruledict1[key], list): - if set(snapruledict1[key]) != set(snapruledict2[key]): - LOG.debug("Key %s in snapruledict1=%s," - "snapruledict2=%s", key, - snapruledict1[key], snapruledict2[key]) - return True - else: - if snapruledict1[key] != snapruledict2[key]: - LOG.debug("Key %s in snapruledict1=%s," - "snapruledict2=%s", key, - snapruledict1[key], snapruledict2[key]) - return True - return False - def get_clusters(self): """Get the clusters""" try: @@ -551,12 +531,11 @@ def perform_module_operation(self): 'desired_retention': desired_retention } - to_modify = self.modify_snapshotrule_required( + to_modify = modify_snapshotrule_required( snap_rule, new_snaprule_dict) LOG.debug("ToModify : %s", to_modify) if to_modify: - result['changed'], result['snapshotrule_details'] = \ self.modify_snapshot_rule( snap_rule_id, new_name, desired_retention, @@ -599,6 +578,19 @@ def get_powerstore_snapshotrule_parameters(): ) +def modify_snapshotrule_required(snapruledict1, snapruledict2): + """to compare two snapshot rule""" + for key in snapruledict1.keys(): + if key in snapruledict2.keys() and snapruledict2[key] is not None and\ + ((isinstance(snapruledict1[key], list) and + set(snapruledict1[key]) != set(snapruledict2[key])) or + (snapruledict1[key] != snapruledict2[key])): + LOG.debug("Key %s in snapruledict1=%s, snapruledict2=%s", key, + snapruledict1[key], snapruledict2[key]) + return True + return False + + def main(): """ Create PowerstoreSnapshotrule object and perform action on it based on user input from playbook """ diff --git a/plugins/modules/dellemc_powerstore_volume.py b/plugins/modules/dellemc_powerstore_volume.py index 95e0a17..caaaa18 100644 --- a/plugins/modules/dellemc_powerstore_volume.py +++ b/plugins/modules/dellemc_powerstore_volume.py @@ -1,5 +1,6 @@ #!/usr/bin/python # Copyright: (c) 2019-2021, DellEMC +# Apache License version 2.0 (see MODULE-LICENSE or http://www.apache.org/licenses/LICENSE-2.0.txt) from __future__ import absolute_import, division, print_function @@ -17,7 +18,8 @@ description: - Managing volume on PowerStore storage system includes create volume, get details of volume, modify name, size, description, protection policy, - performance policy, map or unmap volume to host/host group, and delete volume. + performance policy, map or unmap volume to host/host group, and delete + volume. author: - Ambuj Dubey (@AmbujDube) - Manisha Agrawal (@agrawm3) @@ -95,8 +97,8 @@ host: description: - Host to be mapped/unmapped to a volume. If not specified, an unmapped - volume is created. Only one of the host or host group can be supplied in one - call. + volume is created. Only one of the host or host group can be supplied in + one call. - To represent host, both name or ID can be used interchangeably. The module will detect both. type: str @@ -105,8 +107,8 @@ - Hostgroup to be mapped/unmapped to a volume. If not specified, an unmapped volume is created. - Only one of the host or host group can be mapped in one call. - - To represent a hostgroup, both name or ID can be used interchangeably. The - module will detect both. + - To represent a hostgroup, both name or ID can be used interchangeably. + The module will detect both. type: str mapping_state: description: @@ -136,13 +138,14 @@ notes: - To create a new volume, vol_name and size is required. cap_unit, - description, vg_name, performance_policy, and protection_policy are optional. + description, vg_name, performance_policy, and protection_policy are + optional. - new_name should not be provided when creating a new volume. - size is a required parameter for expand volume. - Clones or Snapshots of a deleted production volume or a clone are not deleted. -- A volume that is attached to a host/host group, or that is part of a volume group - cannot be deleted. +- A volume that is attached to a host/host group, or that is part of a volume + group cannot be deleted. ''' EXAMPLES = r''' @@ -274,82 +277,83 @@ RETURN = r''' changed: - description: Whether or not the resource has changed + description: Whether or not the resource has changed. returned: always type: bool volume_details: - description: Details of the volume + description: Details of the volume. returned: When volume exists type: complex contains: id: - description: The system generated ID given to the volume + description: The system generated ID given to the volume. type: str name: - description: Name of the volume + description: Name of the volume. type: str size: - description: Size of the volume + description: Size of the volume. type: int description: - description: description about the volume + description: description about the volume. type: str performance_policy_id: - description: The performance policy for the volume + description: The performance policy for the volume. type: str protection_policy_id: - description: The protection policy of the volume + description: The protection policy of the volume. type: str volume_groups: - description: The volume group details of the volume + description: The volume group details of the volume. type: complex contains: id: - description: The system generated ID given to the volume group + description: The system generated ID given to the volume + group. type: str name: - description: Name of the volume group + description: Name of the volume group. type: str host: - description: Hosts details mapped to the volume + description: Hosts details mapped to the volume. type: complex contains: id: - description: The host ID mapped to the volume + description: The host ID mapped to the volume. type: str name: - description: Name of the Host mapped to the volume + description: Name of the Host mapped to the volume. type: str host_group: - description: Host groups details mapped to the volume + description: Host groups details mapped to the volume. type: complex contains: id: - description: The host group ID mapped to the volume + description: The host group ID mapped to the volume. type: str name: - description: Name of the Host group mapped to the volume + description: Name of the Host group mapped to the volume. type: str hlu_details: - description: HLU details for mapped host/host group + description: HLU details for mapped host/host group. type: complex contains: host_group_id: - description: The host group ID mapped to the volume + description: The host group ID mapped to the volume. type: str host_id: - description: The host ID mapped to the volume + description: The host ID mapped to the volume. type: str id: - description: The HLU ID + description: The HLU ID. type: str logical_unit_number: - description: Logical unit number for the host/host group volume - access + description: Logical unit number for the host/host group + volume access. type: int wwn: - description: The world wide name of the volume + description: The world wide name of the volume. type: str ''' @@ -369,7 +373,7 @@ VERSION_ERROR = py4ps_version['unsupported_version_message'] # Application type -APPLICATION_TYPE = 'Ansible/1.2.0' +APPLICATION_TYPE = 'Ansible/1.3.0' class PowerStoreVolume(object): @@ -414,16 +418,13 @@ def __init__(self): LOG.info('Got Py4Ps instance for provisioning on PowerStore %s', self.conn) - def get_volume(self): + def get_volume(self, vol_id=None, vol_name=None): """Get volume details""" try: - vol_id = self.module.params['vol_id'] if vol_id is not None: return self.provisioning.get_volume_details(vol_id) else: - volume_name = self.module.params['vol_name'] - volume_info = self.provisioning.get_volume_by_name( - volume_name) + volume_info = self.provisioning.get_volume_by_name(vol_name) if volume_info: if len(volume_info) > 1: error_msg = 'Multiple volumes by the same name found' @@ -648,7 +649,7 @@ def perform_module_operation(self): hlu = self.module.params['hlu'] changed = False - volume = self.get_volume() + volume = self.get_volume(vol_id, vol_name) # fetching the volume id from volume details if volume is not None: vol_id = volume['id'] @@ -686,7 +687,8 @@ def perform_module_operation(self): performance_policy=performance_policy, description=description) if changed: - volume = self.get_volume() + vol_id = self.get_volume_id_by_name(vol_name) + volume = self.get_volume(vol_id=vol_id) if state == 'present' and volume: if host and hostgroup: @@ -708,11 +710,11 @@ def perform_module_operation(self): self.module.fail_json(msg=msg) vg_mod_flag = True if volume['volume_groups']: - if len(volume['volume_groups']) > 0: - # check for modification of VG - if volume_group_id is not None and volume_group_id !=\ - volume['volume_groups'][0]['id']: - vg_mod_flag = False + # check for modification of VG + if len(volume['volume_groups']) > 0 and \ + volume_group_id is not None and \ + volume_group_id != volume['volume_groups'][0]['id']: + vg_mod_flag = False # check for assignment of a VG to an already existing volume elif volume_group_id is not None: vg_mod_flag = False @@ -768,22 +770,18 @@ def perform_module_operation(self): protection_policy_id=update_dict['protection_policy_id'], performance_policy=update_dict['performance_policy_id'], description=update_dict['description']) or changed - if changed and name is not vol_name: - self.module.params['vol_name'] = new_name if state == 'present' and volume and mapping_state in [ - None, 'unmapped']: - if hlu: - error_msg = 'Invalid paramter HLU provided' - LOG.info(error_msg) - self.module.fail_json(msg=error_msg) + None, 'unmapped'] and hlu: + error_msg = 'Invalid paramter HLU provided' + LOG.info(error_msg) + self.module.fail_json(msg=error_msg) - if host or hostgroup: - if state == 'present' and volume and mapping_state is None: - error_msg = 'Mapping state not provided, mandatory for' \ - ' mapping' - LOG.info(error_msg) - self.module.fail_json(msg=error_msg) + if (host or hostgroup) and state == 'present' and volume and \ + mapping_state is None: + error_msg = 'Mapping state not provided, mandatory for mapping' + LOG.info(error_msg) + self.module.fail_json(msg=error_msg) if state == 'present' and volume and mapping_state: if not host and not hostgroup: @@ -809,7 +807,7 @@ def perform_module_operation(self): self.result["changed"] = changed if state == 'present': - self.result["volume_details"] = self.get_volume() + self.result["volume_details"] = self.get_volume(vol_id=vol_id) self.module.exit_json(**self.result) def get_volume_id_by_name(self, volume_name): @@ -976,12 +974,11 @@ def check_modify_volume_required(vol, vol_dict2): vol_dict2[key]: if key == 'protection_policy_id' and vol_dict1[key] is None\ and vol_dict2[key] == '': - pass - else: - LOG.debug("Key %s in vol_dict1=%s,vol_dict2=%s", - key, vol_dict1[key], vol_dict2[key]) - update_dict[key] = vol_dict2[key] - modify_flag = True + continue + LOG.debug("Key %s in vol_dict1=%s,vol_dict2=%s", key, + vol_dict1[key], vol_dict2[key]) + update_dict[key] = vol_dict2[key] + modify_flag = True return modify_flag, update_dict @@ -989,23 +986,22 @@ def check_for_hlu_modification(volume, hlu, host=None, hostgroup=None): hlu_details = volume['hlu_details'] if host: for element in hlu_details: - if element.get('host_id') == host: - if int(element['logical_unit_number']) != hlu: - msg = 'Modification of HLU from {0} to {1} for host {2}' \ - ' was attempted'\ - .format(int(element['logical_unit_number']), hlu, - host) - return False, msg + if element.get('host_id') == host and \ + int(element['logical_unit_number']) != hlu: + msg = 'Modification of HLU from {0} to {1} for host {2} was' \ + ' attempted'.format(int(element['logical_unit_number']), + hlu, host) + return False, msg if hostgroup: for element in hlu_details: - if element.get('host_group_id') == hostgroup: - if int(element['logical_unit_number']) != hlu: - msg = 'Modification of HLU from {0} to {1} for hostgroup'\ - ' {2} was attempted'\ - .format(int(element['logical_unit_number']), hlu, - hostgroup) - return False, msg + if element.get('host_group_id') == hostgroup and \ + int(element['logical_unit_number']) != hlu: + msg = 'Modification of HLU from {0} to {1} for hostgroup ' \ + '{2} was attempted'.\ + format(int(element['logical_unit_number']), hlu, + hostgroup) + return False, msg return True, "" diff --git a/plugins/modules/dellemc_powerstore_volumegroup.py b/plugins/modules/dellemc_powerstore_volumegroup.py index 787408a..4440914 100644 --- a/plugins/modules/dellemc_powerstore_volumegroup.py +++ b/plugins/modules/dellemc_powerstore_volumegroup.py @@ -1,5 +1,6 @@ #!/usr/bin/python # Copyright: (c) 2019-2021, DellEMC +# Apache License version 2.0 (see MODULE-LICENSE or http://www.apache.org/licenses/LICENSE-2.0.txt) from __future__ import absolute_import, division, print_function @@ -170,81 +171,72 @@ RETURN = r""" changed: - description: Whether or not the resource has changed + description: Whether or not the resource has changed. returned: always type: bool add_vols_to_vg: description: A boolean flag to indicate whether volume/s got added to - volume group + volume group. returned: When value exists type: bool create_vg: - description: A boolean flag to indicate whether volume group got created + description: A boolean flag to indicate whether volume group got created. returned: When value exists type: bool delete_vg: - description: A boolean flag to indicate whether volume group got deleted + description: A boolean flag to indicate whether volume group got deleted. returned: When value exists type: bool modify_vg: - description: A boolean flag to indicate whether volume group got modified + description: A boolean flag to indicate whether volume group got modified. returned: When value exists type: bool remove_vols_from_vg: description: A boolean flag to indicate whether volume/s got removed from - volume group + volume group. returned: When value exists type: bool volume_group_details: - description: Details of the volume group + description: Details of the volume group. returned: When volume group exists type: complex contains: id: - description: - - The system generated ID given to the volume group + description: The system generated ID given to the volume group. type: str name: - description: - - Name of the volume group + description: Name of the volume group. type: str description: - description: - - description about the volume group + description: description about the volume group. type: str protection_policy_id: - description: - - The protection policy of the volume group + description: The protection policy of the volume group. type: str is_write_order_consistent: - description: - - A boolean flag to indicate whether snapshot sets of the - volume group will be write-order consistent + description: A boolean flag to indicate whether snapshot sets of + the volume group will be write-order consistent. type: bool type: - description: - - The type of the volume group + description: The type of the volume group. type: str volumes: - description: - - The volumes details of the volume group + description: The volumes details of the volume group. type: complex contains: id: - description: - - The system generated ID given to the volume - associated with the volume group + description: The system generated ID given to the volume + associated with the volume group. type: str name: - description: - - The name of the volume associated with the volume - group. + description: The name of the volume associated with the + volume group. type: str """ @@ -265,7 +257,7 @@ VERSION_ERROR = py4ps_version['unsupported_version_message'] # Application type -APPLICATION_TYPE = 'Ansible/1.2.0' +APPLICATION_TYPE = 'Ansible/1.3.0' class PowerStoreVolumeGroup(object): @@ -323,9 +315,9 @@ def get_volume_group_details(self, vg_id=None, name=None): if name: resp = self.provisioning.get_volume_group_by_name(name) if resp and len(resp) > 0: - id = resp[0]['id'] + vol_grp_id = resp[0]['id'] vg_details = self.provisioning.\ - get_volume_group_details(id) + get_volume_group_details(vol_grp_id) LOG.info("Successfully Got VG with name %s", name) return vg_details return None @@ -426,9 +418,8 @@ def remove_volumes_from_volume_group(self, vg_id, vol_list): msg = "Volume with name {0} not found. Please enter a " \ "correct volume name.".format(vol) self.module.fail_json(msg=msg) - if vol_id in existing_vol_ids: - if vol_id not in ids_to_remove: - ids_to_remove.append(vol_id) + if (vol_id in existing_vol_ids) and (vol_id not in ids_to_remove): + ids_to_remove.append(vol_id) """remove by id""" for vol in vol_id_list: @@ -439,9 +430,8 @@ def remove_volumes_from_volume_group(self, vg_id, vol_list): msg = "Volume with id {0} not found. Please enter a correct " \ "volume id".format(vol) self.module.fail_json(msg=msg) - if vol in existing_vol_ids: - if vol not in ids_to_remove: - ids_to_remove.append(vol) + if (vol in existing_vol_ids) and (vol not in ids_to_remove): + ids_to_remove.append(vol) LOG.debug("Volume IDs to Remove %s", ids_to_remove) @@ -499,9 +489,8 @@ def add_volumes_to_volume_group(self, vg_id, vol_list): msg = "Volume with name {0} not found. Please enter a " \ "correct volume name.".format(vol) self.module.fail_json(msg=msg) - if vol_id not in existing_vol_ids: - if vol_id not in ids_to_add: - ids_to_add.append(vol_id) + if (vol_id not in existing_vol_ids) and (vol_id not in ids_to_add): + ids_to_add.append(vol_id) """add volume by id""" for vol in vol_id_list: @@ -511,9 +500,9 @@ def add_volumes_to_volume_group(self, vg_id, vol_list): msg = "Volume with id {0} not found. Please enter a correct " \ "volume id".format(vol) self.module.fail_json(msg=msg) - if vol_by_id not in existing_vol_ids: - if vol_by_id not in ids_to_add: - ids_to_add.append(vol_by_id) + if (vol_by_id not in existing_vol_ids) and\ + (vol_by_id not in ids_to_add): + ids_to_add.append(vol_by_id) LOG.info("Volume IDs to add %s", ids_to_add) @@ -567,11 +556,15 @@ def is_volume_group_modified(self, volume_group, protection_policy): """Check if the desired volume group state is different from existing volume group""" modified = False + name_modified = False + description_modified = False + prot_pol_modified = False + write_order_modified = False if(('name' in volume_group and self.module.params['new_vg_name'] is not None) and (volume_group['name'].lower() != self.module.params['new_vg_name'].lower())): - modified = True + name_modified = True elif (volume_group['description'] is not None and self.module.params['description'] is not None and volume_group['description'].lower() != @@ -579,7 +572,7 @@ def is_volume_group_modified(self, volume_group, protection_policy): (volume_group['description'] is None and self.module.params['description'] is not None and self.module.params['description'].lower() != 'none'): - modified = True + description_modified = True elif((volume_group['protection_policy_id'] is not None and protection_policy is not None and volume_group['protection_policy_id'] != @@ -587,11 +580,15 @@ def is_volume_group_modified(self, volume_group, protection_policy): (volume_group['protection_policy_id'] is None and protection_policy is not None and protection_policy != '')): - modified = True + prot_pol_modified = True elif('is_write_order_consistent' in volume_group and self.module.params['is_write_order_consistent'] is not None and volume_group['is_write_order_consistent'] != self.module.params['is_write_order_consistent']): + write_order_modified = True + + if name_modified or description_modified or prot_pol_modified or\ + write_order_modified: modified = True return modified