Skip to content

Commit

Permalink
Merge pull request #685 from sdodsley/fleet
Browse files Browse the repository at this point in the history
Add Fleet module
  • Loading branch information
sdodsley authored Jan 16, 2025
2 parents e0198e8 + 46de8c5 commit 80e3cdf
Show file tree
Hide file tree
Showing 2 changed files with 322 additions and 0 deletions.
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -123,6 +123,7 @@ All modules are idempotent with the exception of modules that change or set pass
- purefa_eula - sign, or resign, FlashArray EULA
- purefa_export - manage FlashArrray managed file system exports
- purefa_file - copy file between managed directories
- purefa_fleet - manage FlashArray Fusion fleets and members
- purefa_fs - manage FlashArray managed file systems
- purefa_hardware - manage component identification LEDs
- purefa_hg - manage hostgroups on the FlashArray
Expand Down
321 changes: 321 additions & 0 deletions plugins/modules/purefa_fleet.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,321 @@
#!/usr/bin/python
# -*- coding: utf-8 -*-

# (c) 2024, Simon Dodsley ([email protected])
# 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: purefa_fleet
version_added: '1.33.0'
short_description: Manage Fusion Fleet
description:
- Create/Modify/Delete Fusion fleet and members
author:
- Pure Storage Ansible Team (@sdodsley) <[email protected]>
options:
name:
description:
- Name of the fleet.
- If not provided local array will be used.
type: str
state:
description:
- Define whether to add or remove member from a fleet.
- Create a new fleet if one does not exist.
This will use the current array as the first member.
default: present
choices: [ absent, present, create ]
type: str
member_url:
description:
- Management IP address/FQDN of array to add to fleet.
type: str
member_api:
description:
- API token for target array
type: str
rename:
description:
- new name for fleet
type: str
extends_documentation_fragment:
- purestorage.flasharray.purestorage.fa
"""

EXAMPLES = r"""
- name: Create a new fleet
purestorage.flasharray.purefa_fleet:
name: foo
state: create
fa_url: 10.10.10.2
api_token: e31060a7-21fc-e277-6240-25983c6c4592
- name: Add a member to fleet foo
purestorage.flasharray.purefa_fleet:
name: foo
member_url: array2
member_api: c6033033-fe69-2515-a9e8-966bb7fe4b40
fa_url: 10.10.10.2
api_token: e31060a7-21fc-e277-6240-25983c6c4592
- name: Delete a member from fleet foo
purestorage.flasharray.purefa_fleet:
name: foo
member_url: array2
member_api: c6033033-fe69-2515-a9e8-966bb7fe4b40
state: absent
fa_url: 10.10.10.2
api_token: e31060a7-21fc-e277-6240-25983c6c4592
"""

RETURN = r"""
"""

HAS_URLLIB3 = True
try:
import urllib3
except ImportError:
HAS_URLLIB3 = False

HAS_DISTRO = True
try:
import distro
except ImportError:
HAS_DISTRO = False

HAS_PURESTORAGE = True
try:
from pypureclient import flasharray
from pypureclient.flasharray import (
FleetMemberPost,
FleetmemberpostMember,
FleetmemberpostMembers,
FleetPatch,
)
except ImportError:
HAS_PURESTORAGE = False

from ansible.module_utils.basic import AnsibleModule
from ansible_collections.purestorage.flasharray.plugins.module_utils.purefa import (
get_array,
purefa_argument_spec,
)
from ansible_collections.purestorage.flasharray.plugins.module_utils.version import (
LooseVersion,
)
import platform

VERSION = 1.5
USER_AGENT_BASE = "Ansible"
MIN_REQUIRED_API_VERSION = "2.38"


def create_fleet(module, array):
"""Create new fleet - only ever called once per fleet"""
changed = True
if not module.check_mode:
res = array.post_fleets(names=[module.params["name"]])
if res.status_code != 200:
module.fail_json(
msg="Failed to create fleet {0}. Error: {1}".format(
module.params["name"], res.errors[0].message
)
)
module.exit_json(changed=changed)


def add_fleet_members(module, array):
"""Add new member to the fleet"""
changed = False
existing = False
if not module.params["member_url"] and not module.params["member_api"]:
module.fail_json(msg="missing required arguments: member_api, member_url")
try:
fleet_key = list(array.post_fleets_fleet_key().items)[0].fleet_key
except Exception:
module.fail_json(msg="Fleet key generation failed")
if HAS_URLLIB3 and module.params["disable_warnings"]:
urllib3.disable_warnings()
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(),
}
remote_system = flasharray.Client(
target=module.params["member_url"],
api_token=module.params["member_api"],
user_agent=user_agent,
)
local_name = list(remote_system.get_arrays().items)[0].name
members = list(array.get_fleets_members().items)
for member in range(0, len(members)):
if members[member].member.name == local_name:
existing = True
if not existing:
changed = True
if not module.check_mode:
res = remote_system.post_fleets_members(
fleet_names=[module.params["name"]],
members=FleetMemberPost(
members=[
FleetmemberpostMembers(
key=fleet_key,
member=FleetmemberpostMember(
name=local_name, resource_type="remote-arrays"
),
)
]
),
)
if res.status_code != 200:
module.fail_json(
"Array {0} failed to join fleet {1}. Error: {2}".format(
local_name, module.params["name"], res.errors[0].message
)
)
module.exit_json(changed=changed)


def delete_fleet_members(module, array):
"""Delete member from a fleet"""
changed = False
if not module.params["member_url"] and not module.params["member_api"]:
module.fail_json(msg="missing required arguments: member_api, member_url")
if HAS_URLLIB3 and module.params["disable_warnings"]:
urllib3.disable_warnings()
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(),
}
remote_system = flasharray.Client(
target=module.params["member_url"],
api_token=module.params["member_api"],
user_agent=user_agent,
)
local_name = list(remote_system.get_arrays().items)[0].name
members = list(array.get_fleets_members().items)
for member in range(0, len(members)):
if members[member].member.name == local_name:
changed = True
if not module.check_mode:
if members[member].status not in [
"joined",
"connected",
"partially connected",
]:
res = array.delete_fleets_members(
member_names=[local_name], unreachable=True
)
else:
res = array.delete_fleets_members(member_names=[local_name])
if res.status_code != 200:
module.fail_json(
"Array {0} failed to be removed from fleet. Error: {1}".format(
module.params["member_url"], res.errors[0].message
)
)
module.exit_json(changed=changed)


def rename_fleet(module, array):
"""Rename the fleet"""
changed = False
fleet = list(array.get_fleets().items)[0].name
if module.params["rename"] != fleet:
changed = True
if not module.check_mode:
res = array.patch_fleets(
names=[module.params["name"]],
fleet=FleetPatch(name=module.params["rename"]),
)
if res.status_code != 200:
module.fail_json(
msg="Fleet rename failed. Error: {0}".format(res.errors[0].message)
)
module.exit_json(changed=changed)


def main():
argument_spec = purefa_argument_spec()
argument_spec.update(
dict(
name=dict(type="str"),
state=dict(
type="str", default="present", choices=["absent", "present", "create"]
),
member_url=dict(type="str"),
member_api=dict(type="str"),
rename=dict(type="str"),
)
)

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")

array = get_array(module)
api_version = array.get_rest_version()
if LooseVersion(MIN_REQUIRED_API_VERSION) > LooseVersion(api_version):
module.fail_json(
msg="FlashArray REST version not supported. "
"Minimum version required: {0}".format(MIN_REQUIRED_API_VERSION)
)
state = module.params["state"]

fleet_res = array.get_fleets()
if fleet_res.status_code == 404:
module.fail_json(
msg="Fusion is not enabled on this system. "
"Please speak to Pure Support to enable this feature"
)
else:
try:
fleet = list(fleet_res.items)
except Exception:
fleet = False

if state == "create" and not fleet:
create_fleet(module, array)
elif state == "present" and module.params["rename"]:
rename_fleet(module, array)
elif state == "present" and fleet:
add_fleet_members(module, array)
elif state == "absent" and fleet:
delete_fleet_members(module, array)
else:
module.exit_json(changed=False)


if __name__ == "__main__":
main()

0 comments on commit 80e3cdf

Please sign in to comment.