diff --git a/lib/ansible/module_utils/network/checkpoint/checkpoint.py b/lib/ansible/module_utils/network/checkpoint/checkpoint.py
index 7545ebfe34..2448b73e22 100644
--- a/lib/ansible/module_utils/network/checkpoint/checkpoint.py
+++ b/lib/ansible/module_utils/network/checkpoint/checkpoint.py
@@ -26,6 +26,188 @@
# USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#
+from __future__ import (absolute_import, division, print_function)
+
+import time
+
+from ansible.module_utils.connection import Connection
+
+
+checkpoint_argument_spec_for_objects = dict(
+ auto_publish_session=dict(type='bool'),
+ wait_for_task=dict(type='bool', default=True),
+ state=dict(type='str', required=True, choices=['present', 'absent']),
+ version=dict(type='str')
+)
+
+checkpoint_argument_spec_for_facts = dict(
+ version=dict(type='str')
+)
+
+checkpoint_argument_spec_for_commands = dict(
+ wait_for_task=dict(type='bool', default=True),
+ version=dict(type='str')
+)
+
+
+# send the request to checkpoint
+def send_request(connection, version, url, payload=None):
+ code, response = connection.send_request('/web_api/' + version + url, payload)
+
+ return code, response
+
+
+# get the payload from the user parameters
+def is_checkpoint_param(parameter):
+ if parameter == 'auto_publish_session' or\
+ parameter == 'state' or\
+ parameter == 'wait_for_task' or\
+ parameter == 'version':
+ return False
+ return True
+
+
+# build the payload from the parameters which has value (not None), and they are parameter of checkpoint API as well
+def get_payload_from_parameters(module):
+ payload = {}
+ for parameter in module.params:
+ if module.params[parameter] and is_checkpoint_param(parameter):
+ payload[parameter.replace("_", "-")] = module.params[parameter]
+ return payload
+
+
+# wait for task
+def wait_for_task(module, version, connection, task_id):
+ task_id_payload = {'task-id': task_id}
+ task_complete = False
+ current_iteration = 0
+ max_num_iterations = 300
+
+ # As long as there is a task in progress
+ while not task_complete and current_iteration < max_num_iterations:
+ current_iteration += 1
+ # Check the status of the task
+ code, response = send_request(connection, version, 'show-task', task_id_payload)
+
+ attempts_counter = 0
+ while code != 200:
+ if attempts_counter < 5:
+ attempts_counter += 1
+ time.sleep(2)
+ code, response = send_request(connection, version, 'show-task', task_id_payload)
+ else:
+ response['message'] = "ERROR: Failed to handle asynchronous tasks as synchronous, tasks result is" \
+ " undefined.\n" + response['message']
+ module.fail_json(msg=response)
+
+ # Count the number of tasks that are not in-progress
+ completed_tasks = 0
+ for task in response['tasks']:
+ if task['status'] == 'failed':
+ module.fail_json(msg='Task {0} with task id {1} failed. Look at the logs for more details'
+ .format(task['task-name'], task['task-id']))
+ if task['status'] == 'in progress':
+ break
+ completed_tasks += 1
+
+ # Are we done? check if all tasks are completed
+ if completed_tasks == len(response["tasks"]):
+ task_complete = True
+ else:
+ time.sleep(2) # Wait for two seconds
+ if not task_complete:
+ module.fail_json(msg="ERROR: Timeout.\nTask-id: {0}.".format(task_id_payload['task-id']))
+
+
+# handle publish command, and wait for it to end if the user asked so
+def handle_publish(module, connection, version):
+ if module.params['auto_publish_session']:
+ publish_code, publish_response = send_request(connection, version, 'publish')
+ if publish_code != 200:
+ module.fail_json(msg=publish_response)
+ if module.params['wait_for_task']:
+ wait_for_task(module, version, connection, publish_response['task-id'])
+
+
+# handle a command
+def api_command(module, command):
+ payload = get_payload_from_parameters(module)
+ connection = Connection(module._socket_path)
+ # if user insert a specific version, we add it to the url
+ version = ('v' + module.params['version'] + '/') if module.params['version'] else ''
+
+ code, response = send_request(connection, version, command, payload)
+ result = {'changed': True}
+
+ if code == 200:
+ if module.params['wait_for_task']:
+ if 'task-id' in response:
+ wait_for_task(module, version, connection, response['task-id'])
+ elif 'tasks' in response:
+ for task_id in response['tasks']:
+ wait_for_task(module, version, connection, task_id)
+
+ result[command] = response
+ else:
+ module.fail_json(msg='Checkpoint device returned error {0} with message {1}'.format(code, response))
+
+ return result
+
+
+# handle api call
+def api_call(module, api_call_object):
+ payload = get_payload_from_parameters(module)
+ connection = Connection(module._socket_path)
+ # if user insert a specific version, we add it to the url
+ version = ('v' + module.params['version'] + '/') if module.params['version'] else ''
+
+ payload_for_equals = {'type': api_call_object, 'params': payload}
+ equals_code, equals_response = send_request(connection, version, 'equals', payload_for_equals)
+ # if code is 400 (bad request) or 500 (internal error) - fail
+ if equals_code == 400 or equals_code == 500:
+ module.fail_json(msg=equals_response)
+ result = {'changed': False}
+
+ if module.params['state'] == 'present':
+ if equals_code == 200:
+ if not equals_response['equals']:
+ code, response = send_request(connection, version, 'set-' + api_call_object, payload)
+ if code != 200:
+ module.fail_json(msg=response)
+
+ handle_publish(module, connection, version)
+
+ result['changed'] = True
+ result[api_call_object] = response
+ else:
+ # objects are equals and there is no need for set request
+ pass
+ elif equals_code == 404:
+ code, response = send_request(connection, version, 'add-' + api_call_object, payload)
+ if code != 200:
+ module.fail_json(msg=response)
+
+ handle_publish(module, connection, version)
+
+ result['changed'] = True
+ result[api_call_object] = response
+ else:
+ # state == absent
+ if equals_code == 200:
+ code, response = send_request(connection, version, 'delete-' + api_call_object, payload)
+ if code != 200:
+ module.fail_json(msg=response)
+
+ handle_publish(module, connection, version)
+
+ result['changed'] = True
+ elif equals_code == 404:
+ # no need to delete because object dose not exist
+ pass
+
+ result['checkpoint_session_uid'] = connection.get_session_uid()
+ return result
+
checkpoint_argument_spec = dict(auto_publish_session=dict(type='bool', default=True),
policy_package=dict(type='str', default='standard'),
diff --git a/lib/ansible/modules/network/checkpoint/cp_network.py b/lib/ansible/modules/network/checkpoint/cp_network.py
new file mode 100644
index 0000000000..de892a26a3
--- /dev/null
+++ b/lib/ansible/modules/network/checkpoint/cp_network.py
@@ -0,0 +1,207 @@
+#!/usr/bin/python
+# -*- coding: utf-8 -*-
+#
+# Ansible module to manage CheckPoint Firewall (c) 2019
+#
+# Ansible is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# Ansible is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with Ansible. If not, see .
+#
+
+from __future__ import (absolute_import, division, print_function)
+
+__metaclass__ = type
+
+ANSIBLE_METADATA = {'metadata_version': '1.1',
+ 'status': ['preview'],
+ 'supported_by': 'community'}
+
+DOCUMENTATION = """
+---
+module: cp_network
+short_description: Manages network objects on Checkpoint over Web Services API
+description:
+ - Manages network objects on Checkpoint devices including creating, updating and removing objects.
+ All operations are performed over Web Services API.
+version_added: "2.9"
+author: "Or Soffer (@chkp-orso)"
+options:
+ name:
+ description:
+ - Object name.
+ type: str
+ subnet:
+ description:
+ - IPv4 or IPv6 network address. If both addresses are required use subnet4 and subnet6 fields explicitly.
+ type: str
+ subnet4:
+ description:
+ - IPv4 network address.
+ type: str
+ subnet6:
+ description:
+ - IPv6 network address.
+ type: str
+ mask_length:
+ description:
+ - IPv4 or IPv6 network mask length. If both masks are required use mask-length4 and mask-length6 fields
+ explicitly. Instead of IPv4 mask length it is possible to specify IPv4 mask itself in subnet-mask field.
+ type: int
+ mask_length4:
+ description:
+ - IPv4 network mask length.
+ type: int
+ mask_length6:
+ description:
+ - IPv6 network mask length.
+ type: int
+ subnet_mask:
+ description:
+ - IPv4 network mask.
+ type: str
+ nat_settings:
+ description:
+ - NAT settings.
+ type: dict
+ tags:
+ description:
+ - Collection of tag identifiers.
+ type: list
+ broadcast:
+ description:
+ - Allow broadcast address inclusion.
+ type: str
+ choices:
+ - disallow
+ - allow
+ color:
+ description:
+ - Color of the object. Should be one of existing colors.
+ choices: ['aquamarine', 'black', 'blue', 'crete blue', 'burlywood', 'cyan', 'dark green', 'khaki', 'orchid',
+ 'dark orange', 'dark sea green', 'pink', 'turquoise', 'dark blue', 'firebrick', 'brown', 'forest green',
+ 'gold', 'dark gold', 'gray', 'dark gray', 'light green', 'lemon chiffon', 'coral', 'sea green',
+ 'sky blue', 'magenta', 'purple', 'slate blue', 'violet red', 'navy blue', 'olive', 'orange', 'red',
+ 'sienna', 'yellow']
+ comments:
+ description:
+ - Comments string.
+ type: str
+ details_level:
+ description:
+ - The level of detail for some of the fields in the response can vary from showing only the UID value of the
+ object to a fully detailed representation of the object.
+ type: str
+ choices: [uid, standard, full]
+ groups:
+ description:
+ - Collection of group identifiers.
+ type: list
+ ignore_warnings:
+ description:
+ - Apply changes ignoring warnings.
+ type: bool
+ ignore_errors:
+ description:
+ - Apply changes ignoring errors. You won't be able to publish such a changes. If ignore-warnings flag was omitted
+ - warnings will also be ignored.
+ type: bool
+ uid:
+ description:
+ - Object unique identifier.
+ type: str
+ new_name:
+ description:
+ - New name of the object.
+ type: str
+extends_documentation_fragment: checkpoint_objects
+"""
+
+EXAMPLES = """
+- name: add-network
+ cp_network:
+ name: New Network 3
+ nat_settings:
+ auto-rule: true
+ hide-behind: ip-address
+ install-on: All
+ ip-address: 192.0.2.1
+ method: static
+ state: present
+ subnet: 192.0.2.1
+ subnet_mask: 255.255.255.0
+
+- name: set-network
+ cp_network:
+ name : New Network 1
+ new-name : New Network 2
+ color : green
+ subnet : 192.0.0.0
+ mask-length : 16
+ groups : New Group 1
+ state: present
+
+- name: delete-network
+ cp_network:
+ name : New Network 2
+ state: absent
+"""
+
+RETURN = """
+cp_network:
+ description: The checkpoint object created or updated.
+ returned: always, except when deleting the object.
+ type: dict
+"""
+
+from ansible.module_utils.basic import AnsibleModule
+from ansible.module_utils.network.checkpoint.checkpoint import checkpoint_argument_spec_for_objects, api_call
+
+
+def main():
+ argument_spec = dict(
+ name=dict(type='str'),
+ subnet=dict(type='str'),
+ subnet4=dict(type='str'),
+ subnet6=dict(type='str'),
+ mask_length=dict(type='int'),
+ mask_length4=dict(type='int'),
+ mask_length6=dict(type='int'),
+ subnet_mask=dict(type='str'),
+ nat_settings=dict(type='dict'),
+ tags=dict(type='list'),
+ broadcast=dict(type='str', choices=['disallow', 'allow']),
+ color=dict(type='str', choices=['aquamarine', 'black', 'blue', 'crete blue', 'burlywood', 'cyan', 'dark green',
+ 'khaki', 'orchid', 'dark orange', 'dark sea green', 'pink', 'turquoise',
+ 'dark blue', 'firebrick', 'brown', 'forest green', 'gold', 'dark gold', 'gray',
+ 'dark gray', 'light green', 'lemon chiffon', 'coral', 'sea green', 'sky blue',
+ 'magenta', 'purple', 'slate blue', 'violet red', 'navy blue', 'olive', 'orange',
+ 'red', 'sienna', 'yellow']),
+ comments=dict(type='str'),
+ details_level=dict(type='str', choices=['uid', 'standard', 'full']),
+ groups=dict(type='list'),
+ ignore_warnings=dict(type='bool'),
+ ignore_errors=dict(type='bool'),
+ uid=dict(type='str'),
+ new_name=dict(type='str')
+ )
+ argument_spec.update(checkpoint_argument_spec_for_objects)
+
+ module = AnsibleModule(argument_spec=argument_spec, required_one_of=[['name', 'uid']],
+ mutually_exclusive=[['name', 'uid']])
+ api_call_object = 'network'
+
+ result = api_call(module, api_call_object)
+ module.exit_json(**result)
+
+
+if __name__ == '__main__':
+ main()
diff --git a/lib/ansible/modules/network/checkpoint/cp_publish.py b/lib/ansible/modules/network/checkpoint/cp_publish.py
new file mode 100644
index 0000000000..0ddade67b0
--- /dev/null
+++ b/lib/ansible/modules/network/checkpoint/cp_publish.py
@@ -0,0 +1,76 @@
+#!/usr/bin/python
+# -*- coding: utf-8 -*-
+#
+# Ansible module to manage CheckPoint Firewall (c) 2019
+#
+# Ansible is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# Ansible is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with Ansible. If not, see .
+#
+
+from __future__ import (absolute_import, division, print_function)
+
+__metaclass__ = type
+
+ANSIBLE_METADATA = {'metadata_version': '1.1',
+ 'status': ['preview'],
+ 'supported_by': 'community'}
+
+DOCUMENTATION = """
+---
+module: cp_publish
+short_description: All the changes done by this user will be seen by all users only after publish is called.
+description:
+ - All the changes done by this user will be seen by all users only after publish is called.
+ All operations are performed over Web Services API.
+version_added: "2.9"
+author: "Or Soffer (@chkp-orso)"
+options:
+ uid:
+ description:
+ - Session unique identifier. Specify it to publish a different session than the one you currently use.
+ type: str
+extends_documentation_fragment: checkpoint_commands
+"""
+
+EXAMPLES = """
+- name: publish
+ cp_publish:
+"""
+
+RETURN = """
+cp_publish:
+ description: The checkpoint publish output.
+ returned: always.
+ type: dict
+"""
+
+from ansible.module_utils.basic import AnsibleModule
+from ansible.module_utils.network.checkpoint.checkpoint import checkpoint_argument_spec_for_commands, api_command
+
+
+def main():
+ argument_spec = dict(
+ uid=dict(type='str')
+ )
+ argument_spec.update(checkpoint_argument_spec_for_commands)
+
+ module = AnsibleModule(argument_spec=argument_spec)
+
+ command = "publish"
+
+ result = api_command(module, command)
+ module.exit_json(**result)
+
+
+if __name__ == '__main__':
+ main()
diff --git a/lib/ansible/plugins/doc_fragments/checkpoint_commands.py b/lib/ansible/plugins/doc_fragments/checkpoint_commands.py
new file mode 100644
index 0000000000..9f17df52f2
--- /dev/null
+++ b/lib/ansible/plugins/doc_fragments/checkpoint_commands.py
@@ -0,0 +1,21 @@
+# -*- coding: utf-8 -*-
+
+# Copyright: (c) 2019, Or Soffer
+# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
+
+
+class ModuleDocFragment(object):
+
+ # Standard files documentation fragment
+ DOCUMENTATION = r'''
+options:
+ wait_for_task:
+ description:
+ - Wait for the task to end. Such as publish task.
+ type: bool
+ default: True
+ version:
+ description:
+ - Version of checkpoint. If not given one, the latest version taken.
+ type: str
+'''
diff --git a/lib/ansible/plugins/doc_fragments/checkpoint_objects.py b/lib/ansible/plugins/doc_fragments/checkpoint_objects.py
new file mode 100644
index 0000000000..a5dc858e13
--- /dev/null
+++ b/lib/ansible/plugins/doc_fragments/checkpoint_objects.py
@@ -0,0 +1,34 @@
+# -*- coding: utf-8 -*-
+
+# Copyright: (c) 2019, Or Soffer
+# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
+
+
+class ModuleDocFragment(object):
+
+ # Standard files documentation fragment
+ DOCUMENTATION = r'''
+options:
+ state:
+ description:
+ - State of the access rule (present or absent). Defaults to present.
+ type: str
+ required: True
+ choices:
+ - 'present'
+ - 'absent'
+ auto_publish_session:
+ description:
+ - Publish the current session if changes have been performed
+ after task completes.
+ type: bool
+ wait_for_task:
+ description:
+ - Wait for the task to end. Such as publish task.
+ type: bool
+ default: True
+ version:
+ description:
+ - Version of checkpoint. If not given one, the latest version taken.
+ type: str
+'''
diff --git a/lib/ansible/plugins/httpapi/checkpoint.py b/lib/ansible/plugins/httpapi/checkpoint.py
index ae304117f9..1e0a6164be 100644
--- a/lib/ansible/plugins/httpapi/checkpoint.py
+++ b/lib/ansible/plugins/httpapi/checkpoint.py
@@ -63,7 +63,7 @@ class HttpApi(HttpApiBase):
return response.getcode(), self._response_to_json(value)
except AnsibleConnectionFailure as e:
- return 404, 'Object not found'
+ return 404, e.message
except HTTPError as e:
error = json.loads(e.read())
return e.code, error