1#!/usr/bin/python 2# -*- coding: utf-8 -*- 3# 4# Ansible module to manage PaloAltoNetworks Firewall 5# (c) 2019, Tomi Raittinen <tomi.raittinen@gmail.com> 6# (c) 2016, techbizdev <techbizdev@paloaltonetworks.com> 7# 8# This file is part of Ansible 9# 10# Ansible is free software: you can redistribute it and/or modify 11# it under the terms of the GNU General Public License as published by 12# the Free Software Foundation, either version 3 of the License, or 13# (at your option) any later version. 14# 15# Ansible is distributed in the hope that it will be useful, 16# but WITHOUT ANY WARRANTY; without even the implied warranty of 17# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 18# GNU General Public License for more details. 19# 20# You should have received a copy of the GNU General Public License 21# along with Ansible. If not, see <http://www.gnu.org/licenses/>. 22 23DOCUMENTATION = ''' 24--- 25module: panos_commit 26short_description: commit firewall's candidate configuration 27description: 28 - PanOS module that will commit firewall's candidate configuration on 29 - the device. The new configuration will become active immediately. 30author: 31 - Luigi Mori (@jtschichold) 32 - Ivan Bojer (@ivanbojer) 33 - Tomi Raittinen (@traittinen) 34version_added: "2.3" 35requirements: 36 - pan-python 37deprecated: 38 alternative: Use U(https://galaxy.ansible.com/PaloAltoNetworks/paloaltonetworks) instead. 39 removed_in: "2.12" 40 why: Consolidating code base. 41options: 42 ip_address: 43 description: 44 - IP address (or hostname) of PAN-OS device. 45 required: true 46 password: 47 description: 48 - Password for authentication. If the value is not specified in the 49 task, the value of environment variable C(ANSIBLE_NET_PASSWORD) 50 will be used instead. 51 required: true 52 username: 53 description: 54 - Username for authentication. If the value is not specified in the 55 task, the value of environment variable C(ANSIBLE_NET_USERNAME) 56 will be used instead if defined. C(admin) will be used if nothing 57 above is defined. 58 default: admin 59 interval: 60 description: 61 - interval for checking commit job 62 default: 0.5 63 timeout: 64 description: 65 - timeout for commit job 66 sync: 67 description: 68 - if commit should be synchronous 69 type: bool 70 default: 'yes' 71 description: 72 description: 73 - Commit description/comment 74 type: str 75 version_added: "2.8" 76 commit_changes_by: 77 description: 78 - Commit changes made by specified admin 79 type: list 80 version_added: "2.8" 81 commit_vsys: 82 description: 83 - Commit changes for specified VSYS 84 type: list 85 version_added: "2.8" 86''' 87 88EXAMPLES = ''' 89# Commit candidate config on 192.168.1.1 in sync mode 90- panos_commit: 91 ip_address: "192.168.1.1" 92 username: "admin" 93 password: "admin" 94''' 95 96RETURN = ''' 97panos_commit: 98 description: Information about commit job. 99 returned: always 100 type: complex 101 version_added: 2.8 102 contains: 103 job_id: 104 description: Palo Alto job ID for the commit operation. Only returned if commit job is launched on device. 105 returned: always 106 type: str 107 sample: "139" 108 status_code: 109 description: Palo Alto API status code. Null if commit is successful. 110 returned: always 111 type: str 112 sample: 19 113 status_detail: 114 description: Palo Alto API detailed status message. 115 returned: always 116 type: str 117 sample: Configuration committed successfully 118 status_text: 119 description: Palo Alto API status text. 120 returned: always 121 type: str 122 sample: success 123''' 124 125ANSIBLE_METADATA = {'metadata_version': '1.1', 126 'status': ['deprecated'], 127 'supported_by': 'community'} 128 129 130from ansible.module_utils.basic import AnsibleModule, env_fallback 131import xml.etree.ElementTree as etree 132 133try: 134 import pan.xapi 135 HAS_LIB = True 136except ImportError: 137 HAS_LIB = False 138 139 140def main(): 141 argument_spec = dict( 142 ip_address=dict(required=True, type='str'), 143 password=dict(fallback=(env_fallback, ['ANSIBLE_NET_PASSWORD']), no_log=True), 144 username=dict(fallback=(env_fallback, ['ANSIBLE_NET_USERNAME']), default="admin"), 145 interval=dict(default=0.5), 146 timeout=dict(), 147 sync=dict(type='bool', default=True), 148 description=dict(type='str'), 149 commit_changes_by=dict(type='list'), 150 commit_vsys=dict(type='list') 151 ) 152 module = AnsibleModule(argument_spec=argument_spec, supports_check_mode=False) 153 154 if not HAS_LIB: 155 module.fail_json(msg='pan-python is required for this module') 156 157 ip_address = module.params["ip_address"] 158 if not ip_address: 159 module.fail_json(msg="ip_address should be specified") 160 161 password = module.params["password"] 162 if not password: 163 module.fail_json(msg="password is required") 164 165 username = module.params['username'] 166 if not username: 167 module.fail_json(msg="username is required") 168 169 interval = module.params['interval'] 170 timeout = module.params['timeout'] 171 sync = module.params['sync'] 172 173 xapi = pan.xapi.PanXapi( 174 hostname=ip_address, 175 api_username=username, 176 api_password=password 177 ) 178 179 cmd = "<commit>" 180 181 description = module.params["description"] 182 if description: 183 cmd += "<description>" + description + "</description>" 184 185 commit_changes_by = module.params["commit_changes_by"] 186 commit_vsys = module.params["commit_vsys"] 187 188 if commit_changes_by or commit_vsys: 189 190 cmd += "<partial>" 191 192 if commit_changes_by: 193 cmd += "<admin>" 194 for admin in commit_changes_by: 195 cmd += "<member>" + admin + "</member>" 196 cmd += "</admin>" 197 198 if commit_vsys: 199 cmd += "<vsys>" 200 for vsys in commit_vsys: 201 cmd += "<member>" + vsys + "</member>" 202 cmd += "</vsys>" 203 204 cmd += "</partial><force></force>" 205 206 cmd += "</commit>" 207 208 xapi.commit( 209 cmd=cmd, 210 sync=sync, 211 interval=interval, 212 timeout=timeout 213 ) 214 215 try: 216 result = xapi.xml_root().encode('utf-8') 217 root = etree.fromstring(result) 218 job_id = root.find('./result/job/id').text 219 except AttributeError: 220 job_id = None 221 222 panos_commit_details = dict( 223 status_text=xapi.status, 224 status_code=xapi.status_code, 225 status_detail=xapi.status_detail, 226 job_id=job_id 227 ) 228 229 if "Commit failed" in xapi.status_detail: 230 module.fail_json(msg=xapi.status_detail, panos_commit=panos_commit_details) 231 232 if job_id: 233 module.exit_json(changed=True, msg="Commit successful.", panos_commit=panos_commit_details) 234 else: 235 module.exit_json(changed=False, msg="No changes to commit.", panos_commit=panos_commit_details) 236 237 238if __name__ == '__main__': 239 main() 240