diff options
Diffstat (limited to 'ansible_collections/purestorage/flashblade')
64 files changed, 2873 insertions, 1684 deletions
diff --git a/ansible_collections/purestorage/flashblade/.github/workflows/ansible-lint.yml b/ansible_collections/purestorage/flashblade/.github/workflows/ansible-lint.yml index 0b2102184..384c5ac93 100644 --- a/ansible_collections/purestorage/flashblade/.github/workflows/ansible-lint.yml +++ b/ansible_collections/purestorage/flashblade/.github/workflows/ansible-lint.yml @@ -1,5 +1,5 @@ -name: Ansible Lint # feel free to pick your own name -on: [push, pull_request] +name: Ansible Lint # feel free to pick your own name +"on": [push, pull_request] jobs: build: diff --git a/ansible_collections/purestorage/flashblade/.github/workflows/black.yaml b/ansible_collections/purestorage/flashblade/.github/workflows/black.yaml index e5f9711f6..19b2b01d3 100644 --- a/ansible_collections/purestorage/flashblade/.github/workflows/black.yaml +++ b/ansible_collections/purestorage/flashblade/.github/workflows/black.yaml @@ -1,6 +1,6 @@ name: Lint -on: [push, pull_request] +"on": [push, pull_request] jobs: lint: diff --git a/ansible_collections/purestorage/flashblade/.github/workflows/main.yml b/ansible_collections/purestorage/flashblade/.github/workflows/main.yml index e66ce2991..4368a1708 100644 --- a/ansible_collections/purestorage/flashblade/.github/workflows/main.yml +++ b/ansible_collections/purestorage/flashblade/.github/workflows/main.yml @@ -1,6 +1,6 @@ name: Pure Storage Ansible CI -on: +"on": pull_request: push: schedule: @@ -13,32 +13,20 @@ jobs: strategy: matrix: ansible: - - stable-2.11 - - stable-2.12 - - stable-2.13 - stable-2.14 - stable-2.15 + - stable-2.16 - devel python-version: - - 3.8 - 3.9 - "3.10" - "3.11" exclude: - - python-version: "3.11" - ansible: stable-2.11 - - python-version: "3.11" - ansible: stable-2.12 - - python-version: "3.11" - ansible: stable-2.13 - - python-version: "3.10" - ansible: stable-2.11 - - python-version: 3.8 - ansible: stable-2.14 - - python-version: 3.8 - ansible: stable-2.15 - - python-version: 3.8 + - python-version: 3.9 + ansible: stable-2.16 + - python-version: 3.9 ansible: devel + steps: - name: Check out code uses: actions/checkout@v3 diff --git a/ansible_collections/purestorage/flashblade/.github/workflows/stale.yml b/ansible_collections/purestorage/flashblade/.github/workflows/stale.yml index 7bbc0505b..ee7c9796e 100644 --- a/ansible_collections/purestorage/flashblade/.github/workflows/stale.yml +++ b/ansible_collections/purestorage/flashblade/.github/workflows/stale.yml @@ -1,6 +1,6 @@ name: Mark stale issues and pull requests -on: +"on": schedule: - cron: "0 0 * * *" diff --git a/ansible_collections/purestorage/flashblade/.pylintrc b/ansible_collections/purestorage/flashblade/.pylintrc deleted file mode 100644 index 9570a2b59..000000000 --- a/ansible_collections/purestorage/flashblade/.pylintrc +++ /dev/null @@ -1,587 +0,0 @@ -[MASTER] - -# A comma-separated list of package or module names from where C extensions may -# be loaded. Extensions are loading into the active Python interpreter and may -# run arbitrary code -extension-pkg-whitelist= - -# Add files or directories to the blacklist. They should be base names, not -# paths. -ignore=CVS - -# Add files or directories matching the regex patterns to the blacklist. The -# regex matches against base names, not paths. -ignore-patterns= - -# Python code to execute, usually for sys.path manipulation such as -# pygtk.require(). -#init-hook= - -# Use multiple processes to speed up Pylint. -jobs=1 - -# List of plugins (as comma separated values of python modules names) to load, -# usually to register additional checkers. -load-plugins= - -# Pickle collected data for later comparisons. -persistent=yes - -# Specify a configuration file. -#rcfile= - -# When enabled, pylint would attempt to guess common misconfiguration and emit -# user-friendly hints instead of false-positive error messages -suggestion-mode=yes - -# Allow loading of arbitrary C extensions. Extensions are imported into the -# active Python interpreter and may run arbitrary code. -unsafe-load-any-extension=no - - -[MESSAGES CONTROL] - -# Only show warnings with the listed confidence levels. Leave empty to show -# all. Valid levels: HIGH, INFERENCE, INFERENCE_FAILURE, UNDEFINED -confidence= - -# Disable the message, report, category or checker with the given id(s). You -# can either give multiple identifiers separated by comma (,) or put this -# option multiple times (only on the command line, not in the configuration -# file where it should appear only once).You can also use "--disable=all" to -# disable everything first and then reenable specific checks. For example, if -# you want to run only the similarities checker, you can use "--disable=all -# --enable=similarities". If you want to run only the classes checker, but have -# no Warning level messages displayed, use"--disable=all --enable=classes -# --disable=W" -disable= - abstract-method, - access-member-before-definition, - ansible-deprecated-version, - arguments-differ, - assignment-from-no-return, - assignment-from-none, - attribute-defined-outside-init, - bad-continuation, - bad-indentation, - bad-mcs-classmethod-argument, - broad-except, - c-extension-no-member, - cell-var-from-loop, - chained-comparison, - comparison-with-callable, - consider-iterating-dictionary, - consider-merging-isinstance, - consider-using-dict-comprehension, - consider-using-enumerate, - consider-using-get, - consider-using-in, - consider-using-set-comprehension, - consider-using-ternary, - deprecated-lambda, - deprecated-method, - deprecated-module, - eval-used, - exec-used, - expression-not-assigned, - fixme, - function-redefined, - global-statement, - global-variable-undefined, - import-error, - import-self, - inconsistent-return-statements, - invalid-envvar-default, - invalid-name, - invalid-sequence-index, - keyword-arg-before-vararg, - len-as-condition, - line-too-long, - literal-comparison, - locally-disabled, - method-hidden, - misplaced-comparison-constant, - missing-docstring, - no-else-raise, - no-else-return, - no-init, - no-member, - no-name-in-module, - no-self-use, - no-value-for-parameter, - non-iterator-returned, - not-a-mapping, - not-an-iterable, - not-callable, - old-style-class, - pointless-statement, - pointless-string-statement, - possibly-unused-variable, - protected-access, - redefined-argument-from-local, - redefined-builtin, - redefined-outer-name, - redefined-variable-type, - reimported, - relative-import, - signature-differs, - simplifiable-if-expression, - simplifiable-if-statement, - subprocess-popen-preexec-fn, - super-init-not-called, - superfluous-parens, - too-few-public-methods, - too-many-ancestors, - too-many-arguments, - too-many-boolean-expressions, - too-many-branches, - too-many-function-args, - too-many-instance-attributes, - too-many-lines, - too-many-locals, - too-many-nested-blocks, - too-many-public-methods, - too-many-return-statements, - too-many-statements, - trailing-comma-tuple, - trailing-comma-tuple, - try-except-raise, - unbalanced-tuple-unpacking, - undefined-loop-variable, - unexpected-keyword-arg, - ungrouped-imports, - unidiomatic-typecheck, - unnecessary-pass, - unsubscriptable-object, - unsupported-assignment-operation, - unsupported-delete-operation, - unsupported-membership-test, - unused-argument, - unused-import, - unused-variable, - used-before-assignment, - useless-object-inheritance, - useless-return, - useless-super-delegation, - wrong-import-order, - wrong-import-position, - -# Enable the message, report, category or checker with the given id(s). You can -# either give multiple identifier separated by comma (,) or put this option -# multiple time (only on the command line, not in the configuration file where -# it should appear only once). See also the "--disable" option for examples. -enable=c-extension-no-member - - -[REPORTS] - -# Python expression which should return a note less than 10 (10 is the highest -# note). You have access to the variables errors warning, statement which -# respectively contain the number of errors / warnings messages and the total -# number of statements analyzed. This is used by the global evaluation report -# (RP0004). -evaluation=10.0 - ((float(5 * error + warning + refactor + convention) / statement) * 10) - -# Template used to display messages. This is a python new-style format string -# used to format the message information. See doc for all details -#msg-template= - -# Set the output format. Available formats are text, parseable, colorized, json -# and msvs (visual studio).You can also give a reporter class, eg -# mypackage.mymodule.MyReporterClass. -output-format=text - -# Tells whether to display a full report or only the messages -reports=no - -# Activate the evaluation score. -score=yes - - -[REFACTORING] - -# Maximum number of nested blocks for function / method body -max-nested-blocks=5 - -# Complete name of functions that never returns. When checking for -# inconsistent-return-statements if a never returning function is called then -# it will be considered as an explicit return statement and no message will be -# printed. -never-returning-functions=optparse.Values,sys.exit - - -[VARIABLES] - -# List of additional names supposed to be defined in builtins. Remember that -# you should avoid to define new builtins when possible. -additional-builtins= - -# Tells whether unused global variables should be treated as a violation. -allow-global-unused-variables=yes - -# List of strings which can identify a callback function by name. A callback -# name must start or end with one of those strings. -callbacks=cb_, - _cb - -# A regular expression matching the name of dummy variables (i.e. expectedly -# not used). -dummy-variables-rgx=_+$|(_[a-zA-Z0-9_]*[a-zA-Z0-9]+?$)|dummy|^ignored_|^unused_ - -# Argument names that match this expression will be ignored. Default to name -# with leading underscore -ignored-argument-names=_.*|^ignored_|^unused_ - -# Tells whether we should check for unused import in __init__ files. -init-import=no - -# List of qualified module names which can have objects that can redefine -# builtins. -redefining-builtins-modules=six.moves,past.builtins,future.builtins,io,builtins - - -[BASIC] - -# Naming style matching correct argument names -argument-naming-style=snake_case - -# Regular expression matching correct argument names. Overrides argument- -# naming-style -#argument-rgx= - -# Naming style matching correct attribute names -attr-naming-style=snake_case - -# Regular expression matching correct attribute names. Overrides attr-naming- -# style -#attr-rgx= - -# Bad variable names which should always be refused, separated by a comma -bad-names=foo, - bar, - baz, - toto, - tutu, - tata, - _, - -# Naming style matching correct class attribute names -class-attribute-naming-style=any - -# Regular expression matching correct class attribute names. Overrides class- -# attribute-naming-style -#class-attribute-rgx= - -# Naming style matching correct class names -class-naming-style=PascalCase - -# Regular expression matching correct class names. Overrides class-naming-style -#class-rgx= - -# Naming style matching correct constant names -const-naming-style=UPPER_CASE - -# Regular expression matching correct constant names. Overrides const-naming- -# style -#const-rgx= - -# Minimum line length for functions/classes that require docstrings, shorter -# ones are exempt. -docstring-min-length=-1 - -# Naming style matching correct function names -function-naming-style=snake_case - -# Regular expression matching correct function names. Overrides function- -# naming-style -#function-rgx= - -# Good variable names which should always be accepted, separated by a comma -good-names=i, - j, - k, - f, - e, - ex, - Run, - C, - __metaclass__, - -# Include a hint for the correct naming format with invalid-name -include-naming-hint=no - -# Naming style matching correct inline iteration names -inlinevar-naming-style=any - -# Regular expression matching correct inline iteration names. Overrides -# inlinevar-naming-style -#inlinevar-rgx= - -# Naming style matching correct method names -method-naming-style=snake_case - -# Regular expression matching correct method names. Overrides method-naming- -# style -#method-rgx= - -# Naming style matching correct module names -module-naming-style=snake_case - -# Regular expression matching correct module names. Overrides module-naming- -# style -#module-rgx= -module-rgx=[a-z_][a-z0-9_-]{2,40}$ -method-rgx=[a-z_][a-z0-9_]{2,40}$ -function-rgx=[a-z_][a-z0-9_]{2,40}$ - -# Colon-delimited sets of names that determine each other's naming style when -# the name regexes allow several styles. -name-group= - -# Regular expression which should only match function or class names that do -# not require a docstring. -no-docstring-rgx=^_ - -# List of decorators that produce properties, such as abc.abstractproperty. Add -# to this list to register other decorators that produce valid properties. -property-classes=abc.abstractproperty - -# Naming style matching correct variable names -variable-naming-style=snake_case - -# Regular expression matching correct variable names. Overrides variable- -# naming-style -#variable-rgx= - - -[SPELLING] - -# Limits count of emitted suggestions for spelling mistakes -max-spelling-suggestions=4 - -# Spelling dictionary name. Available dictionaries: none. To make it working -# install python-enchant package. -spelling-dict= - -# List of comma separated words that should not be checked. -spelling-ignore-words= - -# A path to a file that contains private dictionary; one word per line. -spelling-private-dict-file= - -# Tells whether to store unknown words to indicated private dictionary in -# --spelling-private-dict-file option instead of raising a message. -spelling-store-unknown-words=no - - -[FORMAT] - -# Expected format of line ending, e.g. empty (any line ending), LF or CRLF. -expected-line-ending-format= - -# Regexp for a line that is allowed to be longer than the limit. -ignore-long-lines=^\s*(# )?<?https?://\S+>?$ - -# Number of spaces of indent required inside a hanging or continued line. -indent-after-paren=4 - -# String used as indentation unit. This is usually " " (4 spaces) or "\t" (1 -# tab). -indent-string=' ' - -# Maximum number of characters on a single line. -max-line-length=160 - -# Maximum number of lines in a module -max-module-lines=1000 - -# List of optional constructs for which whitespace checking is disabled. `dict- -# separator` is used to allow tabulation in dicts, etc.: {1 : 1,\n222: 2}. -# `trailing-comma` allows a space between comma and closing bracket: (a, ). -# `empty-line` allows space-only lines. -no-space-check=trailing-comma, - dict-separator - -# Allow the body of a class to be on the same line as the declaration if body -# contains single statement. -single-line-class-stmt=no - -# Allow the body of an if to be on the same line as the test if there is no -# else. -single-line-if-stmt=no - - -[TYPECHECK] - -# List of decorators that produce context managers, such as -# contextlib.contextmanager. Add to this list to register other decorators that -# produce valid context managers. -contextmanager-decorators=contextlib.contextmanager - -# List of members which are set dynamically and missed by pylint inference -# system, and so shouldn't trigger E1101 when accessed. Python regular -# expressions are accepted. -#generated-members=PurityFb.* - -# Tells whether missing members accessed in mixin class should be ignored. A -# mixin class is detected if its name ends with "mixin" (case insensitive). -ignore-mixin-members=yes - -# This flag controls whether pylint should warn about no-member and similar -# checks whenever an opaque object is returned when inferring. The inference -# can return multiple potential results while evaluating a Python object, but -# some branches might not be evaluated, which results in partial inference. In -# that case, it might be useful to still emit no-member and other checks for -# the rest of the inferred objects. -ignore-on-opaque-inference=yes - -# List of class names for which member attributes should not be checked (useful -# for classes with dynamically set attributes). This supports the use of -# qualified names. -ignored-classes=optparse.Values,thread._local,_thread._local - -# List of module names for which member attributes should not be checked -# (useful for modules/projects where namespaces are manipulated during runtime -# and thus existing member attributes cannot be deduced by static analysis. It -# supports qualified module names, as well as Unix pattern matching. -ignored-modules= - _MovedItems, -# Show a hint with possible names when a member name was not found. The aspect -# of finding the hint is based on edit distance. -missing-member-hint=yes - -# The minimum edit distance a name should have in order to be considered a -# similar match for a missing member name. -missing-member-hint-distance=1 - -# The total number of similar names that should be taken in consideration when -# showing a hint for a missing member. -missing-member-max-choices=1 - - -[SIMILARITIES] - -# Ignore comments when computing similarities. -ignore-comments=yes - -# Ignore docstrings when computing similarities. -ignore-docstrings=yes - -# Ignore imports when computing similarities. -ignore-imports=no - -# Minimum lines number of a similarity. -min-similarity-lines=4 - - -[LOGGING] - -# Logging modules to check that the string format arguments are in logging -# function parameter format -logging-modules=logging - - -[MISCELLANEOUS] - -# List of note tags to take in consideration, separated by a comma. -notes=FIXME, - XXX, - TODO - - -[CLASSES] - -# List of method names used to declare (i.e. assign) instance attributes. -defining-attr-methods=__init__, - __new__, - setUp - -# List of member names, which should be excluded from the protected access -# warning. -exclude-protected=_asdict, - _fields, - _replace, - _source, - _make - -# List of valid names for the first argument in a class method. -valid-classmethod-first-arg=cls - -# List of valid names for the first argument in a metaclass class method. -valid-metaclass-classmethod-first-arg=mcs - - -[DESIGN] - -# Maximum number of arguments for function / method -max-args=5 - -# Maximum number of attributes for a class (see R0902). -max-attributes=7 - -# Maximum number of boolean expressions in a if statement -max-bool-expr=5 - -# Maximum number of branch for function / method body -max-branches=12 - -# Maximum number of locals for function / method body -max-locals=15 - -# Maximum number of parents for a class (see R0901). -max-parents=7 - -# Maximum number of public methods for a class (see R0904). -max-public-methods=20 - -# Maximum number of return / yield for function / method body -max-returns=6 - -# Maximum number of statements in function / method body -max-statements=50 - -# Minimum number of public methods for a class (see R0903). -min-public-methods=2 - - -[IMPORTS] - -# Allow wildcard imports from modules that define __all__. -allow-wildcard-with-all=no - -# Analyse import fallback blocks. This can be used to support both Python 2 and -# 3 compatible code, which means that the block might have code that exists -# only in one or another interpreter, leading to false positives when analysed. -analyse-fallback-blocks=no - -# Deprecated modules which should not be used, separated by a comma -deprecated-modules=regsub, - TERMIOS, - Bastion, - rexec - -# Create a graph of external dependencies in the given file (report RP0402 must -# not be disabled) -ext-import-graph= - -# Create a graph of every (i.e. internal and external) dependencies in the -# given file (report RP0402 must not be disabled) -import-graph= - -# Create a graph of internal dependencies in the given file (report RP0402 must -# not be disabled) -int-import-graph= - -# Force import order to recognize a module as part of the standard -# compatibility libraries. -known-standard-library= - -# Force import order to recognize a module as part of a third party library. -known-third-party=enchant - - -[EXCEPTIONS] - -# Exceptions that will emit a warning when being caught. Defaults to -# "Exception" -overgeneral-exceptions=Exception diff --git a/ansible_collections/purestorage/flashblade/CHANGELOG.rst b/ansible_collections/purestorage/flashblade/CHANGELOG.rst index c252af127..13b845abd 100644 --- a/ansible_collections/purestorage/flashblade/CHANGELOG.rst +++ b/ansible_collections/purestorage/flashblade/CHANGELOG.rst @@ -5,6 +5,98 @@ Purestorage.Flashblade Release Notes .. contents:: Topics +v1.16.0 +======= + +Minor Changes +------------- + +- purefb_ds - Add `force_bind_password` parameter to allow module to be idempotent. + +Bugfixes +-------- + +- purefb_bucket - Changed logic to allow complex buckets to be created in a single call, rather than having to split into two tasks. +- purefb_lag - Enable LAG port configuration with multi-chassis +- purefb_timeout - Fixed arithmetic error that resulted in module incorrectly reporting changed when no change was required. + +v1.15.0 +======= + +Minor Changes +------------- + +- purefb_bucket - Add support for public buckets +- purefb_bucket - From REST 2.12 the `mode` parameter default changes to `multi-site-writable`. +- purefb_fs - Added SMB Continuous Availability parameter. Requires REST 2.12 or higher. +- purefb_info - Added enhanced information for buckets, filesystems and snapshots, based on new features in REST 2.12 +- purefb_s3acc - Add support for public buckets +- purefb_s3acc - Remove default requirements for ``hard_limit`` and ``default_hard_limit`` + +Bugfixes +-------- + +- purefb_info - Added missing object lock retention details if enabledd + +New Modules +----------- + +- purestorage.flashblade.purefb_hardware - Manage FlashBlade Hardware + +v1.14.0 +======= + +Minor Changes +------------- + +- purefb_bucket_replica - Added support for cascading replica links +- purefb_info - New fields to display free space (remaining quota) for Accounts and Buckets. Space used by destroyed buckets is split out from virtual field to new destroyed_virtual field +- purefb_info - Report encryption state in SMB client policy rules +- purefb_info - Report more detailed space data from Purity//FB 4.3.0 +- purefb_policy - Add deny effect for object store policy rules. Requires Purity//FB 4.3.0+ +- purefb_policy - Added parameter to define object store policy description + +Bugfixes +-------- + +- purefb_userpolicy - Fixed `show` state for all user policies + +v1.13.1 +======= + +Minor Changes +------------- + +- purefb_policy - Add new and updated policy access rights + +Bugfixes +-------- + +- purefb_info - Fixed missing atributes for SMB client policy rules + +v1.13.0 +======= + +v1.12.0 +======= + +Minor Changes +------------- + +- purefb_fs - Added support for SMB client and share policies +- purefb_fs_replica - Added support to delete filesystem replica links from REST 2.10 +- purefb_info - Add drive type in drives subset for //S and //E platforms. Only available from REST 2.9. +- purefb_info - Added support for SMB client and share policies +- purefb_policy - Added support for SMB client and share policies +- purefb_s3acc - Allow human readable quota sizes; eg. 1T, 230K, etc +- purefb_s3user - Add new boolean parameter I(multiple_keys) to limit access keys for a user to a single key. + +Bugfixes +-------- + +- purefb_bucket - Fixed bucket type mode name typo +- purefb_fs - Fixed issue with incorrect promotion state setting + v1.11.0 ======= @@ -24,7 +116,7 @@ Minor Changes Bugfixes -------- -- purefa_info - Fixed issue when more than 10 buckets have lifecycle rules. +- purefb_info - Fixed issue when more than 10 buckets have lifecycle rules. - purefb_s3user - Fix incorrect response when bad key/secret pair provided for new user New Modules @@ -133,7 +225,7 @@ v1.6.0 Minor Changes ------------- -- purefa_virtualhost - New module to manage API Clients +- purefb_virtualhost - New module to manage API Clients - purefb_ad - New module to manage Active Directory Account - purefb_eula - New module to sign EULA - purefb_info - Add Active Directory, Kerberos and Object Store Account information @@ -200,7 +292,7 @@ Minor Changes Bugfixes -------- -- purefa_policy - Resolve multiple issues related to incorrect use of timezones +- purefb_policy - Resolve multiple issues related to incorrect use of timezones - purefb_connect - Ensure changing encryption status on array connection is performed correctly - purefb_connect - Fix breaking change created in purity_fb SDK 1.9.2 for deletion of array connections - purefb_connect - Hide target array API token diff --git a/ansible_collections/purestorage/flashblade/FILES.json b/ansible_collections/purestorage/flashblade/FILES.json index 5cfa68659..d7b39dbda 100644 --- a/ansible_collections/purestorage/flashblade/FILES.json +++ b/ansible_collections/purestorage/flashblade/FILES.json @@ -8,402 +8,388 @@ "format": 1 }, { - "name": "roles", - "ftype": "dir", - "chksum_type": null, - "chksum_sha256": null, - "format": 1 - }, - { - "name": "roles/.keep", - "ftype": "file", - "chksum_type": "sha256", - "chksum_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", - "format": 1 - }, - { - "name": "tests", + "name": "changelogs", "ftype": "dir", "chksum_type": null, "chksum_sha256": null, "format": 1 }, { - "name": "tests/sanity", + "name": "changelogs/fragments", "ftype": "dir", "chksum_type": null, "chksum_sha256": null, "format": 1 }, { - "name": "tests/sanity/ignore-2.9.txt", + "name": "changelogs/fragments/140_more_32_info.yaml", "ftype": "file", "chksum_type": "sha256", - "chksum_sha256": "83175635ac646a5cfc169ec6316e0ea90071bf4e8e9a79212a413f326a3049bd", + "chksum_sha256": "4e57a10a71ab3dd1c151a6867c0da118a21e13df2ef8b9d2fbb799108ddebcd4", "format": 1 }, { - "name": "tests/sanity/ignore-2.13.txt", + "name": "changelogs/fragments/169_pypureclient_fix.yaml", "ftype": "file", "chksum_type": "sha256", - "chksum_sha256": "83175635ac646a5cfc169ec6316e0ea90071bf4e8e9a79212a413f326a3049bd", + "chksum_sha256": "fb6e7bfc1c816ec77dadf6bd4ab040a8089b98a1c9c75ec15603d407c27ce3f2", "format": 1 }, { - "name": "tests/sanity/ignore-2.16.txt", + "name": "changelogs/fragments/96_fix_update_connection.yaml", "ftype": "file", "chksum_type": "sha256", - "chksum_sha256": "83175635ac646a5cfc169ec6316e0ea90071bf4e8e9a79212a413f326a3049bd", + "chksum_sha256": "828cc0c94acf44d1d373402a0cc657527d9fce8ac744319fbe0d8035684932b4", "format": 1 }, { - "name": "tests/sanity/ignore-2.14.txt", + "name": "changelogs/fragments/129-virtualhost.yaml", "ftype": "file", "chksum_type": "sha256", - "chksum_sha256": "83175635ac646a5cfc169ec6316e0ea90071bf4e8e9a79212a413f326a3049bd", + "chksum_sha256": "0af56f02e1b7ad1ea585b3bbce897022faf28b448b69ea755951be3b5da40f7e", "format": 1 }, { - "name": "tests/sanity/ignore-2.11.txt", + "name": "changelogs/fragments/161_add_lifecycle_info.yaml", "ftype": "file", "chksum_type": "sha256", - "chksum_sha256": "83175635ac646a5cfc169ec6316e0ea90071bf4e8e9a79212a413f326a3049bd", + "chksum_sha256": "b8c87e250274f2b5007ce0898c9bb6d79129faedaa8427a52377f86c24c6e90f", "format": 1 }, { - "name": "tests/sanity/ignore-2.12.txt", + "name": "changelogs/fragments/105_max_access_key.yaml", "ftype": "file", "chksum_type": "sha256", - "chksum_sha256": "83175635ac646a5cfc169ec6316e0ea90071bf4e8e9a79212a413f326a3049bd", + "chksum_sha256": "fb9f5707e7466fe7c94479891f218bacd04ae45a37c2f207dcf51ac756fb7259", "format": 1 }, { - "name": "tests/sanity/ignore-2.15.txt", + "name": "changelogs/fragments/153_add_quota.yaml", "ftype": "file", "chksum_type": "sha256", - "chksum_sha256": "83175635ac646a5cfc169ec6316e0ea90071bf4e8e9a79212a413f326a3049bd", + "chksum_sha256": "2b2517ea362d7128333d6fab7f99f6b70c4253d2807eae3ec417aa4451b3ae6c", "format": 1 }, { - "name": "tests/sanity/ignore-2.10.txt", + "name": "changelogs/fragments/76_default_fs_size.yaml", "ftype": "file", "chksum_type": "sha256", - "chksum_sha256": "83175635ac646a5cfc169ec6316e0ea90071bf4e8e9a79212a413f326a3049bd", + "chksum_sha256": "6d8689e8f46ab7d3286b7d3ee46dfa13a8bf0585cc9b197a5ca8271c9dd9590e", "format": 1 }, { - "name": ".github", - "ftype": "dir", - "chksum_type": null, - "chksum_sha256": null, + "name": "changelogs/fragments/185_nfs_export_rule.yaml", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "8f53ac3485ed3849ca99fee6015e2767f636c1186a368b3d4e91ba6076afd7d4", "format": 1 }, { - "name": ".github/CONTRIBUTING.md", + "name": "changelogs/fragments/200_proxy.yaml", "ftype": "file", "chksum_type": "sha256", - "chksum_sha256": "7c429527b799623f57e6363e14ff8a319844c9120f4dfa18bcea3849cdc07128", + "chksum_sha256": "26631d7434c86b739bcd75c8905f8f668555217610cafb47f11a6e24937c7eb8", "format": 1 }, { - "name": ".github/workflows", - "ftype": "dir", - "chksum_type": null, - "chksum_sha256": null, + "name": "changelogs/fragments/211_change_booleans.yaml", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "f04fd18a42e321cb3818a579e14cc50a6d27935196ff04632e2db44f7b807322", "format": 1 }, { - "name": ".github/workflows/black.yaml", + "name": "changelogs/fragments/135_add_user_policies.yaml", "ftype": "file", "chksum_type": "sha256", - "chksum_sha256": "6fb3e0af2e41fb0618586a2990e6645fb9b29d1a7b64b7168c5d27af320569c8", + "chksum_sha256": "a0b78f5b1a5be3bfb87a00a4e638fad67600b0bab4cfddd72b3bfa4d2e217e3f", "format": 1 }, { - "name": ".github/workflows/ansible-lint.yml", + "name": "changelogs/fragments/97_fix_encrpyted_array_connection_info.yaml", "ftype": "file", "chksum_type": "sha256", - "chksum_sha256": "4c85688d98b71e3a6594530a362cd5d2cf83842ceaccd0e0fc76e233777c1cef", + "chksum_sha256": "e140fbfc3ac4eaab3dd9c482e3beb37efd98ad4c3892b36f93ffb00d89c9283f", "format": 1 }, { - "name": ".github/workflows/stale.yml", + "name": "changelogs/fragments/230_prom_fix.yaml", "ftype": "file", "chksum_type": "sha256", - "chksum_sha256": "0bdef4889afabcd627fc30711a0809c7468b8c9e64cbcebe1334f794a41e7bd9", + "chksum_sha256": "9059ca5e0ead1a93ffc503a86539d0b9ee64f0fc2526ba63ba76a0366192e178", "format": 1 }, { - "name": ".github/workflows/main.yml", + "name": "changelogs/fragments/162_new_lifecycle.yaml", "ftype": "file", "chksum_type": "sha256", - "chksum_sha256": "7f7d9b7fc9ac71a4ff36243422b04f4cf163a254c52e8ab647fb84807bc3ea21", + "chksum_sha256": "bd6214f7380736e34ed7a21396f1842c6796afba6c3b7413536522d4b6d0b531", "format": 1 }, { - "name": ".github/ISSUE_TEMPLATE", - "ftype": "dir", - "chksum_type": null, - "chksum_sha256": null, + "name": "changelogs/fragments/225_delete_rl.yaml", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "e9c0ed90d651ea4b4b83a2cb05cd9435e9707323207efc042f056eaa616a629c", "format": 1 }, { - "name": ".github/ISSUE_TEMPLATE/feature_request.md", + "name": "changelogs/fragments/243_policy_desc.yaml", "ftype": "file", "chksum_type": "sha256", - "chksum_sha256": "1f48c52f209a971b8e7eae4120144d28fcf8ee38a7778a7b4d8cf1ab356617d2", + "chksum_sha256": "7612dd96fa7e9be64784224d1fb2579eb57d28751a397fffe4f55213f507f78a", "format": 1 }, { - "name": ".github/ISSUE_TEMPLATE/bug_report.md", + "name": "changelogs/fragments/92_fix_ds_update.yaml", "ftype": "file", "chksum_type": "sha256", - "chksum_sha256": "0c8d64f29fb4536513653bf8c97da30f3340e2041b91c8952db1515d6b23a7b3", + "chksum_sha256": "8befcbbddf6fc2db62ff48b4f3a1030fe115fb7ababfc9b03c8e693628087337", "format": 1 }, { - "name": ".github/pull_request_template.md", + "name": "changelogs/fragments/132_add_timeout.yaml", "ftype": "file", "chksum_type": "sha256", - "chksum_sha256": "565ead1b588caaa10cd6f2ed1bb6c809eb2ad93bf75da3a198690cac778432d6", + "chksum_sha256": "8aea8125471f4717c0efa211756fb2086542362d9bee50295686dbce9ba86db7", "format": 1 }, { - "name": "meta", - "ftype": "dir", - "chksum_type": null, - "chksum_sha256": null, + "name": "changelogs/fragments/202_multiple_snap_rules.yaml", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "4ed9e6c99d409df00b7cd2cb4a60bee536b9e0608c107a0944fb3a738ec0bd9f", "format": 1 }, { - "name": "meta/runtime.yml", + "name": "changelogs/fragments/84_add_cert.yaml", "ftype": "file", "chksum_type": "sha256", - "chksum_sha256": "df18179bb2f5447a56ac92261a911649b96821c0b2c08eea62d5cc6b0195203f", + "chksum_sha256": "1d286bf0fe3301a898bcdcad0bf70955732608eb51468097ca6d70ae269654d8", "format": 1 }, { - "name": ".yamllint", + "name": "changelogs/fragments/121_replication_perf.yaml", "ftype": "file", "chksum_type": "sha256", - "chksum_sha256": "2970fa4875092f99825ac0da3c82d2413ce973087b9945e68fdfa7b3b1e2012e", + "chksum_sha256": "372e2b49c1b2fb2f637e01023dd3a5146ee61171adbf619062ceb5e53a5d3e96", "format": 1 }, { - "name": "README.md", + "name": "changelogs/fragments/217_inventory.yaml", "ftype": "file", "chksum_type": "sha256", - "chksum_sha256": "3c4d2257a4a25daf934a2b149aaa3397371d32f99f0b7042ca51a1a5fe981917", + "chksum_sha256": "4832bed915e1a18327ab9d7c15c65f55094f08215a26028d426ca694a90c2ae7", "format": 1 }, { - "name": "requirements.txt", + "name": "changelogs/fragments/v1.3.0_summary.yaml", "ftype": "file", "chksum_type": "sha256", - "chksum_sha256": "87cb6471722fa1096099f228091480939c5b7c3ac39c2819543324a7701e66a3", + "chksum_sha256": "64bd3d32085373ce61a414518c2ed87bfd003d163d3002d087f41f4a54b0b1a0", "format": 1 }, { - "name": "playbooks", - "ftype": "dir", - "chksum_type": null, - "chksum_sha256": null, + "name": "changelogs/fragments/216_extra_bucket_info.yaml", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "cf88a27b9c51eefd78e80b587012be110c967d0185597cac22cf5de86b73b053", "format": 1 }, { - "name": "playbooks/roles", - "ftype": "dir", - "chksum_type": null, - "chksum_sha256": null, + "name": "changelogs/fragments/154_add_snap_now.yaml", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "6bde815114a219fd03941a080c2e6acebd5ef748e7f67503e8c3ef5f81decd54", "format": 1 }, { - "name": "playbooks/.keep", + "name": "changelogs/fragments/127_add_eula.yaml", "ftype": "file", "chksum_type": "sha256", - "chksum_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", + "chksum_sha256": "9b092f3766cf4309ac60ab77c2e51142ffbc81eb4bfa4da581d531ee2de633ac", "format": 1 }, { - "name": "playbooks/templates", - "ftype": "dir", - "chksum_type": null, - "chksum_sha256": null, + "name": "changelogs/fragments/179_fqcn.yaml", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "d4c60f377dd4cd40de9c777a7d54f6d185afa785fdc45a751d67f2baccf9efdf", "format": 1 }, { - "name": "playbooks/templates/.keep", + "name": "changelogs/fragments/115_multiprotocol.yaml", "ftype": "file", "chksum_type": "sha256", - "chksum_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", + "chksum_sha256": "51375d2aac996039ee4d338cbb7cc8f9d77f423f8f519ab6f84012ff021812ae", "format": 1 }, { - "name": "playbooks/files", - "ftype": "dir", - "chksum_type": null, - "chksum_sha256": null, + "name": "changelogs/fragments/176_nfs_export_policies.yaml", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "36fc1c990afd6fb48068d113d6e4a6846368ad32523554acc9b9d9e5ba861161", "format": 1 }, { - "name": "playbooks/files/.keep", + "name": "changelogs/fragments/187_rename_nfs_policy.yaml", "ftype": "file", "chksum_type": "sha256", - "chksum_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", + "chksum_sha256": "d8b9f4112fea72954805eca3c01cf04524d5bd02a5b2559cdfef68c09d616e49", "format": 1 }, { - "name": "playbooks/vars", - "ftype": "dir", - "chksum_type": null, - "chksum_sha256": null, + "name": "changelogs/fragments/152_s3acc_lowercase.yaml", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "0a02995d6eeb1ac3968e952c61a552e5fc2feeef62ef7642d5f8714157da7d41", "format": 1 }, { - "name": "playbooks/vars/.keep", + "name": "changelogs/fragments/83_add_certgrp.yml", "ftype": "file", "chksum_type": "sha256", - "chksum_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", + "chksum_sha256": "b7513178564ee1707090e4b3df65af56f28a71119e0ebf73b074dc9d2c0e1d65", "format": 1 }, { - "name": "playbooks/tasks", - "ftype": "dir", - "chksum_type": null, - "chksum_sha256": null, + "name": "changelogs/fragments/237_info_policy.yaml", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "3fccb2162770f16d8a32e93a24795388bad88a6fdad1eac209ebe132f4394b63", "format": 1 }, { - "name": "playbooks/tasks/.keep", + "name": "changelogs/fragments/244_add_deny.yaml", "ftype": "file", "chksum_type": "sha256", - "chksum_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", + "chksum_sha256": "823eed6a5affd85e6c8c0d8a313520caebb86ddf32908ce3ee62efa76d8f9cd2", "format": 1 }, { - "name": "CHANGELOG.rst", + "name": "changelogs/fragments/78_update_filesystem_replica_link.yaml", "ftype": "file", "chksum_type": "sha256", - "chksum_sha256": "9f92bbcfdf90122b0ffdbe430cd0ff9b2a3b1e3cd1c099e0436b251de8674d74", + "chksum_sha256": "57a7b5ed892c4ea2f5149023b2bdde9481eb8c0a7593e4e76a4603e706971100", "format": 1 }, { - "name": "settings.json", + "name": "changelogs/fragments/150_fix_joint_nfs_version_change.yaml", "ftype": "file", "chksum_type": "sha256", - "chksum_sha256": "f64528ffd800423e1d49a3c79cdd3892548a57177ea1a1caacbbcd275390b792", + "chksum_sha256": "0e1a7b9242317cf785fa07608c5a661bad07fc79e8fd187264d9263dc0609479", "format": 1 }, { - "name": ".gitignore", + "name": "changelogs/fragments/239_access_rights.yaml", "ftype": "file", "chksum_type": "sha256", - "chksum_sha256": "f3e019033a4ff6d651103704d47629e6d911cb949652bd5e6121d7a918dbc480", + "chksum_sha256": "c050dd606a488ff604f085e772c5dbb7425a11afb40a3b42bc000473fe213150", "format": 1 }, { - "name": "changelogs", - "ftype": "dir", - "chksum_type": null, - "chksum_sha256": null, + "name": "changelogs/fragments/238_user_policy.yaml", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "e0870b9801eb51578bf27f52c0af7acdb599d92ccdcf82ca9391246e50058a62", "format": 1 }, { - "name": "changelogs/config.yaml", + "name": "changelogs/fragments/163_admin_key.yaml", "ftype": "file", "chksum_type": "sha256", - "chksum_sha256": "d1b77eeb2d9f7242075e746537713be29e397fe6954f13a1caf8b10695434b9b", + "chksum_sha256": "bd290345ed66c0809e6be94cabb6f1823b7e0b3f61d6a88a13f16ae849ce4399", "format": 1 }, { - "name": "changelogs/.plugin-cache.yaml", + "name": "changelogs/fragments/164_add_admin.yaml", "ftype": "file", "chksum_type": "sha256", - "chksum_sha256": "b528379cbf853914f8e8192b15e34bad21ea8c2b4de7faaab4f045fe1921fa4b", + "chksum_sha256": "53b89a2de09c79fcb3fdbdf82917985124d53f793046f1164c04a8578adb7df9", "format": 1 }, { - "name": "changelogs/changelog.yaml", + "name": "changelogs/fragments/246_smb_encrypt.yaml", "ftype": "file", "chksum_type": "sha256", - "chksum_sha256": "728d1a92a9effec8bd73c032a3bd53fc8eb4d9029c824a2b6e1179b6922bf488", + "chksum_sha256": "340f37b3d71019cf3ea50de4c91b45570ff493ac3630940ec3b1c85c6fcc9cc5", "format": 1 }, { - "name": "changelogs/fragments", - "ftype": "dir", - "chksum_type": null, - "chksum_sha256": null, + "name": "changelogs/fragments/80_support_reverse_replica_link.yaml", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "f3504f5e1acadaf52bd9d420373b7edce2015435232e5fa53282455361bcd440", "format": 1 }, { - "name": "changelogs/fragments/186_add_tz.yaml", + "name": "changelogs/fragments/220_s3user_key_fix.yaml", "ftype": "file", "chksum_type": "sha256", - "chksum_sha256": "44209d75080c5e4f437f409bb37e0f16c662658a6243fa890339fc076dfa7cd3", + "chksum_sha256": "ae00607f47b12b62456cb037b31474be8b7de0820b46ced24fc4a96b43f0eb76", "format": 1 }, { - "name": "changelogs/fragments/81_purefb_fs_new_options.yaml", + "name": "changelogs/fragments/114_certificate_update.yaml", "ftype": "file", "chksum_type": "sha256", - "chksum_sha256": "abb817b52fdfa70b538ca9efce8d642282383b6961c47bde20ce0a023d2b941d", + "chksum_sha256": "ce77387c64b0714a4abe011d4eabc7b1a803058c1e7b407646ceb8249545e8aa", "format": 1 }, { - "name": "changelogs/fragments/101_fix_policy_and_timezone_error.yaml", + "name": "changelogs/fragments/257_mode_change.yaml", "ftype": "file", "chksum_type": "sha256", - "chksum_sha256": "2e9c5c95b8333fee22646f4e83e9034172182b1e99c084725f08df48e45d3d47", + "chksum_sha256": "29463aaf4d93f7d77361c6f2c7b00eba1c228feacf152b78a0249c27c32f0b11", "format": 1 }, { - "name": "changelogs/fragments/163_admin_key.yaml", + "name": "changelogs/fragments/224_smb_policies.yaml", "ftype": "file", "chksum_type": "sha256", - "chksum_sha256": "bd290345ed66c0809e6be94cabb6f1823b7e0b3f61d6a88a13f16ae849ce4399", + "chksum_sha256": "92a42a7a700f4cab0c2d0e7e9a7de6fd65813784e14284d866a99e4e5e3ec289", "format": 1 }, { - "name": "changelogs/fragments/220_s3user_key_fix.yaml", + "name": "changelogs/fragments/139_add_keytabs.yaml", "ftype": "file", "chksum_type": "sha256", - "chksum_sha256": "ae00607f47b12b62456cb037b31474be8b7de0820b46ced24fc4a96b43f0eb76", + "chksum_sha256": "c4d64b50797e36e3861e530b3e7c080277ebceb17ac5f58d4a08b8ac59c14d10", "format": 1 }, { - "name": "changelogs/fragments/211_change_booleans.yaml", + "name": "changelogs/fragments/107_add_remove_s3user_key.yaml", "ftype": "file", "chksum_type": "sha256", - "chksum_sha256": "f04fd18a42e321cb3818a579e14cc50a6d27935196ff04632e2db44f7b807322", + "chksum_sha256": "8a2bb28b43962c08ea8916db02a401f8bd7b4989bd1aa04f201ed8c602d94124", "format": 1 }, { - "name": "changelogs/fragments/217_inventory.yaml", + "name": "changelogs/fragments/232_multiple_keys.yaml", "ftype": "file", "chksum_type": "sha256", - "chksum_sha256": "4832bed915e1a18327ab9d7c15c65f55094f08215a26028d426ca694a90c2ae7", + "chksum_sha256": "899bef3076d8d2952069177a3c8de917b6ecdaa622ccbfd00933a4756adb4314", "format": 1 }, { - "name": "changelogs/fragments/174_access_policies.yaml", + "name": "changelogs/fragments/175_throttle_support.yaml", "ftype": "file", "chksum_type": "sha256", - "chksum_sha256": "25f5a86a2a977555359c8088fab65902f1ee2b0cc3bc417a7383d5d5176d4802", + "chksum_sha256": "738e0e9c2f7789b1c931b5563416ca436fd0e04401232a502e6ce59fd03da28f", "format": 1 }, { - "name": "changelogs/fragments/152_s3acc_lowercase.yaml", + "name": "changelogs/fragments/123_lifecycle_rule_fix.yaml", "ftype": "file", "chksum_type": "sha256", - "chksum_sha256": "0a02995d6eeb1ac3968e952c61a552e5fc2feeef62ef7642d5f8714157da7d41", + "chksum_sha256": "87a3f72b0ac11e72103dfb4766faecdd2b0c1fe5fad379e322c910c5134f7025", "format": 1 }, { - "name": "changelogs/fragments/76_default_fs_size.yaml", + "name": "changelogs/fragments/188_bucket_type.yaml", "ftype": "file", "chksum_type": "sha256", - "chksum_sha256": "6d8689e8f46ab7d3286b7d3ee46dfa13a8bf0585cc9b197a5ca8271c9dd9590e", + "chksum_sha256": "3c8485b792ba73283807489b10a7b6df8298c5f932aaeec7b6b841b2f504464a", "format": 1 }, { @@ -414,507 +400,563 @@ "format": 1 }, { - "name": "changelogs/fragments/90_imported_keys.yaml", + "name": "changelogs/fragments/247_space_consistency.yaml", "ftype": "file", "chksum_type": "sha256", - "chksum_sha256": "ad1078e90875745edce8071846183eed39c3878156d14f96b5db78ab1c5be973", + "chksum_sha256": "1c6d43da77f2242c6f783856bff87b35ac3936fa2339feb38a6cc1640d46f341", "format": 1 }, { - "name": "changelogs/fragments/150_fix_joint_nfs_version_change.yaml", + "name": "changelogs/fragments/205_fix_multi_lifecycle.yaml", "ftype": "file", "chksum_type": "sha256", - "chksum_sha256": "0e1a7b9242317cf785fa07608c5a661bad07fc79e8fd187264d9263dc0609479", + "chksum_sha256": "909b52d292f6b41efd85760990e8ff59b58fab416ba2c24c4b409878cd724543", "format": 1 }, { - "name": "changelogs/fragments/164_add_admin.yaml", + "name": "changelogs/fragments/183_v2_connections.yaml", "ftype": "file", "chksum_type": "sha256", - "chksum_sha256": "53b89a2de09c79fcb3fdbdf82917985124d53f793046f1164c04a8578adb7df9", + "chksum_sha256": "700e1509315604807c70d5b186542e74e058e4f912b1fe796df41c3d8a125d57", "format": 1 }, { - "name": "changelogs/fragments/129-virtualhost.yaml", + "name": "changelogs/fragments/242_cascade.yaml", "ftype": "file", "chksum_type": "sha256", - "chksum_sha256": "0af56f02e1b7ad1ea585b3bbce897022faf28b448b69ea755951be3b5da40f7e", + "chksum_sha256": "3795c4541a2ac413e40079ad215a431c79974a2e5f8aedb1019e729e6af9d1fb", "format": 1 }, { - "name": "changelogs/fragments/105_max_access_key.yaml", + "name": "changelogs/fragments/215_encrypt_sec_info.yaml", "ftype": "file", "chksum_type": "sha256", - "chksum_sha256": "fb9f5707e7466fe7c94479891f218bacd04ae45a37c2f207dcf51ac756fb7259", + "chksum_sha256": "6915aa0ddabb1f73dbced52d0114b84317958f29a2ef7ea4dcd72a10952f8017", "format": 1 }, { - "name": "changelogs/fragments/169_pypureclient_fix.yaml", + "name": "changelogs/fragments/109_update_info.yaml", "ftype": "file", "chksum_type": "sha256", - "chksum_sha256": "fb6e7bfc1c816ec77dadf6bd4ab040a8089b98a1c9c75ec15603d407c27ce3f2", + "chksum_sha256": "857bb23faa48e2d894f432cca4219681d7b3dab68473b3502dfe9319d751a3e1", "format": 1 }, { - "name": "changelogs/fragments/183_v2_connections.yaml", + "name": "changelogs/fragments/227_s3acc_human_quota.yaml", "ftype": "file", "chksum_type": "sha256", - "chksum_sha256": "700e1509315604807c70d5b186542e74e058e4f912b1fe796df41c3d8a125d57", + "chksum_sha256": "4a6a471aeb6120e67aab7ada3f3c1736dff5bcd43311fef2bd90d846e510b0c1", "format": 1 }, { - "name": "changelogs/fragments/96_fix_update_connection.yaml", + "name": "changelogs/fragments/128_add_32_to_info.yaml", "ftype": "file", "chksum_type": "sha256", - "chksum_sha256": "828cc0c94acf44d1d373402a0cc657527d9fce8ac744319fbe0d8035684932b4", + "chksum_sha256": "b18c7cf868d5699e4ad67e2d924c7a6323353147f8850757f7f2c4c7dda877c8", "format": 1 }, { - "name": "changelogs/fragments/200_proxy.yaml", + "name": "changelogs/fragments/88_add_lifecycle.yml", "ftype": "file", "chksum_type": "sha256", - "chksum_sha256": "26631d7434c86b739bcd75c8905f8f668555217610cafb47f11a6e24937c7eb8", + "chksum_sha256": "fdc6c425f03ffc0b4a008230f290f6ef37874a270909cb2ee311843dc08909f6", "format": 1 }, { - "name": "changelogs/fragments/159_add_lag.yaml", + "name": "changelogs/fragments/136_add_s3user_policy.yaml", "ftype": "file", "chksum_type": "sha256", - "chksum_sha256": "5b1d95e41e550ed7b8bdda62f09e9ae883915afd1b547d5f5bb863b21b803df3", + "chksum_sha256": "b97c8a102be108e8d74c9ec6d9aa73ec151fe7a77c676452d7b96cf75a4ecf6b", "format": 1 }, { - "name": "changelogs/fragments/115_multiprotocol.yaml", + "name": "changelogs/fragments/268_multi-chassis-lag.yaml", "ftype": "file", "chksum_type": "sha256", - "chksum_sha256": "51375d2aac996039ee4d338cbb7cc8f9d77f423f8f519ab6f84012ff021812ae", + "chksum_sha256": "dc873b8118b78049c297f0bb54508ae3c5df169373c1a8810e5034b9bab75e3d", "format": 1 }, { - "name": "changelogs/fragments/85_add_banner.yaml", + "name": "changelogs/fragments/158_support_lags.yaml", "ftype": "file", "chksum_type": "sha256", - "chksum_sha256": "ee600c3bcae632d7450ff3447192f8ca2d1622eecd67bc87c59fdd3dd8326bc6", + "chksum_sha256": "68b3e104addfa10fb7f2f974bff2e5dad2c950e261c603f37409f42ab7afed02", "format": 1 }, { - "name": "changelogs/fragments/185_nfs_export_rule.yaml", + "name": "changelogs/fragments/194_lists_for_service.yaml", "ftype": "file", "chksum_type": "sha256", - "chksum_sha256": "8f53ac3485ed3849ca99fee6015e2767f636c1186a368b3d4e91ba6076afd7d4", + "chksum_sha256": "9e139b9ea88f7700071e57500cff497a6be300d8425b4a4ddaba77c36a8dc128", "format": 1 }, { - "name": "changelogs/fragments/128_add_32_to_info.yaml", + "name": "changelogs/fragments/186_add_tz.yaml", "ftype": "file", "chksum_type": "sha256", - "chksum_sha256": "b18c7cf868d5699e4ad67e2d924c7a6323353147f8850757f7f2c4c7dda877c8", + "chksum_sha256": "44209d75080c5e4f437f409bb37e0f16c662658a6243fa890339fc076dfa7cd3", "format": 1 }, { - "name": "changelogs/fragments/161_add_lifecycle_info.yaml", + "name": "changelogs/fragments/255_smb_ca.yaml", "ftype": "file", "chksum_type": "sha256", - "chksum_sha256": "b8c87e250274f2b5007ce0898c9bb6d79129faedaa8427a52377f86c24c6e90f", + "chksum_sha256": "70852998288bfd96ffe5999e13409b9f8645ba86743e8ca197f16fd4433f10c8", "format": 1 }, { - "name": "changelogs/fragments/138_add_ad_module.yaml", + "name": "changelogs/fragments/222_bucket_type_fix.yaml", "ftype": "file", "chksum_type": "sha256", - "chksum_sha256": "972d7c56c40a909882eeb3c199f4b7dfd05b080d8b159d2f4915c3d86beb055d", + "chksum_sha256": "68e1b5898249bac3068d141454779600b5682070bae9590d7335f9a2fff5d4fb", "format": 1 }, { - "name": "changelogs/fragments/202_multiple_snap_rules.yaml", + "name": "changelogs/fragments/167_fix_logins.yaml", "ftype": "file", "chksum_type": "sha256", - "chksum_sha256": "4ed9e6c99d409df00b7cd2cb4a60bee536b9e0608c107a0944fb3a738ec0bd9f", + "chksum_sha256": "426451dd9cb0925943b74eae2fe37702574efc7974f630a049737bfa74991ff3", "format": 1 }, { - "name": "changelogs/fragments/213_sec_update.yaml", + "name": "changelogs/fragments/81_purefb_fs_new_options.yaml", "ftype": "file", "chksum_type": "sha256", - "chksum_sha256": "6b71174c00e982cada0d051fae5e28c853207ec6d0f42a783db35a9519733769", + "chksum_sha256": "abb817b52fdfa70b538ca9efce8d642282383b6961c47bde20ce0a023d2b941d", "format": 1 }, { - "name": "changelogs/fragments/218_object_account_info.yaml", + "name": "changelogs/fragments/138_add_ad_module.yaml", "ftype": "file", "chksum_type": "sha256", - "chksum_sha256": "ef0f569461747bfcb2f294a8317d113b829323f9e6994e652d4344b2590099fa", + "chksum_sha256": "972d7c56c40a909882eeb3c199f4b7dfd05b080d8b159d2f4915c3d86beb055d", "format": 1 }, { - "name": "changelogs/fragments/80_support_reverse_replica_link.yaml", + "name": "changelogs/fragments/174_access_policies.yaml", "ftype": "file", "chksum_type": "sha256", - "chksum_sha256": "f3504f5e1acadaf52bd9d420373b7edce2015435232e5fa53282455361bcd440", + "chksum_sha256": "25f5a86a2a977555359c8088fab65902f1ee2b0cc3bc417a7383d5d5176d4802", "format": 1 }, { - "name": "changelogs/fragments/84_add_cert.yaml", + "name": "changelogs/fragments/86_add_syslog.yaml", "ftype": "file", "chksum_type": "sha256", - "chksum_sha256": "1d286bf0fe3301a898bcdcad0bf70955732608eb51468097ca6d70ae269654d8", + "chksum_sha256": "e42ee9ea2a2bffa465347a52a3fcf4bfaa51f377e7f33bf4a405eb46ae507442", "format": 1 }, { - "name": "changelogs/fragments/123_lifecycle_rule_fix.yaml", + "name": "changelogs/fragments/90_imported_keys.yaml", "ftype": "file", "chksum_type": "sha256", - "chksum_sha256": "87a3f72b0ac11e72103dfb4766faecdd2b0c1fe5fad379e322c910c5134f7025", + "chksum_sha256": "ad1078e90875745edce8071846183eed39c3878156d14f96b5db78ab1c5be973", "format": 1 }, { - "name": "changelogs/fragments/83_add_certgrp.yml", + "name": "changelogs/fragments/245_quota_plus.yaml", "ftype": "file", "chksum_type": "sha256", - "chksum_sha256": "b7513178564ee1707090e4b3df65af56f28a71119e0ebf73b074dc9d2c0e1d65", + "chksum_sha256": "8379355479a2da9937127e1af246827353a15d4ec169da72c6090007f18760fb", "format": 1 }, { - "name": "changelogs/fragments/97_fix_encrpyted_array_connection_info.yaml", + "name": "changelogs/fragments/79_hide_connect_api.yaml", "ftype": "file", "chksum_type": "sha256", - "chksum_sha256": "e140fbfc3ac4eaab3dd9c482e3beb37efd98ad4c3892b36f93ffb00d89c9283f", + "chksum_sha256": "b4cd3cbdb65de6b71cfbe179d56a42be2afbf6486e1ce0df9fdd3a7042bd57b0", "format": 1 }, { - "name": "changelogs/fragments/136_add_s3user_policy.yaml", + "name": "changelogs/fragments/166_lag_mac_note.yaml", "ftype": "file", "chksum_type": "sha256", - "chksum_sha256": "b97c8a102be108e8d74c9ec6d9aa73ec151fe7a77c676452d7b96cf75a4ecf6b", + "chksum_sha256": "b639987ccd53708ee210a1812bd8c6af30292a3a1b6b42c7b839dd7120967e13", "format": 1 }, { - "name": "changelogs/fragments/114_certificate_update.yaml", + "name": "changelogs/fragments/223_add_drive_type.yaml", "ftype": "file", "chksum_type": "sha256", - "chksum_sha256": "ce77387c64b0714a4abe011d4eabc7b1a803058c1e7b407646ceb8249545e8aa", + "chksum_sha256": "f63992a09eef7139800eddd09d4a094b36d150e0e0074c552078045f27b1cf3a", "format": 1 }, { - "name": "changelogs/fragments/162_new_lifecycle.yaml", + "name": "changelogs/fragments/218_object_account_info.yaml", "ftype": "file", "chksum_type": "sha256", - "chksum_sha256": "bd6214f7380736e34ed7a21396f1842c6796afba6c3b7413536522d4b6d0b531", + "chksum_sha256": "ef0f569461747bfcb2f294a8317d113b829323f9e6994e652d4344b2590099fa", "format": 1 }, { - "name": "changelogs/fragments/132_add_timeout.yaml", + "name": "changelogs/fragments/77_filesystem_policies_info.yaml", "ftype": "file", "chksum_type": "sha256", - "chksum_sha256": "8aea8125471f4717c0efa211756fb2086542362d9bee50295686dbce9ba86db7", + "chksum_sha256": "8c7090d551cb59c49622a89c0ed25f12ad89104a9e2ab6708a01fc01fce9e049", "format": 1 }, { - "name": "changelogs/fragments/154_add_snap_now.yaml", + "name": "changelogs/fragments/85_add_banner.yaml", "ftype": "file", "chksum_type": "sha256", - "chksum_sha256": "6bde815114a219fd03941a080c2e6acebd5ef748e7f67503e8c3ef5f81decd54", + "chksum_sha256": "ee600c3bcae632d7450ff3447192f8ca2d1622eecd67bc87c59fdd3dd8326bc6", "format": 1 }, { - "name": "changelogs/fragments/158_support_lags.yaml", + "name": "changelogs/fragments/254_update_212_info.yaml", "ftype": "file", "chksum_type": "sha256", - "chksum_sha256": "68b3e104addfa10fb7f2f974bff2e5dad2c950e261c603f37409f42ab7afed02", + "chksum_sha256": "7dfe31757a7c234c82a6175df2c608c4657b23ac5c029671a083cbdfc4846960", "format": 1 }, { - "name": "changelogs/fragments/92_fix_ds_update.yaml", + "name": "changelogs/fragments/191_add_quota_info.yaml", "ftype": "file", "chksum_type": "sha256", - "chksum_sha256": "8befcbbddf6fc2db62ff48b4f3a1030fe115fb7ababfc9b03c8e693628087337", + "chksum_sha256": "58ae5507364e9af847ac1806d27d6497bd36967ef3bdb34e3716cc294c178440", "format": 1 }, { - "name": "changelogs/fragments/86_add_syslog.yaml", + "name": "changelogs/fragments/131-apiclient.yaml", "ftype": "file", "chksum_type": "sha256", - "chksum_sha256": "e42ee9ea2a2bffa465347a52a3fcf4bfaa51f377e7f33bf4a405eb46ae507442", + "chksum_sha256": "04e3fdc25643fb469342e82df9213e49d2e4eb3e5411035f49f825d19542721c", "format": 1 }, { - "name": "changelogs/fragments/191_add_quota_info.yaml", + "name": "changelogs/fragments/159_add_lag.yaml", "ftype": "file", "chksum_type": "sha256", - "chksum_sha256": "58ae5507364e9af847ac1806d27d6497bd36967ef3bdb34e3716cc294c178440", + "chksum_sha256": "5b1d95e41e550ed7b8bdda62f09e9ae883915afd1b547d5f5bb863b21b803df3", "format": 1 }, { - "name": "changelogs/fragments/188_bucket_type.yaml", + "name": "changelogs/fragments/266_bucket_fix.yaml", "ftype": "file", "chksum_type": "sha256", - "chksum_sha256": "3c8485b792ba73283807489b10a7b6df8298c5f932aaeec7b6b841b2f504464a", + "chksum_sha256": "5dca2f8134265d79896952375deb436840338d883d644dea4d0ce7037c052eff", "format": 1 }, { - "name": "changelogs/fragments/109_update_info.yaml", + "name": "changelogs/fragments/213_sec_update.yaml", "ftype": "file", "chksum_type": "sha256", - "chksum_sha256": "857bb23faa48e2d894f432cca4219681d7b3dab68473b3502dfe9319d751a3e1", + "chksum_sha256": "6b71174c00e982cada0d051fae5e28c853207ec6d0f42a783db35a9519733769", "format": 1 }, { - "name": "changelogs/fragments/121_replication_perf.yaml", + "name": "changelogs/fragments/147_no_gateway.yaml", "ftype": "file", "chksum_type": "sha256", - "chksum_sha256": "372e2b49c1b2fb2f637e01023dd3a5146ee61171adbf619062ceb5e53a5d3e96", + "chksum_sha256": "0ca2ad2e1c1d60b110b87b2b37013bae6ee9daff64056f1dea691f2376cb8448", "format": 1 }, { - "name": "changelogs/fragments/153_add_quota.yaml", + "name": "changelogs/fragments/184_certificate_typos.yaml", "ftype": "file", "chksum_type": "sha256", - "chksum_sha256": "2b2517ea362d7128333d6fab7f99f6b70c4253d2807eae3ec417aa4451b3ae6c", + "chksum_sha256": "827c27fb0d7c31d13e89e829db35890c97a16cf437149264074c1c6fa52be9be", "format": 1 }, { - "name": "changelogs/fragments/v1.3.0_summary.yaml", + "name": "changelogs/fragments/212_object_account_quota.yaml", "ftype": "file", "chksum_type": "sha256", - "chksum_sha256": "64bd3d32085373ce61a414518c2ed87bfd003d163d3002d087f41f4a54b0b1a0", + "chksum_sha256": "2d9dd6bbb0f690de495ad9416117baf213d1d60f164fbcaedafa5f941ebeba28", "format": 1 }, { - "name": "changelogs/fragments/113_policy_cleanup.yaml", + "name": "changelogs/fragments/101_fix_policy_and_timezone_error.yaml", "ftype": "file", "chksum_type": "sha256", - "chksum_sha256": "11023f4d159bc146016fe9e9f40d18edb659518cb9dbc733750146e00de2b05c", + "chksum_sha256": "ca31175fc5d17623bd2988bdd67901cf45b209a2bae6e1edbac128489cbee6cd", "format": 1 }, { - "name": "changelogs/fragments/216_extra_bucket_info.yaml", + "name": "changelogs/fragments/90_delete_conn_fix.yaml", "ftype": "file", "chksum_type": "sha256", - "chksum_sha256": "cf88a27b9c51eefd78e80b587012be110c967d0185597cac22cf5de86b73b053", + "chksum_sha256": "787138033d123fa59a9d3cdb424dc093183a020eebf1e76b46cbf059006e18e5", "format": 1 }, { - "name": "changelogs/fragments/212_object_account_quota.yaml", + "name": "changelogs/fragments/263_fix_multiple_modules_idempotency.yaml", "ftype": "file", "chksum_type": "sha256", - "chksum_sha256": "2d9dd6bbb0f690de495ad9416117baf213d1d60f164fbcaedafa5f941ebeba28", + "chksum_sha256": "9ece785a9cccd881deb20eaa8b83c2d23e799ce239e7897709b9a6b4436ad239", "format": 1 }, { - "name": "changelogs/fragments/215_encrypt_sec_info.yaml", + "name": "changelogs/fragments/252_object_lock_info.yaml", "ftype": "file", "chksum_type": "sha256", - "chksum_sha256": "6915aa0ddabb1f73dbced52d0114b84317958f29a2ef7ea4dcd72a10952f8017", + "chksum_sha256": "57a27b67a14a08762e081ac9facaec3004e366385cfdd272972e906a69467f9b", "format": 1 }, { - "name": "changelogs/fragments/131-apiclient.yaml", + "name": "changelogs/fragments/112_fix_check_mode.yaml", "ftype": "file", "chksum_type": "sha256", - "chksum_sha256": "92dd9507a2a0476d24f0c1e7a5342925be49b4a84142fe8e33f4a76f422283c3", + "chksum_sha256": "11f8266ad857ed327ddbe8ef65f810a54e6c57df7ef24d1ec1d4c132abaa23a7", "format": 1 }, { - "name": "changelogs/fragments/107_add_remove_s3user_key.yaml", + "name": "changelogs/fragments/108_dns_search_fix.yaml", "ftype": "file", "chksum_type": "sha256", - "chksum_sha256": "8a2bb28b43962c08ea8916db02a401f8bd7b4989bd1aa04f201ed8c602d94124", + "chksum_sha256": "056e8181176826dc43b62100b6c50c8770680f0fcc37cf73737848233382b2e8", "format": 1 }, { - "name": "changelogs/fragments/88_add_lifecycle.yml", + "name": "changelogs/fragments/258_add_public_buckets.yaml", "ftype": "file", "chksum_type": "sha256", - "chksum_sha256": "fdc6c425f03ffc0b4a008230f290f6ef37874a270909cb2ee311843dc08909f6", + "chksum_sha256": "7c921911e2d432aae93be67fa251495af3bea63abf874f4837a59b6e7b61f85b", "format": 1 }, { - "name": "changelogs/fragments/147_no_gateway.yaml", + "name": "changelogs/fragments/113_policy_cleanup.yaml", "ftype": "file", "chksum_type": "sha256", - "chksum_sha256": "0ca2ad2e1c1d60b110b87b2b37013bae6ee9daff64056f1dea691f2376cb8448", + "chksum_sha256": "11023f4d159bc146016fe9e9f40d18edb659518cb9dbc733750146e00de2b05c", "format": 1 }, { - "name": "changelogs/fragments/78_update_filesystem_replica_link.yaml", + "name": "changelogs/changelog.yaml", "ftype": "file", "chksum_type": "sha256", - "chksum_sha256": "57a7b5ed892c4ea2f5149023b2bdde9481eb8c0a7593e4e76a4603e706971100", + "chksum_sha256": "58ab80ddfd28321e4c9f245810097a8efbcd09898e013b3a83e650d2dd8440ed", "format": 1 }, { - "name": "changelogs/fragments/176_nfs_export_policies.yaml", + "name": "changelogs/.plugin-cache.yaml", "ftype": "file", "chksum_type": "sha256", - "chksum_sha256": "36fc1c990afd6fb48068d113d6e4a6846368ad32523554acc9b9d9e5ba861161", + "chksum_sha256": "74f8ee5c9b2c27b9b655d822e47443fc68975023c10a6e58c08dc4b925c61bb3", "format": 1 }, { - "name": "changelogs/fragments/112_fix_check_mode.yaml", + "name": "changelogs/config.yaml", "ftype": "file", "chksum_type": "sha256", - "chksum_sha256": "11f8266ad857ed327ddbe8ef65f810a54e6c57df7ef24d1ec1d4c132abaa23a7", + "chksum_sha256": "d1b77eeb2d9f7242075e746537713be29e397fe6954f13a1caf8b10695434b9b", "format": 1 }, { - "name": "changelogs/fragments/79_hide_connect_api.yaml", - "ftype": "file", - "chksum_type": "sha256", - "chksum_sha256": "b4cd3cbdb65de6b71cfbe179d56a42be2afbf6486e1ce0df9fdd3a7042bd57b0", + "name": "tests", + "ftype": "dir", + "chksum_type": null, + "chksum_sha256": null, "format": 1 }, { - "name": "changelogs/fragments/167_fix_logins.yaml", + "name": "tests/config.yaml", "ftype": "file", "chksum_type": "sha256", - "chksum_sha256": "426451dd9cb0925943b74eae2fe37702574efc7974f630a049737bfa74991ff3", + "chksum_sha256": "9a009a349eaaf78c93ff56072d2ef171937bdb884e4976592ab5aaa9c68e1044", "format": 1 }, { - "name": "changelogs/fragments/194_lists_for_service.yaml", + "name": "COPYING.GPLv3", "ftype": "file", "chksum_type": "sha256", - "chksum_sha256": "9e139b9ea88f7700071e57500cff497a6be300d8425b4a4ddaba77c36a8dc128", + "chksum_sha256": "8ceb4b9ee5adedde47b31e975c1d90c73ad27b6b165a1dcd80c7c545eb65b903", "format": 1 }, { - "name": "changelogs/fragments/175_throttle_support.yaml", + "name": "playbooks", + "ftype": "dir", + "chksum_type": null, + "chksum_sha256": null, + "format": 1 + }, + { + "name": "playbooks/tasks", + "ftype": "dir", + "chksum_type": null, + "chksum_sha256": null, + "format": 1 + }, + { + "name": "playbooks/tasks/.keep", "ftype": "file", "chksum_type": "sha256", - "chksum_sha256": "738e0e9c2f7789b1c931b5563416ca436fd0e04401232a502e6ce59fd03da28f", + "chksum_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", "format": 1 }, { - "name": "changelogs/fragments/139_add_keytabs.yaml", + "name": "playbooks/.keep", "ftype": "file", "chksum_type": "sha256", - "chksum_sha256": "c4d64b50797e36e3861e530b3e7c080277ebceb17ac5f58d4a08b8ac59c14d10", + "chksum_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", "format": 1 }, { - "name": "changelogs/fragments/135_add_user_policies.yaml", + "name": "playbooks/templates", + "ftype": "dir", + "chksum_type": null, + "chksum_sha256": null, + "format": 1 + }, + { + "name": "playbooks/templates/.keep", "ftype": "file", "chksum_type": "sha256", - "chksum_sha256": "a0b78f5b1a5be3bfb87a00a4e638fad67600b0bab4cfddd72b3bfa4d2e217e3f", + "chksum_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", "format": 1 }, { - "name": "changelogs/fragments/77_filesystem_policies_info.yaml", + "name": "playbooks/vars", + "ftype": "dir", + "chksum_type": null, + "chksum_sha256": null, + "format": 1 + }, + { + "name": "playbooks/vars/.keep", "ftype": "file", "chksum_type": "sha256", - "chksum_sha256": "8c7090d551cb59c49622a89c0ed25f12ad89104a9e2ab6708a01fc01fce9e049", + "chksum_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", "format": 1 }, { - "name": "changelogs/fragments/127_add_eula.yaml", + "name": "playbooks/files", + "ftype": "dir", + "chksum_type": null, + "chksum_sha256": null, + "format": 1 + }, + { + "name": "playbooks/files/.keep", "ftype": "file", "chksum_type": "sha256", - "chksum_sha256": "9b092f3766cf4309ac60ab77c2e51142ffbc81eb4bfa4da581d531ee2de633ac", + "chksum_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", "format": 1 }, { - "name": "changelogs/fragments/187_rename_nfs_policy.yaml", + "name": "playbooks/roles", + "ftype": "dir", + "chksum_type": null, + "chksum_sha256": null, + "format": 1 + }, + { + "name": "requirements.txt", "ftype": "file", "chksum_type": "sha256", - "chksum_sha256": "d8b9f4112fea72954805eca3c01cf04524d5bd02a5b2559cdfef68c09d616e49", + "chksum_sha256": "87cb6471722fa1096099f228091480939c5b7c3ac39c2819543324a7701e66a3", "format": 1 }, { - "name": "changelogs/fragments/140_more_32_info.yaml", + "name": "plugins", + "ftype": "dir", + "chksum_type": null, + "chksum_sha256": null, + "format": 1 + }, + { + "name": "plugins/modules", + "ftype": "dir", + "chksum_type": null, + "chksum_sha256": null, + "format": 1 + }, + { + "name": "plugins/modules/purefb_ds.py", "ftype": "file", "chksum_type": "sha256", - "chksum_sha256": "4e57a10a71ab3dd1c151a6867c0da118a21e13df2ef8b9d2fbb799108ddebcd4", + "chksum_sha256": "d1f931875f4c3db92d3ceae0503fdf65635155ddba3a73ebb5a1bf1d2fde2d13", "format": 1 }, { - "name": "changelogs/fragments/205_fix_multi_lifecycle.yaml", + "name": "plugins/modules/purefb_alert.py", "ftype": "file", "chksum_type": "sha256", - "chksum_sha256": "c4080535eeb4ad5e56715dc1dd7683679072d027a65bce93a49adb4b56b68618", + "chksum_sha256": "80d6d4747cf607c7f73ac70a70a7c5f71c527c628f928e49b21de377f5cdbc25", "format": 1 }, { - "name": "changelogs/fragments/166_lag_mac_note.yaml", + "name": "plugins/modules/purefb_target.py", "ftype": "file", "chksum_type": "sha256", - "chksum_sha256": "b639987ccd53708ee210a1812bd8c6af30292a3a1b6b42c7b839dd7120967e13", + "chksum_sha256": "47eea0605e82c442152c801f95a3f55e31f816720bde09b7153caa4d9c58228f", "format": 1 }, { - "name": "changelogs/fragments/90_delete_conn_fix.yaml", + "name": "plugins/modules/purefb_inventory.py", "ftype": "file", "chksum_type": "sha256", - "chksum_sha256": "787138033d123fa59a9d3cdb424dc093183a020eebf1e76b46cbf059006e18e5", + "chksum_sha256": "fccc8b5171f8a6252437a24c7c27829f0c41a7f13f3d058fc6dc80f69b820e3c", "format": 1 }, { - "name": "changelogs/fragments/108_dns_search_fix.yaml", + "name": "plugins/modules/purefb_ra.py", "ftype": "file", "chksum_type": "sha256", - "chksum_sha256": "056e8181176826dc43b62100b6c50c8770680f0fcc37cf73737848233382b2e8", + "chksum_sha256": "3a9172183c8afdd07d3eb854f466a6c687ea881f6978053909ad9908f76db71b", "format": 1 }, { - "name": "changelogs/fragments/184_certificate_typos.yaml", + "name": "plugins/modules/purefb_timeout.py", "ftype": "file", "chksum_type": "sha256", - "chksum_sha256": "827c27fb0d7c31d13e89e829db35890c97a16cf437149264074c1c6fa52be9be", + "chksum_sha256": "1a109f31c4f6aa429394238674140d2e38f36aaba2c007522f6749ee2c0bf31b", "format": 1 }, { - "name": "changelogs/fragments/179_fqcn.yaml", + "name": "plugins/modules/purefb_bladename.py", "ftype": "file", "chksum_type": "sha256", - "chksum_sha256": "d4c60f377dd4cd40de9c777a7d54f6d185afa785fdc45a751d67f2baccf9efdf", + "chksum_sha256": "1b21f650ae77744ba23b47de5b5bcf220ee68c77b127f569908c48eba08a8f24", "format": 1 }, { - "name": "LICENSE", + "name": "plugins/modules/purefb_remote_cred.py", "ftype": "file", "chksum_type": "sha256", - "chksum_sha256": "3972dc9744f6499f0f9b2dbf76696f2ae7ad8af9b23dde66d6af86c9dfb36986", + "chksum_sha256": "51baa72db5641ac2a00f98b07cc626fc65d11412ae11c24e7c5f2a381d2b63df", "format": 1 }, { - "name": "COPYING.GPLv3", + "name": "plugins/modules/purefb_smtp.py", "ftype": "file", "chksum_type": "sha256", - "chksum_sha256": "8ceb4b9ee5adedde47b31e975c1d90c73ad27b6b165a1dcd80c7c545eb65b903", + "chksum_sha256": "76d37be7050f2e57b7fa09cae4b7555fe8b644c031ae7b93a3de5af2cbe19781", "format": 1 }, { - "name": "plugins", - "ftype": "dir", - "chksum_type": null, - "chksum_sha256": null, + "name": "plugins/modules/purefb_policy.py", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "9c6abe4b8cf5db61cd7a27db057f8d2f28cf0d2ec2bf9b398cf3f9eba68bb0e1", "format": 1 }, { - "name": "plugins/modules", - "ftype": "dir", - "chksum_type": null, - "chksum_sha256": null, + "name": "plugins/modules/purefb_ad.py", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "40baf6272707344af09ee6c329457532462df5fedf087fc58662e295847444df", "format": 1 }, { - "name": "plugins/modules/purefb_bladename.py", + "name": "plugins/modules/purefb_apiclient.py", "ftype": "file", "chksum_type": "sha256", - "chksum_sha256": "1b21f650ae77744ba23b47de5b5bcf220ee68c77b127f569908c48eba08a8f24", + "chksum_sha256": "2cc1381512d001748885bd41104f8215397c74f464b696c216368de7598e47bb", "format": 1 }, { - "name": "plugins/modules/purefb_proxy.py", + "name": "plugins/modules/purefb_fs_replica.py", "ftype": "file", "chksum_type": "sha256", - "chksum_sha256": "42514c4241a3e3f254d0cd0fd8a27f394a417990aed0dcc4888efc93fb2a2b7c", + "chksum_sha256": "cecd86a22b1111751fd5aa60cddf16e974d5511ceff825a0e878b51b88ae3ef8", "format": 1 }, { - "name": "plugins/modules/purefb_admin.py", + "name": "plugins/modules/purefb_eula.py", "ftype": "file", "chksum_type": "sha256", - "chksum_sha256": "76c2ce2781241b7338e05f4d443090cb5fd5e7cb6fc1845ae5f78e9a0f9f5002", + "chksum_sha256": "1d06a41aeae5febbc2d1fecd64b888e5947f14b0944f473c3c5d1d46e50acfc4", "format": 1 }, { @@ -925,66 +967,66 @@ "format": 1 }, { - "name": "plugins/modules/purefb_dns.py", + "name": "plugins/modules/purefb_dsrole.py", "ftype": "file", "chksum_type": "sha256", - "chksum_sha256": "9ebd127691bb88001865cba5e1813f0895111b8806c3c5fbfef5a21c24954bdb", + "chksum_sha256": "d625a7248695e857cc0eaf32beb340de4772c406278de8b3c81b1ce2740854c3", "format": 1 }, { - "name": "plugins/modules/purefb_tz.py", + "name": "plugins/modules/purefb_messages.py", "ftype": "file", "chksum_type": "sha256", - "chksum_sha256": "4d08c8115e92f613d74e1bbf53a59a379f95513e3a7d231a9f745a9dfe1d23d5", + "chksum_sha256": "0a0bcd83ebb86063ed9fb3db1bacbda9a89d4d82f11590b1d2cbfd978cd1c198", "format": 1 }, { - "name": "plugins/modules/purefb_alert.py", + "name": "plugins/modules/purefb_connect.py", "ftype": "file", "chksum_type": "sha256", - "chksum_sha256": "80d6d4747cf607c7f73ac70a70a7c5f71c527c628f928e49b21de377f5cdbc25", + "chksum_sha256": "820c57b48e107ef852e6b2665c65ef76d67ffcde916cb21a368dcdae8e1e23e4", "format": 1 }, { - "name": "plugins/modules/purefb_remote_cred.py", + "name": "plugins/modules/purefb_proxy.py", "ftype": "file", "chksum_type": "sha256", - "chksum_sha256": "51baa72db5641ac2a00f98b07cc626fc65d11412ae11c24e7c5f2a381d2b63df", + "chksum_sha256": "42514c4241a3e3f254d0cd0fd8a27f394a417990aed0dcc4888efc93fb2a2b7c", "format": 1 }, { - "name": "plugins/modules/purefb_ad.py", + "name": "plugins/modules/purefb_admin.py", "ftype": "file", "chksum_type": "sha256", - "chksum_sha256": "40baf6272707344af09ee6c329457532462df5fedf087fc58662e295847444df", + "chksum_sha256": "76c2ce2781241b7338e05f4d443090cb5fd5e7cb6fc1845ae5f78e9a0f9f5002", "format": 1 }, { - "name": "plugins/modules/purefb_connect.py", + "name": "plugins/modules/purefb_userpolicy.py", "ftype": "file", "chksum_type": "sha256", - "chksum_sha256": "78d93cd41217bfcca2d6cc900b560fb0a03d16e502162e52eb89c0e432b08820", + "chksum_sha256": "387f3f81064bcde26ef875b63c0fdb71472206a4c41ccc1db4a20eae0b0422eb", "format": 1 }, { - "name": "plugins/modules/purefb_fs.py", + "name": "plugins/modules/purefb_userquota.py", "ftype": "file", "chksum_type": "sha256", - "chksum_sha256": "dfee64d096d76c62d7b9081845b29b4f924bc2d6e6e699c3ff2b0ceb1b3c5714", + "chksum_sha256": "cf1a39e2b307e395b54c2a6ced7335971cf127f03ca6f1bd8af17a2aff28b9c2", "format": 1 }, { - "name": "plugins/modules/purefb_network.py", + "name": "plugins/modules/purefb_snmp_mgr.py", "ftype": "file", "chksum_type": "sha256", - "chksum_sha256": "21398dfcfc59ad0c094ea608027bd44c121ecffc8fbff9ae96fde4f61ba65774", + "chksum_sha256": "2ff095c16f369a129dff76ab9c2660ba2f45d0bc62b2c07bcbf58d62067addfd", "format": 1 }, { - "name": "plugins/modules/purefb_target.py", + "name": "plugins/modules/purefb_banner.py", "ftype": "file", "chksum_type": "sha256", - "chksum_sha256": "47eea0605e82c442152c801f95a3f55e31f816720bde09b7153caa4d9c58228f", + "chksum_sha256": "5daf1a121e8086c3ce3b510c9a52119ba256e49591932f4a575484fc7230b1f9", "format": 1 }, { @@ -995,283 +1037,353 @@ "format": 1 }, { - "name": "plugins/modules/purefb_ds.py", + "name": "plugins/modules/purefb_certs.py", "ftype": "file", "chksum_type": "sha256", - "chksum_sha256": "756950f76e59b5099a8a331bb9afa80976cd7e37c605791f517af6442b9040b7", + "chksum_sha256": "b79151ea9333e6bde34361ab8a8e18b8d961ed6ed18c601c0b574d12020fa35f", "format": 1 }, { - "name": "plugins/modules/purefb_userpolicy.py", + "name": "plugins/modules/purefb_s3user.py", "ftype": "file", "chksum_type": "sha256", - "chksum_sha256": "8e9fe1856db864f057d4eb3bafb1107dce0d7c429acc4deeb25dfba991e510f0", + "chksum_sha256": "cb785aa8af88dc04e7e8d89a564855b928e34af99099cfd2d3774663212c5a93", "format": 1 }, { - "name": "plugins/modules/purefb_certs.py", + "name": "plugins/modules/purefb_groupquota.py", "ftype": "file", "chksum_type": "sha256", - "chksum_sha256": "b79151ea9333e6bde34361ab8a8e18b8d961ed6ed18c601c0b574d12020fa35f", + "chksum_sha256": "fb933221f221bc66e49534594bd0ed6c06f3d83fe57b1ec45bfda80ec593becd", "format": 1 }, { - "name": "plugins/modules/purefb_snmp_agent.py", + "name": "plugins/modules/purefb_tz.py", "ftype": "file", "chksum_type": "sha256", - "chksum_sha256": "2da4ecae583c8c94c55046e4a72a9437ac1f01aefa83e77d315e02792edf4a2c", + "chksum_sha256": "4d08c8115e92f613d74e1bbf53a59a379f95513e3a7d231a9f745a9dfe1d23d5", "format": 1 }, { - "name": "plugins/modules/purefb_subnet.py", + "name": "plugins/modules/purefb_dns.py", "ftype": "file", "chksum_type": "sha256", - "chksum_sha256": "2ff34ed58891cf1dcca1757f2d2a2d79a21f40e61195cc2d509fc56108560409", + "chksum_sha256": "9ebd127691bb88001865cba5e1813f0895111b8806c3c5fbfef5a21c24954bdb", "format": 1 }, { - "name": "plugins/modules/purefb_apiclient.py", + "name": "plugins/modules/purefb_phonehome.py", "ftype": "file", "chksum_type": "sha256", - "chksum_sha256": "2cc1381512d001748885bd41104f8215397c74f464b696c216368de7598e47bb", + "chksum_sha256": "53bcb5901f85f1938f06ef36f36ed37537b5ec2997b596c3906971ee016a3b9f", "format": 1 }, { - "name": "plugins/modules/purefb_inventory.py", + "name": "plugins/modules/purefb_syslog.py", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "fe39dc9131937befc223fd3efd96a369238fa320618e77323fedaa8c7f2e7621", + "format": 1 + }, + { + "name": "plugins/modules/purefb_snap.py", "ftype": "file", "chksum_type": "sha256", - "chksum_sha256": "391dedb1a7265a3f57b2193ee5efa254e981d3f4be1c6425adb036c6ddb7cf6b", + "chksum_sha256": "c0ccbd3a590ee10c35445717c2f0378abb36078d3fbb5908e195e40022eaa802", "format": 1 }, { - "name": "plugins/modules/purefb_syslog.py", + "name": "plugins/modules/purefb_keytabs.py", "ftype": "file", "chksum_type": "sha256", - "chksum_sha256": "fe39dc9131937befc223fd3efd96a369238fa320618e77323fedaa8c7f2e7621", + "chksum_sha256": "9e68ef5023904b2b70f95567ef69356b43ed4324ab18fd080cc054c217326445", "format": 1 }, { "name": "plugins/modules/purefb_lag.py", "ftype": "file", "chksum_type": "sha256", - "chksum_sha256": "911181fd37fedbb616cb2d2cc6b94c070a04ca56f4a69b97299ccff40be2c803", + "chksum_sha256": "aa70ba13b897ebb5b2d3059e253f173410259d98e72089632025b9d83b63927c", "format": 1 }, { - "name": "plugins/modules/purefb_messages.py", + "name": "plugins/modules/purefb_ntp.py", "ftype": "file", "chksum_type": "sha256", - "chksum_sha256": "0a0bcd83ebb86063ed9fb3db1bacbda9a89d4d82f11590b1d2cbfd978cd1c198", + "chksum_sha256": "3df2990a95399fb343b3d9733534ffe3cef10b5546b939924aa17d04fb10fdd2", "format": 1 }, { - "name": "plugins/modules/purefb_banner.py", + "name": "plugins/modules/purefb_fs.py", "ftype": "file", "chksum_type": "sha256", - "chksum_sha256": "5daf1a121e8086c3ce3b510c9a52119ba256e49591932f4a575484fc7230b1f9", + "chksum_sha256": "2438642ad2a6ce605587eb84e0573010449ce0710d601cbf337dfa4690d7b736", "format": 1 }, { - "name": "plugins/modules/purefb_snmp_mgr.py", + "name": "plugins/modules/purefb_snmp_agent.py", "ftype": "file", "chksum_type": "sha256", - "chksum_sha256": "2ff095c16f369a129dff76ab9c2660ba2f45d0bc62b2c07bcbf58d62067addfd", + "chksum_sha256": "2da4ecae583c8c94c55046e4a72a9437ac1f01aefa83e77d315e02792edf4a2c", "format": 1 }, { - "name": "plugins/modules/purefb_ra.py", + "name": "plugins/modules/purefb_network.py", "ftype": "file", "chksum_type": "sha256", - "chksum_sha256": "3a9172183c8afdd07d3eb854f466a6c687ea881f6978053909ad9908f76db71b", + "chksum_sha256": "21398dfcfc59ad0c094ea608027bd44c121ecffc8fbff9ae96fde4f61ba65774", "format": 1 }, { - "name": "plugins/modules/purefb_groupquota.py", + "name": "plugins/modules/purefb_hardware.py", "ftype": "file", "chksum_type": "sha256", - "chksum_sha256": "fb933221f221bc66e49534594bd0ed6c06f3d83fe57b1ec45bfda80ec593becd", + "chksum_sha256": "ec8d5321dfbb3825a06ae8332c1755a8befd5edd56b8b9064b05f70d065a2f1f", "format": 1 }, { - "name": "plugins/modules/purefb_s3acc.py", + "name": "plugins/modules/purefb_lifecycle.py", "ftype": "file", "chksum_type": "sha256", - "chksum_sha256": "ff4391301e7e1a21329460afd11d73b60fec6dbab050bea8ab0d8c740f571218", + "chksum_sha256": "beff3e20624460b82775e554a8c27cfd6b345d3a5a787f96df582a7026e23449", "format": 1 }, { - "name": "plugins/modules/purefb_info.py", + "name": "plugins/modules/purefb_virtualhost.py", "ftype": "file", "chksum_type": "sha256", - "chksum_sha256": "45900eaeaafc923ea85e88c1dc87d2948e5a07f3ccb3aa2a4767c69fb2da3ac9", + "chksum_sha256": "37d614801411069d3c3aab20c018daf17496832bc73e59976b5bc25f8f5cddc2", "format": 1 }, { - "name": "plugins/modules/purefb_s3user.py", + "name": "plugins/modules/purefb_subnet.py", "ftype": "file", "chksum_type": "sha256", - "chksum_sha256": "4e3221ed572489da65f749e185123f662047918a8f9b8b9391f665d343e6acf4", + "chksum_sha256": "2ff34ed58891cf1dcca1757f2d2a2d79a21f40e61195cc2d509fc56108560409", "format": 1 }, { - "name": "plugins/modules/purefb_pingtrace.py", + "name": "plugins/modules/purefb_info.py", "ftype": "file", "chksum_type": "sha256", - "chksum_sha256": "facfd9bbb4ec84cca4c6dc3608da73a2ab8af7a9b5b1f139fbcf6f91b4f83612", + "chksum_sha256": "b6dc24aac2c4733f7f37f0901a70fc3a9679cb06994d1407ba85f92bcc110d53", "format": 1 }, { - "name": "plugins/modules/purefb_snap.py", + "name": "plugins/modules/purefb_s3acc.py", "ftype": "file", "chksum_type": "sha256", - "chksum_sha256": "c0ccbd3a590ee10c35445717c2f0378abb36078d3fbb5908e195e40022eaa802", + "chksum_sha256": "0173d7180a53b8b4c1b74e39eb48cc85089834375c8c2055688b5e533782be3d", "format": 1 }, { - "name": "plugins/modules/purefb_smtp.py", + "name": "plugins/modules/purefb_bucket_replica.py", "ftype": "file", "chksum_type": "sha256", - "chksum_sha256": "76d37be7050f2e57b7fa09cae4b7555fe8b644c031ae7b93a3de5af2cbe19781", + "chksum_sha256": "3a838e4dd90a4bf16368994e1214340362abf4f3338c8c6197783c147ed19c43", "format": 1 }, { - "name": "plugins/modules/purefb_keytabs.py", + "name": "plugins/modules/purefb_bucket.py", "ftype": "file", "chksum_type": "sha256", - "chksum_sha256": "9e68ef5023904b2b70f95567ef69356b43ed4324ab18fd080cc054c217326445", + "chksum_sha256": "11e6a2e0aa40ab4f7e50a4c2be3dfd17363e094b8ac126b5ad042c4d65c16055", "format": 1 }, { - "name": "plugins/modules/purefb_dsrole.py", + "name": "plugins/modules/purefb_pingtrace.py", "ftype": "file", "chksum_type": "sha256", - "chksum_sha256": "d625a7248695e857cc0eaf32beb340de4772c406278de8b3c81b1ce2740854c3", + "chksum_sha256": "facfd9bbb4ec84cca4c6dc3608da73a2ab8af7a9b5b1f139fbcf6f91b4f83612", "format": 1 }, { - "name": "plugins/modules/purefb_bucket.py", + "name": "plugins/module_utils", + "ftype": "dir", + "chksum_type": null, + "chksum_sha256": null, + "format": 1 + }, + { + "name": "plugins/module_utils/purefb.py", "ftype": "file", "chksum_type": "sha256", - "chksum_sha256": "2b7e76b4c8be29be79485ec99cf01ce365e725801f7467931d6eb656c5f64120", + "chksum_sha256": "3f821bf25d0ecc7d686e0fe39b96b01fbdd87ebbf3047b5ae5a720a9fac47e30", "format": 1 }, { - "name": "plugins/modules/purefb_ntp.py", + "name": "plugins/doc_fragments", + "ftype": "dir", + "chksum_type": null, + "chksum_sha256": null, + "format": 1 + }, + { + "name": "plugins/doc_fragments/purestorage.py", "ftype": "file", "chksum_type": "sha256", - "chksum_sha256": "3df2990a95399fb343b3d9733534ffe3cef10b5546b939924aa17d04fb10fdd2", + "chksum_sha256": "cb96797756b79883247778bbf7c9ed0c9a34e3e6f14d97b753e3d6401ec25f0f", "format": 1 }, { - "name": "plugins/modules/purefb_policy.py", + "name": ".github", + "ftype": "dir", + "chksum_type": null, + "chksum_sha256": null, + "format": 1 + }, + { + "name": ".github/pull_request_template.md", "ftype": "file", "chksum_type": "sha256", - "chksum_sha256": "6cbb2b5f7a2bbbebefc28ab19d06344fdf43f316a31839a440f2f29b652d130b", + "chksum_sha256": "565ead1b588caaa10cd6f2ed1bb6c809eb2ad93bf75da3a198690cac778432d6", "format": 1 }, { - "name": "plugins/modules/purefb_bucket_replica.py", + "name": ".github/ISSUE_TEMPLATE", + "ftype": "dir", + "chksum_type": null, + "chksum_sha256": null, + "format": 1 + }, + { + "name": ".github/ISSUE_TEMPLATE/bug_report.md", "ftype": "file", "chksum_type": "sha256", - "chksum_sha256": "a8ad0c4a4506527009dbb28920c81b8cef6dddde65382af33e47c22522d27332", + "chksum_sha256": "0c8d64f29fb4536513653bf8c97da30f3340e2041b91c8952db1515d6b23a7b3", "format": 1 }, { - "name": "plugins/modules/purefb_virtualhost.py", + "name": ".github/ISSUE_TEMPLATE/feature_request.md", "ftype": "file", "chksum_type": "sha256", - "chksum_sha256": "37d614801411069d3c3aab20c018daf17496832bc73e59976b5bc25f8f5cddc2", + "chksum_sha256": "1f48c52f209a971b8e7eae4120144d28fcf8ee38a7778a7b4d8cf1ab356617d2", "format": 1 }, { - "name": "plugins/modules/purefb_userquota.py", + "name": ".github/workflows", + "ftype": "dir", + "chksum_type": null, + "chksum_sha256": null, + "format": 1 + }, + { + "name": ".github/workflows/ansible-lint.yml", "ftype": "file", "chksum_type": "sha256", - "chksum_sha256": "cf1a39e2b307e395b54c2a6ced7335971cf127f03ca6f1bd8af17a2aff28b9c2", + "chksum_sha256": "62dbc43cafdab8da066ba0d86a08924e433f8b2919cdef935c116c5962d3a572", "format": 1 }, { - "name": "plugins/modules/purefb_fs_replica.py", + "name": ".github/workflows/stale.yml", "ftype": "file", "chksum_type": "sha256", - "chksum_sha256": "6ef60aaaa8d397ecbef11da23f16d707829db7613811a3142f426076b2e8d577", + "chksum_sha256": "544ccc9f17e16d9087802e3dcec69741e6ff79e31cf7302947ce2c08126ce1d4", "format": 1 }, { - "name": "plugins/modules/purefb_timeout.py", + "name": ".github/workflows/black.yaml", "ftype": "file", "chksum_type": "sha256", - "chksum_sha256": "2c25d12eff522c44580b77e457c0496368e877bfe72cb41f1a9402a96ad18418", + "chksum_sha256": "803b5d6a6d7448701e1b7eb09595f783cb7ca83bd4d298f91c60ce7143c3607b", "format": 1 }, { - "name": "plugins/modules/purefb_phonehome.py", + "name": ".github/workflows/main.yml", "ftype": "file", "chksum_type": "sha256", - "chksum_sha256": "53bcb5901f85f1938f06ef36f36ed37537b5ec2997b596c3906971ee016a3b9f", + "chksum_sha256": "2be488cb3b6926e2f859ec951616fcfe2d50df7cdf98a978f2929c2b247633b4", "format": 1 }, { - "name": "plugins/modules/purefb_eula.py", + "name": ".github/CONTRIBUTING.md", "ftype": "file", "chksum_type": "sha256", - "chksum_sha256": "1d06a41aeae5febbc2d1fecd64b888e5947f14b0944f473c3c5d1d46e50acfc4", + "chksum_sha256": "7c429527b799623f57e6363e14ff8a319844c9120f4dfa18bcea3849cdc07128", "format": 1 }, { - "name": "plugins/modules/purefb_lifecycle.py", + "name": "README.md", "ftype": "file", "chksum_type": "sha256", - "chksum_sha256": "beff3e20624460b82775e554a8c27cfd6b345d3a5a787f96df582a7026e23449", + "chksum_sha256": "33409fed9bd21cc282415c711d33a6a5170c7565354c40696ee532f0bc46c2a6", "format": 1 }, { - "name": "plugins/doc_fragments", + "name": "meta", "ftype": "dir", "chksum_type": null, "chksum_sha256": null, "format": 1 }, { - "name": "plugins/doc_fragments/purestorage.py", + "name": "meta/runtime.yml", "ftype": "file", "chksum_type": "sha256", - "chksum_sha256": "cb96797756b79883247778bbf7c9ed0c9a34e3e6f14d97b753e3d6401ec25f0f", + "chksum_sha256": "4833e2900333e7a035d7e0f63f6d55777c2697476ee0a2f9bfcf250167c7571d", "format": 1 }, { - "name": "plugins/module_utils", + "name": "README.rst", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "9eca16f5db9ebc48387f94f50a9762c57fcb6a6eb4cd6c258f13b0a9a371be8e", + "format": 1 + }, + { + "name": "LICENSE", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "3972dc9744f6499f0f9b2dbf76696f2ae7ad8af9b23dde66d6af86c9dfb36986", + "format": 1 + }, + { + "name": ".gitignore", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "f3e019033a4ff6d651103704d47629e6d911cb949652bd5e6121d7a918dbc480", + "format": 1 + }, + { + "name": "roles", "ftype": "dir", "chksum_type": null, "chksum_sha256": null, "format": 1 }, { - "name": "plugins/module_utils/purefb.py", + "name": "roles/.keep", "ftype": "file", "chksum_type": "sha256", - "chksum_sha256": "5a7a9657951dec2667ad720e965452a0003924cd36fe260527c01f83948d0473", + "chksum_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", "format": 1 }, { - "name": "README.rst", + "name": "settings.json", "ftype": "file", "chksum_type": "sha256", - "chksum_sha256": "9eca16f5db9ebc48387f94f50a9762c57fcb6a6eb4cd6c258f13b0a9a371be8e", + "chksum_sha256": "f64528ffd800423e1d49a3c79cdd3892548a57177ea1a1caacbbcd275390b792", "format": 1 }, { - "name": ".pylintrc", + "name": ".git-blame-ignore-revs", "ftype": "file", "chksum_type": "sha256", - "chksum_sha256": "75d8dc97586bc956a906be2aa0b86ec465eb78ce48d3d651ea1ddad3935d27cf", + "chksum_sha256": "272d9a8e8654881cd42bb4108716e720bc634065d74064fb09f29d0e6e817e21", "format": 1 }, { - "name": ".git-blame-ignore-revs", + "name": ".yamllint", "ftype": "file", "chksum_type": "sha256", - "chksum_sha256": "272d9a8e8654881cd42bb4108716e720bc634065d74064fb09f29d0e6e817e21", + "chksum_sha256": "2970fa4875092f99825ac0da3c82d2413ce973087b9945e68fdfa7b3b1e2012e", + "format": 1 + }, + { + "name": "CHANGELOG.rst", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "de7d63a6d1d411e66f64971129b4630faaca228eb0a8348f261034aab83faa04", "format": 1 } ], diff --git a/ansible_collections/purestorage/flashblade/MANIFEST.json b/ansible_collections/purestorage/flashblade/MANIFEST.json index c111f1bf6..2af712175 100644 --- a/ansible_collections/purestorage/flashblade/MANIFEST.json +++ b/ansible_collections/purestorage/flashblade/MANIFEST.json @@ -2,7 +2,7 @@ "collection_info": { "namespace": "purestorage", "name": "flashblade", - "version": "1.11.0", + "version": "1.16.0", "authors": [ "Pure Storage Ansible Team <pure-ansible-team@purestorage.com>" ], @@ -14,7 +14,7 @@ "object", "nfs" ], - "description": "Collection of modules to manage Pure Storage FlashBlades", + "description": "Collection of modules to manage Pure Storage FlashBlade", "license": [ "GPL-3.0-or-later", "BSD-2-Clause" @@ -30,7 +30,7 @@ "name": "FILES.json", "ftype": "file", "chksum_type": "sha256", - "chksum_sha256": "1bb9f78982cdd6334e7f063927e0a32f11b5d6c6940b0cd253d3311be4717cda", + "chksum_sha256": "14b510daea00c6bbcbf0a5bcfff2c45937740e569c86fedeb64970968dc4eecc", "format": 1 }, "format": 1 diff --git a/ansible_collections/purestorage/flashblade/README.md b/ansible_collections/purestorage/flashblade/README.md index 7972158bc..6f288d63f 100644 --- a/ansible_collections/purestorage/flashblade/README.md +++ b/ansible_collections/purestorage/flashblade/README.md @@ -15,15 +15,16 @@ The Pure Storage FlashBlade collection consists of the latest versions of the Fl ## Prerequisites -- Ansible 2.9 or later +- Ansible 2.14 or later - Pure Storage FlashBlade system running Purity//FB 2.1.2 or later - some modules require higher versions of Purity//FB -- purity_fb >=v1.12.2 +- purity-fb >=v1.12.2 - py-pure-client >=v1.27.0 -- python >=3.6 +- python >=3.9 - netaddr - datetime - pytz +- distro ## Idempotency @@ -48,6 +49,7 @@ All modules are idempotent with the exception of modules that change or set pass - purefb_fs - manage filesystems on a FlashBlade - purefb_fs_replica - manage filesystem replica links on a FlashBlade - purefb_groupquota - manage individual group quotas on FlashBlade filesystems +- purefb_hardware - manage hardware LED identifiers and hardware connectors - purefb_info - get information about the configuration of a FlashBlade - purefb_inventory - get information about the hardware inventory of a FlashBlade - purefb_keytabs - manage FlashBlade Kerberos keytabs diff --git a/ansible_collections/purestorage/flashblade/changelogs/.plugin-cache.yaml b/ansible_collections/purestorage/flashblade/changelogs/.plugin-cache.yaml index 9834bdfed..c99d4477d 100644 --- a/ansible_collections/purestorage/flashblade/changelogs/.plugin-cache.yaml +++ b/ansible_collections/purestorage/flashblade/changelogs/.plugin-cache.yaml @@ -101,6 +101,11 @@ plugins: name: purefb_groupquota namespace: '' version_added: 1.7.0 + purefb_hardware: + description: Manage FlashBlade Hardware + name: purefb_hardware + namespace: '' + version_added: 1.15.0 purefb_info: description: Collect information from Pure Storage FlashBlade name: purefb_info @@ -251,4 +256,4 @@ plugins: strategy: {} test: {} vars: {} -version: 1.11.0 +version: 1.16.0 diff --git a/ansible_collections/purestorage/flashblade/changelogs/changelog.yaml b/ansible_collections/purestorage/flashblade/changelogs/changelog.yaml index 9995182fa..eaeb07ed3 100644 --- a/ansible_collections/purestorage/flashblade/changelogs/changelog.yaml +++ b/ansible_collections/purestorage/flashblade/changelogs/changelog.yaml @@ -38,7 +38,7 @@ releases: 1.11.0: changes: bugfixes: - - purefa_info - Fixed issue when more than 10 buckets have lifecycle rules. + - purefb_info - Fixed issue when more than 10 buckets have lifecycle rules. - purefb_s3user - Fix incorrect response when bad key/secret pair provided for new user minor_changes: @@ -69,6 +69,105 @@ releases: name: purefb_pingtrace namespace: '' release_date: '2023-04-13' + 1.12.0: + changes: + bugfixes: + - purefb_bucket - Fixed bucket type mode name typo + - purefb_fs - Fixed issue with incorrect promotion state setting + minor_changes: + - purefb_fs - Added support for SMB client and share policies + - purefb_fs_replica - Added support to delete filesystem replica links from + REST 2.10 + - purefb_info - Add drive type in drives subset for //S and //E platforms. Only + available from REST 2.9. + - purefb_info - Added support for SMB client and share policies + - purefb_policy - Added support for SMB client and share policies + - purefb_s3acc - Allow human readable quota sizes; eg. 1T, 230K, etc + - purefb_s3user - Add new boolean parameter I(multiple_keys) to limit access + keys for a user to a single key. + fragments: + - 222_bucket_type_fix.yaml + - 223_add_drive_type.yaml + - 224_smb_policies.yaml + - 225_delete_rl.yaml + - 227_s3acc_human_quota.yaml + - 230_prom_fix.yaml + - 232_multiple_keys.yaml + release_date: '2023-07-10' + 1.13.0: + release_date: '2023-09-07' + 1.13.1: + changes: + bugfixes: + - purefb_info - Fixed missing atributes for SMB client policy rules + minor_changes: + - purefb_policy - Add new and updated policy access rights + fragments: + - 237_info_policy.yaml + - 239_access_rights.yaml + release_date: '2023-09-07' + 1.14.0: + changes: + bugfixes: + - purefb_userpolicy - Fixed `show` state for all user policies + minor_changes: + - purefb_bucket_replica - Added support for cascading replica links + - purefb_info - New fields to display free space (remaining quota) for Accounts + and Buckets. Space used by destroyed buckets is split out from virtual field + to new destroyed_virtual field + - purefb_info - Report encryption state in SMB client policy rules + - purefb_info - Report more detailed space data from Purity//FB 4.3.0 + - purefb_policy - Add deny effect for object store policy rules. Requires Purity//FB + 4.3.0+ + - purefb_policy - Added parameter to define object store policy description + fragments: + - 238_user_policy.yaml + - 242_cascade.yaml + - 243_policy_desc.yaml + - 244_add_deny.yaml + - 245_quota_plus.yaml + - 246_smb_encrypt.yaml + - 247_space_consistency.yaml + release_date: '2023-10-04' + 1.15.0: + changes: + bugfixes: + - purefb_info - Added missing object lock retention details if enabledd + minor_changes: + - purefb_bucket - Add support for public buckets + - purefb_bucket - From REST 2.12 the `mode` parameter default changes to `multi-site-writable`. + - purefb_fs - Added SMB Continuous Availability parameter. Requires REST 2.12 + or higher. + - purefb_info - Added enhanced information for buckets, filesystems and snapshots, + based on new features in REST 2.12 + - purefb_s3acc - Add support for public buckets + - purefb_s3acc - Remove default requirements for ``hard_limit`` and ``default_hard_limit`` + fragments: + - 252_object_lock_info.yaml + - 254_update_212_info.yaml + - 255_smb_ca.yaml + - 257_mode_change.yaml + - 258_add_public_buckets.yaml + modules: + - description: Manage FlashBlade Hardware + name: purefb_hardware + namespace: '' + release_date: '2024-01-12' + 1.16.0: + changes: + bugfixes: + - purefb_bucket - Changed logic to allow complex buckets to be created in a + single call, rather than having to split into two tasks. + - purefb_lag - Enable LAG port configuration with multi-chassis + - purefb_timeout - Fixed arithmetic error that resulted in module incorrectly + reporting changed when no change was required. + minor_changes: + - purefb_ds - Add `force_bind_password` parameter to allow module to be idempotent. + fragments: + - 263_fix_multiple_modules_idempotency.yaml + - 266_bucket_fix.yaml + - 268_multi-chassis-lag.yaml + release_date: '2024-02-27' 1.3.0: changes: bugfixes: @@ -111,7 +210,7 @@ releases: 1.4.0: changes: bugfixes: - - purefa_policy - Resolve multiple issues related to incorrect use of timezones + - purefb_policy - Resolve multiple issues related to incorrect use of timezones - purefb_connect - Ensure changing encryption status on array connection is performed correctly - purefb_connect - Fix breaking change created in purity_fb SDK 1.9.2 for deletion @@ -201,7 +300,7 @@ releases: 1.6.0: changes: minor_changes: - - purefa_virtualhost - New module to manage API Clients + - purefb_virtualhost - New module to manage API Clients - purefb_ad - New module to manage Active Directory Account - purefb_eula - New module to sign EULA - purefb_info - Add Active Directory, Kerberos and Object Store Account information diff --git a/ansible_collections/purestorage/flashblade/changelogs/fragments/101_fix_policy_and_timezone_error.yaml b/ansible_collections/purestorage/flashblade/changelogs/fragments/101_fix_policy_and_timezone_error.yaml index e6c1ea64d..4db1d1d7f 100644 --- a/ansible_collections/purestorage/flashblade/changelogs/fragments/101_fix_policy_and_timezone_error.yaml +++ b/ansible_collections/purestorage/flashblade/changelogs/fragments/101_fix_policy_and_timezone_error.yaml @@ -1,2 +1,2 @@ bugfixes: - - purefa_policy - Resolve multiple issues related to incorrect use of timezones + - purefb_policy - Resolve multiple issues related to incorrect use of timezones diff --git a/ansible_collections/purestorage/flashblade/changelogs/fragments/131-apiclient.yaml b/ansible_collections/purestorage/flashblade/changelogs/fragments/131-apiclient.yaml index 7a3f021b5..ec8c5ec64 100644 --- a/ansible_collections/purestorage/flashblade/changelogs/fragments/131-apiclient.yaml +++ b/ansible_collections/purestorage/flashblade/changelogs/fragments/131-apiclient.yaml @@ -1,2 +1,2 @@ minor_changes: - - purefa_virtualhost - New module to manage API Clients + - purefb_virtualhost - New module to manage API Clients diff --git a/ansible_collections/purestorage/flashblade/changelogs/fragments/205_fix_multi_lifecycle.yaml b/ansible_collections/purestorage/flashblade/changelogs/fragments/205_fix_multi_lifecycle.yaml index b6810884b..ec892b23c 100644 --- a/ansible_collections/purestorage/flashblade/changelogs/fragments/205_fix_multi_lifecycle.yaml +++ b/ansible_collections/purestorage/flashblade/changelogs/fragments/205_fix_multi_lifecycle.yaml @@ -1,2 +1,2 @@ bugfixes: - - purefa_info - Fixed issue when more than 10 buckets have lifecycle rules. + - purefb_info - Fixed issue when more than 10 buckets have lifecycle rules. diff --git a/ansible_collections/purestorage/flashblade/changelogs/fragments/222_bucket_type_fix.yaml b/ansible_collections/purestorage/flashblade/changelogs/fragments/222_bucket_type_fix.yaml new file mode 100644 index 000000000..d6cda4cb9 --- /dev/null +++ b/ansible_collections/purestorage/flashblade/changelogs/fragments/222_bucket_type_fix.yaml @@ -0,0 +1,2 @@ +bugfixes: + - purefb_bucket - Fixed bucket type mode name typo diff --git a/ansible_collections/purestorage/flashblade/changelogs/fragments/223_add_drive_type.yaml b/ansible_collections/purestorage/flashblade/changelogs/fragments/223_add_drive_type.yaml new file mode 100644 index 000000000..7fb4f3e80 --- /dev/null +++ b/ansible_collections/purestorage/flashblade/changelogs/fragments/223_add_drive_type.yaml @@ -0,0 +1,2 @@ +minor_changes: + - purefb_info - Add drive type in drives subset for //S and //E platforms. Only available from REST 2.9. diff --git a/ansible_collections/purestorage/flashblade/changelogs/fragments/224_smb_policies.yaml b/ansible_collections/purestorage/flashblade/changelogs/fragments/224_smb_policies.yaml new file mode 100644 index 000000000..351711dfe --- /dev/null +++ b/ansible_collections/purestorage/flashblade/changelogs/fragments/224_smb_policies.yaml @@ -0,0 +1,4 @@ +minor_changes: + - purefb_info - Added support for SMB client and share policies + - purefb_fs - Added support for SMB client and share policies + - purefb_policy - Added support for SMB client and share policies diff --git a/ansible_collections/purestorage/flashblade/changelogs/fragments/225_delete_rl.yaml b/ansible_collections/purestorage/flashblade/changelogs/fragments/225_delete_rl.yaml new file mode 100644 index 000000000..3ccdea050 --- /dev/null +++ b/ansible_collections/purestorage/flashblade/changelogs/fragments/225_delete_rl.yaml @@ -0,0 +1,2 @@ +minor_changes: + - purefb_fs_replica - Added support to delete filesystem replica links from REST 2.10 diff --git a/ansible_collections/purestorage/flashblade/changelogs/fragments/227_s3acc_human_quota.yaml b/ansible_collections/purestorage/flashblade/changelogs/fragments/227_s3acc_human_quota.yaml new file mode 100644 index 000000000..677cfded7 --- /dev/null +++ b/ansible_collections/purestorage/flashblade/changelogs/fragments/227_s3acc_human_quota.yaml @@ -0,0 +1,2 @@ +minor_changes: + - purefb_s3acc - Allow human readable quota sizes; eg. 1T, 230K, etc diff --git a/ansible_collections/purestorage/flashblade/changelogs/fragments/230_prom_fix.yaml b/ansible_collections/purestorage/flashblade/changelogs/fragments/230_prom_fix.yaml new file mode 100644 index 000000000..0c3611993 --- /dev/null +++ b/ansible_collections/purestorage/flashblade/changelogs/fragments/230_prom_fix.yaml @@ -0,0 +1,2 @@ +bugfixes: + - purefb_fs - Fixed issue with incorrect promotion state setting diff --git a/ansible_collections/purestorage/flashblade/changelogs/fragments/232_multiple_keys.yaml b/ansible_collections/purestorage/flashblade/changelogs/fragments/232_multiple_keys.yaml new file mode 100644 index 000000000..b46b238e3 --- /dev/null +++ b/ansible_collections/purestorage/flashblade/changelogs/fragments/232_multiple_keys.yaml @@ -0,0 +1,2 @@ +minor_changes: + - purefb_s3user - Add new boolean parameter I(multiple_keys) to limit access keys for a user to a single key. diff --git a/ansible_collections/purestorage/flashblade/changelogs/fragments/237_info_policy.yaml b/ansible_collections/purestorage/flashblade/changelogs/fragments/237_info_policy.yaml new file mode 100644 index 000000000..fa4662b69 --- /dev/null +++ b/ansible_collections/purestorage/flashblade/changelogs/fragments/237_info_policy.yaml @@ -0,0 +1,2 @@ +bugfixes: + - purefb_info - Fixed missing atributes for SMB client policy rules diff --git a/ansible_collections/purestorage/flashblade/changelogs/fragments/238_user_policy.yaml b/ansible_collections/purestorage/flashblade/changelogs/fragments/238_user_policy.yaml new file mode 100644 index 000000000..b49f6ebc0 --- /dev/null +++ b/ansible_collections/purestorage/flashblade/changelogs/fragments/238_user_policy.yaml @@ -0,0 +1,2 @@ +bugfixes: + - purefb_userpolicy - Fixed `show` state for all user policies diff --git a/ansible_collections/purestorage/flashblade/changelogs/fragments/239_access_rights.yaml b/ansible_collections/purestorage/flashblade/changelogs/fragments/239_access_rights.yaml new file mode 100644 index 000000000..c0c59fd42 --- /dev/null +++ b/ansible_collections/purestorage/flashblade/changelogs/fragments/239_access_rights.yaml @@ -0,0 +1,2 @@ +minor_changes: + - purefb_policy - Add new and updated policy access rights diff --git a/ansible_collections/purestorage/flashblade/changelogs/fragments/242_cascade.yaml b/ansible_collections/purestorage/flashblade/changelogs/fragments/242_cascade.yaml new file mode 100644 index 000000000..d0c00a484 --- /dev/null +++ b/ansible_collections/purestorage/flashblade/changelogs/fragments/242_cascade.yaml @@ -0,0 +1,2 @@ +minor_changes: + - purefb_bucket_replica - Added support for cascading replica links diff --git a/ansible_collections/purestorage/flashblade/changelogs/fragments/243_policy_desc.yaml b/ansible_collections/purestorage/flashblade/changelogs/fragments/243_policy_desc.yaml new file mode 100644 index 000000000..a3ce1be18 --- /dev/null +++ b/ansible_collections/purestorage/flashblade/changelogs/fragments/243_policy_desc.yaml @@ -0,0 +1,2 @@ +minor_changes: + - purefb_policy - Added parameter to define object store policy description diff --git a/ansible_collections/purestorage/flashblade/changelogs/fragments/244_add_deny.yaml b/ansible_collections/purestorage/flashblade/changelogs/fragments/244_add_deny.yaml new file mode 100644 index 000000000..ce741d778 --- /dev/null +++ b/ansible_collections/purestorage/flashblade/changelogs/fragments/244_add_deny.yaml @@ -0,0 +1,2 @@ +minor_changes: + - purefb_policy - Add deny effect for object store policy rules. Requires Purity//FB 4.3.0+ diff --git a/ansible_collections/purestorage/flashblade/changelogs/fragments/245_quota_plus.yaml b/ansible_collections/purestorage/flashblade/changelogs/fragments/245_quota_plus.yaml new file mode 100644 index 000000000..607d8a637 --- /dev/null +++ b/ansible_collections/purestorage/flashblade/changelogs/fragments/245_quota_plus.yaml @@ -0,0 +1,2 @@ +minor_changes: + - purefb_info - New fields to display free space (remaining quota) for Accounts and Buckets. Space used by destroyed buckets is split out from virtual field to new destroyed_virtual field diff --git a/ansible_collections/purestorage/flashblade/changelogs/fragments/246_smb_encrypt.yaml b/ansible_collections/purestorage/flashblade/changelogs/fragments/246_smb_encrypt.yaml new file mode 100644 index 000000000..de6b9ae19 --- /dev/null +++ b/ansible_collections/purestorage/flashblade/changelogs/fragments/246_smb_encrypt.yaml @@ -0,0 +1,2 @@ +minor_changes: + - purefb_info - Report encryption state in SMB client policy rules diff --git a/ansible_collections/purestorage/flashblade/changelogs/fragments/247_space_consistency.yaml b/ansible_collections/purestorage/flashblade/changelogs/fragments/247_space_consistency.yaml new file mode 100644 index 000000000..692bae5d7 --- /dev/null +++ b/ansible_collections/purestorage/flashblade/changelogs/fragments/247_space_consistency.yaml @@ -0,0 +1,2 @@ +minor_changes: + - purefb_info - Report more detailed space data from Purity//FB 4.3.0 diff --git a/ansible_collections/purestorage/flashblade/changelogs/fragments/252_object_lock_info.yaml b/ansible_collections/purestorage/flashblade/changelogs/fragments/252_object_lock_info.yaml new file mode 100644 index 000000000..49fdeaa8c --- /dev/null +++ b/ansible_collections/purestorage/flashblade/changelogs/fragments/252_object_lock_info.yaml @@ -0,0 +1,2 @@ +bugfixes: + - purefb_info - Added missing object lock retention details if enabledd diff --git a/ansible_collections/purestorage/flashblade/changelogs/fragments/254_update_212_info.yaml b/ansible_collections/purestorage/flashblade/changelogs/fragments/254_update_212_info.yaml new file mode 100644 index 000000000..2a7d90be9 --- /dev/null +++ b/ansible_collections/purestorage/flashblade/changelogs/fragments/254_update_212_info.yaml @@ -0,0 +1,2 @@ +minor_changes: + - purefb_info - Added enhanced information for buckets, filesystems and snapshots, based on new features in REST 2.12 diff --git a/ansible_collections/purestorage/flashblade/changelogs/fragments/255_smb_ca.yaml b/ansible_collections/purestorage/flashblade/changelogs/fragments/255_smb_ca.yaml new file mode 100644 index 000000000..8517e3b45 --- /dev/null +++ b/ansible_collections/purestorage/flashblade/changelogs/fragments/255_smb_ca.yaml @@ -0,0 +1,2 @@ +minor_changes: + - purefb_fs - Added SMB Continuous Availability parameter. Requires REST 2.12 or higher. diff --git a/ansible_collections/purestorage/flashblade/changelogs/fragments/257_mode_change.yaml b/ansible_collections/purestorage/flashblade/changelogs/fragments/257_mode_change.yaml new file mode 100644 index 000000000..e00c10643 --- /dev/null +++ b/ansible_collections/purestorage/flashblade/changelogs/fragments/257_mode_change.yaml @@ -0,0 +1,2 @@ +minor_changes: + - purefb_bucket - From REST 2.12 the `mode` parameter default changes to `multi-site-writable`. diff --git a/ansible_collections/purestorage/flashblade/changelogs/fragments/258_add_public_buckets.yaml b/ansible_collections/purestorage/flashblade/changelogs/fragments/258_add_public_buckets.yaml new file mode 100644 index 000000000..5600e7e84 --- /dev/null +++ b/ansible_collections/purestorage/flashblade/changelogs/fragments/258_add_public_buckets.yaml @@ -0,0 +1,4 @@ +minor_changes: + - purefb_s3acc - Add support for public buckets + - purefb_s3acc - Remove default requirements for ``hard_limit`` and ``default_hard_limit`` + - purefb_bucket - Add support for public buckets diff --git a/ansible_collections/purestorage/flashblade/changelogs/fragments/263_fix_multiple_modules_idempotency.yaml b/ansible_collections/purestorage/flashblade/changelogs/fragments/263_fix_multiple_modules_idempotency.yaml new file mode 100644 index 000000000..693e846d3 --- /dev/null +++ b/ansible_collections/purestorage/flashblade/changelogs/fragments/263_fix_multiple_modules_idempotency.yaml @@ -0,0 +1,4 @@ +minor_changes: + - purefb_ds - Add `force_bind_password` parameter to allow module to be idempotent. +bugfixes: + - purefb_timeout - Fixed arithmetic error that resulted in module incorrectly reporting changed when no change was required. diff --git a/ansible_collections/purestorage/flashblade/changelogs/fragments/266_bucket_fix.yaml b/ansible_collections/purestorage/flashblade/changelogs/fragments/266_bucket_fix.yaml new file mode 100644 index 000000000..f7d66e894 --- /dev/null +++ b/ansible_collections/purestorage/flashblade/changelogs/fragments/266_bucket_fix.yaml @@ -0,0 +1,2 @@ +bugfixes: + - purefb_bucket - Changed logic to allow complex buckets to be created in a single call, rather than having to split into two tasks. diff --git a/ansible_collections/purestorage/flashblade/changelogs/fragments/268_multi-chassis-lag.yaml b/ansible_collections/purestorage/flashblade/changelogs/fragments/268_multi-chassis-lag.yaml new file mode 100644 index 000000000..e9146f642 --- /dev/null +++ b/ansible_collections/purestorage/flashblade/changelogs/fragments/268_multi-chassis-lag.yaml @@ -0,0 +1,2 @@ +bugfixes: + - purefb_lag - Enable LAG port configuration with multi-chassis diff --git a/ansible_collections/purestorage/flashblade/meta/runtime.yml b/ansible_collections/purestorage/flashblade/meta/runtime.yml index 2ee3c9fa9..be99ccf4b 100644 --- a/ansible_collections/purestorage/flashblade/meta/runtime.yml +++ b/ansible_collections/purestorage/flashblade/meta/runtime.yml @@ -1,2 +1,2 @@ --- -requires_ansible: '>=2.9.10' +requires_ansible: '>=2.14.0' diff --git a/ansible_collections/purestorage/flashblade/plugins/module_utils/purefb.py b/ansible_collections/purestorage/flashblade/plugins/module_utils/purefb.py index cf987a3e5..87b27a821 100644 --- a/ansible_collections/purestorage/flashblade/plugins/module_utils/purefb.py +++ b/ansible_collections/purestorage/flashblade/plugins/module_utils/purefb.py @@ -32,6 +32,12 @@ from __future__ import absolute_import, division, print_function __metaclass__ = type +HAS_DISTRO = True +try: + import distro +except ImportError: + HAS_DISTRO = False + HAS_PURITY_FB = True try: from purity_fb import PurityFb @@ -47,19 +53,27 @@ except ImportError: from os import environ import platform -VERSION = "1.4" +VERSION = "1.5" USER_AGENT_BASE = "Ansible" API_AGENT_VERSION = "1.5" def get_blade(module): """Return System Object or Fail""" - user_agent = "%(base)s %(class)s/%(version)s (%(platform)s)" % { - "base": USER_AGENT_BASE, - "class": __name__, - "version": VERSION, - "platform": platform.platform(), - } + if HAS_DISTRO: + user_agent = "%(base)s %(class)s/%(version)s (%(platform)s)" % { + "base": USER_AGENT_BASE, + "class": __name__, + "version": VERSION, + "platform": distro.name(pretty=True), + } + else: + user_agent = "%(base)s %(class)s/%(version)s (%(platform)s)" % { + "base": USER_AGENT_BASE, + "class": __name__, + "version": VERSION, + "platform": platform.platform(), + } blade_name = module.params["fb_url"] api = module.params["api_token"] @@ -100,12 +114,20 @@ def get_blade(module): def get_system(module): """Return System Object or Fail""" - user_agent = "%(base)s %(class)s/%(version)s (%(platform)s)" % { - "base": USER_AGENT_BASE, - "class": __name__, - "version": VERSION, - "platform": platform.platform(), - } + if HAS_DISTRO: + user_agent = "%(base)s %(class)s/%(version)s (%(platform)s)" % { + "base": USER_AGENT_BASE, + "class": __name__, + "version": VERSION, + "platform": distro.name(pretty=True), + } + else: + user_agent = "%(base)s %(class)s/%(version)s (%(platform)s)" % { + "base": USER_AGENT_BASE, + "class": __name__, + "version": VERSION, + "platform": platform.platform(), + } blade_name = module.params["fb_url"] api = module.params["api_token"] diff --git a/ansible_collections/purestorage/flashblade/plugins/modules/purefb_bucket.py b/ansible_collections/purestorage/flashblade/plugins/modules/purefb_bucket.py index 67b6b1545..27cd7e317 100644 --- a/ansible_collections/purestorage/flashblade/plugins/modules/purefb_bucket.py +++ b/ansible_collections/purestorage/flashblade/plugins/modules/purefb_bucket.py @@ -59,11 +59,72 @@ options: description: - The type of bucket to be created. Also referred to a VSO Mode. - Requires Purity//FB 3.3.3 or higher - - I(multi-site) type can only be used after feature is + - I(multi-site-writable) type can only be used after feature is enabled by Pure Technical Support type: str - choices: [ "classic", "multi-site" ] + choices: [ "classic", "multi-site-writable" ] version_added: '1.10.0' + quota: + description: + - User quota in M, G, T or P units. This cannot be 0. + - This value will override the object store account's default bucket quota. + type: str + version_added: '1.12.0' + hard_limit: + description: + - Whether the I(quota) value is enforced or not. + - If not provided the object store account default value will be used. + type: bool + version_added: '1.12.0' + retention_lock: + description: + - Set retention lock level for the bucket + - Once set to I(ratcheted) can only be lowered by Pure Technical Services + type: str + choices: [ "ratcheted", "unlocked" ] + default: unlocked + version_added: '1.12.0' + retention_mode: + description: + - The retention mode used to apply locks on new objects if none is specified by the S3 client + - Use "" to clear + - Once set to I(compliance) this can only be changed by contacting Pure Technical Services + type: str + choices: [ "compliance", "governance", "" ] + version_added: '1.12.0' + object_lock_enabled: + description: + - If set to true, then S3 APIs relating to object lock may be used + type: bool + default: false + version_added: '1.12.0' + freeze_locked_objects: + description: + - If set to true, a locked object will be read-only and no new versions of + the object may be created due to modifications + - After enabling, can be disabled only by contacting Pure Technical Services + type: bool + default: false + version_added: '1.12.0' + default_retention: + description: + - The retention period, in days, used to apply locks on new objects if + none is specified by the S3 client + - Valid values between 1 and 365000 + - Use "" to clear + type: str + version_added: '1.12.0' + block_new_public_policies: + description: + - If set to true, adding bucket policies that grant public access to a bucket is not allowed. + type: bool + version_added: 1.15.0 + block_public_access: + description: + - If set to true, access to a bucket with a public policy is restricted to only authenticated + users within the account that bucket belongs to. + type: bool + version_added: 1.15.0 extends_documentation_fragment: - purestorage.flashblade.purestorage.fb """ @@ -125,7 +186,7 @@ try: except ImportError: HAS_PYPURECLIENT = False -from ansible.module_utils.basic import AnsibleModule +from ansible.module_utils.basic import AnsibleModule, human_to_bytes from ansible_collections.purestorage.flashblade.plugins.module_utils.purefb import ( get_blade, get_system, @@ -135,7 +196,9 @@ from ansible_collections.purestorage.flashblade.plugins.module_utils.purefb impo MIN_REQUIRED_API_VERSION = "1.5" VERSIONING_VERSION = "1.9" -VSO_VERSION = "2.4" +VSO_VERSION = "2.5" +QUOTA_VERSION = "2.8" +MODE_VERSION = "2.12" def get_s3acc(module, blade): @@ -161,18 +224,56 @@ def get_bucket(module, blade): def create_bucket(module, blade): """Create bucket""" changed = True + bladev2 = get_system(module) if not module.check_mode: try: api_version = blade.api_version.list_versions().versions - if VSO_VERSION in api_version and module.params["mode"]: - bladev2 = get_system(module) - res = bladev2.post_buckets( - names=[module.params["name"]], - bucket=flashblade.BucketPost( + if VSO_VERSION in api_version: + account_defaults = list( + bladev2.get_object_store_accounts( + names=[module.params["account"]] + ).items + )[0] + if QUOTA_VERSION in api_version: + if not module.params["hard_limit"]: + module.params["hard_limit"] = ( + account_defaults.hard_limit_enabled + ) + if module.params["quota"]: + quota = str(human_to_bytes(module.params["quota"])) + else: + if not account_defaults.quota_limit: + quota = "" + else: + quota = str(account_defaults.quota_limit) + if not module.params["retention_mode"]: + module.params["retention_mode"] = "" + if not module.params["default_retention"]: + module.params["default_retention"] = "" + else: + module.params["default_retention"] = str( + int(module.params["default_retention"]) * 86400000 + ) + if module.params["object_lock_enabled"]: + bucket = flashblade.BucketPost( + account=flashblade.Reference(name=module.params["account"]), + bucket_type=module.params["mode"], + hard_limit_enabled=module.params["hard_limit"], + quota_limit=quota, + ) + else: + bucket = flashblade.BucketPost( + account=flashblade.Reference(name=module.params["account"]), + bucket_type=module.params["mode"], + hard_limit_enabled=module.params["hard_limit"], + quota_limit=quota, + ) + else: + bucket = flashblade.BucketPost( account=flashblade.Reference(name=module.params["account"]), bucket_type=module.params["mode"], - ), - ) + ) + res = bladev2.post_buckets(names=[module.params["name"]], bucket=bucket) if res.status_code != 200: module.fail_json( msg="Object Store Bucket {0} creation failed. Error: {1}".format( @@ -180,37 +281,79 @@ def create_bucket(module, blade): res.errors[0].message, ) ) - elif VERSIONING_VERSION in api_version: - attr = BucketPost() - attr.account = Reference(name=module.params["account"]) - blade.buckets.create_buckets(names=[module.params["name"]], bucket=attr) - else: - attr = Bucket() - attr.account = Reference(name=module.params["account"]) - blade.buckets.create_buckets( - names=[module.params["name"]], account=attr - ) - if ( - module.params["versioning"] != "absent" - and VERSIONING_VERSION in api_version - ): - try: - blade.buckets.update_buckets( - names=[module.params["name"]], - bucket=BucketPatch(versioning=module.params["versioning"]), + if QUOTA_VERSION in api_version: + bucket = flashblade.BucketPatch( + retention_lock=module.params["retention_lock"], + object_lock_config=flashblade.ObjectLockConfigRequestBody( + default_retention_mode=module.params["retention_mode"], + enabled=module.params["object_lock_enabled"], + freeze_locked_objects=module.params[ + "freeze_locked_objects" + ], + default_retention=module.params["default_retention"], + ), + versioning=module.params["versioning"], ) - except Exception: + else: + bucket = flashblade.BucketPatch( + retention_lock=module.params["retention_lock"], + versioning=module.params["versioning"], + ) + res = bladev2.patch_buckets( + names=[module.params["name"]], bucket=bucket + ) + if res.status_code != 200: module.fail_json( - msg="Object Store Bucket {0} Created but versioning state failed".format( - module.params["name"] + msg="Object Store Bucket {0} creation update failed. Error: {1}".format( + module.params["name"], + res.errors[0].message, ) ) + else: + attr = BucketPost() + attr.account = Reference(name=module.params["account"]) + blade.buckets.create_buckets(names=[module.params["name"]], bucket=attr) + if module.params["versioning"] != "absent": + try: + blade.buckets.update_buckets( + names=[module.params["name"]], + bucket=BucketPatch(versioning=module.params["versioning"]), + ) + except Exception: + module.fail_json( + msg="Object Store Bucket {0} Created but versioning state failed".format( + module.params["name"] + ) + ) except Exception: + blade.buckets.update_buckets( + names=[module.params["name"]], bucket=BucketPatch(destroyed=True) + ) + blade.buckets.delete_buckets(names=[module.params["name"]]) module.fail_json( msg="Object Store Bucket {0}: Creation failed".format( module.params["name"] ) ) + if MODE_VERSION in api_version: + if not module.params["block_new_public_policies"]: + module.params["block_new_public_policies"] = False + if not module.params["block_public_access"]: + module.params["block_public_access"] = False + pac = BucketPatch( + public_access_config=flashblade.PublicAccessConfig( + block_new_public_policies=module.params[ + "block_new_public_policies" + ], + block_public_access=module.params["block_public_access"], + ) + ) + res = bladev2.patch_buckets(bucket=pac, names=[module.params["name"]]) + if res.status_code != 200: + module.warn( + msg="Failed to set Public Access config correctly for bucket {0}. " + "Error: {1}".format(module.params["name"], res.errors[0].message) + ) module.exit_json(changed=changed) @@ -272,13 +415,42 @@ def recover_bucket(module, blade): def update_bucket(module, blade, bucket): """Update Bucket""" changed = False + change_pac = False + bladev2 = get_system(module) + bucket_detail = list(bladev2.get_buckets(names=[module.params["name"]]).items)[0] api_version = blade.api_version.list_versions().versions if VSO_VERSION in api_version: - if module.params["mode"]: - bladev2 = get_system(module) - bucket_detail = bladev2.get_buckets(names=[module.params["name"]]) - if list(bucket_detail.items)[0].bucket_type != module.params["mode"]: - module.warn("Changing bucket type is not permitted.") + if module.params["mode"] and bucket_detail.bucket_type != module.params["mode"]: + module.warn("Changing bucket type is not permitted.") + if QUOTA_VERSION in api_version: + if ( + bucket_detail.retention_lock == "ratcheted" + and getattr( + bucket_detail.object_lock_config, "default_retention_mode", None + ) + == "compliance" + and module.params["retention_mode"] != "compliance" + ): + module.warn( + "Changing retention_mode can onlt be performed by Pure Technical Support." + ) + if not module.params["object_lock_enabled"] and getattr( + bucket_detail.object_lock_config, "enabled", False + ): + module.warn("Object lock cannot be disabled.") + if not module.params["freeze_locked_objects"] and getattr( + bucket_detail.object_lock_config, "freeze_locked_objects", False + ): + module.warn("Freeze locked onjects cannot be disabled.") + if getattr(bucket_detail.object_lock_config, "default_retention", 0) > 1: + if ( + bucket_detail.object_lock_config.default_retention / 86400000 + > int(module.params["default_retention"]) + and bucket_detail.retention_lock == "ratcheted" + ): + module.warn( + "Default retention can only be reduced by Pure Technical Support." + ) if VERSIONING_VERSION in api_version: if bucket.versioning != "none": @@ -316,7 +488,39 @@ def update_bucket(module, blade, bucket): module.params["name"] ) ) - module.exit_json(changed=changed) + if MODE_VERSION in api_version: + current_pac = { + "block_new_public_policies": bucket_detail.public_access_config.block_new_public_policies, + "block_public_access": bucket_detail.public_access_config.block_public_access, + } + if module.params["block_new_public_policies"] is None: + new_public_policies = current_pac["block_new_public_policies"] + else: + new_public_policies = module.params["block_new_public_policies"] + if module.params["block_public_access"] is None: + new_public_access = current_pac["block_public_access"] + else: + new_public_access = module.params["block_public_access"] + new_pac = { + "block_new_public_policies": new_public_policies, + "block_public_access": new_public_access, + } + if current_pac != new_pac: + change_pac = True + pac = BucketPatch( + public_access_config=flashblade.PublicAccessConfig( + block_new_public_policies=new_pac.block_new_public_policies, + block_public_access=new_pac.block_public_access, + ) + ) + if change_pac and not module.check_mode: + res = bladev2.patch_buckets(bucket=pac, names=[module.params["name"]]) + if res.status_code != 200: + module.fail_json( + msg="Failed to update Public Access config correctly for bucket {0}. " + "Error: {1}".format(module.params["name"], res.errors[0].message) + ) + module.exit_json(changed=(changed or change_pac)) def eradicate_bucket(module, blade): @@ -341,7 +545,21 @@ def main(): name=dict(required=True), account=dict(required=True), eradicate=dict(default="false", type="bool"), - mode=dict(type="str", choices=["classic", "multi-site"]), + mode=dict( + type="str", + choices=["classic", "multi-site-writable"], + ), + retention_mode=dict(type="str", choices=["compliance", "governance", ""]), + default_retention=dict(type="str"), + retention_lock=dict( + type="str", choices=["ratcheted", "unlocked"], default="unlocked" + ), + hard_limit=dict(type="bool"), + block_new_public_policies=dict(type="bool"), + block_public_access=dict(type="bool"), + object_lock_enabled=dict(type="bool", default=False), + freeze_locked_objects=dict(type="bool", default=False), + quota=dict(type="str"), versioning=dict( default="absent", choices=["enabled", "suspended", "absent"] ), @@ -362,9 +580,13 @@ def main(): api_version = blade.api_version.list_versions().versions if MIN_REQUIRED_API_VERSION not in api_version: module.fail_json(msg="Purity//FB must be upgraded to support this module.") - if module.params["mode"] and VSO_VERSION not in api_version: - module.fail_json(msg="VSO mode requires Purity//FB 3.3.3 or higher.") + # From REST 2.12 classic is no longer the default mode + if MODE_VERSION in api_version: + if not module.params["mode"]: + module.params["mode"] = "multi-site-writable" + elif not module.params["mode"]: + module.params["mode"] = "classic" bucket = get_bucket(module, blade) if not get_s3acc(module, blade): module.fail_json( diff --git a/ansible_collections/purestorage/flashblade/plugins/modules/purefb_bucket_replica.py b/ansible_collections/purestorage/flashblade/plugins/modules/purefb_bucket_replica.py index 6ac3775ae..265fd5481 100644 --- a/ansible_collections/purestorage/flashblade/plugins/modules/purefb_bucket_replica.py +++ b/ansible_collections/purestorage/flashblade/plugins/modules/purefb_bucket_replica.py @@ -58,6 +58,14 @@ options: - Name of remote credential name to use. required: false type: str + cascading: + description: + - Objects replicated to this bucket via a replica link from + another array will also be replicated by this link to the + remote bucket + type: bool + default: false + version_added: "1.14.0" extends_documentation_fragment: - purestorage.flashblade.purestorage.fb """ @@ -96,11 +104,19 @@ try: except ImportError: HAS_PURITY_FB = False +HAS_PYPURECLIENT = True +try: + from pypureclient import flashblade +except ImportError: + HAS_PYPURECLIENT = False + MIN_REQUIRED_API_VERSION = "1.9" +CASCADE_API_VERSION = "2.2" from ansible.module_utils.basic import AnsibleModule from ansible_collections.purestorage.flashblade.plugins.module_utils.purefb import ( get_blade, + get_system, purefb_argument_spec, ) @@ -167,24 +183,46 @@ def get_connected(module, blade): def create_rl(module, blade, remote_cred): """Create Bucket Replica Link""" changed = True + api_version = blade.api_version.list_versions().versions if not module.check_mode: - try: - if not module.params["target_bucket"]: - module.params["target_bucket"] = module.params["name"] - else: - module.params["target_bucket"] = module.params["target_bucket"].lower() - blade.bucket_replica_links.create_bucket_replica_links( + if not module.params["target_bucket"]: + module.params["target_bucket"] = module.params["name"] + else: + module.params["target_bucket"] = module.params["target_bucket"].lower() + if CASCADE_API_VERSION in api_version: + bladev2 = get_system(module) + new_rl = flashblade.BucketReplicaLinkPost( + cascading_enabled=module.params["cascading"], + paused=module.params["paused"], + ) + res = bladev2.post_bucket_replica_links( local_bucket_names=[module.params["name"]], remote_bucket_names=[module.params["target_bucket"]], remote_credentials_names=[remote_cred.name], - bucket_replica_link=BucketReplicaLink(paused=module.params["paused"]), + bucket_replica_link=new_rl, ) - except Exception: - module.fail_json( - msg="Failed to create bucket replica link {0}.".format( - module.params["name"] + if res.status_code != 200: + module.fail_json( + msg="Failed to create bucket replica link {0}.".format( + module.params["name"] + ) + ) + else: + try: + blade.bucket_replica_links.create_bucket_replica_links( + local_bucket_names=[module.params["name"]], + remote_bucket_names=[module.params["target_bucket"]], + remote_credentials_names=[remote_cred.name], + bucket_replica_link=BucketReplicaLink( + paused=module.params["paused"] + ), + ) + except Exception: + module.fail_json( + msg="Failed to create bucket replica link {0}.".format( + module.params["name"] + ) ) - ) module.exit_json(changed=changed) @@ -245,6 +283,7 @@ def main(): target=dict(type="str"), target_bucket=dict(type="str"), paused=dict(type="bool", default=False), + cascading=dict(type="bool", default=False), credential=dict(type="str"), state=dict(default="present", choices=["present", "absent"]), ) diff --git a/ansible_collections/purestorage/flashblade/plugins/modules/purefb_connect.py b/ansible_collections/purestorage/flashblade/plugins/modules/purefb_connect.py index 508c6a322..846351453 100644 --- a/ansible_collections/purestorage/flashblade/plugins/modules/purefb_connect.py +++ b/ansible_collections/purestorage/flashblade/plugins/modules/purefb_connect.py @@ -117,7 +117,9 @@ RETURN = r""" HAS_PURITYFB = True try: - from purity_fb import PurityFb, ArrayConnection, ArrayConnectionPost + from purity_fb import PurityFb + from purity_fb import ArrayConnection as ArrayConnectionv1 + from purity_fb import ArrayConnectionPost as ArrayConnectionPostv1 except ImportError: HAS_PURITYFB = False @@ -224,7 +226,7 @@ def create_connection(module, blade): .items[0] .connection_key ) - connection_info = ArrayConnectionPost( + connection_info = ArrayConnectionPostv1( management_address=module.params["target_url"], encrypted=module.params["encrypted"], connection_key=connection_key, @@ -346,7 +348,7 @@ def update_connection(module, blade, target_blade): module.fail_json( msg="Cannot turn array connection encryption on if file system replica links exist" ) - new_attr = ArrayConnection(encrypted=module.params["encrypted"]) + new_attr = ArrayConnectionv1(encrypted=module.params["encrypted"]) changed = True if not module.check_mode: try: diff --git a/ansible_collections/purestorage/flashblade/plugins/modules/purefb_ds.py b/ansible_collections/purestorage/flashblade/plugins/modules/purefb_ds.py index 6433d3d9d..2a81648e5 100644 --- a/ansible_collections/purestorage/flashblade/plugins/modules/purefb_ds.py +++ b/ansible_collections/purestorage/flashblade/plugins/modules/purefb_ds.py @@ -67,6 +67,15 @@ options: description: - Sets the password of the bind_user user name account. type: str + force_bind_password: + type: bool + default: true + description: + - Will force the bind password to be reset even if the bind user password + is unchanged. + - If set to I(false) and I(bind_user) is unchanged the password will not + be reset. + version_added: 1.16.0 bind_user: description: - Sets the user name that can be used to bind to and query the directory. @@ -257,6 +266,8 @@ def delete_ds(module, blade): def update_ds(module, blade): """Update Directory Service""" mod_ds = False + changed = False + password_required = False attr = {} try: ds_now = blade.directory_services.list_directory_services( @@ -278,21 +289,31 @@ def update_ds(module, blade): if sorted(module.params["uri"][0:30]) != sorted(ds_now.uris): attr["uris"] = module.params["uri"][0:30] mod_ds = True + password_required = True if module.params["base_dn"]: if module.params["base_dn"] != ds_now.base_dn: attr["base_dn"] = module.params["base_dn"] mod_ds = True if module.params["bind_user"]: if module.params["bind_user"] != ds_now.bind_user: + password_required = True attr["bind_user"] = module.params["bind_user"] mod_ds = True + elif module.params["force_bind_password"]: + password_required = True + mod_ds = True if module.params["enable"]: if module.params["enable"] != ds_now.enabled: attr["enabled"] = module.params["enable"] mod_ds = True - if module.params["bind_password"]: - attr["bind_password"] = module.params["bind_password"] - mod_ds = True + if password_required: + if module.params["bind_password"]: + attr["bind_password"] = module.params["bind_password"] + mod_ds = True + else: + module.fail_json( + msg="'bind_password' must be provided for this task" + ) if module.params["dstype"] == "smb": if module.params["join_ou"] != ds_now.smb.join_ou: attr["smb"] = {"join_ou": module.params["join_ou"]} @@ -397,6 +418,7 @@ def main(): state=dict(type="str", default="present", choices=["absent", "present"]), enable=dict(type="bool", default=False), bind_password=dict(type="str", no_log=True), + force_bind_password=dict(type="bool", default=True), bind_user=dict(type="str"), base_dn=dict(type="str"), join_ou=dict(type="str"), diff --git a/ansible_collections/purestorage/flashblade/plugins/modules/purefb_fs.py b/ansible_collections/purestorage/flashblade/plugins/modules/purefb_fs.py index a07180793..8d332e8b7 100644 --- a/ansible_collections/purestorage/flashblade/plugins/modules/purefb_fs.py +++ b/ansible_collections/purestorage/flashblade/plugins/modules/purefb_fs.py @@ -179,6 +179,27 @@ options: - Only valid for Purity//FB 3.3.0 or higher type: str version_added: "1.9.0" + share_policy: + description: + - Name of SMB share policy to assign to filesystem + - Only valid with REST 2.10 or higher + - Remove policy with empty string + type: str + version_added: "1.12.0" + client_policy: + description: + - Name of SMB client policy to assign to filesystem + - Only valid with REST 2.10 or higher + - Remove policy with empty string + type: str + version_added: "1.12.0" + continuous_availability: + description: + - Deifines if the file system will be continuously available during + disruptive scenarios such as network disruption, blades failover, etc + type: bool + default: true + version_added: "1.15.0" extends_documentation_fragment: - purestorage.flashblade.purestorage.fb """ @@ -267,6 +288,7 @@ try: FileSystemPatch, NfsPatch, Reference, + Smb, ) except ImportError: HAS_PYPURECLIENT = False @@ -290,6 +312,8 @@ NFSV4_API_VERSION = "1.6" REPLICATION_API_VERSION = "1.9" MULTIPROTOCOL_API_VERSION = "1.11" EXPORT_POLICY_API_VERSION = "2.3" +SMB_POLICY_API_VERSION = "2.10" +CA_API_VERSION = "2.12" def get_fs(module, blade): @@ -488,12 +512,71 @@ def create_fs(module, blade): res.errors[0].message, ) ) + if SMB_POLICY_API_VERSION in api_version: + system = get_system(module) + if module.params["client_policy"]: + export_attr = FileSystemPatch( + smb=Smb( + client_policy=Reference(name=module.params["client_policy"]) + ) + ) + res = system.patch_file_systems( + names=[module.params["name"]], file_system=export_attr + ) + if res.status_code != 200: + module.fail_json( + msg="Filesystem {0} created, but failed to assign client " + "policy {1}. Error: {2}".format( + module.params["name"], + module.params["client_policy"], + res.errors[0].message, + ) + ) + if module.params["share_policy"]: + export_attr = FileSystemPatch( + smb=Smb(share_policy=Reference(name=module.params["share_policy"])) + ) + res = system.patch_file_systems( + names=[module.params["name"]], file_system=export_attr + ) + if res.status_code != 200: + module.fail_json( + msg="Filesystem {0} created, but failed to assign share " + "policy {1}. Error: {2}".format( + module.params["name"], + module.params["share_policy"], + res.errors[0].message, + ) + ) + if CA_API_VERSION in api_version: + ca_attr = FileSystemPatch( + smb=Smb( + continuous_availability_enabled=module.params[ + "continuous_availability" + ] + ) + ) + res = system.patch_file_systems( + names=[module.params["name"]], file_system=ca_attr + ) + if res.status_code != 200: + module.fail_json( + msg="Filesystem {0} created, but failed to set continuous availability" + "Error: {1}".format( + module.params["name"], + res.errors[0].message, + ) + ) + module.exit_json(changed=changed) def modify_fs(module, blade): """Modify Filesystem""" changed = False + change_export = False + change_share = False + change_ca = False mod_fs = False attr = {} if module.params["policy"] and module.params["policy_state"] == "present": @@ -689,7 +772,7 @@ def modify_fs(module, blade): module.params["name"] ) ) - attr["requested_promotion_state"] = module.params["promote"] + attr["requested_promotion_state"] = "demoted" mod_fs = True if mod_fs: changed = True @@ -721,12 +804,12 @@ def modify_fs(module, blade): module.params["name"], message ) ) + system = get_system(module) + current_fs = list( + system.get_file_systems(filter="name='" + module.params["name"] + "'").items + )[0] if EXPORT_POLICY_API_VERSION in api_version and module.params["export_policy"]: - system = get_system(module) change_export = False - current_fs = list( - system.get_file_systems(filter="name='" + module.params["name"] + "'").items - )[0] if ( current_fs.nfs.export_policy.name and current_fs.nfs.export_policy.name != module.params["export_policy"] @@ -752,8 +835,84 @@ def modify_fs(module, blade): res.errors[0].message, ) ) + if SMB_POLICY_API_VERSION in api_version and module.params["client_policy"]: + change_client = False + if ( + current_fs.smb.client_policy.name + and current_fs.smb.client_policy.name != module.params["client_policy"] + ): + change_client = True + if not current_fs.smb.client_policy.name and module.params["client_policy"]: + change_client = True + if change_client and not module.check_mode: + client_attr = FileSystemPatch( + smb=Smb(client_policy=Reference(name=module.params["client_policy"])) + ) + res = system.patch_file_systems( + names=[module.params["name"]], file_system=client_attr + ) + if res.status_code != 200: + module.fail_json( + msg="Failed to modify client policy {1} for " + "filesystem {0}. Error: {2}".format( + module.params["name"], + module.params["client_policy"], + res.errors[0].message, + ) + ) + if SMB_POLICY_API_VERSION in api_version and module.params["share_policy"]: + change_share = False + if ( + current_fs.smb.share_policy.name + and current_fs.smb.share_policy.name != module.params["share_policy"] + ): + change_share = True + if not current_fs.smb.share_policy.name and module.params["share_policy"]: + change_share = True + if change_share and not module.check_mode: + share_attr = FileSystemPatch( + smb=Smb(share_policy=Reference(name=module.params["share_policy"])) + ) + res = system.patch_file_systems( + names=[module.params["name"]], file_system=share_attr + ) + if res.status_code != 200: + module.fail_json( + msg="Failed to modify share policy {1} for " + "filesystem {0}. Error: {2}".format( + module.params["name"], + module.params["share_policy"], + res.errors[0].message, + ) + ) + if CA_API_VERSION in api_version: + change_ca = False + if ( + module.params["continuous_availability"] + != current_fs.continuous_availability_enabled + ): + change_ca = True + if not module.check_mode: + ca_attr = FileSystemPatch( + smb=Smb( + continuous_availability_enabled=module.params[ + "continuous_availability" + ] + ) + ) + res = system.patch_file_systems( + names=[module.params["name"]], file_system=ca_attr + ) + if res.status_code != 200: + module.fail_json( + msg="Failed to modify continuous availability for " + "filesystem {0}. Error: {1}".format( + module.params["name"], + res.errors[0].message, + ) + ) - module.exit_json(changed=changed) + module.exit_json(changed=(changed or change_export or change_share or change_ca)) def _delete_fs(module, blade): @@ -910,6 +1069,9 @@ def main(): ), size=dict(type="str"), export_policy=dict(type="str"), + share_policy=dict(type="str"), + client_policy=dict(type="str"), + continuous_availability=dict(type="bool", default="true"), ) ) diff --git a/ansible_collections/purestorage/flashblade/plugins/modules/purefb_fs_replica.py b/ansible_collections/purestorage/flashblade/plugins/modules/purefb_fs_replica.py index f96903788..ca52a64bd 100644 --- a/ansible_collections/purestorage/flashblade/plugins/modules/purefb_fs_replica.py +++ b/ansible_collections/purestorage/flashblade/plugins/modules/purefb_fs_replica.py @@ -53,6 +53,12 @@ options: - Name of filesystem snapshot policy to apply to the replica link. required: false type: str + in_progress: + description: + - Confirmation that you wish to delete a filesystem replica link + - This may cancel any in-progress replication transfers) + type: bool + default: false extends_documentation_fragment: - purestorage.flashblade.purestorage.fb """ @@ -97,9 +103,12 @@ MIN_REQUIRED_API_VERSION = "1.9" from ansible.module_utils.basic import AnsibleModule from ansible_collections.purestorage.flashblade.plugins.module_utils.purefb import ( get_blade, + get_system, purefb_argument_spec, ) +DELETE_RL_API_VERSION = "2.10" + def get_local_fs(module, blade): """Return Filesystem or None""" @@ -241,6 +250,30 @@ def delete_rl_policy(module, blade): module.exit_json(changed=changed) +def delete_rl(module, blade): + """Delete filesystem replica link""" + changed = True + if not module.check_mode: + res = list( + blade.delete_file_system_replica_links( + local_file_system_names=[module.params["name"]], + remote_file_system_names=[module.params["target_fs"]], + remote_names=[module.params["target_array"]], + cancel_in_progress_transfers=module.params["in_progress"], + ) + ) + if res.status_code != 200: + module.fail_json( + msg="Failed to delete replica link from {0} to {1}:{2}. Error: {3}".format( + module.params["name"], + module.params["target_array"], + module.params["target_fs"], + res.errors[0].message, + ) + ) + module.exit_jsob(changed=changed) + + def main(): argument_spec = purefb_argument_spec() argument_spec.update( @@ -249,6 +282,7 @@ def main(): target_fs=dict(type="str"), target_array=dict(type="str"), policy=dict(type="str"), + in_progress=dict(type="bool", default=False), state=dict(default="present", choices=["present", "absent"]), ) ) @@ -296,6 +330,12 @@ def main(): policy = None if state == "present" and not local_replica_link: create_rl(module, blade) + elif state == "absent" and local_replica_link: + if DELETE_RL_API_VERSION not in versions: + module.fail_json("Deleting a replica link requires REST 2.10 or higher") + else: + bladev6 = get_system(module) + delete_rl(module, bladev6) elif state == "present" and local_replica_link and policy: add_rl_policy(module, blade) elif state == "absent" and policy: diff --git a/ansible_collections/purestorage/flashblade/plugins/modules/purefb_hardware.py b/ansible_collections/purestorage/flashblade/plugins/modules/purefb_hardware.py new file mode 100644 index 000000000..49849156b --- /dev/null +++ b/ansible_collections/purestorage/flashblade/plugins/modules/purefb_hardware.py @@ -0,0 +1,187 @@ +#!/usr/bin/python +# -*- coding: utf-8 -*- + +# (c) 2021, Simon Dodsley (simon@purestorage.com) +# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) + +from __future__ import absolute_import, division, print_function + +__metaclass__ = type + +ANSIBLE_METADATA = { + "metadata_version": "1.1", + "status": ["preview"], + "supported_by": "community", +} + +DOCUMENTATION = r""" +--- +module: purefb_hardware +version_added: '1.15.0' +short_description: Manage FlashBlade Hardware +description: +- Enable or disable FlashBlade visual identification lights and set connector parameters +author: +- Pure Storage Ansible Team (@sdodsley) <pure-ansible-team@purestorage.com> +options: + name: + description: + - Name of hardware component + type: str + required: true + enabled: + description: + - State of the component identification LED + type: bool + speed: + description: + - If the component specified is a connector, set the configured speed + of each lane in the connector in gigabits-per-second + type: int + choices: [ 10, 25, 40 ] + ports: + description: + - If the component specificed is a connector, the number of configured + ports in the connector + type: int + choices: [ 1, 4 ] +extends_documentation_fragment: +- purestorage.flashblade.purestorage.fb +""" + +EXAMPLES = r""" +- name: Set connector to be 4 x 40Gb ports + purestorage.flashblade.purefb_hardware: + name: "CH1.FM1.ETH1" + speed: 40 + ports: 4 + fb_url: 10.10.10.2 + api_token: T-68618f31-0c9e-4e57-aa44-5306a2cf10e3 + +- name: Enable identification LED + purestorage.flashblade.purefb_hardware: + name: "CH1.FB1" + enabled: True + fb_url: 10.10.10.2 + api_token: T-68618f31-0c9e-4e57-aa44-5306a2cf10e3 + +- name: Disable identification LED + purestorage.flashblade.purefb_hardware: + name: "CH1.FB1" + enabled: False + fb_url: 10.10.10.2 + api_token: T-68618f31-0c9e-4e57-aa44-5306a2cf10e3 +""" + +RETURN = r""" +""" + +HAS_PURESTORAGE = True +try: + from pypureclient import flashblade +except ImportError: + HAS_PURESTORAGE = False + +from ansible.module_utils.basic import AnsibleModule +from ansible_collections.purestorage.flashblade.plugins.module_utils.purefb import ( + get_system, + purefb_argument_spec, +) + +MIN_REQUIRED_API_VERSION = "2.2" + + +def main(): + argument_spec = purefb_argument_spec() + argument_spec.update( + dict( + enabled=dict(type="bool"), + name=dict(type="str", required=True), + speed=dict( + type="int", + choices=[10, 25, 40], + ), + ports=dict( + type="int", + choices=[1, 4], + ), + ) + ) + + module = AnsibleModule(argument_spec, supports_check_mode=True) + if not HAS_PURESTORAGE: + module.fail_json(msg="py-pure-client sdk is required for this module") + + blade = get_system(module) + api_version = list(blade.get_versions().items) + + if MIN_REQUIRED_API_VERSION not in api_version: + module.fail_json( + msg="FlashBlade REST version not supported. " + "Minimum version required: {0}".format(MIN_REQUIRED_API_VERSION) + ) + if module.params["speed"]: + speed = module.params["speed"] * 1000000000 + changed = False + change_connector = False + hardware = None + res = blade.get_hardware(names=[module.params["name"]]) + if res.status_code == 200: + hardware = list(res.items)[0] + if hardware.identify_enabled != module.params["enabled"]: + changed = True + if not module.check_mode: + res = blade.patch_hardware( + names=[module.params["name"]], + hardware=flashblade.Hardware( + identify_enabled=module.params["enabled"] + ), + ) + if res.status_code != 200: + module.fail_json( + msg="Failed to set identification LED for {0}. Error: {1}".format( + module.params["name"], res.errors[0].message + ) + ) + res = blade.get_hardware_connectors(names=[module.params["name"]]) + if res.status_code == 200: + if res.status_code == 200: + connector = list(res.items)[0] + if connector.port_count != module.params["ports"]: + new_port = module.params["ports"] + changed = True + if not module.check_mode: + res = blade.patch_hardware_connectors( + names=[module.params["name"]], + hardware_connector=flashblade.HardwareConnector( + port_count=module.params["ports"] + ), + ) + if res.status_code != 200: + module.fail_json( + msg="Failed to change connector port count {0}. Error: Invalid port count".format( + module.params["name"] + ) + ) + if connector.lane_speed != speed: + new_speed = speed + changed = True + if not module.check_mode: + res = blade.patch_hardware_connectors( + names=[module.params["name"]], + hardware_connector=flashblade.HardwareConnector( + lane_speed=speed + ), + ) + if res.status_code != 200: + module.fail_json( + msg="Failed to change connector lane speed {0}. Error: Invalid lane speed".format( + module.params["name"] + ) + ) + + module.exit_json(changed=changed) + + +if __name__ == "__main__": + main() diff --git a/ansible_collections/purestorage/flashblade/plugins/modules/purefb_info.py b/ansible_collections/purestorage/flashblade/plugins/modules/purefb_info.py index 8525bd8e3..033312e82 100644 --- a/ansible_collections/purestorage/flashblade/plugins/modules/purefb_info.py +++ b/ansible_collections/purestorage/flashblade/plugins/modules/purefb_info.py @@ -81,357 +81,7 @@ RETURN = r""" purefb_info: description: Returns the information collected from the FlashBlade returned: always - type: complex - sample: { - "admins": { - "pureuser": { - "api_token_timeout": null, - "local": true, - "public_key": null - }, - "another_user": { - "api_token_timeout": null, - "local": false, - "public_key": null - }, - }, - "buckets": { - "central": { - "account_name": "jake", - "bucket_type": "classic", - "created": 1628900154000, - "data_reduction": null, - "destroyed": false, - "id": "43758f09-9e71-7bf7-5757-2028a95a2b65", - "lifecycle_rules": {}, - "object_count": 0, - "snapshot_space": 0, - "time_remaining": null, - "total_physical_space": 0, - "unique_space": 0, - "versioning": "none", - "virtual_space": 0 - }, - "test": { - "account_name": "acme", - "bucket_type": "classic", - "created": 1630591952000, - "data_reduction": 3.6, - "destroyed": false, - "id": "d5f6149c-fbef-f3c5-58b6-8fd143110ba9", - "lifecycle_rules": { - "test": { - "abort_incomplete_multipart_uploads_after (days)": 1, - "cleanup_expired_object_delete_marker": true, - "enabled": true, - "keep_current_version_for (days)": null, - "keep_current_version_until": "2023-12-21", - "keep_previous_version_for (days)": null, - "prefix": "foo" - } - }, - }, - }, - "capacity": { - "aggregate": { - "data_reduction": 1.1179228, - "snapshots": 0, - "total_physical": 17519748439, - "unique": 17519748439, - "virtual": 19585726464 - }, - "file-system": { - "data_reduction": 1.3642412, - "snapshots": 0, - "total_physical": 4748219708, - "unique": 4748219708, - "virtual": 6477716992 - }, - "object-store": { - "data_reduction": 1.0263462, - "snapshots": 0, - "total_physical": 12771528731, - "unique": 12771528731, - "virtual": 6477716992 - }, - "total": 83359896948925 - }, - "config": { - "alert_watchers": { - "enabled": true, - "name": "notify@acmestorage.com" - }, - "array_management": { - "base_dn": null, - "bind_password": null, - "bind_user": null, - "enabled": false, - "name": "management", - "services": [ - "management" - ], - "uris": [] - }, - "directory_service_roles": { - "array_admin": { - "group": null, - "group_base": null - }, - "ops_admin": { - "group": null, - "group_base": null - }, - "readonly": { - "group": null, - "group_base": null - }, - "storage_admin": { - "group": null, - "group_base": null - } - }, - "dns": { - "domain": "demo.acmestorage.com", - "name": "demo-fb-1", - "nameservers": [ - "8.8.8.8" - ], - "search": [ - "demo.acmestorage.com" - ] - }, - "nfs_directory_service": { - "base_dn": null, - "bind_password": null, - "bind_user": null, - "enabled": false, - "name": "nfs", - "services": [ - "nfs" - ], - "uris": [] - }, - "ntp": [ - "0.ntp.pool.org" - ], - "smb_directory_service": { - "base_dn": null, - "bind_password": null, - "bind_user": null, - "enabled": false, - "name": "smb", - "services": [ - "smb" - ], - "uris": [] - }, - "smtp": { - "name": "demo-fb-1", - "relay_host": null, - "sender_domain": "acmestorage.com" - }, - "ssl_certs": { - "certificate": "-----BEGIN CERTIFICATE-----\n\n-----END CERTIFICATE-----", - "common_name": "Acme Storage", - "country": "US", - "email": null, - "intermediate_certificate": null, - "issued_by": "Acme Storage", - "issued_to": "Acme Storage", - "key_size": 4096, - "locality": null, - "name": "global", - "organization": "Acme Storage", - "organizational_unit": "Acme Storage", - "passphrase": null, - "private_key": null, - "state": null, - "status": "self-signed", - "valid_from": "1508433967000", - "valid_to": "2458833967000" - } - }, - "default": { - "blades": 15, - "buckets": 7, - "filesystems": 2, - "flashblade_name": "demo-fb-1", - "object_store_accounts": 1, - "object_store_users": 1, - "purity_version": "2.2.0", - "snapshots": 1, - "total_capacity": 83359896948925, - "smb_mode": "native" - }, - "filesystems": { - "k8s-pvc-d24b1357-579e-11e8-811f-ecf4bbc88f54": { - "default_group_quota": 0, - "default_user_quota": 0, - "destroyed": false, - "fast_remove": false, - "hard_limit": true, - "nfs_rules": "10.21.255.0/24(rw,no_root_squash)", - "provisioned": 21474836480, - "snapshot_enabled": false - }, - "z": { - "default_group_quota": 0, - "default_user_quota": 0, - "destroyed": false, - "fast_remove": false, - "hard_limit": false, - "provisioned": 1073741824, - "snapshot_enabled": false - } - }, - "lag": { - "uplink": { - "lag_speed": 0, - "port_speed": 40000000000, - "ports": [ - { - "name": "CH1.FM1.ETH1.1" - }, - { - "name": "CH1.FM1.ETH1.2" - }, - ], - "status": "healthy" - } - }, - "network": { - "fm1.admin0": { - "address": "10.10.100.6", - "gateway": "10.10.100.1", - "mtu": 1500, - "netmask": "255.255.255.0", - "services": [ - "support" - ], - "type": "vip", - "vlan": 2200 - }, - "fm2.admin0": { - "address": "10.10.100.7", - "gateway": "10.10.100.1", - "mtu": 1500, - "netmask": "255.255.255.0", - "services": [ - "support" - ], - "type": "vip", - "vlan": 2200 - }, - "nfs1": { - "address": "10.10.100.4", - "gateway": "10.10.100.1", - "mtu": 1500, - "netmask": "255.255.255.0", - "services": [ - "data" - ], - "type": "vip", - "vlan": 2200 - }, - "vir0": { - "address": "10.10.100.5", - "gateway": "10.10.100.1", - "mtu": 1500, - "netmask": "255.255.255.0", - "services": [ - "management" - ], - "type": "vip", - "vlan": 2200 - } - }, - "performance": { - "aggregate": { - "bytes_per_op": 0, - "bytes_per_read": 0, - "bytes_per_write": 0, - "read_bytes_per_sec": 0, - "reads_per_sec": 0, - "usec_per_other_op": 0, - "usec_per_read_op": 0, - "usec_per_write_op": 0, - "write_bytes_per_sec": 0, - "writes_per_sec": 0 - }, - "http": { - "bytes_per_op": 0, - "bytes_per_read": 0, - "bytes_per_write": 0, - "read_bytes_per_sec": 0, - "reads_per_sec": 0, - "usec_per_other_op": 0, - "usec_per_read_op": 0, - "usec_per_write_op": 0, - "write_bytes_per_sec": 0, - "writes_per_sec": 0 - }, - "nfs": { - "bytes_per_op": 0, - "bytes_per_read": 0, - "bytes_per_write": 0, - "read_bytes_per_sec": 0, - "reads_per_sec": 0, - "usec_per_other_op": 0, - "usec_per_read_op": 0, - "usec_per_write_op": 0, - "write_bytes_per_sec": 0, - "writes_per_sec": 0 - }, - "s3": { - "bytes_per_op": 0, - "bytes_per_read": 0, - "bytes_per_write": 0, - "read_bytes_per_sec": 0, - "reads_per_sec": 0, - "usec_per_other_op": 0, - "usec_per_read_op": 0, - "usec_per_write_op": 0, - "write_bytes_per_sec": 0, - "writes_per_sec": 0 - } - }, - "snapshots": { - "z.188": { - "destroyed": false, - "source": "z", - "source_destroyed": false, - "suffix": "188" - } - }, - "subnet": { - "new-mgmt": { - "gateway": "10.10.100.1", - "interfaces": [ - { - "name": "fm1.admin0" - }, - { - "name": "fm2.admin0" - }, - { - "name": "nfs1" - }, - { - "name": "vir0" - } - ], - "lag": "uplink", - "mtu": 1500, - "prefix": "10.10.100.0/24", - "services": [ - "data", - "management", - "support" - ], - "vlan": 2200 - } - } - } + type: dict """ @@ -458,6 +108,9 @@ VSO_VERSION = "2.4" DRIVES_API_VERSION = "2.5" SECURITY_API_VERSION = "2.7" BUCKET_API_VERSION = "2.8" +SMB_CLIENT_API_VERSION = "2.10" +SPACE_API_VERSION = "2.11" +PUBLIC_API_VERSION = "2.12" def _millisecs_to_time(millisecs): @@ -711,12 +364,12 @@ def generate_config_dict(blade): "engine_id": snmp_agents.items[agent].engine_id, } if config_info["snmp_agents"][agent_name]["version"] == "v3": - config_info["snmp_agents"][agent_name][ - "auth_protocol" - ] = snmp_agents.items[agent].v3.auth_protocol - config_info["snmp_agents"][agent_name][ - "privacy_protocol" - ] = snmp_agents.items[agent].v3.privacy_protocol + config_info["snmp_agents"][agent_name]["auth_protocol"] = ( + snmp_agents.items[agent].v3.auth_protocol + ) + config_info["snmp_agents"][agent_name]["privacy_protocol"] = ( + snmp_agents.items[agent].v3.privacy_protocol + ) config_info["snmp_agents"][agent_name]["user"] = snmp_agents.items[ agent ].v3.user @@ -730,12 +383,12 @@ def generate_config_dict(blade): "notification": snmp_managers.items[manager].notification, } if config_info["snmp_managers"][mgr_name]["version"] == "v3": - config_info["snmp_managers"][mgr_name][ - "auth_protocol" - ] = snmp_managers.items[manager].v3.auth_protocol - config_info["snmp_managers"][mgr_name][ - "privacy_protocol" - ] = snmp_managers.items[manager].v3.privacy_protocol + config_info["snmp_managers"][mgr_name]["auth_protocol"] = ( + snmp_managers.items[manager].v3.auth_protocol + ) + config_info["snmp_managers"][mgr_name]["privacy_protocol"] = ( + snmp_managers.items[manager].v3.privacy_protocol + ) config_info["snmp_managers"][mgr_name]["user"] = snmp_managers.items[ manager ].v3.user @@ -920,33 +573,77 @@ def generate_network_dict(blade): return net_info -def generate_capacity_dict(blade): +def generate_capacity_dict(module, blade): capacity_info = {} - total_cap = blade.arrays.list_arrays_space() - file_cap = blade.arrays.list_arrays_space(type="file-system") - object_cap = blade.arrays.list_arrays_space(type="object-store") - capacity_info["total"] = total_cap.items[0].capacity - capacity_info["aggregate"] = { - "data_reduction": total_cap.items[0].space.data_reduction, - "snapshots": total_cap.items[0].space.snapshots, - "total_physical": total_cap.items[0].space.total_physical, - "unique": total_cap.items[0].space.unique, - "virtual": total_cap.items[0].space.virtual, - } - capacity_info["file-system"] = { - "data_reduction": file_cap.items[0].space.data_reduction, - "snapshots": file_cap.items[0].space.snapshots, - "total_physical": file_cap.items[0].space.total_physical, - "unique": file_cap.items[0].space.unique, - "virtual": file_cap.items[0].space.virtual, - } - capacity_info["object-store"] = { - "data_reduction": object_cap.items[0].space.data_reduction, - "snapshots": object_cap.items[0].space.snapshots, - "total_physical": object_cap.items[0].space.total_physical, - "unique": object_cap.items[0].space.unique, - "virtual": file_cap.items[0].space.virtual, - } + api_version = blade.api_version.list_versions().versions + if SPACE_API_VERSION in api_version: + blade2 = get_system(module) + total_cap = list(blade2.get_arrays_space().items)[0] + file_cap = list(blade2.get_arrays_space(type="file-system").items)[0] + object_cap = list(blade2.get_arrays_space(type="object-store").items)[0] + capacity_info["total"] = total_cap.space.capacity + capacity_info["aggregate"] = { + "data_reduction": total_cap.space.data_reduction, + "snapshots": total_cap.space.snapshots, + "total_physical": total_cap.space.total_physical, + "unique": total_cap.space.unique, + "virtual": total_cap.space.virtual, + "total_provisioned": total_cap.space.total_provisioned, + "available_provisioned": total_cap.space.available_provisioned, + "available_ratio": total_cap.space.available_ratio, + "destroyed": total_cap.space.destroyed, + "destroyed_virtual": total_cap.space.destroyed_virtual, + } + capacity_info["file-system"] = { + "data_reduction": file_cap.space.data_reduction, + "snapshots": file_cap.space.snapshots, + "total_physical": file_cap.space.total_physical, + "unique": file_cap.space.unique, + "virtual": file_cap.space.virtual, + "total_provisioned": total_cap.space.total_provisioned, + "available_provisioned": total_cap.space.available_provisioned, + "available_ratio": total_cap.space.available_ratio, + "destroyed": total_cap.space.destroyed, + "destroyed_virtual": total_cap.space.destroyed_virtual, + } + capacity_info["object-store"] = { + "data_reduction": object_cap.space.data_reduction, + "snapshots": object_cap.space.snapshots, + "total_physical": object_cap.space.total_physical, + "unique": object_cap.space.unique, + "virtual": file_cap.space.virtual, + "total_provisioned": total_cap.space.total_provisioned, + "available_provisioned": total_cap.space.available_provisioned, + "available_ratio": total_cap.space.available_ratio, + "destroyed": total_cap.space.destroyed, + "destroyed_virtual": total_cap.space.destroyed_virtual, + } + else: + total_cap = blade.arrays.list_arrays_space() + file_cap = blade.arrays.list_arrays_space(type="file-system") + object_cap = blade.arrays.list_arrays_space(type="object-store") + capacity_info["total"] = total_cap.items[0].capacity + capacity_info["aggregate"] = { + "data_reduction": total_cap.items[0].space.data_reduction, + "snapshots": total_cap.items[0].space.snapshots, + "total_physical": total_cap.items[0].space.total_physical, + "unique": total_cap.items[0].space.unique, + "virtual": total_cap.items[0].space.virtual, + } + capacity_info["file-system"] = { + "data_reduction": file_cap.items[0].space.data_reduction, + "snapshots": file_cap.items[0].space.snapshots, + "total_physical": file_cap.items[0].space.total_physical, + "unique": file_cap.items[0].space.unique, + "virtual": file_cap.items[0].space.virtual, + } + capacity_info["object-store"] = { + "data_reduction": object_cap.items[0].space.data_reduction, + "snapshots": object_cap.items[0].space.snapshots, + "total_physical": object_cap.items[0].space.total_physical, + "unique": object_cap.items[0].space.unique, + "virtual": file_cap.items[0].space.virtual, + } return capacity_info @@ -973,6 +670,17 @@ def generate_snap_dict(blade): snap_info[snapshot]["source_location"] = snaps.items[ snap ].source_location.name + snap_info[snapshot]["policies"] = [] + if PUBLIC_API_VERSION in api_version: + for policy in range(0, len(snaps.items[snap].policies)): + snap_info[snapshot]["policies"].append( + { + "name": snaps.items[snap].policies[policy].name, + "location": snaps.items[snap] + .policies[policy] + .location.name, + } + ) return snap_info @@ -1065,6 +773,19 @@ def generate_bucket_dict(module, blade): "total_physical_space": buckets.items[bckt].space.total_physical, "unique_space": buckets.items[bckt].space.unique, "virtual_space": buckets.items[bckt].space.virtual, + "total_provisioned_space": getattr( + buckets.items[bckt].space, "total_provisioned", None + ), + "available_provisioned_space": getattr( + buckets.items[bckt].space, "available_provisioned", None + ), + "available_ratio": getattr( + buckets.items[bckt].space, "available_ratio", None + ), + "destroyed_space": getattr(buckets.items[bckt].space, "destroyed", None), + "destroyed_virtual_space": getattr( + buckets.items[bckt].space, "destroyed_virtual", None + ), "created": buckets.items[bckt].created, "destroyed": buckets.items[bckt].destroyed, "time_remaining": buckets.items[bckt].time_remaining, @@ -1139,6 +860,19 @@ def generate_bucket_dict(module, blade): bucket ].object_lock_config.freeze_locked_objects, } + if buckets[bucket].object_lock_config.enabled: + bucket_info[buckets[bucket].name]["object_lock_config"][ + "default_retention" + ] = getattr( + buckets[bucket].object_lock_config, "default_retention", "" + ) + bucket_info[buckets[bucket].name]["object_lock_config"][ + "default_retention_mode" + ] = getattr( + buckets[bucket].object_lock_config, + "default_retention_mode", + "", + ) bucket_info[buckets[bucket].name]["eradication_config"] = { "eradication_delay": buckets[ bucket @@ -1147,6 +881,19 @@ def generate_bucket_dict(module, blade): bucket ].eradication_config.manual_eradication, } + if PUBLIC_API_VERSION in api_version: + bucket_info[buckets[bucket].name]["public_status"] = buckets[ + bucket + ].public_status + bucket_info[buckets[bucket].name]["public_access_config"] = { + "block_new_public_policies": buckets[ + bucket + ].public_access_config.block_new_public_policies, + "block_public_access": buckets[ + bucket + ].public_access_config.block_public_access, + } + return bucket_info @@ -1181,10 +928,50 @@ def generate_ad_dict(blade): "service_principals": ad_account.service_principal_names, "join_ou": ad_account.join_ou, "encryption_types": ad_account.encryption_types, + "global_catalog_servers": getattr( + ad_account, "global_catalog_servers", None + ), } return ad_info +def generate_bucket_access_policies_dict(blade): + policies_info = {} + policies = list(blade.get_buckets_bucket_access_policies().items) + for policy in range(0, len(policies)): + policy_name = policies[policy].name + policies_info[policy_name] = { + "description": policies[policy].description, + "enabled": policies[policy].enabled, + "local": policies[policy].is_local, + "rules": [], + } + for rule in range(0, len(policies[policy].rules)): + policies_info[policy_name]["rules"].append( + { + "actions": policies[policy].rules[rule].actions, + "resources": policies[policy].rules[rule].resources, + "all_principals": policies[policy].rules[rule].principals.all, + "effect": policies[policy].rules[rule].effect, + "name": policies[policy].rules[rule].name, + } + ) + return policies_info + + +def generate_bucket_cross_object_policies_dict(blade): + policies_info = {} + policies = list(blade.get_buckets_cross_origin_resource_sharing_policies().items) + for policy in range(0, len(policies)): + policy_name = policies[policy].name + policies_info[policy_name] = { + "allowed_headers": policies[policy].allowed_headers, + "allowed_methods": policies[policy].allowed_methods, + "allowed_origins": policies[policy].allowed_origins, + } + return policies_info + + def generate_object_store_access_policies_dict(blade): policies_info = {} policies = list(blade.get_object_store_access_policies().items) @@ -1247,6 +1034,45 @@ def generate_nfs_export_policies_dict(blade): return policies_info +def generate_smb_client_policies_dict(blade): + policies_info = {} + policies = list(blade.get_smb_client_policies().items) + for policy in range(0, len(policies)): + policy_name = policies[policy].name + policies_info[policy_name] = { + "local": policies[policy].is_local, + "enabled": policies[policy].enabled, + "version": policies[policy].version, + "rules": [], + } + for rule in range(0, len(policies[policy].rules)): + policies_info[policy_name]["rules"].append( + { + "name": policies[policy].rules[rule].name, + "change": getattr(policies[policy].rules[rule], "change", None), + "full_control": getattr( + policies[policy].rules[rule], "full_control", None + ), + "principal": getattr( + policies[policy].rules[rule], "principal", None + ), + "read": getattr(policies[policy].rules[rule], "read", None), + "client": getattr(policies[policy].rules[rule], "client", None), + "index": getattr(policies[policy].rules[rule], "index", None), + "policy_version": getattr( + policies[policy].rules[rule], "policy_version", None + ), + "encryption": getattr( + policies[policy].rules[rule], "encryption", None + ), + "permission": getattr( + policies[policy].rules[rule], "permission", None + ), + } + ) + return policies_info + + def generate_object_store_accounts_dict(blade): account_info = {} accounts = list(blade.get_object_store_accounts().items) @@ -1259,6 +1085,19 @@ def generate_object_store_accounts_dict(blade): "total_physical_space": accounts[account].space.total_physical, "unique_space": accounts[account].space.unique, "virtual_space": accounts[account].space.virtual, + "total_provisioned_space": getattr( + accounts[account].space, "total_provisioned", None + ), + "available_provisioned_space": getattr( + accounts[account].space, "available_provisioned", None + ), + "available_ratio": getattr( + accounts[account].space, "available_ratio", None + ), + "destroyed_space": getattr(accounts[account].space, "destroyed", None), + "destroyed_virtual_space": getattr( + accounts[account].space, "destroyed_virtual", None + ), "quota_limit": getattr(accounts[account], "quota_limit", None), "hard_limit_enabled": getattr( accounts[account], "hard_limit_enabled", None @@ -1277,6 +1116,17 @@ def generate_object_store_accounts_dict(blade): } except AttributeError: pass + try: + account_info[acc_name]["public_access_config"] = { + "block_new_public_policies": accounts[ + account + ].public_access_config.block_new_public_policies, + "block_public_access": accounts[ + account + ].public_access_config.block_public_access, + } + except AttributeError: + pass acc_users = list( blade.get_object_store_users(filter='name="' + acc_name + '/*"').items ) @@ -1413,6 +1263,24 @@ def generate_fs_dict(module, blade): "quota": fs_user_quotas[user_quota].quota, "usage": fs_user_quotas[user_quota].usage, } + if PUBLIC_API_VERSION in api_version: + for v2fs in range(0, len(fsys_v2)): + if fsys_v2[v2fs].name == share: + fs_info[share]["smb_client_policy"] = getattr( + fsys_v2[v2fs].smb.client_policy, "name", None + ) + fs_info[share]["smb_share_policy"] = getattr( + fsys_v2[v2fs].smb.share_policy, "name", None + ) + fs_info[share]["smb_continuous_availability_enabled"] = fsys_v2[ + v2fs + ].smb.continuous_availability_enabled + fs_info[share]["multi_protocol_access_control_style"] = getattr( + fsys_v2[v2fs].multi_protocol, "access_control_style", None + ) + fs_info[share]["multi_protocol_safeguard_acls"] = fsys_v2[ + v2fs + ].multi_protocol.safeguard_acls return fs_info @@ -1433,6 +1301,7 @@ def generate_drives_dict(blade): "raw_capacity": getattr(drives[drive], "raw_capacity", None), "status": getattr(drives[drive], "status", None), "details": getattr(drives[drive], "details", None), + "type": getattr(drives[drive], "type", None), } return drives_info @@ -1495,7 +1364,7 @@ def main(): if "config" in subset or "all" in subset: info["config"] = generate_config_dict(blade) if "capacity" in subset or "all" in subset: - info["capacity"] = generate_capacity_dict(blade) + info["capacity"] = generate_capacity_dict(module, blade) if "lags" in subset or "all" in subset: info["lag"] = generate_lag_dict(blade) if "network" in subset or "all" in subset: @@ -1537,8 +1406,17 @@ def main(): info["access_policies"] = generate_object_store_access_policies_dict( blade ) + if PUBLIC_API_VERSION in api_version: + info["bucket_access_policies"] = generate_bucket_access_policies_dict( + blade + ) + info["bucket_cross_origin_policies"] = ( + generate_bucket_cross_object_policies_dict(blade) + ) if NFS_POLICY_API_VERSION in api_version: info["export_policies"] = generate_nfs_export_policies_dict(blade) + if SMB_CLIENT_API_VERSION in api_version: + info["share_policies"] = generate_smb_client_policies_dict(blade) if "drives" in subset or "all" in subset and DRIVES_API_VERSION in api_version: info["drives"] = generate_drives_dict(blade) module.exit_json(changed=False, purefb_info=info) diff --git a/ansible_collections/purestorage/flashblade/plugins/modules/purefb_inventory.py b/ansible_collections/purestorage/flashblade/plugins/modules/purefb_inventory.py index b17bc3f9e..1ef96f870 100644 --- a/ansible_collections/purestorage/flashblade/plugins/modules/purefb_inventory.py +++ b/ansible_collections/purestorage/flashblade/plugins/modules/purefb_inventory.py @@ -47,65 +47,7 @@ RETURN = r""" purefb_inventory: description: Returns the inventory information for the FlashBlade returned: always - type: complex - sample: { - "blades": { - "CH1.FB1": { - "model": "FB-17TB", - "serial": "PPCXA1942AFF5", - "slot": 1, - "status": "healthy" - } - }, - "chassis": { - "CH1": { - "index": 1, - "model": null, - "serial": "PMPAM163402AE", - "slot": null, - "status": "healthy" - } - }, - "controllers": {}, - "ethernet": { - "CH1.FM1.ETH1": { - "model": "624410002", - "serial": "APF16360021PRV", - "slot": 1, - "speed": 40000000000, - "status": "healthy" - } - }, - "fans": { - "CH1.FM1.FAN1": { - "slot": 1, - "status": "healthy" - } - }, - "modules": { - "CH1.FM1": { - "model": "EFM-110", - "serial": "PSUFS1640002C", - "slot": 1, - "status": "healthy" - }, - "CH1.FM2": { - "model": "EFM-110", - "serial": "PSUFS1640004A", - "slot": 2, - "status": "healthy" - } - }, - "power": { - "CH1.PWR1": { - "model": "DS1600SPE-3", - "serial": "M0500E00D8AJZ", - "slot": 1, - "status": "healthy" - } - }, - "switch": {} - } + type: dict """ diff --git a/ansible_collections/purestorage/flashblade/plugins/modules/purefb_lag.py b/ansible_collections/purestorage/flashblade/plugins/modules/purefb_lag.py index e5c46e730..8bf3ce48a 100644 --- a/ansible_collections/purestorage/flashblade/plugins/modules/purefb_lag.py +++ b/ansible_collections/purestorage/flashblade/plugins/modules/purefb_lag.py @@ -163,16 +163,19 @@ def update_lag(module, blade): ) new_ports = [] for port in range(0, len(module.params["ports"])): - new_ports.append( - module.params["ports"][port].split(".")[0].upper() - + ".FM1." - + module.params["ports"][port].split(".")[1].upper() - ) - new_ports.append( - module.params["ports"][port].split(".")[0].upper() - + ".FM2." - + module.params["ports"][port].split(".")[1].upper() - ) + if module.params["ports"][port].split(".")[0].upper()[0] != "X": + new_ports.append( + module.params["ports"][port].split(".")[0].upper() + + ".FM1." + + module.params["ports"][port].split(".")[1].upper() + ) + new_ports.append( + module.params["ports"][port].split(".")[0].upper() + + ".FM2." + + module.params["ports"][port].split(".")[1].upper() + ) + else: + new_ports.append(module.params["ports"][port].upper()) ports = [] for final_port in range(0, len(new_ports)): ports.append(flashblade.FixedReference(name=new_ports[final_port])) diff --git a/ansible_collections/purestorage/flashblade/plugins/modules/purefb_policy.py b/ansible_collections/purestorage/flashblade/plugins/modules/purefb_policy.py index 273166de8..ebe70aa48 100644 --- a/ansible_collections/purestorage/flashblade/plugins/modules/purefb_policy.py +++ b/ansible_collections/purestorage/flashblade/plugins/modules/purefb_policy.py @@ -52,7 +52,7 @@ options: - Type of policy default: snapshot type: str - choices: [ snapshot, access, nfs ] + choices: [ snapshot, access, nfs, smb_share, smb_client ] version_added: "1.9.0" account: description: @@ -72,7 +72,7 @@ options: Rules are additive. type: str default: allow - choices: [ allow ] + choices: [ allow, deny ] version_added: "1.9.0" actions: description: @@ -83,6 +83,7 @@ options: choices: - s3:* - s3:AbortMultipartUpload + - s3:BypassGovernanceRetention - s3:CreateBucket - s3:DeleteBucket - s3:DeleteObject @@ -94,7 +95,12 @@ options: - s3:GetLifecycleConfiguration - s3:GetObject - s3:GetObjectAcl + - s3:GetObjectLegalHold + - s3:GetObjectLockConfiguration + - s3:GetObjectRetention + - s3:GetObjectTagging - s3:GetObjectVersion + - s3:GetObjectVersionTagging - s3:ListAllMyBuckets - s3:ListBucket - s3:ListBucketMultipartUploads @@ -103,6 +109,10 @@ options: - s3:PutBucketVersioning - s3:PutLifecycleConfiguration - s3:PutObject + - s3:PutObjectLegalHold + - s3:PutObjectLockConfiguration + - s3:PutObjectRetention + - s3:ResolveSafemodeConflicts version_added: "1.9.0" object_resources: description: @@ -213,7 +223,7 @@ options: description: - Any user whose UID is affected by an I(access) of `root_squash` or `all_squash` will have their UID mapped to anonuid. - The defaultis null, which means 65534. + The default is null, which means 65534. Use "" to clear. type: str version_added: "1.9.0" @@ -241,7 +251,6 @@ options: - Accepted notation is a single IP address, subnet in CIDR notation, netgroup, or anonymous (*). type: str - default: "*" version_added: "1.9.0" fileid_32bit: description: @@ -284,8 +293,8 @@ options: version_added: "1.9.0" rename: description: - - New name for export policy - - Only applies to NFS export policies + - New name for policy + - Only applies to NFS and SMB policies type: str version_added: "1.10.0" destroy_snapshots: @@ -294,6 +303,47 @@ options: type: bool version_added: '1.11.0' default: false + principal: + description: + - The user or group who is the subject of this rule, and their domain + type: str + version_added: '1.12.0' + change: + description: + - The state of the SMB share principals Change access permission. + - Setting to "" will clear the current setting + type: str + choices: [ allow, deny, "" ] + version_added: '1.12.0' + read: + description: + - The state of the SMB share principals Read access permission. + - Setting to "" will clear the current setting + type: str + choices: [ allow, deny, "" ] + version_added: '1.12.0' + full_control: + description: + - The state of the SMB share principals Full Control access permission. + - Setting to "" will clear the current setting + type: str + choices: [ allow, deny, "" ] + version_added: '1.12.0' + smb_encryption: + description: + - The status of SMB encryption in a client policy rule + type: str + choices: [ disabled, optional, required ] + default: optional + version_added: '1.12.0' + desc: + description: + - A description of an object store policy, + optionally specified when the policy is created. + - Cannot be modified for an existing policy. + type: str + default: "" + version_added: '1.14.0' extends_documentation_fragment: - purestorage.flashblade.purestorage.fb """ @@ -359,6 +409,20 @@ EXAMPLES = r""" object_resources: "*" fb_url: 10.10.10.2 api_token: T-9f276a18-50ab-446e-8a0c-666a3529a1b6 +- name: Create an empty SMB client policy + purestorage.flashblade.purefb_policy: + name: test_smb_client + policy_type: smb_client + fb_url: 10.10.10.2 + api_token: T-9f276a18-50ab-446e-8a0c-666a3529a1b6 +- name: Create an SMB client policy with a client rule + purestorage.flashblade.purefb_policy: + name: test_smb_client + policy_type: smb_client + client: "10.0.1.0/24" + permission: rw + fb_url: 10.10.10.2 + api_token: T-9f276a18-50ab-446e-8a0c-666a3529a1b6 - name: Create an empty NFS export policy purestorage.flashblade.purefb_policy: name: test_nfs_export @@ -460,7 +524,9 @@ RETURN = r""" HAS_PURITYFB = True try: - from purity_fb import Policy, PolicyRule, PolicyPatch + from purity_fb import Policy as Policyv1 + from purity_fb import PolicyRule as PolicyRulev1 + from purity_fb import PolicyPatch as PolicyPatchv1 except ImportError: HAS_PURITYFB = False @@ -473,7 +539,13 @@ try: NfsExportPolicy, NfsExportPolicyRule, Policy, + PolicyPatch, PolicyRule, + SmbSharePolicyRule, + SmbSharePolicy, + SmbClientPolicyRule, + SmbClientPolicy, + ObjectStoreAccessPolicyPost, ) except ImportError: HAS_PYPURECLIENT = False @@ -503,6 +575,8 @@ SNAPSHOT_POLICY_API_VERSION = "2.1" ACCESS_POLICY_API_VERSION = "2.2" NFS_POLICY_API_VERSION = "2.3" NFS_RENAME_API_VERSION = "2.4" +SMB_POLICY_API_VERSION = "2.10" +SMB_ENCRYPT_API_VERSION = "2.11" def _convert_to_millisecs(hour): @@ -596,6 +670,614 @@ def _get_local_tz(module, timezone="UTC"): return timezone +def delete_smb_share_policy(module, blade): + """Delete SMB Share Policy, or Rule + + If principal is provided then delete the principal rule if it exists. + """ + + changed = False + policy_delete = True + if module.params["principal"]: + policy_delete = False + prin_rule = blade.get_smb_share_policies_rules( + policy_names=[module.params["name"]], + filter="principal='" + module.params["principal"] + "'", + ) + if prin_rule.status_code == 200: + rule = list(prin_rule.items)[0] + changed = True + if not module.check_mode: + res = blade.delete_smb_share_policies_rules(names=[rule.name]) + if res.status_code != 200: + module.fail_json( + msg="Failed to delete rule for principal {0} in policy {1}. " + "Error: {2}".format( + module.params["principal"], + module.params["name"], + res.errors[0].message, + ) + ) + if policy_delete: + changed = True + if not module.check_mode: + res = blade.delete_smb_share_policies(names=[module.params["name"]]) + if res.status_code != 200: + module.fail_json( + msg="Failed to delete SMB share policy {0}. Error: {1}".format( + module.params["name"], res.errors[0].message + ) + ) + module.exit_json(changed=changed) + + +def rename_smb_share_policy(module, blade): + """Rename SMB Share Policy""" + + changed = True + if not module.check_mode: + res = blade.patch_smb_share_policies( + names=[module.params["name"]], + policy=SmbSharePolicy(name=module.params["rename"]), + ) + if res.status_code != 200: + module.fail_json( + msg="Failed to rename SMB share policy {0} to {1}. Error: {2}".format( + module.params["name"], + module.params["rename"], + res.errors[0].message, + ) + ) + module.exit_json(changed=changed) + + +def create_smb_share_policy(module, blade): + """Create SMB Share Policy""" + changed = True + if not module.check_mode: + res = blade.post_smb_share_policies(names=[module.params["name"]]) + if res.status_code != 200: + module.fail_json( + msg="Failed to create SMB share policy {0}.Error: {1}".format( + module.params["name"], res.errors[0].message + ) + ) + if not module.params["enabled"]: + res = blade.patch_smb_share_policies( + policy=SmbSharePolicy(enabled=False), names=[module.params["name"]] + ) + if res.status_code != 200: + blade.delete_smb_share_policies(names=[module.params["name"]]) + module.fail_json( + msg="Failed to create SMB share policy {0}.Error: {1}".format( + module.params["name"], res.errors[0].message + ) + ) + if not module.params["principal"]: + module.fail_json(msg="principal is required to create a new rule") + else: + rule = SmbSharePolicyRule( + principal=module.params["principal"], + change=module.params["change"], + read=module.params["read"], + full_control=module.params["full_control"], + ) + res = blade.post_smb_share_policies_rules( + policy_names=[module.params["name"]], + rule=rule, + ) + if res.status_code != 200: + module.fail_json( + msg="Failed to create rule for policy {0}. Error: {1}".format( + module.params["name"], + res.errors[0].message, + ) + ) + module.exit_json(changed=changed) + + +def update_smb_share_policy(module, blade): + """Update SMB Share Policy Rule""" + + changed = False + if module.params["principal"]: + current_policy_rule = blade.get_smb_share_policies_rules( + policy_names=[module.params["name"]], + filter="principal='" + module.params["principal"] + "'", + ) + if ( + current_policy_rule.status_code == 200 + and current_policy_rule.total_item_count == 0 + ): + rule = SmbSharePolicyRule( + principal=module.params["principal"], + change=module.params["change"], + read=module.params["read"], + full_control=module.params["full_control"], + ) + changed = True + if not module.check_mode: + if module.params["before_rule"]: + before_name = ( + module.params["name"] + "." + str(module.params["before_rule"]) + ) + res = blade.post_smb_share_policies_rules( + policy_names=[module.params["name"]], + rule=rule, + before_rule_name=before_name, + ) + else: + res = blade.post_smb_share_policies_rules( + policy_names=[module.params["name"]], + rule=rule, + ) + if res.status_code != 200: + module.fail_json( + msg="Failed to create rule for principal {0} " + "in policy {1}. Error: {2}".format( + module.params["principal"], + module.params["name"], + res.errors[0].message, + ) + ) + else: + rules = list(current_policy_rule.items) + cli_count = None + old_policy_rule = rules[0] + current_rule = { + "principal": sorted(old_policy_rule.principal), + "read": sorted(old_policy_rule.read), + "change": sorted(old_policy_rule.change), + "full_control": sorted(old_policy_rule.full_control), + } + if module.params["read"]: + if module.params["read"] == "": + new_read = "" + else: + new_read = module.params["read"] + else: + new_read = current_rule["read"] + if module.params["full_control"]: + if module.params["full_control"] == "": + new_full_control = "" + else: + new_full_control = module.params["full_control"] + else: + new_full_control = current_rule["full_control"] + if module.params["change"]: + if module.params["change"] == "": + new_change = "" + else: + new_change = module.params["change"] + else: + new_change = current_rule["change"] + if module.params["principal"]: + new_principal = module.params["principal"] + else: + new_principal = current_rule["principal"] + new_rule = { + "principal": new_principal, + "read": new_read, + "change": new_change, + "full_control": new_full_control, + } + if current_rule != new_rule: + changed = True + if not module.check_mode: + rule = SmbSharePolicyRule( + principal=module.params["principal"], + change=module.params["change"], + read=module.params["read"], + full_control=module.params["full_control"], + ) + res = blade.patch_smb_share_policies_rules( + names=[ + module.params["name"] + "." + str(old_policy_rule.index) + ], + rule=rule, + ) + if res.status_code != 200: + module.fail_json( + msg="Failed to update SMB share rule {0}. Error: {1}".format( + module.params["name"] + + "." + + str(old_policy_rule.index), + res.errors[0].message, + ) + ) + if ( + module.params["before_rule"] + and module.params["before_rule"] != old_policy_rule.index + ): + changed = True + if not module.check_mode: + before_name = ( + module.params["name"] + "." + str(module.params["before_rule"]) + ) + res = blade.patch_smb_share_policies_rules( + names=[ + module.params["name"] + "." + str(old_policy_rule.index) + ], + rule=SmbSharePolicyRule(), + before_rule_name=before_name, + ) + if res.status_code != 200: + module.fail_json( + msg="Failed to move SMB share rule {0}. Error: {1}".format( + module.params["name"] + + "." + + str(old_policy_rule.index), + res.errors[0].message, + ) + ) + current_policy = list( + blade.get_smb_share_policies(names=[module.params["name"]]).items + )[0] + if current_policy.enabled != module.params["enabled"]: + changed = True + if not module.check_mode: + res = blade.patch_smb_share_policies( + policy=SmbSharePolicy(enabled=module.params["enabled"]), + names=[module.params["name"]], + ) + if res.status_code != 200: + module.fail_json( + msg="Failed to change state of SMB share policy {0}.Error: {1}".format( + module.params["name"], res.errors[0].message + ) + ) + module.exit_json(changed=changed) + + +def delete_smb_client_policy(module, blade): + """Delete SMB CLient Policy, or Rule + + If client is provided then delete the client rule if it exists. + """ + + changed = False + policy_delete = True + if module.params["client"]: + policy_delete = False + res = blade.get_smb_client_policies_rules( + policy_names=[module.params["name"]], + filter="client='" + module.params["client"] + "'", + ) + if res.status_code == 200: + if res.total_item_count == 0: + pass + elif res.total_item_count == 1: + rule = list(res.items)[0] + if module.params["client"] == rule.client: + changed = True + if not module.check_mode: + res = blade.delete_smb_client_policies_rules(names=[rule.name]) + if res.status_code != 200: + module.fail_json( + msg="Failed to delete rule for client {0} in policy {1}. " + "Error: {2}".format( + module.params["client"], + module.params["name"], + res.errors[0].message, + ) + ) + else: + rules = list(res.items) + for cli in range(0, len(rules)): + if rules[cli].client == "*": + changed = True + if not module.check_mode: + res = blade.delete_smb_client_policies_rules( + names=[rules[cli].name] + ) + if res.status_code != 200: + module.fail_json( + msg="Failed to delete rule for client {0} in policy {1}. " + "Error: {2}".format( + module.params["client"], + module.params["name"], + res.errors[0].message, + ) + ) + if policy_delete: + changed = True + if not module.check_mode: + res = blade.delete_smb_client_policies(names=[module.params["name"]]) + if res.status_code != 200: + module.fail_json( + msg="Failed to delete SMB client policy {0}. Error: {1}".format( + module.params["name"], res.errors[0].message + ) + ) + module.exit_json(changed=changed) + + +def rename_smb_client_policy(module, blade): + """Rename SMB Client Policy""" + + changed = True + if not module.check_mode: + res = blade.patch_smb_client_policies( + names=[module.params["name"]], + policy=SmbClientPolicy(name=module.params["rename"]), + ) + if res.status_code != 200: + module.fail_json( + msg="Failed to rename SMB client policy {0} to {1}. Error: {2}".format( + module.params["name"], + module.params["rename"], + res.errors[0].message, + ) + ) + module.exit_json(changed=changed) + + +def create_smb_client_policy(module, blade): + """Create SMB Client Policy""" + changed = True + versions = blade.api_version.list_versions().versions + if not module.check_mode: + res = blade.post_smb_client_policies(names=[module.params["name"]]) + if res.status_code != 200: + module.fail_json( + msg="Failed to create SMB client policy {0}.Error: {1}".format( + module.params["name"], res.errors[0].message + ) + ) + if not module.params["enabled"]: + res = blade.patch_smb_client_policies( + policy=SmbClientPolicy(enabled=False), names=[module.params["name"]] + ) + if res.status_code != 200: + blade.delete_smb_client_policies(names=[module.params["name"]]) + module.fail_json( + msg="Failed to create SMB client policy {0}.Error: {1}".format( + module.params["name"], res.errors[0].message + ) + ) + if not module.params["client"]: + module.fail_json(msg="client is required to create a new rule") + else: + if SMB_ENCRYPT_API_VERSION in versions: + rule = SmbClientPolicyRule( + client=module.params["client"], + permission=module.params["permission"], + access=module.params["access"], + encryption=module.params["smb_encryption"], + ) + else: + rule = SmbClientPolicyRule( + client=module.params["client"], + access=module.params["access"], + permission=module.params["permission"], + ) + res = blade.post_smb_client_policies_rules( + policy_names=[module.params["name"]], + rule=rule, + ) + if res.status_code != 200: + module.fail_json( + msg="Failed to rule for policy {0}. Error: {1}".format( + module.params["name"], + res.errors[0].message, + ) + ) + module.exit_json(changed=changed) + + +def update_smb_client_policy(module, blade): + """Update SMB Client Policy Rule""" + + changed = False + versions = blade.api_version.list_versions().versions + if module.params["client"]: + current_policy_rule = blade.get_smb_client_policies_rules( + policy_names=[module.params["name"]], + filter="client='" + module.params["client"] + "'", + ) + if ( + current_policy_rule.status_code == 200 + and current_policy_rule.total_item_count == 0 + ): + if SMB_ENCRYPT_API_VERSION in versions: + rule = SmbClientPolicyRule( + client=module.params["client"], + permission=module.params["permission"], + access=module.params["access"], + encryption=module.params["smb_encryption"], + ) + else: + rule = SmbClientPolicyRule( + client=module.params["client"], + permission=module.params["permission"], + access=module.params["access"], + ) + changed = True + if not module.check_mode: + if module.params["before_rule"]: + before_name = ( + module.params["name"] + "." + str(module.params["before_rule"]) + ) + res = blade.post_smb_client_policies_rules( + policy_names=[module.params["name"]], + rule=rule, + before_rule_name=before_name, + ) + else: + res = blade.post_smb_client_policies_rules( + policy_names=[module.params["name"]], + rule=rule, + ) + if res.status_code != 200: + module.fail_json( + msg="Failed to create rule for client {0} " + "in policy {1}. Error: {2}".format( + module.params["client"], + module.params["name"], + res.errors[0].message, + ) + ) + else: + rules = list(current_policy_rule.items) + cli_count = None + done = False + if module.params["client"] == "*": + for cli in range(0, len(rules)): + if rules[cli].client == "*": + cli_count = cli + if not cli_count: + if SMB_ENCRYPT_API_VERSION in versions: + rule = SmbClientPolicyRule( + client=module.params["client"], + permission=module.params["permission"], + access=module.params["access"], + encryption=module.params["smb_encryption"], + ) + else: + rule = SmbClientPolicyRule( + client=module.params["client"], + permission=module.params["permission"], + access=module.params["access"], + ) + done = True + changed = True + if not module.check_mode: + if module.params["before_rule"]: + res = blade.post_smb_client_policies_rules( + policy_names=[module.params["name"]], + rule=rule, + before_rule_name=( + module.params["name"] + + "." + + str(module.params["before_rule"]), + ), + ) + else: + res = blade.post_smb_client_policies_rules( + policy_names=[module.params["name"]], + rule=rule, + ) + if res.status_code != 200: + module.fail_json( + msg="Failed to create rule for " + "client {0} in policy {1}. Error: {2}".format( + module.params["client"], + module.params["name"], + res.errors[0].message, + ) + ) + if not done: + old_policy_rule = rules[0] + if SMB_ENCRYPT_API_VERSION in versions: + current_rule = { + "client": sorted(old_policy_rule.client), + "permission": sorted(old_policy_rule.permission), + "encryption": old_policy_rule.encryption, + } + else: + current_rule = { + "client": sorted(old_policy_rule.client), + "permission": sorted(old_policy_rule.permission), + } + if SMB_ENCRYPT_API_VERSION in versions: + if module.params["smb_encryption"]: + new_encryption = module.params["smb_encryption"] + else: + new_encryption = current_rule["encryption"] + if module.params["permission"]: + new_permission = sorted(module.params["permission"]) + else: + new_permission = sorted(current_rule["permission"]) + if module.params["client"]: + new_client = sorted(module.params["client"]) + else: + new_client = sorted(current_rule["client"]) + if SMB_ENCRYPT_API_VERSION in versions: + new_rule = { + "client": new_client, + "permission": new_permission, + "encryption": new_encryption, + } + else: + new_rule = { + "client": new_client, + "permission": new_permission, + } + if current_rule != new_rule: + changed = True + if not module.check_mode: + if SMB_ENCRYPT_API_VERSION in versions: + rule = SmbClientPolicyRule( + client=module.params["client"], + permission=module.params["permission"], + encryption=module.params["smb_encryption"], + ) + else: + rule = SmbClientPolicyRule( + client=module.params["client"], + permission=module.params["permission"], + ) + res = blade.patch_smb_client_policies_rules( + names=[ + module.params["name"] + "." + str(old_policy_rule.index) + ], + rule=rule, + ) + if res.status_code != 200: + module.fail_json( + msg="Failed to update SMB client rule {0}. Error: {1}".format( + module.params["name"] + + "." + + str(old_policy_rule.index), + res.errors[0].message, + ) + ) + if ( + module.params["before_rule"] + and module.params["before_rule"] != old_policy_rule.index + ): + changed = True + if not module.check_mode: + before_name = ( + module.params["name"] + + "." + + str(module.params["before_rule"]) + ) + res = blade.patch_smb_client_policies_rules( + names=[ + module.params["name"] + "." + str(old_policy_rule.index) + ], + rule=SmbClientPolicyRule(), + before_rule_name=before_name, + ) + if res.status_code != 200: + module.fail_json( + msg="Failed to move SMB client rule {0}. Error: {1}".format( + module.params["name"] + + "." + + str(old_policy_rule.index), + res.errors[0].message, + ) + ) + current_policy = list( + blade.get_smb_client_policies(names=[module.params["name"]]).items + )[0] + if current_policy.enabled != module.params["enabled"]: + changed = True + if not module.check_mode: + res = blade.patch_smb_client_policies( + policy=SmbClientPolicy(enabled=module.params["enabled"]), + names=[module.params["name"]], + ) + if res.status_code != 200: + module.fail_json( + msg="Failed to change state of SMB client policy {0}.Error: {1}".format( + module.params["name"], res.errors[0].message + ) + ) + module.exit_json(changed=changed) + + def delete_nfs_policy(module, blade): """Delete NFS Export Policy, or Rule @@ -926,9 +1608,7 @@ def create_nfs_policy(module, blade): module.params["name"], res.errors[0].message ) ) - if not module.params["client"]: - module.fail_json(msg="client is required to create a new rule") - else: + if module.params["client"]: rule = NfsExportPolicyRule( client=module.params["client"], permission=module.params["permission"], @@ -1061,8 +1741,12 @@ def create_os_policy(module, blade): """Create Object Store Access Policy""" changed = True policy_name = module.params["account"] + "/" + module.params["name"] + versions = list(blade.get_versions().items) if not module.check_mode: - res = blade.post_object_store_access_policies(names=[policy_name]) + res = blade.post_object_store_access_policies( + names=[policy_name], + policy=ObjectStoreAccessPolicyPost(description=module.params["desc"]), + ) if res.status_code != 200: module.fail_json( msg="Failed to create access policy {0}.".format(policy_name) @@ -1078,11 +1762,19 @@ def create_os_policy(module, blade): s3_delimiters=module.params["s3_delimiters"], s3_prefixes=module.params["s3_prefixes"], ) - rule = PolicyRuleObjectAccessPost( - actions=module.params["actions"], - resources=module.params["object_resources"], - conditions=conditions, - ) + if SMB_ENCRYPT_API_VERSION in versions: + rule = PolicyRuleObjectAccessPost( + actions=module.params["actions"], + resources=module.params["object_resources"], + conditions=conditions, + effect=module.params["effect"], + ) + else: + rule = PolicyRuleObjectAccessPost( + actions=module.params["actions"], + resources=module.params["object_resources"], + conditions=conditions, + ) res = blade.post_object_store_access_policies_rules( policy_names=policy_name, names=[module.params["rule"]], @@ -1118,22 +1810,30 @@ def update_os_policy(module, blade): policy_names=[policy_name], names=[module.params["rule"]] ) if current_policy_rule.status_code != 200: - conditions = PolicyRuleObjectAccessCondition( - source_ips=module.params["source_ips"], - s3_delimiters=module.params["s3_delimiters"], - s3_prefixes=module.params["s3_prefixes"], - ) - rule = PolicyRuleObjectAccessPost( - actions=module.params["actions"], - resources=module.params["object_resources"], - conditions=conditions, - ) - res = blade.post_object_store_access_policies_rules( - policy_names=policy_name, - names=[module.params["rule"]], - enforce_action_restrictions=module.params["ignore_enforcement"], - rule=rule, - ) + changed = True + if not module.check_mode: + conditions = PolicyRuleObjectAccessCondition( + source_ips=module.params["source_ips"], + s3_delimiters=module.params["s3_delimiters"], + s3_prefixes=module.params["s3_prefixes"], + ) + rule = PolicyRuleObjectAccessPost( + actions=module.params["actions"], + resources=module.params["object_resources"], + conditions=conditions, + ) + res = blade.post_object_store_access_policies_rules( + policy_names=policy_name, + names=[module.params["rule"]], + enforce_action_restrictions=module.params["ignore_enforcement"], + rule=rule, + ) + if res.status_code != 200: + module.fail_json( + msg="Failed to create rule {0} in policy {1}. Error: {2}".format( + module.params["rule"], policy_name, res.errors[0].message + ) + ) else: old_policy_rule = list(current_policy_rule.items)[0] current_rule = { @@ -1500,10 +2200,10 @@ def create_policy(module, blade): msg="every parameter is out of range (300 to 34560000)" ) if module.params["at"]: - attr = Policy( + attr = Policyv1( enabled=module.params["enabled"], rules=[ - PolicyRule( + PolicyRulev1( keep_for=module.params["keep_for"] * 1000, every=module.params["every"] * 1000, at=_convert_to_millisecs(module.params["at"]), @@ -1512,17 +2212,17 @@ def create_policy(module, blade): ], ) else: - attr = Policy( + attr = Policyv1( enabled=module.params["enabled"], rules=[ - PolicyRule( + PolicyRulev1( keep_for=module.params["keep_for"] * 1000, every=module.params["every"] * 1000, ) ], ) else: - attr = Policy(enabled=module.params["enabled"]) + attr = Policyv1(enabled=module.params["enabled"]) blade.policies.create_policies(names=[module.params["name"]], policy=attr) except Exception: module.fail_json( @@ -1798,11 +2498,11 @@ def update_policy(module, blade, policy): changed = True if not module.check_mode: try: - attr = PolicyPatch() + attr = PolicyPatchv1() attr.enabled = module.params["enabled"] if at_time: attr.add_rules = [ - PolicyRule( + PolicyRulev1( keep_for=module.params["keep_for"] * 1000, every=module.params["every"] * 1000, at=at_time, @@ -1811,13 +2511,13 @@ def update_policy(module, blade, policy): ] else: attr.add_rules = [ - PolicyRule( + PolicyRulev1( keep_for=module.params["keep_for"] * 1000, every=module.params["every"] * 1000, ) ] attr.remove_rules = [ - PolicyRule( + PolicyRulev1( keep_for=current_policy["keep_for"] * 1000, every=current_policy["every"] * 1000, at=current_policy["at"], @@ -1842,7 +2542,9 @@ def main(): type="str", default="present", choices=["absent", "present", "copy"] ), policy_type=dict( - type="str", default="snapshot", choices=["snapshot", "access", "nfs"] + type="str", + default="snapshot", + choices=["snapshot", "access", "nfs", "smb_share", "smb_client"], ), enabled=dict(type="bool", default=True), timezone=dict(type="str"), @@ -1858,13 +2560,14 @@ def main(): rename=dict(type="str"), rule=dict(type="str"), user=dict(type="str"), - effect=dict(type="str", default="allow", choices=["allow"]), + effect=dict(type="str", default="allow", choices=["allow", "deny"]), actions=dict( type="list", elements="str", choices=[ "s3:*", "s3:AbortMultipartUpload", + "s3:BypassGovernanceRetention", "s3:CreateBucket", "s3:DeleteBucket", "s3:DeleteObject", @@ -1876,7 +2579,12 @@ def main(): "s3:GetLifecycleConfiguration", "s3:GetObject", "s3:GetObjectAcl", + "s3:GetObjectLegalHold", + "s3:GetObjectLockConfiguration", + "s3:GetObjectRetention", + "s3:GetObjectTagging", "s3:GetObjectVersion", + "s3:GetObjectVersionTagging", "s3:ListAllMyBuckets", "s3:ListBucket", "s3:ListBucketMultipartUploads", @@ -1885,6 +2593,10 @@ def main(): "s3:PutBucketVersioning", "s3:PutLifecycleConfiguration", "s3:PutObject", + "s3:PutObjectLegalHold", + "s3:PutObjectLockConfiguration", + "s3:PutObjectRetention", + "s3:ResolveSafemodeConflicts", ], ), object_resources=dict(type="list", elements="str"), @@ -1901,7 +2613,7 @@ def main(): anonuid=dict(type="str"), anongid=dict(type="str"), atime=dict(type="bool", default=True), - client=dict(type="str", default="*"), + client=dict(type="str"), fileid_32bit=dict(type="bool", default=False), permission=dict(type="str", choices=["rw", "ro"], default="ro"), secure=dict(type="bool", default=False), @@ -1913,6 +2625,16 @@ def main(): default=["sys"], ), before_rule=dict(type="int"), + principal=dict(type="str"), + change=dict(type="str", choices=["deny", "allow", ""]), + read=dict(type="str", choices=["deny", "allow", ""]), + full_control=dict(type="str", choices=["deny", "allow", ""]), + smb_encryption=dict( + type="str", + default="optional", + choices=["disabled", "optional", "required"], + ), + desc=dict(type="str", default=""), ) ) @@ -1920,6 +2642,8 @@ def main(): required_if = [ ["policy_type", "access", ["account", "name"]], ["policy_type", "nfs", ["name"]], + ["policy_type", "smb_client", ["name"]], + ["policy_type", "smb_share", ["name"]], ] module = AnsibleModule( @@ -2037,6 +2761,102 @@ def main(): create_nfs_policy(module, blade) elif state == "absent" and policy: delete_nfs_policy(module, blade) + elif module.params["policy_type"] == "smb_client": + if SMB_POLICY_API_VERSION not in versions: + module.fail_json( + msg=( + "Minimum FlashBlade REST version required: {0}".format( + SMB_POLICY_API_VERSION + ) + ) + ) + if not HAS_PYPURECLIENT: + module.fail_json(msg="py-pure-client sdk is required for this module") + blade = get_system(module) + try: + policy = list( + blade.get_smb_client_policies(names=[module.params["name"]]).items + )[0] + except AttributeError: + policy = None + if module.params["rename"]: + try: + new_policy = list( + blade.get_smb_client_policies(names=[module.params["rename"]]).items + )[0] + except AttributeError: + new_policy = None + if policy and state == "present" and not module.params["rename"]: + if module.params["before_rule"]: + res = blade.get_smb_client_policies_rules( + policy_names=[module.params["name"]], + names=[ + module.params["name"] + "." + str(module.params["before_rule"]) + ], + ) + if res.status_code != 200: + module.fail_json( + msg="Rule index {0} does not exist.".format( + module.params["before_rule"] + ) + ) + update_smb_client_policy(module, blade) + elif ( + state == "present" and module.params["rename"] and policy and not new_policy + ): + rename_smb_client_policy(module, blade) + elif state == "present" and not policy and not module.params["rename"]: + create_smb_client_policy(module, blade) + elif state == "absent" and policy: + delete_smb_client_policy(module, blade) + elif module.params["policy_type"] == "smb_share": + if SMB_POLICY_API_VERSION not in versions: + module.fail_json( + msg=( + "Minimum FlashBlade REST version required: {0}".format( + SMB_POLICY_API_VERSION + ) + ) + ) + if not HAS_PYPURECLIENT: + module.fail_json(msg="py-pure-client sdk is required for this module") + blade = get_system(module) + try: + policy = list( + blade.get_smb_share_policies(names=[module.params["name"]]).items + )[0] + except AttributeError: + policy = None + if module.params["rename"]: + try: + new_policy = list( + blade.get_smb_share_policies(names=[module.params["rename"]]).items + )[0] + except AttributeError: + new_policy = None + if policy and state == "present" and not module.params["rename"]: + if module.params["before_rule"]: + res = blade.get_smb_share_policies_rules( + policy_names=[module.params["name"]], + names=[ + module.params["name"] + "." + str(module.params["before_rule"]) + ], + ) + if res.status_code != 200: + module.fail_json( + msg="Rule index {0} does not exist.".format( + module.params["before_rule"] + ) + ) + update_smb_share_policy(module, blade) + elif ( + state == "present" and module.params["rename"] and policy and not new_policy + ): + rename_smb_share_policy(module, blade) + elif state == "present" and not policy and not module.params["rename"]: + create_smb_share_policy(module, blade) + elif state == "absent" and policy: + delete_smb_share_policy(module, blade) elif SNAPSHOT_POLICY_API_VERSION in versions: if not HAS_PYPURECLIENT: module.fail_json(msg="py-pure-client sdk is required for this module") diff --git a/ansible_collections/purestorage/flashblade/plugins/modules/purefb_s3acc.py b/ansible_collections/purestorage/flashblade/plugins/modules/purefb_s3acc.py index 034731994..33aa9a30f 100644 --- a/ansible_collections/purestorage/flashblade/plugins/modules/purefb_s3acc.py +++ b/ansible_collections/purestorage/flashblade/plugins/modules/purefb_s3acc.py @@ -38,6 +38,7 @@ options: quota: description: - The effective quota limit to be applied against the size of the account in bytes. + - Values can be entered as K, M, T or P - If set to '' (empty string), the account is unlimited in size. version_added: 1.11.0 type: str @@ -48,11 +49,11 @@ options: will still be sent if the account has a value set for I(quota_limit). version_added: 1.11.0 type: bool - default: false default_quota: description: - The value of this field will be used to configure the I(quota_limit) field of newly created buckets associated with this object store account, if the bucket creation does not specify its own value. + - Values can be entered as K, M, T or P - If set to '' (empty string), the bucket default is unlimited in size. version_added: 1.11.0 type: str @@ -62,13 +63,23 @@ options: associated with this object store account, if the bucket creation does not specify its own value. version_added: 1.11.0 type: bool - default: false + block_new_public_policies: + description: + - If set to true, adding bucket policies that grant public access to a bucket is not allowed. + type: bool + version_added: 1.15.0 + block_public_access: + description: + - If set to true, access to a bucket with a public policy is restricted to only authenticated + users within the account that bucket belongs to. + type: bool + version_added: 1.15.0 extends_documentation_fragment: - purestorage.flashblade.purestorage.fb """ EXAMPLES = r""" -- name: Crrate object store account foo (with no quotas) +- name: Create object store account foo (with no quotas) purestorage.flashblade.purefb_s3acc: name: foo fb_url: 10.10.10.2 @@ -97,11 +108,15 @@ RETURN = r""" HAS_PURESTORAGE = True try: - from pypureclient.flashblade import ObjectStoreAccountPatch, BucketDefaults + from pypureclient.flashblade import ( + ObjectStoreAccountPatch, + BucketDefaults, + PublicAccessConfig, + ) except ImportError: HAS_PURESTORAGE = False -from ansible.module_utils.basic import AnsibleModule +from ansible.module_utils.basic import AnsibleModule, human_to_bytes from ansible_collections.purestorage.flashblade.plugins.module_utils.purefb import ( get_blade, get_system, @@ -111,6 +126,7 @@ from ansible_collections.purestorage.flashblade.plugins.module_utils.purefb impo MIN_REQUIRED_API_VERSION = "1.3" QUOTA_API_VERSION = "2.1" +PUBLIC_API_VERSION = "2.12" def get_s3acc(module, blade): @@ -126,16 +142,28 @@ def get_s3acc(module, blade): def update_s3acc(module): """Update Object Store Account""" changed = False + public = False blade = get_system(module) acc_settings = list( blade.get_object_store_accounts(names=[module.params["name"]]).items )[0] - current_account = { - "hard_limit": acc_settings.hard_limit_enabled, - "default_hard_limit": acc_settings.bucket_defaults.hard_limit_enabled, - "quota": str(acc_settings.quota_limit), - "default_quota": str(acc_settings.bucket_defaults.quota_limit), - } + if getattr(acc_settings, "public_access_config", None): + public = True + current_account = { + "hard_limit": acc_settings.hard_limit_enabled, + "default_hard_limit": acc_settings.bucket_defaults.hard_limit_enabled, + "quota": str(acc_settings.quota_limit), + "default_quota": str(acc_settings.bucket_defaults.quota_limit), + "block_new_public_policies": acc_settings.public_access_config.block_new_public_policies, + "block_public_access": acc_settings.public_access_config.block_public_access, + } + else: + current_account = { + "hard_limit": acc_settings.hard_limit_enabled, + "default_hard_limit": acc_settings.bucket_defaults.hard_limit_enabled, + "quota": str(acc_settings.quota_limit), + "default_quota": str(acc_settings.bucket_defaults.quota_limit), + } if current_account["quota"] == "None": current_account["quota"] = "" if current_account["default_quota"] == "None": @@ -144,12 +172,48 @@ def update_s3acc(module): module.params["quota"] = current_account["quota"] if module.params["default_quota"] is None: module.params["default_quota"] = current_account["default_quota"] - new_account = { - "hard_limit": module.params["hard_limit"], - "default_hard_limit": module.params["default_hard_limit"], - "quota": module.params["quota"], - "default_quota": module.params["default_quota"], - } + if not module.params["default_quota"]: + module.params["default_quota"] = "" + if not module.params["quota"]: + quota = "" + else: + quota = str(human_to_bytes(module.params["quota"])) + if not module.params["default_quota"]: + default_quota = "" + else: + default_quota = str(human_to_bytes(module.params["default_quota"])) + if module.params["hard_limit"] is None: + hard_limit = current_account["hard_limit"] + else: + hard_limit = module.params["hard_limit"] + if module.params["default_hard_limit"] is None: + default_hard_limit = current_account["default_hard_limit"] + else: + default_hard_limit = module.params["default_hard_limit"] + if public: + if module.params["block_new_public_policies"] is None: + new_public_policies = current_account["block_new_public_policies"] + else: + new_public_policies = module.params["block_new_public_policies"] + if module.params["block_public_access"] is None: + public_access = current_account["block_public_access"] + else: + public_access = module.params["block_public_access"] + new_account = { + "hard_limit": hard_limit, + "default_hard_limit": default_hard_limit, + "quota": quota, + "default_quota": default_quota, + "block_new_public_policies": new_public_policies, + "block_public_access": public_access, + } + else: + new_account = { + "hard_limit": module.params["hard_limit"], + "default_hard_limit": module.params["default_hard_limit"], + "quota": quota, + "default_quota": default_quota, + } if new_account != current_account: changed = True if not module.check_mode: @@ -169,12 +233,14 @@ def update_s3acc(module): msg="Failed to update account {0}. " "Error: {1}".format(module.params["name"], res.errors[0].message) ) + module.exit_json(changed=changed) def create_s3acc(module, blade): """Create Object Store Account""" changed = True + versions = blade.api_version.list_versions().versions if not module.check_mode: try: blade.object_store_accounts.create_object_store_accounts( @@ -188,27 +254,26 @@ def create_s3acc(module, blade): ) if module.params["quota"] or module.params["default_quota"]: blade2 = get_system(module) - if module.params["quota"] and not module.params["default_quota"]: - osa = ObjectStoreAccountPatch( - hard_limit_enabled=module.params["hard_limit"], - quota_limit=module.params["quota"], - ) - if not module.params["quota"] and module.params["default_quota"]: - osa = ObjectStoreAccountPatch( - bucket_defaults=BucketDefaults( - hard_limit_enabled=module.params["default_hard_limit"], - quota_limit=module.params["default_quota"], - ) - ) + if not module.params["default_quota"]: + default_quota = "" else: - osa = ObjectStoreAccountPatch( - hard_limit_enabled=module.params["hard_limit"], - quota_limit=module.params["quota"], - bucket_defaults=BucketDefaults( - hard_limit_enabled=module.params["default_hard_limit"], - quota_limit=module.params["default_quota"], - ), - ) + default_quota = str(human_to_bytes(module.params["default_quota"])) + if not module.params["quota"]: + quota = "" + else: + quota = str(human_to_bytes(module.params["quota"])) + if not module.params["hard_limit"]: + module.params["hard_limit"] = False + if not module.params["default_hard_limit"]: + module.params["default_hard_limit"] = False + osa = ObjectStoreAccountPatch( + hard_limit_enabled=module.params["hard_limit"], + quota_limit=quota, + bucket_defaults=BucketDefaults( + hard_limit_enabled=module.params["default_hard_limit"], + quota_limit=default_quota, + ), + ) res = blade2.patch_object_store_accounts( object_store_account=osa, names=[module.params["name"]] ) @@ -220,6 +285,28 @@ def create_s3acc(module, blade): msg="Failed to set quotas correctly for account {0}. " "Error: {1}".format(module.params["name"], res.errors[0].message) ) + if PUBLIC_API_VERSION in versions: + if not module.params["block_new_public_policies"]: + module.params["block_new_public_policies"] = False + if not module.params["block_public_access"]: + module.params["block_public_access"] = False + osa = ObjectStoreAccountPatch( + public_access_config=PublicAccessConfig( + block_new_public_policies=module.params[ + "block_new_public_policies" + ], + block_public_access=module.params["block_public_access"], + ) + ) + res = blade2.patch_object_store_accounts( + object_store_account=osa, names=[module.params["name"]] + ) + if res.status_code != 200: + module.fail_json( + msg="Failed to Public Access config correctly for account {0}. " + "Error: {1}".format(module.params["name"], res.errors[0].message) + ) + module.exit_json(changed=changed) @@ -258,8 +345,10 @@ def main(): argument_spec.update( dict( name=dict(required=True, type="str"), - hard_limit=dict(type="bool", default=False), - default_hard_limit=dict(type="bool", default=False), + hard_limit=dict(type="bool"), + default_hard_limit=dict(type="bool"), + block_new_public_policies=dict(type="bool"), + block_public_access=dict(type="bool"), quota=dict(type="str"), default_quota=dict(type="str"), state=dict(default="present", choices=["present", "absent"]), diff --git a/ansible_collections/purestorage/flashblade/plugins/modules/purefb_s3user.py b/ansible_collections/purestorage/flashblade/plugins/modules/purefb_s3user.py index 55bc05c3f..1905184b1 100644 --- a/ansible_collections/purestorage/flashblade/plugins/modules/purefb_s3user.py +++ b/ansible_collections/purestorage/flashblade/plugins/modules/purefb_s3user.py @@ -48,6 +48,12 @@ options: - If enabled this will override I(imported_key) type: bool default: false + multiple_keys: + description: + - Allow multiple access keys to be created for the user. + type: bool + default: false + version_added: "1.12.0" remove_key: description: - Access key to be removed from user @@ -181,27 +187,29 @@ def update_s3user(module, blade): key_count += 1 if not exists: if key_count < 2: - changed = True - if not module.check_mode: - try: - if ( - module.params["access_key"] - and module.params["imported_key"] + try: + if module.params["access_key"] and module.params["imported_key"]: + module.warn("'access_key: true' overrides imported keys") + if module.params["access_key"]: + if key_count == 0 or ( + key_count >= 1 and module.params["multiple_keys"] ): - module.warn("'access_key: true' overrides imported keys") - if module.params["access_key"]: - result = blade.object_store_access_keys.create_object_store_access_keys( - object_store_access_key=ObjectStoreAccessKey( - user={"name": user} + changed = True + if not module.check_mode: + result = blade.object_store_access_keys.create_object_store_access_keys( + object_store_access_key=ObjectStoreAccessKey( + user={"name": user} + ) ) - ) - s3user_facts["fb_s3user"] = { - "user": user, - "access_key": result.items[0].secret_access_key, - "access_id": result.items[0].name, - } - else: - if IMPORT_KEY_API_VERSION in versions: + s3user_facts["fb_s3user"] = { + "user": user, + "access_key": result.items[0].secret_access_key, + "access_id": result.items[0].name, + } + else: + if IMPORT_KEY_API_VERSION in versions: + changed = True + if not module.check_mode: blade.object_store_access_keys.create_object_store_access_keys( names=[module.params["imported_key"]], object_store_access_key=ObjectStoreAccessKeyPost( @@ -211,19 +219,19 @@ def update_s3user(module, blade): ], ), ) - except Exception: - if module.params["imported_key"]: - module.fail_json( - msg="Object Store User {0}: Access Key import failed".format( - user - ) + except Exception: + if module.params["imported_key"]: + module.fail_json( + msg="Object Store User {0}: Access Key import failed".format( + user ) - else: - module.fail_json( - msg="Object Store User {0}: Access Key creation failed".format( - user - ) + ) + else: + module.fail_json( + msg="Object Store User {0}: Access Key creation failed".format( + user ) + ) else: module.warn( "Object Store User {0}: Maximum Access Key count reached".format( @@ -370,6 +378,7 @@ def main(): name=dict(required=True, type="str"), account=dict(required=True, type="str"), access_key=dict(default="false", type="bool"), + multiple_keys=dict(default="false", type="bool"), imported_key=dict(type="str", no_log=False), remove_key=dict(type="str", no_log=False), imported_secret=dict(type="str", no_log=True), diff --git a/ansible_collections/purestorage/flashblade/plugins/modules/purefb_timeout.py b/ansible_collections/purestorage/flashblade/plugins/modules/purefb_timeout.py index 21e83c002..79f53adc2 100644 --- a/ansible_collections/purestorage/flashblade/plugins/modules/purefb_timeout.py +++ b/ansible_collections/purestorage/flashblade/plugins/modules/purefb_timeout.py @@ -123,7 +123,7 @@ def main(): if 5 < module.params["timeout"] > 180 and module.params["timeout"] != 0: module.fail_json(msg="Timeout value must be between 5 and 180 minutes") blade = get_system(module) - current_timeout = list(blade.get_arrays().items)[0].idle_timeout * 60000 + current_timeout = list(blade.get_arrays().items)[0].idle_timeout / 60000 if state == "present" and current_timeout != module.params["timeout"]: set_timeout(module, blade) elif state == "absent" and current_timeout != 0: diff --git a/ansible_collections/purestorage/flashblade/plugins/modules/purefb_userpolicy.py b/ansible_collections/purestorage/flashblade/plugins/modules/purefb_userpolicy.py index 6e7dbe49d..be8716454 100644 --- a/ansible_collections/purestorage/flashblade/plugins/modules/purefb_userpolicy.py +++ b/ansible_collections/purestorage/flashblade/plugins/modules/purefb_userpolicy.py @@ -249,6 +249,7 @@ def main(): names=[module.params["account"] + "/" + module.params["name"]] ).status_code != 200 + and state != "show" ): module.fail_json( msg="Account User {0}/{1} does not exist".format( diff --git a/ansible_collections/purestorage/flashblade/tests/config.yaml b/ansible_collections/purestorage/flashblade/tests/config.yaml new file mode 100644 index 000000000..9e402bda7 --- /dev/null +++ b/ansible_collections/purestorage/flashblade/tests/config.yaml @@ -0,0 +1,2 @@ +modules: + python_requires: ">=3.6" diff --git a/ansible_collections/purestorage/flashblade/tests/sanity/ignore-2.10.txt b/ansible_collections/purestorage/flashblade/tests/sanity/ignore-2.10.txt deleted file mode 100644 index 771db46ec..000000000 --- a/ansible_collections/purestorage/flashblade/tests/sanity/ignore-2.10.txt +++ /dev/null @@ -1,2 +0,0 @@ -plugins/modules/purefb_info.py validate-modules:return-syntax-error -plugins/modules/purefb_inventory.py validate-modules:return-syntax-error diff --git a/ansible_collections/purestorage/flashblade/tests/sanity/ignore-2.11.txt b/ansible_collections/purestorage/flashblade/tests/sanity/ignore-2.11.txt deleted file mode 100644 index 771db46ec..000000000 --- a/ansible_collections/purestorage/flashblade/tests/sanity/ignore-2.11.txt +++ /dev/null @@ -1,2 +0,0 @@ -plugins/modules/purefb_info.py validate-modules:return-syntax-error -plugins/modules/purefb_inventory.py validate-modules:return-syntax-error diff --git a/ansible_collections/purestorage/flashblade/tests/sanity/ignore-2.12.txt b/ansible_collections/purestorage/flashblade/tests/sanity/ignore-2.12.txt deleted file mode 100644 index 771db46ec..000000000 --- a/ansible_collections/purestorage/flashblade/tests/sanity/ignore-2.12.txt +++ /dev/null @@ -1,2 +0,0 @@ -plugins/modules/purefb_info.py validate-modules:return-syntax-error -plugins/modules/purefb_inventory.py validate-modules:return-syntax-error diff --git a/ansible_collections/purestorage/flashblade/tests/sanity/ignore-2.13.txt b/ansible_collections/purestorage/flashblade/tests/sanity/ignore-2.13.txt deleted file mode 100644 index 771db46ec..000000000 --- a/ansible_collections/purestorage/flashblade/tests/sanity/ignore-2.13.txt +++ /dev/null @@ -1,2 +0,0 @@ -plugins/modules/purefb_info.py validate-modules:return-syntax-error -plugins/modules/purefb_inventory.py validate-modules:return-syntax-error diff --git a/ansible_collections/purestorage/flashblade/tests/sanity/ignore-2.14.txt b/ansible_collections/purestorage/flashblade/tests/sanity/ignore-2.14.txt deleted file mode 100644 index 771db46ec..000000000 --- a/ansible_collections/purestorage/flashblade/tests/sanity/ignore-2.14.txt +++ /dev/null @@ -1,2 +0,0 @@ -plugins/modules/purefb_info.py validate-modules:return-syntax-error -plugins/modules/purefb_inventory.py validate-modules:return-syntax-error diff --git a/ansible_collections/purestorage/flashblade/tests/sanity/ignore-2.15.txt b/ansible_collections/purestorage/flashblade/tests/sanity/ignore-2.15.txt deleted file mode 100644 index 771db46ec..000000000 --- a/ansible_collections/purestorage/flashblade/tests/sanity/ignore-2.15.txt +++ /dev/null @@ -1,2 +0,0 @@ -plugins/modules/purefb_info.py validate-modules:return-syntax-error -plugins/modules/purefb_inventory.py validate-modules:return-syntax-error diff --git a/ansible_collections/purestorage/flashblade/tests/sanity/ignore-2.16.txt b/ansible_collections/purestorage/flashblade/tests/sanity/ignore-2.16.txt deleted file mode 100644 index 771db46ec..000000000 --- a/ansible_collections/purestorage/flashblade/tests/sanity/ignore-2.16.txt +++ /dev/null @@ -1,2 +0,0 @@ -plugins/modules/purefb_info.py validate-modules:return-syntax-error -plugins/modules/purefb_inventory.py validate-modules:return-syntax-error diff --git a/ansible_collections/purestorage/flashblade/tests/sanity/ignore-2.9.txt b/ansible_collections/purestorage/flashblade/tests/sanity/ignore-2.9.txt deleted file mode 100644 index 771db46ec..000000000 --- a/ansible_collections/purestorage/flashblade/tests/sanity/ignore-2.9.txt +++ /dev/null @@ -1,2 +0,0 @@ -plugins/modules/purefb_info.py validate-modules:return-syntax-error -plugins/modules/purefb_inventory.py validate-modules:return-syntax-error |