1#!/usr/bin/python 2# -*- coding: utf-8 -*- 3# 4# Copyright: (c) 2019, 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_remote_user 18short_description: Manages default settings for remote user accounts on a BIG-IP 19description: 20 - Manages default settings for remote user accounts on a BIG-IP. 21version_added: 2.9 22options: 23 default_role: 24 description: 25 - Specifies the default role for all remote user accounts. 26 - The default system value is C(no-access). 27 type: str 28 choices: 29 - acceleration-policy-editor 30 - admin 31 - application-editor 32 - auditor 33 - certificate-manager 34 - firewall-manager 35 - fraud-protection-manager 36 - guest 37 - irule-manager 38 - manager 39 - no-access 40 - operator 41 - resource-admin 42 - user-manager 43 - web-application-security-administrator 44 - web-application-security-editor 45 default_partition: 46 description: 47 - Specifies the default partition for all remote user accounts. 48 - The default system value is C(all) for all partitions. 49 type: str 50 console_access: 51 description: 52 - Enables or disables the default console access for all remote user accounts. 53 - The default system value is C(disabled). 54 type: bool 55 description: 56 description: 57 - User defined description. 58 type: str 59extends_documentation_fragment: f5 60author: 61 - Wojciech Wypior (@wojtek0806) 62''' 63 64EXAMPLES = r''' 65- name: Modify default partition and console access 66 bigip_remote_user: 67 default_partition: Common 68 console_access: yes 69 provider: 70 password: secret 71 server: lb.mydomain.com 72 user: admin 73 delegate_to: localhost 74 75- name: Modify default role, partition and console access 76 bigip_remote_user: 77 default_partition: Common 78 default_role: manager 79 console_access: yes 80 description: "Changed new settings" 81 provider: 82 password: secret 83 server: lb.mydomain.com 84 user: admin 85 delegate_to: localhost 86 87- name: Revert to default settings 88 bigip_remote_user: 89 default_partition: all 90 default_role: "no-access" 91 console_access: no 92 description: "" 93 provider: 94 password: secret 95 server: lb.mydomain.com 96 user: admin 97 delegate_to: localhost 98''' 99 100RETURN = r''' 101default_role: 102 description: The default role for all remote user accounts. 103 returned: changed 104 type: str 105 sample: auditor 106default_partition: 107 description: The default partition for all remote user accounts. 108 returned: changed 109 type: str 110 sample: Common 111console_access: 112 description: The default console access for all remote user accounts 113 returned: changed 114 type: bool 115 sample: no 116description: 117 description: The user defined description. 118 returned: changed 119 type: str 120 sample: Foo is bar 121''' 122 123from ansible.module_utils.basic import AnsibleModule 124 125try: 126 from library.module_utils.network.f5.bigip import F5RestClient 127 from library.module_utils.network.f5.common import F5ModuleError 128 from library.module_utils.network.f5.common import AnsibleF5Parameters 129 from library.module_utils.network.f5.common import f5_argument_spec 130 from library.module_utils.network.f5.common import flatten_boolean 131 from library.module_utils.network.f5.compare import cmp_str_with_none 132except ImportError: 133 from ansible.module_utils.network.f5.bigip import F5RestClient 134 from ansible.module_utils.network.f5.common import F5ModuleError 135 from ansible.module_utils.network.f5.common import AnsibleF5Parameters 136 from ansible.module_utils.network.f5.common import f5_argument_spec 137 from ansible.module_utils.network.f5.common import flatten_boolean 138 from ansible.module_utils.network.f5.compare import cmp_str_with_none 139 140 141class Parameters(AnsibleF5Parameters): 142 api_map = { 143 'defaultPartition': 'default_partition', 144 'defaultRole': 'default_role', 145 'remoteConsoleAccess': 'console_access', 146 } 147 148 api_attributes = [ 149 'defaultPartition', 150 'defaultRole', 151 'description', 152 'remoteConsoleAccess', 153 154 ] 155 156 returnables = [ 157 'default_partition', 158 'default_role', 159 'console_access', 160 'description', 161 ] 162 163 updatables = [ 164 'default_partition', 165 'default_role', 166 'console_access', 167 'description', 168 ] 169 170 171class ApiParameters(Parameters): 172 pass 173 174 175class ModuleParameters(Parameters): 176 @property 177 def console_access(self): 178 result = flatten_boolean(self._values['console_access']) 179 if result == 'yes': 180 return 'tmsh' 181 if result == 'no': 182 return 'disabled' 183 184 185class Changes(Parameters): 186 def to_return(self): 187 result = {} 188 try: 189 for returnable in self.returnables: 190 result[returnable] = getattr(self, returnable) 191 result = self._filter_params(result) 192 except Exception: 193 pass 194 return result 195 196 197class UsableChanges(Changes): 198 pass 199 200 201class ReportableChanges(Changes): 202 @property 203 def console_access(self): 204 if self._values['console_access'] is None: 205 return None 206 if self._values['console_access'] == 'tmsh': 207 return 'yes' 208 if self._values['console_access'] == 'disabled': 209 return 'no' 210 211 212class Difference(object): 213 def __init__(self, want, have=None): 214 self.want = want 215 self.have = have 216 217 def compare(self, param): 218 try: 219 result = getattr(self, param) 220 return result 221 except AttributeError: 222 return self.__default(param) 223 224 def __default(self, param): 225 attr1 = getattr(self.want, param) 226 try: 227 attr2 = getattr(self.have, param) 228 if attr1 != attr2: 229 return attr1 230 except AttributeError: 231 return attr1 232 233 @property 234 def description(self): 235 result = cmp_str_with_none(self.want.description, self.have.description) 236 return result 237 238 239class ModuleManager(object): 240 def __init__(self, *args, **kwargs): 241 self.module = kwargs.get('module', None) 242 self.client = F5RestClient(**self.module.params) 243 self.want = ModuleParameters(params=self.module.params) 244 self.have = ApiParameters() 245 self.changes = UsableChanges() 246 247 def _update_changed_options(self): 248 diff = Difference(self.want, self.have) 249 updatables = Parameters.updatables 250 changed = dict() 251 for k in updatables: 252 change = diff.compare(k) 253 if change is None: 254 continue 255 else: 256 if isinstance(change, dict): 257 changed.update(change) 258 else: 259 changed[k] = change 260 if changed: 261 self.changes = UsableChanges(params=changed) 262 return True 263 return False 264 265 def _announce_deprecations(self, result): 266 warnings = result.pop('__warnings', []) 267 for warning in warnings: 268 self.client.module.deprecate( 269 msg=warning['msg'], 270 version=warning['version'] 271 ) 272 273 def exec_module(self): 274 result = dict() 275 276 changed = self.update() 277 278 reportable = ReportableChanges(params=self.changes.to_return()) 279 changes = reportable.to_return() 280 result.update(**changes) 281 result.update(dict(changed=changed)) 282 self._announce_deprecations(result) 283 return result 284 285 def should_update(self): 286 result = self._update_changed_options() 287 if result: 288 return True 289 return False 290 291 def update(self): 292 self.have = self.read_current_from_device() 293 if not self.should_update(): 294 return False 295 if self.module.check_mode: 296 return True 297 self.update_on_device() 298 return True 299 300 def update_on_device(self): 301 params = self.changes.api_params() 302 uri = "https://{0}:{1}/mgmt/tm/auth/remote-user/".format( 303 self.client.provider['server'], 304 self.client.provider['server_port'], 305 ) 306 resp = self.client.api.patch(uri, json=params) 307 try: 308 response = resp.json() 309 except ValueError as ex: 310 raise F5ModuleError(str(ex)) 311 312 if 'code' in response and response['code'] == 400: 313 if 'message' in response: 314 raise F5ModuleError(response['message']) 315 else: 316 raise F5ModuleError(resp.content) 317 318 def read_current_from_device(self): 319 uri = "https://{0}:{1}/mgmt/tm/auth/remote-user/".format( 320 self.client.provider['server'], 321 self.client.provider['server_port'], 322 ) 323 resp = self.client.api.get(uri) 324 try: 325 response = resp.json() 326 except ValueError as ex: 327 raise F5ModuleError(str(ex)) 328 329 if 'code' in response and response['code'] == 400: 330 if 'message' in response: 331 raise F5ModuleError(response['message']) 332 else: 333 raise F5ModuleError(resp.content) 334 return ApiParameters(params=response) 335 336 337class ArgumentSpec(object): 338 def __init__(self): 339 self.supports_check_mode = True 340 self.choices = [ 341 'acceleration-policy-editor', 342 'admin', 343 'application-editor', 344 'auditor', 345 'certificate-manager', 346 'firewall-manager', 347 'fraud-protection-manager', 348 'guest', 349 'irule-manager', 350 'manager', 351 'no-access', 352 'operator', 353 'resource-admin', 354 'user-manager', 355 'web-application-security-administrator', 356 'web-application-security-editor' 357 ] 358 argument_spec = dict( 359 default_role=dict( 360 choices=self.choices 361 ), 362 default_partition=dict(), 363 console_access=dict(type='bool'), 364 description=dict() 365 ) 366 self.argument_spec = {} 367 self.argument_spec.update(f5_argument_spec) 368 self.argument_spec.update(argument_spec) 369 self.required_one_of = [ 370 ['default_role', 'default_partition'] 371 ] 372 373 374def main(): 375 spec = ArgumentSpec() 376 377 module = AnsibleModule( 378 argument_spec=spec.argument_spec, 379 supports_check_mode=spec.supports_check_mode, 380 ) 381 382 try: 383 mm = ModuleManager(module=module) 384 results = mm.exec_module() 385 module.exit_json(**results) 386 except F5ModuleError as ex: 387 module.fail_json(msg=str(ex)) 388 389 390if __name__ == '__main__': 391 main() 392