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_asm_policy_manage 18short_description: Manage BIG-IP ASM policies 19description: 20 - Manage BIG-IP ASM policies, create from templates and manage global policy settings. 21version_added: 2.8 22options: 23 active: 24 description: 25 - If C(yes) will apply and activate existing inactive policy. If C(no), it will 26 deactivate existing active policy. Generally should be C(yes) only in cases where 27 you want to activate new or existing policy. 28 default: no 29 type: bool 30 name: 31 description: 32 - The ASM policy to manage or create. 33 type: str 34 required: True 35 state: 36 description: 37 - When C(state) is C(present), and C(template) parameter is provided, 38 new ASM policy is created from template with the given policy C(name). 39 - When C(state) is present and no C(template) parameter is provided 40 new blank ASM policy is created with the given policy C(name). 41 - When C(state) is C(absent), ensures that the policy is removed, even if it is 42 currently active. 43 type: str 44 choices: 45 - present 46 - absent 47 default: present 48 template: 49 description: 50 - An ASM policy built-in template. If the template does not exist we will raise an error. 51 - Once the policy has been created, this value cannot change. 52 - The C(Comprehensive), C(Drupal), C(Fundamental), C(Joomla), 53 C(Vulnerability Assessment Baseline), and C(Wordpress) templates are only available 54 on BIG-IP versions >= 13. 55 type: str 56 choices: 57 - ActiveSync v1.0 v2.0 (http) 58 - ActiveSync v1.0 v2.0 (https) 59 - Comprehensive 60 - Drupal 61 - Fundamental 62 - Joomla 63 - LotusDomino 6.5 (http) 64 - LotusDomino 6.5 (https) 65 - OWA Exchange 2003 (http) 66 - OWA Exchange 2003 (https) 67 - OWA Exchange 2003 with ActiveSync (http) 68 - OWA Exchange 2003 with ActiveSync (https) 69 - OWA Exchange 2007 (http) 70 - OWA Exchange 2007 (https) 71 - OWA Exchange 2007 with ActiveSync (http) 72 - OWA Exchange 2007 with ActiveSync (https) 73 - OWA Exchange 2010 (http) 74 - OWA Exchange 2010 (https) 75 - Oracle 10g Portal (http) 76 - Oracle 10g Portal (https) 77 - Oracle Applications 11i (http) 78 - Oracle Applications 11i (https) 79 - PeopleSoft Portal 9 (http) 80 - PeopleSoft Portal 9 (https) 81 - Rapid Deployment Policy 82 - SAP NetWeaver 7 (http) 83 - SAP NetWeaver 7 (https) 84 - SharePoint 2003 (http) 85 - SharePoint 2003 (https) 86 - SharePoint 2007 (http) 87 - SharePoint 2007 (https) 88 - SharePoint 2010 (http) 89 - SharePoint 2010 (https) 90 - Vulnerability Assessment Baseline 91 - Wordpress 92 partition: 93 description: 94 - Device partition to manage resources on. 95 type: str 96 default: Common 97extends_documentation_fragment: f5 98author: 99 - Wojciech Wypior (@wojtek0806) 100''' 101 102EXAMPLES = r''' 103- name: Create ASM policy from template 104 bigip_asm_policy: 105 name: new_sharepoint_policy 106 template: SharePoint 2007 (http) 107 state: present 108 provider: 109 server: lb.mydomain.com 110 user: admin 111 password: secret 112 delegate_to: localhost 113 114- name: Create blank ASM policy 115 bigip_asm_policy: 116 name: new_blank_policy 117 state: present 118 provider: 119 server: lb.mydomain.com 120 user: admin 121 password: secret 122 delegate_to: localhost 123 124- name: Create blank ASM policy and activate 125 bigip_asm_policy: 126 name: new_blank_policy 127 active: yes 128 state: present 129 provider: 130 server: lb.mydomain.com 131 user: admin 132 password: secret 133 delegate_to: localhost 134 135- name: Activate ASM policy 136 bigip_asm_policy: 137 name: inactive_policy 138 active: yes 139 state: present 140 provider: 141 server: lb.mydomain.com 142 user: admin 143 password: secret 144 delegate_to: localhost 145 146- name: Deactivate ASM policy 147 bigip_asm_policy_manage: 148 name: active_policy 149 state: present 150 provider: 151 server: lb.mydomain.com 152 user: admin 153 password: secret 154 delegate_to: localhost 155''' 156 157RETURN = r''' 158active: 159 description: Set when activating/deactivating ASM policy 160 returned: changed 161 type: bool 162 sample: yes 163state: 164 description: Action performed on the target device. 165 returned: changed 166 type: str 167 sample: absent 168template: 169 description: Name of the built-in ASM policy template 170 returned: changed 171 type: str 172 sample: OWA Exchange 2007 (https) 173name: 174 description: Name of the ASM policy to be managed/created 175 returned: changed 176 type: str 177 sample: Asm_APP1_Transparent 178''' 179 180import time 181from ansible.module_utils.basic import AnsibleModule 182from ansible.module_utils.basic import env_fallback 183from distutils.version import LooseVersion 184 185try: 186 from library.module_utils.network.f5.bigip import F5RestClient 187 from library.module_utils.network.f5.common import F5ModuleError 188 from library.module_utils.network.f5.common import AnsibleF5Parameters 189 from library.module_utils.network.f5.common import fq_name 190 from library.module_utils.network.f5.common import transform_name 191 from library.module_utils.network.f5.common import f5_argument_spec 192 from library.module_utils.network.f5.icontrol import tmos_version 193 from library.module_utils.network.f5.icontrol import module_provisioned 194except ImportError: 195 from ansible.module_utils.network.f5.bigip import F5RestClient 196 from ansible.module_utils.network.f5.common import F5ModuleError 197 from ansible.module_utils.network.f5.common import AnsibleF5Parameters 198 from ansible.module_utils.network.f5.common import fq_name 199 from ansible.module_utils.network.f5.common import transform_name 200 from ansible.module_utils.network.f5.common import f5_argument_spec 201 from ansible.module_utils.network.f5.icontrol import tmos_version 202 from ansible.module_utils.network.f5.icontrol import module_provisioned 203 204 205class Parameters(AnsibleF5Parameters): 206 updatables = [ 207 'active', 208 ] 209 210 returnables = [ 211 'name', 212 'template', 213 'active', 214 ] 215 216 api_attributes = [ 217 'name', 218 'active', 219 ] 220 api_map = { 221 } 222 223 @property 224 def template_link(self): 225 if self._values['template_link'] is not None: 226 return self._values['template_link'] 227 228 result = None 229 230 uri = "https://{0}:{1}/mgmt/tm/asm/policy-templates/".format( 231 self.client.provider['server'], 232 self.client.provider['server_port'], 233 ) 234 235 query = "?$filter=contains(name,'{0}')".format(self.template.upper()) 236 resp = self.client.api.get(uri + query) 237 238 try: 239 response = resp.json() 240 except ValueError as ex: 241 raise F5ModuleError(str(ex)) 242 243 if 'code' in response and response['code'] == 400: 244 if 'message' in response: 245 raise F5ModuleError(response['message']) 246 else: 247 raise F5ModuleError(resp.content) 248 if 'items' in response and response['items'] != []: 249 result = dict(link=response['items'][0]['selfLink']) 250 251 return result 252 253 def to_return(self): 254 result = {} 255 for returnable in self.returnables: 256 result[returnable] = getattr(self, returnable) 257 result = self._filter_params(result) 258 return result 259 260 261class V1Parameters(Parameters): 262 @property 263 def template(self): 264 if self._values['template'] is None: 265 return None 266 template_map = { 267 'ActiveSync v1.0 v2.0 (http)': 'POLICY_TEMPLATE_ACTIVESYNC_V1_0_V2_0_HTTP', 268 'ActiveSync v1.0 v2.0 (https)': 'POLICY_TEMPLATE_ACTIVESYNC_V1_0_V2_0_HTTPS', 269 'LotusDomino 6.5 (http)': 'POLICY_TEMPLATE_LOTUSDOMINO_6_5_HTTP', 270 'LotusDomino 6.5 (https)': 'POLICY_TEMPLATE_LOTUSDOMINO_6_5_HTTPS', 271 'OWA Exchange 2003 (http)': 'POLICY_TEMPLATE_OWA_EXCHANGE_2003_HTTP', 272 'OWA Exchange 2003 (https)': 'POLICY_TEMPLATE_OWA_EXCHANGE_2003_HTTPS', 273 'OWA Exchange 2003 with ActiveSync (http)': 'POLICY_TEMPLATE_OWA_EXCHANGE_2003_WITH_ACTIVESYNC_HTTP', 274 'OWA Exchange 2003 with ActiveSync (https)': 'POLICY_TEMPLATE_OWA_EXCHANGE_2003_WITH_ACTIVESYNC_HTTPS', 275 'OWA Exchange 2007 (http)': 'POLICY_TEMPLATE_OWA_EXCHANGE_2007_HTTP', 276 'OWA Exchange 2007 (https)': 'POLICY_TEMPLATE_OWA_EXCHANGE_2007_HTTPS', 277 'OWA Exchange 2007 with ActiveSync (http)': 'POLICY_TEMPLATE_OWA_EXCHANGE_2007_WITH_ACTIVESYNC_HTTP', 278 'OWA Exchange 2007 with ActiveSync (https)': 'POLICY_TEMPLATE_OWA_EXCHANGE_2007_WITH_ACTIVESYNC_HTTPS', 279 'OWA Exchange 2010 (http)': 'POLICY_TEMPLATE_OWA_EXCHANGE_2010_HTTP', 280 'OWA Exchange 2010 (https)': 'POLICY_TEMPLATE_OWA_EXCHANGE_2010_HTTPS', 281 'Oracle 10g Portal (http)': 'POLICY_TEMPLATE_ORACLE_10G_PORTAL_HTTP', 282 'Oracle 10g Portal (https)': 'POLICY_TEMPLATE_ORACLE_10G_PORTAL_HTTPS', 283 'Oracle Applications 11i (http)': 'POLICY_TEMPLATE_ORACLE_APPLICATIONS_11I_HTTP', 284 'Oracle Applications 11i (https)': 'POLICY_TEMPLATE_ORACLE_APPLICATIONS_11I_HTTPS', 285 'PeopleSoft Portal 9 (http)': 'POLICY_TEMPLATE_PEOPLESOFT_PORTAL_9_HTTP', 286 'PeopleSoft Portal 9 (https)': 'POLICY_TEMPLATE_PEOPLESOFT_PORTAL_9_HTTPS', 287 'Rapid Deployment Policy': 'POLICY_TEMPLATE_RAPID_DEPLOYMENT', 288 'SAP NetWeaver 7 (http)': 'POLICY_TEMPLATE_SAP_NETWEAVER_7_HTTP', 289 'SAP NetWeaver 7 (https)': 'POLICY_TEMPLATE_SAP_NETWEAVER_7_HTTPS', 290 'SharePoint 2003 (http)': 'POLICY_TEMPLATE_SHAREPOINT_2003_HTTP', 291 'SharePoint 2003 (https)': 'POLICY_TEMPLATE_SHAREPOINT_2003_HTTPS', 292 'SharePoint 2007 (http)': 'POLICY_TEMPLATE_SHAREPOINT_2007_HTTP', 293 'SharePoint 2007 (https)': 'POLICY_TEMPLATE_SHAREPOINT_2007_HTTPS', 294 'SharePoint 2010 (http)': 'POLICY_TEMPLATE_SHAREPOINT_2010_HTTP', 295 'SharePoint 2010 (https)': 'POLICY_TEMPLATE_SHAREPOINT_2010_HTTPS' 296 } 297 if self._values['template'] in template_map: 298 return template_map[self._values['template']] 299 else: 300 raise F5ModuleError( 301 "The specified template is not valid for this version of BIG-IP." 302 ) 303 304 305class V2Parameters(Parameters): 306 @property 307 def template(self): 308 if self._values['template'] is None: 309 return None 310 template_map = { 311 'ActiveSync v1.0 v2.0 (http)': 'POLICY_TEMPLATE_ACTIVESYNC_V1_0_V2_0_HTTP', 312 'ActiveSync v1.0 v2.0 (https)': 'POLICY_TEMPLATE_ACTIVESYNC_V1_0_V2_0_HTTPS', 313 'Comprehensive': 'POLICY_TEMPLATE_COMPREHENSIVE', # v13 314 'Drupal': 'POLICY_TEMPLATE_DRUPAL', # v13 315 'Fundamental': 'POLICY_TEMPLATE_FUNDAMENTAL', # v13 316 'Joomla': 'POLICY_TEMPLATE_JOOMLA', # v13 317 'LotusDomino 6.5 (http)': 'POLICY_TEMPLATE_LOTUSDOMINO_6_5_HTTP', 318 'LotusDomino 6.5 (https)': 'POLICY_TEMPLATE_LOTUSDOMINO_6_5_HTTPS', 319 'OWA Exchange 2003 (http)': 'POLICY_TEMPLATE_OWA_EXCHANGE_2003_HTTP', 320 'OWA Exchange 2003 (https)': 'POLICY_TEMPLATE_OWA_EXCHANGE_2003_HTTPS', 321 'OWA Exchange 2003 with ActiveSync (http)': 'POLICY_TEMPLATE_OWA_EXCHANGE_2003_WITH_ACTIVESYNC_HTTP', 322 'OWA Exchange 2003 with ActiveSync (https)': 'POLICY_TEMPLATE_OWA_EXCHANGE_2003_WITH_ACTIVESYNC_HTTPS', 323 'OWA Exchange 2007 (http)': 'POLICY_TEMPLATE_OWA_EXCHANGE_2007_HTTP', 324 'OWA Exchange 2007 (https)': 'POLICY_TEMPLATE_OWA_EXCHANGE_2007_HTTPS', 325 'OWA Exchange 2007 with ActiveSync (http)': 'POLICY_TEMPLATE_OWA_EXCHANGE_2007_WITH_ACTIVESYNC_HTTP', 326 'OWA Exchange 2007 with ActiveSync (https)': 'POLICY_TEMPLATE_OWA_EXCHANGE_2007_WITH_ACTIVESYNC_HTTPS', 327 'OWA Exchange 2010 (http)': 'POLICY_TEMPLATE_OWA_EXCHANGE_2010_HTTP', 328 'OWA Exchange 2010 (https)': 'POLICY_TEMPLATE_OWA_EXCHANGE_2010_HTTPS', 329 'Oracle 10g Portal (http)': 'POLICY_TEMPLATE_ORACLE_10G_PORTAL_HTTP', 330 'Oracle 10g Portal (https)': 'POLICY_TEMPLATE_ORACLE_10G_PORTAL_HTTPS', 331 'Oracle Applications 11i (http)': 'POLICY_TEMPLATE_ORACLE_APPLICATIONS_11I_HTTP', 332 'Oracle Applications 11i (https)': 'POLICY_TEMPLATE_ORACLE_APPLICATIONS_11I_HTTPS', 333 'PeopleSoft Portal 9 (http)': 'POLICY_TEMPLATE_PEOPLESOFT_PORTAL_9_HTTP', 334 'PeopleSoft Portal 9 (https)': 'POLICY_TEMPLATE_PEOPLESOFT_PORTAL_9_HTTPS', 335 'Rapid Deployment Policy': 'POLICY_TEMPLATE_RAPID_DEPLOYMENT', 336 'SAP NetWeaver 7 (http)': 'POLICY_TEMPLATE_SAP_NETWEAVER_7_HTTP', 337 'SAP NetWeaver 7 (https)': 'POLICY_TEMPLATE_SAP_NETWEAVER_7_HTTPS', 338 'SharePoint 2003 (http)': 'POLICY_TEMPLATE_SHAREPOINT_2003_HTTP', 339 'SharePoint 2003 (https)': 'POLICY_TEMPLATE_SHAREPOINT_2003_HTTPS', 340 'SharePoint 2007 (http)': 'POLICY_TEMPLATE_SHAREPOINT_2007_HTTP', 341 'SharePoint 2007 (https)': 'POLICY_TEMPLATE_SHAREPOINT_2007_HTTPS', 342 'SharePoint 2010 (http)': 'POLICY_TEMPLATE_SHAREPOINT_2010_HTTP', 343 'SharePoint 2010 (https)': 'POLICY_TEMPLATE_SHAREPOINT_2010_HTTPS', 344 'Vulnerability Assessment Baseline': 'POLICY_TEMPLATE_VULNERABILITY_ASSESSMENT', # v13 345 'Wordpress': 'POLICY_TEMPLATE_WORDPRESS' # v13 346 } 347 return template_map[self._values['template']] 348 349 350class Changes(Parameters): 351 @property 352 def template(self): 353 if self._values['template'] is None: 354 return None 355 template_map = { 356 'POLICY_TEMPLATE_ACTIVESYNC_V1_0_V2_0_HTTP': 'ActiveSync v1.0 v2.0 (http)', 357 'POLICY_TEMPLATE_ACTIVESYNC_V1_0_V2_0_HTTPS': 'ActiveSync v1.0 v2.0 (https)', 358 'POLICY_TEMPLATE_COMPREHENSIVE': 'Comprehensive', 359 'POLICY_TEMPLATE_DRUPAL': 'Drupal', 360 'POLICY_TEMPLATE_FUNDAMENTAL': 'Fundamental', 361 'POLICY_TEMPLATE_JOOMLA': 'Joomla', 362 'POLICY_TEMPLATE_LOTUSDOMINO_6_5_HTTP': 'LotusDomino 6.5 (http)', 363 'POLICY_TEMPLATE_LOTUSDOMINO_6_5_HTTPS': 'LotusDomino 6.5 (https)', 364 'POLICY_TEMPLATE_OWA_EXCHANGE_2003_HTTP': 'OWA Exchange 2003 (http)', 365 'POLICY_TEMPLATE_OWA_EXCHANGE_2003_HTTPS': 'OWA Exchange 2003 (https)', 366 'POLICY_TEMPLATE_OWA_EXCHANGE_2003_WITH_ACTIVESYNC_HTTP': 'OWA Exchange 2003 with ActiveSync (http)', 367 'POLICY_TEMPLATE_OWA_EXCHANGE_2003_WITH_ACTIVESYNC_HTTPS': 'OWA Exchange 2003 with ActiveSync (https)', 368 'POLICY_TEMPLATE_OWA_EXCHANGE_2007_HTTP': 'OWA Exchange 2007 (http)', 369 'POLICY_TEMPLATE_OWA_EXCHANGE_2007_HTTPS': 'OWA Exchange 2007 (https)', 370 'POLICY_TEMPLATE_OWA_EXCHANGE_2007_WITH_ACTIVESYNC_HTTP': 'OWA Exchange 2007 with ActiveSync (http)', 371 'POLICY_TEMPLATE_OWA_EXCHANGE_2007_WITH_ACTIVESYNC_HTTPS': 'OWA Exchange 2007 with ActiveSync (https)', 372 'POLICY_TEMPLATE_OWA_EXCHANGE_2010_HTTP': 'OWA Exchange 2010 (http)', 373 'POLICY_TEMPLATE_OWA_EXCHANGE_2010_HTTPS': 'OWA Exchange 2010 (https)', 374 'POLICY_TEMPLATE_ORACLE_10G_PORTAL_HTTP': 'Oracle 10g Portal (http)', 375 'POLICY_TEMPLATE_ORACLE_10G_PORTAL_HTTPS': 'Oracle 10g Portal (https)', 376 'POLICY_TEMPLATE_ORACLE_APPLICATIONS_11I_HTTP': 'Oracle Applications 11i (http)', 377 'POLICY_TEMPLATE_ORACLE_APPLICATIONS_11I_HTTPS': 'Oracle Applications 11i (https)', 378 'POLICY_TEMPLATE_PEOPLESOFT_PORTAL_9_HTTP': 'PeopleSoft Portal 9 (http)', 379 'POLICY_TEMPLATE_PEOPLESOFT_PORTAL_9_HTTPS': 'PeopleSoft Portal 9 (https)', 380 'POLICY_TEMPLATE_RAPID_DEPLOYMENT': 'Rapid Deployment Policy', 381 'POLICY_TEMPLATE_SAP_NETWEAVER_7_HTTP': 'SAP NetWeaver 7 (http)', 382 'POLICY_TEMPLATE_SAP_NETWEAVER_7_HTTPS': 'SAP NetWeaver 7 (https)', 383 'POLICY_TEMPLATE_SHAREPOINT_2003_HTTP': 'SharePoint 2003 (http)', 384 'POLICY_TEMPLATE_SHAREPOINT_2003_HTTPS': 'SharePoint 2003 (https)', 385 'POLICY_TEMPLATE_SHAREPOINT_2007_HTTP': 'SharePoint 2007 (http)', 386 'POLICY_TEMPLATE_SHAREPOINT_2007_HTTPS': 'SharePoint 2007 (https)', 387 'POLICY_TEMPLATE_SHAREPOINT_2010_HTTP': 'SharePoint 2010 (http)', 388 'POLICY_TEMPLATE_SHAREPOINT_2010_HTTPS': 'SharePoint 2010 (https)', 389 'POLICY_TEMPLATE_VULNERABILITY_ASSESSMENT': 'Vulnerability Assessment Baseline', 390 'POLICY_TEMPLATE_WORDPRESS': 'Wordpress', 391 } 392 return template_map[self._values['template']] 393 394 395class Difference(object): 396 def __init__(self, want, have=None): 397 self.want = want 398 self.have = have 399 400 def compare(self, param): 401 try: 402 result = getattr(self, param) 403 return result 404 except AttributeError: 405 return self.__default(param) 406 407 def __default(self, param): 408 attr1 = getattr(self.want, param) 409 try: 410 attr2 = getattr(self.have, param) 411 if attr1 != attr2: 412 return attr1 413 except AttributeError: 414 return attr1 415 416 @property 417 def active(self): 418 if self.want.active is True and self.have.active is False: 419 return True 420 if self.want.active is False and self.have.active is True: 421 return False 422 423 424class BaseManager(object): 425 def __init__(self, *args, **kwargs): 426 self.client = kwargs.get('client', None) 427 self.module = kwargs.get('module', None) 428 self.have = None 429 self.changes = Changes() 430 431 def exec_module(self): 432 changed = False 433 result = dict() 434 state = self.want.state 435 436 if state == "present": 437 changed = self.present() 438 elif state == "absent": 439 changed = self.absent() 440 441 changes = self.changes.to_return() 442 result.update(**changes) 443 result.update(dict(changed=changed)) 444 return result 445 446 def _set_changed_options(self): 447 changed = {} 448 for key in Parameters.returnables: 449 if getattr(self.want, key) is not None: 450 changed[key] = getattr(self.want, key) 451 if changed: 452 self.changes = Changes(params=changed) 453 454 def should_update(self): 455 result = self._update_changed_options() 456 if result: 457 return True 458 return False 459 460 def _update_changed_options(self): 461 diff = Difference(self.want, self.have) 462 updatables = Parameters.updatables 463 changed = dict() 464 for k in updatables: 465 change = diff.compare(k) 466 if change is None: 467 continue 468 else: 469 if isinstance(change, dict): 470 changed.update(change) 471 else: 472 changed[k] = change 473 if changed: 474 self.changes = Changes(params=changed) 475 return True 476 return False 477 478 def present(self): 479 if self.exists(): 480 return self.update() 481 else: 482 return self.create() 483 484 def absent(self): 485 if not self.exists(): 486 return False 487 else: 488 return self.remove() 489 490 def create(self): 491 if self.want.active is None: 492 self.want.update(dict(active=False)) 493 self._set_changed_options() 494 if self.module.check_mode: 495 return True 496 497 if self.want.template is not None: 498 self.create_from_template() 499 if self.want.template is None: 500 self.create_blank() 501 if self.want.active: 502 self.activate() 503 return True 504 else: 505 return True 506 507 def update(self): 508 self.have = self.read_current_from_device() 509 if not self.should_update(): 510 return False 511 if self.module.check_mode: 512 return True 513 self.update_on_device() 514 if self.changes.active: 515 self.activate() 516 return True 517 518 def activate(self): 519 self.have = self.read_current_from_device() 520 task_id = self.apply_on_device() 521 if self.wait_for_task(task_id): 522 return True 523 else: 524 raise F5ModuleError('Apply policy task failed.') 525 526 def create_blank(self): 527 self.create_on_device() 528 if self.exists(): 529 return True 530 else: 531 raise F5ModuleError( 532 'Failed to create ASM policy: {0}'.format(self.want.name) 533 ) 534 535 def remove(self): 536 if self.module.check_mode: 537 return True 538 self.remove_from_device() 539 if self.exists(): 540 raise F5ModuleError( 541 'Failed to delete ASM policy: {0}'.format(self.want.name) 542 ) 543 return True 544 545 def is_activated(self): 546 if self.want.active is True: 547 return True 548 else: 549 return False 550 551 def exists(self): 552 uri = 'https://{0}:{1}/mgmt/tm/asm/policies/'.format( 553 self.client.provider['server'], 554 self.client.provider['server_port'], 555 ) 556 557 query = "?$filter=contains(name,'{0}')+and+contains(partition,'{1}')&$select=name,partition".format( 558 self.want.name, self.want.partition 559 ) 560 resp = self.client.api.get(uri + query) 561 562 try: 563 response = resp.json() 564 except ValueError as ex: 565 raise F5ModuleError(str(ex)) 566 if 'items' in response and response['items'] != []: 567 return True 568 return False 569 570 def wait_for_task(self, task_id): 571 uri = "https://{0}:{1}/mgmt/tm/asm/tasks/apply-policy/{2}".format( 572 self.client.provider['server'], 573 self.client.provider['server_port'], 574 task_id 575 ) 576 while True: 577 resp = self.client.api.get(uri) 578 579 try: 580 response = resp.json() 581 except ValueError as ex: 582 raise F5ModuleError(str(ex)) 583 584 if 'code' in response and response['code'] == 400: 585 if 'message' in response: 586 raise F5ModuleError(response['message']) 587 else: 588 raise F5ModuleError(resp.content) 589 590 if response['status'] in ['COMPLETED', 'FAILURE']: 591 break 592 time.sleep(1) 593 594 if response['status'] == 'FAILURE': 595 return False 596 if response['status'] == 'COMPLETED': 597 return True 598 599 def _get_policy_id(self): 600 policy_id = None 601 uri = "https://{0}:{1}/mgmt/tm/asm/policies/".format( 602 self.client.provider['server'], 603 self.client.provider['server_port'], 604 ) 605 query = "?$filter=contains(name,'{0}')+and+contains(partition,'{1}')&$select=name,id".format( 606 self.want.name, self.want.partition 607 ) 608 resp = self.client.api.get(uri + query) 609 610 try: 611 response = resp.json() 612 except ValueError as ex: 613 raise F5ModuleError(str(ex)) 614 615 if 'items' in response and response['items'] != []: 616 policy_id = response['items'][0]['id'] 617 if not policy_id: 618 raise F5ModuleError("The policy was not found") 619 620 return policy_id 621 622 def update_on_device(self): 623 params = self.changes.api_params() 624 policy_id = self._get_policy_id() 625 uri = "https://{0}:{1}/mgmt/tm/asm/policies/{2}".format( 626 self.client.provider['server'], 627 self.client.provider['server_port'], 628 policy_id 629 ) 630 if not params['active']: 631 resp = self.client.api.patch(uri, json=params) 632 633 try: 634 response = resp.json() 635 except ValueError as ex: 636 raise F5ModuleError(str(ex)) 637 638 if 'code' in response and response['code'] == 400: 639 if 'message' in response: 640 raise F5ModuleError(response['message']) 641 else: 642 raise F5ModuleError(resp.content) 643 644 def read_current_from_device(self): 645 policy_id = self._get_policy_id() 646 uri = "https://{0}:{1}/mgmt/tm/asm/policies/{2}".format( 647 self.client.provider['server'], 648 self.client.provider['server_port'], 649 policy_id 650 ) 651 resp = self.client.api.get(uri) 652 653 try: 654 response = resp.json() 655 except ValueError as ex: 656 raise F5ModuleError(str(ex)) 657 658 if 'code' in response and response['code'] == 400: 659 if 'message' in response: 660 raise F5ModuleError(response['message']) 661 else: 662 raise F5ModuleError(resp.content) 663 664 response.update((dict(self_link=response['selfLink']))) 665 666 return Parameters(params=response) 667 668 def apply_on_device(self): 669 uri = "https://{0}:{1}/mgmt/tm/asm/tasks/apply-policy/".format( 670 self.client.provider['server'], 671 self.client.provider['server_port'], 672 ) 673 params = dict(policyReference={'link': self.have.self_link}) 674 resp = self.client.api.post(uri, json=params) 675 676 try: 677 response = resp.json() 678 except ValueError as ex: 679 raise F5ModuleError(str(ex)) 680 681 if 'code' in response and response['code'] in [400, 403]: 682 if 'message' in response: 683 raise F5ModuleError(response['message']) 684 else: 685 raise F5ModuleError(resp.content) 686 return response['id'] 687 688 def create_from_template_on_device(self): 689 full_name = fq_name(self.want.partition, self.want.name) 690 cmd = 'tmsh create asm policy {0} policy-template {1}'.format(full_name, self.want.template) 691 uri = "https://{0}:{1}/mgmt/tm/util/bash/".format( 692 self.client.provider['server'], 693 self.client.provider['server_port'], 694 ) 695 args = dict( 696 command='run', 697 utilCmdArgs='-c "{0}"'.format(cmd) 698 ) 699 resp = self.client.api.post(uri, json=args) 700 701 try: 702 response = resp.json() 703 if 'commandResult' in response: 704 if 'Unexpected Error' in response['commandResult']: 705 raise F5ModuleError(response['commandResult']) 706 except ValueError as ex: 707 raise F5ModuleError(str(ex)) 708 709 if 'code' in response and response['code'] == 400: 710 if 'message' in response: 711 raise F5ModuleError(response['message']) 712 else: 713 raise F5ModuleError(resp.content) 714 715 def create_on_device(self): 716 params = self.changes.api_params() 717 params['name'] = self.want.name 718 params['partition'] = self.want.partition 719 # we need to remove active from params as API will raise an error if the active is set to True, 720 # policies can only be activated via apply-policy task endpoint. 721 params.pop('active') 722 uri = "https://{0}:{1}/mgmt/tm/asm/policies/".format( 723 self.client.provider['server'], 724 self.client.provider['server_port'], 725 ) 726 resp = self.client.api.post(uri, json=params) 727 728 try: 729 response = resp.json() 730 except ValueError as ex: 731 raise F5ModuleError(str(ex)) 732 733 if 'code' in response and response['code'] in [400, 401, 403]: 734 if 'message' in response: 735 raise F5ModuleError(response['message']) 736 else: 737 raise F5ModuleError(resp.content) 738 time.sleep(2) 739 return True 740 741 def remove_from_device(self): 742 policy_id = self._get_policy_id() 743 uri = "https://{0}:{1}/mgmt/tm/asm/policies/{2}".format( 744 self.client.provider['server'], 745 self.client.provider['server_port'], 746 policy_id 747 ) 748 response = self.client.api.delete(uri) 749 if response.status in [200, 201]: 750 return True 751 raise F5ModuleError(response.content) 752 753 754class ModuleManager(object): 755 def __init__(self, *args, **kwargs): 756 self.module = kwargs.get('module', None) 757 self.client = F5RestClient(**self.module.params) 758 self.kwargs = kwargs 759 760 def exec_module(self): 761 if not module_provisioned(self.client, 'asm'): 762 raise F5ModuleError( 763 "ASM must be provisioned to use this module." 764 ) 765 if self.version_is_less_than_13(): 766 manager = self.get_manager('v1') 767 else: 768 manager = self.get_manager('v2') 769 return manager.exec_module() 770 771 def get_manager(self, type): 772 if type == 'v1': 773 return V1Manager(**self.kwargs) 774 elif type == 'v2': 775 return V2Manager(**self.kwargs) 776 777 def version_is_less_than_13(self): 778 version = tmos_version(self.client) 779 if LooseVersion(version) < LooseVersion('13.0.0'): 780 return True 781 else: 782 return False 783 784 785class V1Manager(BaseManager): 786 def __init__(self, *args, **kwargs): 787 module = kwargs.get('module', None) 788 client = F5RestClient(**module.params) 789 super(V1Manager, self).__init__(client=client, module=module) 790 self.want = V1Parameters(params=module.params, client=client) 791 792 def create_from_template(self): 793 self.create_from_template_on_device() 794 795 796class V2Manager(BaseManager): 797 def __init__(self, *args, **kwargs): 798 module = kwargs.get('module', None) 799 client = F5RestClient(**module.params) 800 super(V2Manager, self).__init__(client=client, module=module) 801 self.want = V2Parameters(params=module.params, client=client) 802 803 # TODO Include creating ASM policies from custom templates in v13 804 805 def create_from_template(self): 806 if not self.create_from_template_on_device(): 807 return False 808 809 810class ArgumentSpec(object): 811 def __init__(self): 812 self.template_map = [ 813 'ActiveSync v1.0 v2.0 (http)', 814 'ActiveSync v1.0 v2.0 (https)', 815 'Comprehensive', 816 'Drupal', 817 'Fundamental', 818 'Joomla', 819 'LotusDomino 6.5 (http)', 820 'LotusDomino 6.5 (https)', 821 'OWA Exchange 2003 (http)', 822 'OWA Exchange 2003 (https)', 823 'OWA Exchange 2003 with ActiveSync (http)', 824 'OWA Exchange 2003 with ActiveSync (https)', 825 'OWA Exchange 2007 (http)', 826 'OWA Exchange 2007 (https)', 827 'OWA Exchange 2007 with ActiveSync (http)', 828 'OWA Exchange 2007 with ActiveSync (https)', 829 'OWA Exchange 2010 (http)', 830 'OWA Exchange 2010 (https)', 831 'Oracle 10g Portal (http)', 832 'Oracle 10g Portal (https)', 833 'Oracle Applications 11i (http)', 834 'Oracle Applications 11i (https)', 835 'PeopleSoft Portal 9 (http)', 836 'PeopleSoft Portal 9 (https)', 837 'Rapid Deployment Policy', 838 'SAP NetWeaver 7 (http)', 839 'SAP NetWeaver 7 (https)', 840 'SharePoint 2003 (http)', 841 'SharePoint 2003 (https)', 842 'SharePoint 2007 (http)', 843 'SharePoint 2007 (https)', 844 'SharePoint 2010 (http)', 845 'SharePoint 2010 (https)', 846 'Vulnerability Assessment Baseline', 847 'Wordpress', 848 ] 849 self.supports_check_mode = True 850 argument_spec = dict( 851 name=dict( 852 required=True, 853 ), 854 template=dict( 855 choices=self.template_map 856 ), 857 active=dict( 858 type='bool', 859 default='no' 860 ), 861 state=dict( 862 default='present', 863 choices=['present', 'absent'] 864 ), 865 partition=dict( 866 default='Common', 867 fallback=(env_fallback, ['F5_PARTITION']) 868 ) 869 ) 870 self.argument_spec = {} 871 self.argument_spec.update(f5_argument_spec) 872 self.argument_spec.update(argument_spec) 873 874 875def main(): 876 spec = ArgumentSpec() 877 878 module = AnsibleModule( 879 argument_spec=spec.argument_spec, 880 supports_check_mode=spec.supports_check_mode, 881 ) 882 883 try: 884 mm = ModuleManager(module=module) 885 results = mm.exec_module() 886 module.exit_json(**results) 887 except F5ModuleError as ex: 888 module.fail_json(msg=str(ex)) 889 890 891if __name__ == '__main__': 892 main() 893