diff --git a/CHANGELOG.rst b/CHANGELOG.rst index 7d8302e..9e85793 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -5,13 +5,25 @@ SAP Operations Collection Release Notes .. contents:: Topics -v1.21.0 +v1.22.0 ======= +Release Summary +--------------- + +Feature release + New Modules ----------- -- sap.sap_operations.cf_marketplace_info - Fetches Cloud Foundry marketplace service offerings +- sap.sap_operations.cf_service_instance - Manage Cloud Foundry service instances +- sap.sap_operations.cf_service_instance_info - Fetch information about Cloud Foundry service instance +- sap.sap_operations.cf_service_instances_info - Fetch information about Cloud Foundry service instances +- sap.sap_operations.cf_service_plans_info - Fetch information about Cloud Foundry service plans +- sap.sap_operations.cf_spaces_info - Fetch information about Cloud Foundry spaces + +v1.21.0 +======= v1.20.0 ======= diff --git a/README.md b/README.md index 4bca8c3..70cce64 100644 --- a/README.md +++ b/README.md @@ -25,6 +25,14 @@ If not, see . Added roles to manage SAP JVM, SAP Cloud Connector and Cloud Foundry cli +Added modules to manage SAP BTP Cloud Foundry. + + - sap.sap_operations.cf_service_instance + - sap.sap_operations.cf_service_instance_info + - sap.sap_operations.cf_service_instances_info + - sap.sap_operations.cf_service_plans_info + - sap.sap_operations.cf_spaces_info + # Ansible Collection - sap.sap_operations This collection contains modules and plugins to assist in automating SAP day 2 operations with Ansible. diff --git a/galaxy.yml b/galaxy.yml index 295cd81..84fb7b0 100644 --- a/galaxy.yml +++ b/galaxy.yml @@ -24,7 +24,7 @@ namespace: sap name: sap_operations -version: 1.21.0 +version: 1.22.0 readme: README.md diff --git a/meta/runtime.yml b/meta/runtime.yml index 78c2a84..4a99a66 100644 --- a/meta/runtime.yml +++ b/meta/runtime.yml @@ -24,3 +24,8 @@ requires_ansible: ">=2.14.0" action_groups: cf: - sap.sap_operations.cf_marketplace_info + - sap.sap_operations.cf_service_instance + - sap.sap_operations.cf_service_instance_info + - sap.sap_operations.cf_service_instances_info + - sap.sap_operations.cf_plans_info + - sap.sap_operations.cf_spaces_info diff --git a/plugins/action/cf_marketplace_info.py b/plugins/action/cf_marketplace_info.py index 6c0dba4..cc8ea90 100644 --- a/plugins/action/cf_marketplace_info.py +++ b/plugins/action/cf_marketplace_info.py @@ -79,9 +79,10 @@ def run_method( if headers is None: headers = {"Content-Type": "application/x-www-form-urlencoded"} # headers_command = [f'-H "{k}:{v}"' for k, v in headers] + data_string = json.dumps(data) command_run = subprocess.run( [ - f'cf curl "{path}" -X {method} -d "{json.dumps(data)}"', + f"""cf curl "{path}" -X {method} -d '{data_string}'""", ], # + headers_command, shell=True, # nosec B602 @@ -99,7 +100,6 @@ def run_method( def run_action(client: CFClient) -> dict: - rc = client.authenticate() if rc.get("failed"): return rc diff --git a/plugins/action/cf_service_instance.py b/plugins/action/cf_service_instance.py new file mode 100644 index 0000000..c69bfc0 --- /dev/null +++ b/plugins/action/cf_service_instance.py @@ -0,0 +1,266 @@ +# -*- coding: utf-8 -*- +# +# SPDX-License-Identifier: GPL-3.0-only +# SPDX-FileCopyrightText: 2024 Red Hat, Project Atmosphere +# +# Copyright 2024 Red Hat, Project Atmosphere +# +# This program is free software: you can redistribute it and/or modify it under the terms of the GNU +# General Public License as published by the Free Software Foundation, version 3 of the License. +# +# This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without +# even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. +# See the GNU General Public License for more details. +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +# You should have received a copy of the GNU General Public License along with this program. +# If not, see . + +from __future__ import absolute_import, division, print_function +import tempfile +from ansible.errors import AnsibleActionFail + +__metaclass__ = type +import json +from ansible.plugins.action import ActionBase + +from ansible_collections.sap.sap_operations.plugins.action.cf_marketplace_info import ( + CFClient, +) +from ansible_collections.sap.sap_operations.plugins.action.cf_service_plans_info import ( + run_action as get_service_plans, +) + +from ansible_collections.sap.sap_operations.plugins.action.cf_spaces_info import ( + run_action as get_spaces, +) + + +def validate_service_plan_name(client: CFClient, name: str, service_plan: str) -> dict: + result = get_service_plans(client, service_offering_names=[name]) + if result["failed"]: + return result + + return { + "failed": service_plan + not in [sp["name"] for sp in result["cf_service_plans_info"]] + } + + +def run_action( # noqa C901 + # TODO: simplify function + client: CFClient, + name: str, + service: str, + state: str, + space_name: str, + parameters: dict, + service_plan_name: str, + metadata: dict, +) -> dict: + rc = client.authenticate() + if rc.get("failed"): + return rc + + rc, stdout, stderr = client.run_method( + path="/v3/service_instances", + method="GET", + headers={}, + data={}, + ) + + if rc or stderr: + return {"failed": True, "msg": stdout} + + service_instances = json.loads(stdout) + + if "errors" in service_instances: + return { + "failed": True, + "msg": "Error retrieving service instances", + "errors": service_instances["errors"], + } + service_instance = None + + for si in service_instances["resources"]: + if name and si["name"] == name: + service_instance = si + + if state == "absent" and service_instance: + service_instance_guid = service_instance["guid"] + rc, stdout, stderr = client.run_method( + path=f"/v3/service_instances/{service_instance_guid}", + method="DELETE", + headers={}, + data={}, + ) + if rc: + return { + "failed": True, + "msg": "Could not delete service_instance", + "stdout": stdout, + } + return dict(failed=False, changed=True) + + if state == "absent" and not service_instance: + return {"failed": False, "changed": False} + + if state == "present" and service_instance: + return {"failed": False, "changed": False} + + if state == "present" and not service_instance: + data = {} + data["type"] = "managed" + data["name"] = name + data["parameters"] = parameters + data["metadata"] = metadata + service_plans = get_service_plans(client, service_offering_names=[service]) + service_plan = [ + sp + for sp in service_plans["cf_service_plans_info"] + if sp["name"] == service_plan_name + ][0] + + spaces = get_spaces(client) + space = [sp for sp in spaces["cf_spaces_info"] if sp["name"] == space_name][0] + + data["relationships"] = {} + data["relationships"]["service_plan"] = {} + data["relationships"]["service_plan"]["data"] = {} + data["relationships"]["service_plan"]["data"]["guid"] = service_plan["guid"] + + data["relationships"]["space"] = {} + data["relationships"]["space"]["data"] = {} + data["relationships"]["space"]["data"]["guid"] = space["guid"] + + rc, stdout, stderr = client.run_method( + path="/v3/service_instances/", + method="POST", + # headers={"Content-type": "application/json"}, + data=data, + ) + if rc or stderr or "errors" in stdout: + return { + "failed": True, + "msg": "Could not create service_instance", + "stdout": stdout, + "stderr": stderr, + } + # return get_service_instance_info(client, name=name) + return {"failed": False, "stdout": stdout, "stderr": stderr} + + +class CFServiceInstanceModule(ActionBase): + _VALID_ARGS = frozenset( + [ + "username", + "password", + "api_endpoint", + "name", + "service", + "state", + "space", + "service_plan", + "parameters", + "metadata", + ] + ) + argument_spec = dict( + username=dict(type="str", required=False), + password=dict(type="str", required=False), + api_endpoint=dict(type="str", required=False), + name=dict(type="str", required=True), + service=dict(type="str", required=True), + state=dict( + type="str", required=False, default="present", choices=["present", "absent"] + ), + space=dict(type="str", required=True), + service_plan=dict(type="str", required=False), + parameters=dict(type="dict", required=False), + metadata=dict(type="dict", required=False), + ) + required_together = [ + [ + "username", + "password", + "api_endpoint", + ], + ] + mutually_exclusive = [] + required_one_of = [ + [ + "username", + "password", + "api_endpoint", + ], + ] + + def __init__( + self, task, connection, play_context, loader, templar, shared_loader_obj + ): + """Initialize action module.""" + super().__init__( + task, connection, play_context, loader, templar, shared_loader_obj + ) + + def run(self, tmp=None, task_vars=None): + validation_results, _ignore = self.validate_argument_spec( + argument_spec=self.argument_spec, + required_together=self.required_together, + mutually_exclusive=self.mutually_exclusive, + required_one_of=self.required_one_of, + ) + + if validation_results.error_messages: + raise AnsibleActionFail( + message="Validation failed: {0}".format( + ", ".join(validation_results.error_messages) + ), + ) + api_endpoint = self._task.args.get("api_endpoint") + username = self._task.args.get("username") + password = self._task.args.get("password") + name = self._task.args.get("name") + service = self._task.args.get("service") + state = self._task.args.get("state", "present") + space_name = self._task.args.get("space") + parameters = self._task.args.get("parameters", {}) + service_plan = self._task.args.get("service_plan") + metadata = self._task.args.get("metadata", {}) + + with tempfile.TemporaryDirectory() as cf_home: + client = CFClient( + api_endpoint=api_endpoint, + username=username, + password=password, + cf_home=cf_home, + ) + if state == "present": + validation_results = validate_service_plan_name( + client, name=service, service_plan=service_plan + ) + if validation_results["failed"]: + return { + "failed": True, + "msg": f"Service plan name {service_plan=} is not correct for service name {service=}", + } + + return run_action( + client, + name=name, + service=service, + state=state, + space_name=space_name, + parameters=parameters, + service_plan_name=service_plan, + metadata=metadata, + ) + + +class ActionModule(CFServiceInstanceModule): + pass diff --git a/plugins/action/cf_service_instance_info.py b/plugins/action/cf_service_instance_info.py new file mode 100644 index 0000000..6a995d6 --- /dev/null +++ b/plugins/action/cf_service_instance_info.py @@ -0,0 +1,146 @@ +# -*- coding: utf-8 -*- +# +# SPDX-License-Identifier: GPL-3.0-only +# SPDX-FileCopyrightText: 2024 Red Hat, Project Atmosphere +# +# Copyright 2024 Red Hat, Project Atmosphere +# +# This program is free software: you can redistribute it and/or modify it under the terms of the GNU +# General Public License as published by the Free Software Foundation, version 3 of the License. +# +# This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without +# even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. +# See the GNU General Public License for more details. +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +# You should have received a copy of the GNU General Public License along with this program. +# If not, see . + +from __future__ import absolute_import, division, print_function +import tempfile +from ansible.errors import AnsibleActionFail + +__metaclass__ = type +import json +from ansible.plugins.action import ActionBase + +from ansible_collections.sap.sap_operations.plugins.action.cf_marketplace_info import ( + CFClient, +) + + +def run_action(client: CFClient, name: str = "", guid: str = "") -> dict: + + rc = client.authenticate() + if rc.get("failed"): + return rc + + rc, stdout, stderr = client.run_method( + path="/v3/service_instances", + method="GET", + headers={}, + data={}, + ) + + if rc or stderr: + return {"failed": True, "msg": stdout} + + service_instances = json.loads(stdout) + + if "errors" in service_instances: + return { + "failed": True, + "msg": "Error retrieving service instances", + "errors": service_instances["errors"], + } + for service_instance in service_instances["resources"]: + if name and service_instance["name"] == name: + return dict( + failed=False, + changed=False, + cf_service_instance_info=service_instance, + ) + if guid and service_instance["guid"] == guid: + return dict( + failed=False, + changed=False, + cf_service_instance_info=service_instance, + ) + + return dict( + failed=True, + msg="Service instance not found", + ) + + +class CFServiceInstanceInfoModule(ActionBase): + _VALID_ARGS = frozenset(["username", "password", "api_endpoint", "name", "guid"]) + argument_spec = dict( + username=dict(type="str", required=False), + password=dict(type="str", required=False), + api_endpoint=dict(type="str", required=False), + name=dict(type="str", required=False), + guid=dict(type="str", required=False), + ) + required_together = [ + [ + "username", + "password", + "api_endpoint", + ], + ] + mutually_exclusive = [["name", "guid"]] + required_one_of = [ + [ + "username", + "password", + "api_endpoint", + ], + ["name", "guid"], + ] + + def __init__( + self, task, connection, play_context, loader, templar, shared_loader_obj + ): + """Initialize action module.""" + super().__init__( + task, connection, play_context, loader, templar, shared_loader_obj + ) + + def run(self, tmp=None, task_vars=None): + validation_results, _ignore = self.validate_argument_spec( + argument_spec=self.argument_spec, + required_together=self.required_together, + mutually_exclusive=self.mutually_exclusive, + required_one_of=self.required_one_of, + ) + + if validation_results.error_messages: + raise AnsibleActionFail( + message="Validation failed: {0}".format( + ", ".join(validation_results.error_messages) + ), + ) + api_endpoint = self._task.args.get("api_endpoint") + username = self._task.args.get("username") + password = self._task.args.get("password") + name = self._task.args.get("name", "") + guid = self._task.args.get("guid", "") + + with tempfile.TemporaryDirectory() as cf_home: + client = CFClient( + api_endpoint=api_endpoint, + username=username, + password=password, + cf_home=cf_home, + ) + return run_action(client, name=name, guid=guid) + + +class ActionModule(CFServiceInstanceInfoModule): + pass diff --git a/plugins/action/cf_service_instances_info.py b/plugins/action/cf_service_instances_info.py new file mode 100644 index 0000000..2f2a7c0 --- /dev/null +++ b/plugins/action/cf_service_instances_info.py @@ -0,0 +1,127 @@ +# -*- coding: utf-8 -*- +# +# SPDX-License-Identifier: GPL-3.0-only +# SPDX-FileCopyrightText: 2024 Red Hat, Project Atmosphere +# +# Copyright 2024 Red Hat, Project Atmosphere +# +# This program is free software: you can redistribute it and/or modify it under the terms of the GNU +# General Public License as published by the Free Software Foundation, version 3 of the License. +# +# This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without +# even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. +# See the GNU General Public License for more details. +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +# You should have received a copy of the GNU General Public License along with this program. +# If not, see . + +from __future__ import absolute_import, division, print_function +import tempfile +from ansible.errors import AnsibleActionFail + +__metaclass__ = type +import json +from ansible.plugins.action import ActionBase + +from ansible_collections.sap.sap_operations.plugins.action.cf_marketplace_info import ( + CFClient, +) + + +def run_action(client: CFClient) -> dict: + + rc = client.authenticate() + if rc.get("failed"): + return rc + + rc, stdout, stderr = client.run_method( + path="/v3/service_instances", + method="GET", + headers={}, + data={}, + ) + + if rc or stderr: + return {"failed": True, "msg": stdout} + + result = json.loads(stdout) + + if "errors" in result: + return { + "failed": True, + "msg": "Error retrieving service instances", + "errors": result["errors"], + } + + return dict( + failed=False, changed=False, cf_service_instances_info=result["resources"] + ) + + +class CFServiceInstancesInfoModule(ActionBase): + _VALID_ARGS = frozenset(["username", "password", "api_endpoint"]) + argument_spec = dict( + username=dict(type="str", required=False), + password=dict(type="str", required=False), + api_endpoint=dict(type="str", required=False), + ) + required_together = [ + [ + "username", + "password", + "api_endpoint", + ], + ] + mutually_exclusive = [] + required_one_of = [ + [ + "username", + "password", + "api_endpoint", + ] + ] + + def __init__( + self, task, connection, play_context, loader, templar, shared_loader_obj + ): + """Initialize action module.""" + super().__init__( + task, connection, play_context, loader, templar, shared_loader_obj + ) + + def run(self, tmp=None, task_vars=None): + validation_results, _ignore = self.validate_argument_spec( + argument_spec=self.argument_spec, + required_together=self.required_together, + mutually_exclusive=self.mutually_exclusive, + required_one_of=self.required_one_of, + ) + + if validation_results.error_messages: + raise AnsibleActionFail( + message="Validation failed: {0}".format( + ", ".join(validation_results.error_messages) + ), + ) + api_endpoint = self._task.args.get("api_endpoint") + username = self._task.args.get("username") + password = self._task.args.get("password") + + with tempfile.TemporaryDirectory() as cf_home: + client = CFClient( + api_endpoint=api_endpoint, + username=username, + password=password, + cf_home=cf_home, + ) + return run_action(client) + + +class ActionModule(CFServiceInstancesInfoModule): + pass diff --git a/plugins/action/cf_service_plans_info.py b/plugins/action/cf_service_plans_info.py new file mode 100644 index 0000000..baa0728 --- /dev/null +++ b/plugins/action/cf_service_plans_info.py @@ -0,0 +1,132 @@ +# -*- coding: utf-8 -*- +# +# SPDX-License-Identifier: GPL-3.0-only +# SPDX-FileCopyrightText: 2024 Red Hat, Project Atmosphere +# +# Copyright 2024 Red Hat, Project Atmosphere +# +# This program is free software: you can redistribute it and/or modify it under the terms of the GNU +# General Public License as published by the Free Software Foundation, version 3 of the License. +# +# This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without +# even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. +# See the GNU General Public License for more details. +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +# You should have received a copy of the GNU General Public License along with this program. +# If not, see . + +from __future__ import absolute_import, division, print_function +import tempfile +from ansible.errors import AnsibleActionFail + +__metaclass__ = type +import json +from ansible.plugins.action import ActionBase + +from ansible_collections.sap.sap_operations.plugins.action.cf_marketplace_info import ( + CFClient, +) + + +def run_action(client: CFClient, service_offering_names: list[str]) -> dict: + rc = client.authenticate() + if rc.get("failed"): + return rc + parameters_string = "" + if service_offering_names: + service_offering_names_string = ",".join(service_offering_names) + parameters_string += f"service_offering_names={service_offering_names_string}," + + rc, stdout, stderr = client.run_method( + path=f"/v3/service_plans?{parameters_string}", + method="GET", + headers={}, + data={}, + ) + + if rc or stderr: + return {"failed": True, "msg": stdout} + + result = json.loads(stdout) + + if "errors" in result: + return { + "failed": True, + "msg": "Error retrieving service plans", + "errors": result["errors"], + } + + return dict(failed=False, changed=False, cf_service_plans_info=result["resources"]) + + +class CFServicePlansInfoModule(ActionBase): + _VALID_ARGS = frozenset( + ["username", "password", "api_endpoint", "service_offering_names"] + ) + argument_spec = dict( + username=dict(type="str", required=False), + password=dict(type="str", required=False), + api_endpoint=dict(type="str", required=False), + service_offering_names=dict(type="list", elements="str", required=False), + ) + required_together = [ + [ + "username", + "password", + "api_endpoint", + ], + ] + mutually_exclusive = [] + required_one_of = [ + [ + "username", + "password", + "api_endpoint", + ] + ] + + def __init__( + self, task, connection, play_context, loader, templar, shared_loader_obj + ): + """Initialize action module.""" + super().__init__( + task, connection, play_context, loader, templar, shared_loader_obj + ) + + def run(self, tmp=None, task_vars=None): + validation_results, _ignore = self.validate_argument_spec( + argument_spec=self.argument_spec, + required_together=self.required_together, + mutually_exclusive=self.mutually_exclusive, + required_one_of=self.required_one_of, + ) + + if validation_results.error_messages: + raise AnsibleActionFail( + message="Validation failed: {0}".format( + ", ".join(validation_results.error_messages) + ), + ) + api_endpoint = self._task.args.get("api_endpoint") + username = self._task.args.get("username") + password = self._task.args.get("password") + service_offering_names = self._task.args.get("service_offering_names", []) + + with tempfile.TemporaryDirectory() as cf_home: + client = CFClient( + api_endpoint=api_endpoint, + username=username, + password=password, + cf_home=cf_home, + ) + return run_action(client, service_offering_names=service_offering_names) + + +class ActionModule(CFServicePlansInfoModule): + pass diff --git a/plugins/action/cf_spaces_info.py b/plugins/action/cf_spaces_info.py new file mode 100644 index 0000000..17d4dce --- /dev/null +++ b/plugins/action/cf_spaces_info.py @@ -0,0 +1,124 @@ +# -*- coding: utf-8 -*- +# +# SPDX-License-Identifier: GPL-3.0-only +# SPDX-FileCopyrightText: 2024 Red Hat, Project Atmosphere +# +# Copyright 2024 Red Hat, Project Atmosphere +# +# This program is free software: you can redistribute it and/or modify it under the terms of the GNU +# General Public License as published by the Free Software Foundation, version 3 of the License. +# +# This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without +# even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. +# See the GNU General Public License for more details. +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +# You should have received a copy of the GNU General Public License along with this program. +# If not, see . + +from __future__ import absolute_import, division, print_function +import tempfile +from ansible.errors import AnsibleActionFail + +__metaclass__ = type +import json +from ansible.plugins.action import ActionBase + +from ansible_collections.sap.sap_operations.plugins.action.cf_marketplace_info import ( + CFClient, +) + + +def run_action(client: CFClient) -> dict: + rc = client.authenticate() + if rc.get("failed"): + return rc + + rc, stdout, stderr = client.run_method( + path="/v3/spaces", + method="GET", + headers={}, + data={}, + ) + + if rc or stderr: + return {"failed": True, "msg": stdout} + + result = json.loads(stdout) + + if "errors" in result: + return { + "failed": True, + "msg": "Error retrieving spaces instances", + "errors": result["errors"], + } + + return dict(failed=False, changed=False, cf_spaces_info=result["resources"]) + + +class CFSpacesInfo(ActionBase): + _VALID_ARGS = frozenset(["username", "password", "api_endpoint"]) + argument_spec = dict( + username=dict(type="str", required=False), + password=dict(type="str", required=False), + api_endpoint=dict(type="str", required=False), + ) + required_together = [ + [ + "username", + "password", + "api_endpoint", + ], + ] + mutually_exclusive = [] + required_one_of = [ + [ + "username", + "password", + "api_endpoint", + ] + ] + + def __init__( + self, task, connection, play_context, loader, templar, shared_loader_obj + ): + """Initialize action module.""" + super().__init__( + task, connection, play_context, loader, templar, shared_loader_obj + ) + + def run(self, tmp=None, task_vars=None): + validation_results, _ignore = self.validate_argument_spec( + argument_spec=self.argument_spec, + required_together=self.required_together, + mutually_exclusive=self.mutually_exclusive, + required_one_of=self.required_one_of, + ) + + if validation_results.error_messages: + raise AnsibleActionFail( + message="Validation failed: {0}".format( + ", ".join(validation_results.error_messages) + ), + ) + api_endpoint = self._task.args.get("api_endpoint") + username = self._task.args.get("username") + password = self._task.args.get("password") + + with tempfile.TemporaryDirectory() as cf_home: + client = CFClient( + api_endpoint=api_endpoint, + username=username, + password=password, + cf_home=cf_home, + ) + return run_action(client) + + +class ActionModule(CFSpacesInfo): + pass diff --git a/plugins/doc_fragments/cloud_foundry.py b/plugins/doc_fragments/cloud_foundry.py new file mode 100644 index 0000000..aed92c7 --- /dev/null +++ b/plugins/doc_fragments/cloud_foundry.py @@ -0,0 +1,48 @@ +# SPDX-License-Identifier: GPL-3.0-only +# SPDX-FileCopyrightText: 2024 Red Hat, Project Atmosphere +# +# Copyright 2024 Red Hat, Project Atmosphere +# +# This program is free software: you can redistribute it and/or modify it under the terms of the GNU +# General Public License as published by the Free Software Foundation, version 3 of the License. +# +# This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without +# even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. +# See the GNU General Public License for more details. +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +# You should have received a copy of the GNU General Public License along with this program. +# If not, see . + + +class ModuleDocFragment(object): + DOCUMENTATION = r""" +--- +requirements: + - "python >= 3.6" + - cf CLI should be installed and available in PATH +author: + - Kirill Satarin (@kksat) +options: + username: + description: + - The username for authentication with the Cloud Foundry API. + - This is SAP BTP user email address + type: str + required: false + password: + description: + - The password for authentication with the Cloud Foundry API. + type: str + required: false + api_endpoint: + description: + - The endpoint URL of the Cloud Foundry API. + type: str + required: false +""" diff --git a/plugins/modules/cf_marketplace_info.py b/plugins/modules/cf_marketplace_info.py index f96f746..dce250a 100644 --- a/plugins/modules/cf_marketplace_info.py +++ b/plugins/modules/cf_marketplace_info.py @@ -30,6 +30,7 @@ extends_documentation_fragment: - sap.sap_operations.community - sap.sap_operations.action_plugin + - sap.sap_operations.cloud_foundry author: - Kirill Satarin (@kksat) @@ -43,27 +44,7 @@ version_added: "1.21.0" -options: - username: - description: - - The username for authentication with the Cloud Foundry API. - - This is SAP BTP user email address - type: str - required: false - password: - description: - - The password for authentication with the Cloud Foundry API. - type: str - required: false - api_endpoint: - description: - - The endpoint URL of the Cloud Foundry API. - type: str - required: false - -requirements: - - "python >= 3.6" - - cf CLI should be installed and available in PATH +options: {} notes: - This module does not modify any data and is safe to run in check mode. diff --git a/plugins/modules/cf_service_instance.py b/plugins/modules/cf_service_instance.py new file mode 100644 index 0000000..7f8df6c --- /dev/null +++ b/plugins/modules/cf_service_instance.py @@ -0,0 +1,107 @@ +#!/usr/bin/python +# -*- coding: utf-8 -*- + +# SPDX-License-Identifier: GPL-3.0-only +# SPDX-FileCopyrightText: 2024 Red Hat, Project Atmosphere +# +# Copyright 2024 Red Hat, Project Atmosphere +# +# This program is free software: you can redistribute it and/or modify it under the terms of the GNU +# General Public License as published by the Free Software Foundation, version 3 of the License. +# +# This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without +# even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. +# See the GNU General Public License for more details. +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +# You should have received a copy of the GNU General Public License along with this program. +# If not, see . + + +DOCUMENTATION = r""" +--- +module: cf_service_instance + +extends_documentation_fragment: + - sap.sap_operations.community + - sap.sap_operations.action_plugin + - sap.sap_operations.cloud_foundry + +author: + - Kirill Satarin (@kksat) + +short_description: Manage Cloud Foundry service instances + +description: + - This plugin allows managing service instances in a Cloud Foundry environment. + - It can create, delete service instances. + - Plugin is idempotent it will not create service instance with the same name it is already exists. + # TODO: add functionality to change service instance configuration + - Plugin will not change service instance parameters or metadata if service instance already exists (this functionality is planned) + +version_added: 1.22.0 + +options: + name: + description: The name of the service instance. + type: str + required: True + service: + description: The name of the service offering. + type: str + required: True + state: + description: The desired state of the service instance. + choices: ['present', 'absent'] + default: 'present' + type: str + required: False + space: + description: The name of the space in which to manage the service instance. + type: str + required: True + service_plan: + description: The name of the service plan for the service instance. + type: str + required: False + parameters: + description: A dictionary containing configuration parameters for the service instance. + type: dict + required: False + metadata: + description: A dictionary containing metadata for the service instance. + type: dict + required: False +""" + +EXAMPLES = r""" +--- +- name: Create a service instance + sap.sap_operations.cf_service_instance: + name: my_service_instance + service: my_service + state: present + space: my_space + service_plan: my_service_plan + parameters: + param1: value1 + param2: value2 + metadata: + label1: value1 + label2: value2 + +- name: Delete a service instance + sap.sap_operations.cf_service_instance: + name: my_service_instance + service: my_service + state: absent + space: my_space +""" + +RETURN = r""" + """ diff --git a/plugins/modules/cf_service_instance_info.py b/plugins/modules/cf_service_instance_info.py new file mode 100644 index 0000000..6fcb4c0 --- /dev/null +++ b/plugins/modules/cf_service_instance_info.py @@ -0,0 +1,77 @@ +#!/usr/bin/python +# -*- coding: utf-8 -*- + +# SPDX-License-Identifier: GPL-3.0-only +# SPDX-FileCopyrightText: 2024 Red Hat, Project Atmosphere +# +# Copyright 2024 Red Hat, Project Atmosphere +# +# This program is free software: you can redistribute it and/or modify it under the terms of the GNU +# General Public License as published by the Free Software Foundation, version 3 of the License. +# +# This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without +# even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. +# See the GNU General Public License for more details. +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +# You should have received a copy of the GNU General Public License along with this program. +# If not, see . + + +DOCUMENTATION = r""" +--- +module: cf_service_instance_info + +extends_documentation_fragment: + - sap.sap_operations.community + - sap.sap_operations.action_plugin + - sap.sap_operations.cloud_foundry + +author: + - Kirill Satarin (@kksat) + +short_description: Fetch information about Cloud Foundry service instance + +description: + - Fetch information about Cloud Foundry service instance + - One of I(name) or I(guid) is required + +version_added: 1.22.0 + +options: + name: + description: The name of the service instance. + type: str + required: False + guid: + description: The guid of the service instance. + type: str + required: False +""" + +EXAMPLES = r""" +--- +- name: Fetch information about service instance + sap.sap_operations.cf_service_instance_info: + username: user@email.domain + password: secret + api_endpoint: + name: service_instance_name + +- name: Fetch information about service instance + sap.sap_operations.cf_service_instance_info: + username: user@email.domain + password: secret + api_endpoint: + guid: service_instance_guid +""" + +RETURN = r""" +--- +# TODO: add documentation + """ diff --git a/plugins/modules/cf_service_instances_info.py b/plugins/modules/cf_service_instances_info.py new file mode 100644 index 0000000..580294b --- /dev/null +++ b/plugins/modules/cf_service_instances_info.py @@ -0,0 +1,60 @@ +#!/usr/bin/python +# -*- coding: utf-8 -*- + +# SPDX-License-Identifier: GPL-3.0-only +# SPDX-FileCopyrightText: 2024 Red Hat, Project Atmosphere +# +# Copyright 2024 Red Hat, Project Atmosphere +# +# This program is free software: you can redistribute it and/or modify it under the terms of the GNU +# General Public License as published by the Free Software Foundation, version 3 of the License. +# +# This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without +# even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. +# See the GNU General Public License for more details. +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +# You should have received a copy of the GNU General Public License along with this program. +# If not, see . + + +DOCUMENTATION = r""" +--- +module: cf_service_instances_info + +extends_documentation_fragment: + - sap.sap_operations.community + - sap.sap_operations.action_plugin + - sap.sap_operations.cloud_foundry + +author: + - Kirill Satarin (@kksat) + +short_description: Fetch information about Cloud Foundry service instances + +description: + - Fetch information about Cloud Foundry service instances + +version_added: 1.22.0 + +options: {} +""" + +EXAMPLES = r""" +--- +- name: Fetch information about service instances + sap.sap_operations.cf_service_instances_info: + username: user@email.domain + password: secret + api_endpoint: +""" + +RETURN = r""" +--- +# TODO: add documentation + """ diff --git a/plugins/modules/cf_service_plans_info.py b/plugins/modules/cf_service_plans_info.py new file mode 100644 index 0000000..4a279c1 --- /dev/null +++ b/plugins/modules/cf_service_plans_info.py @@ -0,0 +1,60 @@ +#!/usr/bin/python +# -*- coding: utf-8 -*- + +# SPDX-License-Identifier: GPL-3.0-only +# SPDX-FileCopyrightText: 2024 Red Hat, Project Atmosphere +# +# Copyright 2024 Red Hat, Project Atmosphere +# +# This program is free software: you can redistribute it and/or modify it under the terms of the GNU +# General Public License as published by the Free Software Foundation, version 3 of the License. +# +# This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without +# even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. +# See the GNU General Public License for more details. +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +# You should have received a copy of the GNU General Public License along with this program. +# If not, see . + + +DOCUMENTATION = r""" +--- +module: cf_service_plans_info + +extends_documentation_fragment: + - sap.sap_operations.community + - sap.sap_operations.action_plugin + - sap.sap_operations.cloud_foundry + +author: + - Kirill Satarin (@kksat) + +short_description: Fetch information about Cloud Foundry service plans + +description: + - Fetch information about Cloud Foundry service plans + +version_added: 1.22.0 + +options: {} +""" + +EXAMPLES = r""" +--- +- name: Fetch information about service plans + sap.sap_operations.cf_service_plans_info: + username: user@email.domain + password: secret + api_endpoint: +""" + +RETURN = r""" +--- +# TODO: add documentation + """ diff --git a/plugins/modules/cf_spaces_info.py b/plugins/modules/cf_spaces_info.py new file mode 100644 index 0000000..d692e09 --- /dev/null +++ b/plugins/modules/cf_spaces_info.py @@ -0,0 +1,60 @@ +#!/usr/bin/python +# -*- coding: utf-8 -*- + +# SPDX-License-Identifier: GPL-3.0-only +# SPDX-FileCopyrightText: 2024 Red Hat, Project Atmosphere +# +# Copyright 2024 Red Hat, Project Atmosphere +# +# This program is free software: you can redistribute it and/or modify it under the terms of the GNU +# General Public License as published by the Free Software Foundation, version 3 of the License. +# +# This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without +# even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. +# See the GNU General Public License for more details. +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +# You should have received a copy of the GNU General Public License along with this program. +# If not, see . + + +DOCUMENTATION = r""" +--- +module: cf_spaces_info + +extends_documentation_fragment: + - sap.sap_operations.community + - sap.sap_operations.action_plugin + - sap.sap_operations.cloud_foundry + +author: + - Kirill Satarin (@kksat) + +short_description: Fetch information about Cloud Foundry spaces + +description: + - Fetch information about Cloud Foundry spaces + +version_added: 1.22.0 + +options: {} +""" + +EXAMPLES = r""" +--- +- name: Fetch information about spaces + sap.sap_operations.cf_spaces_info: + username: user@email.domain + password: secret + api_endpoint: +""" + +RETURN = r""" +--- +# TODO: add documentation + """