1#!/usr/bin/python 2# -*- coding: utf-8 -*- 3# 4# Copyright: (c) 2018, F5 Networks Inc. 5# GNU General Public License v3.0 (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) 6 7from __future__ import absolute_import, division, print_function 8__metaclass__ = type 9 10 11ANSIBLE_METADATA = {'metadata_version': '1.1', 12 'status': ['preview'], 13 'supported_by': 'certified'} 14 15DOCUMENTATION = r''' 16--- 17module: bigip_firewall_global_rules 18short_description: Manage AFM global rule settings on BIG-IP 19description: 20 - Configures the global network firewall rules. These firewall rules are 21 applied to all packets except those going through the management 22 interface. They are applied first, before any firewall rules for the 23 packet's virtual server, route domain, and/or self IP. 24version_added: 2.8 25options: 26 enforced_policy: 27 description: 28 - Specifies an enforced firewall policy. 29 - C(enforced_policy) rules are enforced globally. 30 type: str 31 service_policy: 32 description: 33 - Specifies a service policy that would apply to traffic globally. 34 - The service policy is applied to all flows, provided if there are 35 no other context specific service policy configuration that 36 overrides the global service policy. For example, when a service 37 policy is configured both at a global level, as well as on a 38 firewall rule, and a flow matches the rule, the more specific 39 service policy configuration in the rule will override the service 40 policy setting at the global level. 41 - The service policy associated here can be created using the 42 C(bigip_service_policy) module. 43 type: str 44 staged_policy: 45 description: 46 - Specifies a staged firewall policy. 47 - C(staged_policy) rules are not enforced while all the visibility 48 aspects namely statistics, reporting and logging function as if 49 the staged-policy rules were enforced globally. 50 type: str 51 description: 52 description: 53 - Description for the global list of firewall rules. 54 type: str 55extends_documentation_fragment: f5 56author: 57 - Tim Rupp (@caphrim007) 58''' 59 60EXAMPLES = r''' 61- name: Change enforced policy in AFM global rules 62 bigip_firewall_global_rules: 63 enforced_policy: enforcing1 64 provider: 65 password: secret 66 server: lb.mydomain.com 67 user: admin 68 delegate_to: localhost 69''' 70 71RETURN = r''' 72enforced_policy: 73 description: The new global Enforced Policy. 74 returned: changed 75 type: str 76 sample: /Common/enforced1 77service_policy: 78 description: The new global Service Policy. 79 returned: changed 80 type: str 81 sample: /Common/service1 82staged_policy: 83 description: The new global Staged Policy. 84 returned: changed 85 type: str 86 sample: /Common/staged1 87description: 88 description: The new description. 89 returned: changed 90 type: str 91 sample: My description 92''' 93 94from ansible.module_utils.basic import AnsibleModule 95 96try: 97 from library.module_utils.network.f5.bigip import F5RestClient 98 from library.module_utils.network.f5.common import F5ModuleError 99 from library.module_utils.network.f5.common import AnsibleF5Parameters 100 from library.module_utils.network.f5.common import fq_name 101 from library.module_utils.network.f5.common import f5_argument_spec 102 from library.module_utils.network.f5.compare import cmp_str_with_none 103except ImportError: 104 from ansible.module_utils.network.f5.bigip import F5RestClient 105 from ansible.module_utils.network.f5.common import F5ModuleError 106 from ansible.module_utils.network.f5.common import AnsibleF5Parameters 107 from ansible.module_utils.network.f5.common import fq_name 108 from ansible.module_utils.network.f5.common import f5_argument_spec 109 from ansible.module_utils.network.f5.compare import cmp_str_with_none 110 111 112class Parameters(AnsibleF5Parameters): 113 api_map = { 114 'enforcedPolicy': 'enforced_policy', 115 'servicePolicy': 'service_policy', 116 'stagedPolicy': 'staged_policy', 117 } 118 119 api_attributes = [ 120 'enforcedPolicy', 121 'servicePolicy', 122 'stagedPolicy', 123 'description', 124 ] 125 126 returnables = [ 127 'enforced_policy', 128 'service_policy', 129 'staged_policy', 130 'description', 131 ] 132 133 updatables = [ 134 'enforced_policy', 135 'service_policy', 136 'staged_policy', 137 'description', 138 ] 139 140 141class ApiParameters(Parameters): 142 @property 143 def description(self): 144 if self._values['description'] in [None, 'none']: 145 return None 146 return self._values['description'] 147 148 149class ModuleParameters(Parameters): 150 @property 151 def enforced_policy(self): 152 if self._values['enforced_policy'] is None: 153 return None 154 if self._values['enforced_policy'] in ['', 'none']: 155 return '' 156 return fq_name(self.partition, self._values['enforced_policy']) 157 158 @property 159 def service_policy(self): 160 if self._values['service_policy'] is None: 161 return None 162 if self._values['service_policy'] in ['', 'none']: 163 return '' 164 return fq_name(self.partition, self._values['service_policy']) 165 166 @property 167 def staged_policy(self): 168 if self._values['staged_policy'] is None: 169 return None 170 if self._values['staged_policy'] in ['', 'none']: 171 return '' 172 return fq_name(self.partition, self._values['staged_policy']) 173 174 @property 175 def description(self): 176 if self._values['description'] is None: 177 return None 178 elif self._values['description'] in ['none', '']: 179 return '' 180 return self._values['description'] 181 182 183class Changes(Parameters): 184 def to_return(self): 185 result = {} 186 try: 187 for returnable in self.returnables: 188 result[returnable] = getattr(self, returnable) 189 result = self._filter_params(result) 190 except Exception: 191 pass 192 return result 193 194 195class UsableChanges(Changes): 196 pass 197 198 199class ReportableChanges(Changes): 200 pass 201 202 203class Difference(object): 204 def __init__(self, want, have=None): 205 self.want = want 206 self.have = have 207 208 def compare(self, param): 209 try: 210 result = getattr(self, param) 211 return result 212 except AttributeError: 213 return self.__default(param) 214 215 def __default(self, param): 216 attr1 = getattr(self.want, param) 217 try: 218 attr2 = getattr(self.have, param) 219 if attr1 != attr2: 220 return attr1 221 except AttributeError: 222 return attr1 223 224 @property 225 def description(self): 226 return cmp_str_with_none(self.want.description, self.have.description) 227 228 @property 229 def enforced_policy(self): 230 return cmp_str_with_none(self.want.enforced_policy, self.have.enforced_policy) 231 232 @property 233 def staged_policy(self): 234 return cmp_str_with_none(self.want.staged_policy, self.have.staged_policy) 235 236 @property 237 def service_policy(self): 238 return cmp_str_with_none(self.want.service_policy, self.have.service_policy) 239 240 241class ModuleManager(object): 242 def __init__(self, *args, **kwargs): 243 self.module = kwargs.get('module', None) 244 self.client = F5RestClient(**self.module.params) 245 self.want = ModuleParameters(params=self.module.params) 246 self.have = ApiParameters() 247 self.changes = UsableChanges() 248 249 def _set_changed_options(self): 250 changed = {} 251 for key in Parameters.returnables: 252 if getattr(self.want, key) is not None: 253 changed[key] = getattr(self.want, key) 254 if changed: 255 self.changes = UsableChanges(params=changed) 256 257 def _update_changed_options(self): 258 diff = Difference(self.want, self.have) 259 updatables = Parameters.updatables 260 changed = dict() 261 for k in updatables: 262 change = diff.compare(k) 263 if change is None: 264 continue 265 else: 266 if isinstance(change, dict): 267 changed.update(change) 268 else: 269 changed[k] = change 270 if changed: 271 self.changes = UsableChanges(params=changed) 272 return True 273 return False 274 275 def should_update(self): 276 result = self._update_changed_options() 277 if result: 278 return True 279 return False 280 281 def exec_module(self): 282 result = dict() 283 284 changed = self.present() 285 286 reportable = ReportableChanges(params=self.changes.to_return()) 287 changes = reportable.to_return() 288 result.update(**changes) 289 result.update(dict(changed=changed)) 290 self._announce_deprecations(result) 291 return result 292 293 def _announce_deprecations(self, result): 294 warnings = result.pop('__warnings', []) 295 for warning in warnings: 296 self.client.module.deprecate( 297 msg=warning['msg'], 298 version=warning['version'] 299 ) 300 301 def present(self): 302 return self.update() 303 304 def update(self): 305 self.have = self.read_current_from_device() 306 if not self.should_update(): 307 return False 308 if self.module.check_mode: 309 return True 310 self.update_on_device() 311 return True 312 313 def update_on_device(self): 314 params = self.changes.api_params() 315 uri = "https://{0}:{1}/mgmt/tm/security/firewall/global-rules".format( 316 self.client.provider['server'], 317 self.client.provider['server_port'] 318 ) 319 resp = self.client.api.patch(uri, json=params) 320 try: 321 response = resp.json() 322 except ValueError as ex: 323 raise F5ModuleError(str(ex)) 324 325 if 'code' in response and response['code'] == 400: 326 if 'message' in response: 327 raise F5ModuleError(response['message']) 328 else: 329 raise F5ModuleError(resp.content) 330 331 def read_current_from_device(self): 332 uri = "https://{0}:{1}/mgmt/tm/security/firewall/global-rules".format( 333 self.client.provider['server'], 334 self.client.provider['server_port'] 335 ) 336 resp = self.client.api.get(uri) 337 try: 338 response = resp.json() 339 except ValueError as ex: 340 raise F5ModuleError(str(ex)) 341 342 if 'code' in response and response['code'] == 400: 343 if 'message' in response: 344 raise F5ModuleError(response['message']) 345 else: 346 raise F5ModuleError(resp.content) 347 return ApiParameters(params=response) 348 349 350class ArgumentSpec(object): 351 def __init__(self): 352 self.supports_check_mode = True 353 argument_spec = dict( 354 enforced_policy=dict(), 355 service_policy=dict(), 356 staged_policy=dict(), 357 description=dict(), 358 ) 359 self.argument_spec = {} 360 self.argument_spec.update(f5_argument_spec) 361 self.argument_spec.update(argument_spec) 362 363 364def main(): 365 spec = ArgumentSpec() 366 367 module = AnsibleModule( 368 argument_spec=spec.argument_spec, 369 supports_check_mode=spec.supports_check_mode, 370 ) 371 372 try: 373 mm = ModuleManager(module=module) 374 results = mm.exec_module() 375 module.exit_json(**results) 376 except F5ModuleError as ex: 377 module.fail_json(msg=str(ex)) 378 379 380if __name__ == '__main__': 381 main() 382