Removes the f5-sdk from bigip_user (#47794)

This is more work in the ongoing effort to remove the f5-sdk from
all f5 ansible modules
pull/48002/head
Tim Rupp 6 years ago committed by GitHub
parent 67aa98c30f
commit 5b5d41e958
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

@ -1,7 +1,7 @@
#!/usr/bin/python #!/usr/bin/python
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
# #
# Copyright (c) 2017 F5 Networks Inc. # Copyright: (c) 2017, F5 Networks Inc.
# GNU General Public License v3.0 (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) # GNU General Public License v3.0 (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
from __future__ import absolute_import, division, print_function from __future__ import absolute_import, division, print_function
@ -89,56 +89,61 @@ author:
EXAMPLES = r''' EXAMPLES = r'''
- name: Add the user 'johnd' as an admin - name: Add the user 'johnd' as an admin
bigip_user: bigip_user:
server: lb.mydomain.com
user: admin
password: secret
username_credential: johnd username_credential: johnd
password_credential: password password_credential: password
full_name: John Doe full_name: John Doe
partition_access: all:admin partition_access: all:admin
update_password: on_create update_password: on_create
state: present state: present
provider:
server: lb.mydomain.com
user: admin
password: secret
delegate_to: localhost delegate_to: localhost
- name: Change the user "johnd's" role and shell - name: Change the user "johnd's" role and shell
bigip_user: bigip_user:
server: lb.mydomain.com
user: admin
password: secret
username_credential: johnd username_credential: johnd
partition_access: NewPartition:manager partition_access: NewPartition:manager
shell: tmsh shell: tmsh
state: present state: present
provider:
server: lb.mydomain.com
user: admin
password: secret
delegate_to: localhost delegate_to: localhost
- name: Make the user 'johnd' an admin and set to advanced shell - name: Make the user 'johnd' an admin and set to advanced shell
bigip_user: bigip_user:
server: lb.mydomain.com
user: admin
password: secret
name: johnd name: johnd
partition_access: all:admin partition_access: all:admin
shell: bash shell: bash
state: present state: present
provider:
server: lb.mydomain.com
user: admin
password: secret
delegate_to: localhost delegate_to: localhost
- name: Remove the user 'johnd' - name: Remove the user 'johnd'
bigip_user: bigip_user:
server: lb.mydomain.com
user: admin
password: secret
name: johnd name: johnd
state: absent state: absent
provider:
server: lb.mydomain.com
user: admin
password: secret
delegate_to: localhost delegate_to: localhost
- name: Update password - name: Update password
bigip_user: bigip_user:
server: lb.mydomain.com
user: admin
password: secret
state: present state: present
username_credential: johnd username_credential: johnd
password_credential: newsupersecretpassword password_credential: newsupersecretpassword
provider:
server: lb.mydomain.com
user: admin
password: secret
delegate_to: localhost delegate_to: localhost
# Note that the second time this task runs, it would fail because # Note that the second time this task runs, it would fail because
@ -150,22 +155,24 @@ EXAMPLES = r'''
# * Include `ignore_errors` on this task # * Include `ignore_errors` on this task
- name: Change the Admin password - name: Change the Admin password
bigip_user: bigip_user:
server: lb.mydomain.com
user: admin
password: secret
state: present state: present
username_credential: admin username_credential: admin
password_credential: NewSecretPassword password_credential: NewSecretPassword
provider:
server: lb.mydomain.com
user: admin
password: secret
delegate_to: localhost delegate_to: localhost
- name: Change the root user's password - name: Change the root user's password
bigip_user: bigip_user:
server: lb.mydomain.com
user: admin
password: secret
username_credential: root username_credential: root
password_credential: secret password_credential: secret
state: present state: present
provider:
server: lb.mydomain.com
user: admin
password: secret
delegate_to: localhost delegate_to: localhost
''' '''
@ -193,30 +200,27 @@ import re
from ansible.module_utils.basic import AnsibleModule from ansible.module_utils.basic import AnsibleModule
from ansible.module_utils.basic import env_fallback from ansible.module_utils.basic import env_fallback
from ansible.module_utils.six import string_types
from distutils.version import LooseVersion from distutils.version import LooseVersion
try: try:
from library.module_utils.network.f5.bigip import HAS_F5SDK from library.module_utils.network.f5.bigip import F5RestClient
from library.module_utils.network.f5.bigip import F5Client
from library.module_utils.network.f5.common import F5ModuleError from library.module_utils.network.f5.common import F5ModuleError
from library.module_utils.network.f5.common import AnsibleF5Parameters from library.module_utils.network.f5.common import AnsibleF5Parameters
from library.module_utils.network.f5.common import cleanup_tokens from library.module_utils.network.f5.common import cleanup_tokens
from library.module_utils.network.f5.common import f5_argument_spec from library.module_utils.network.f5.common import f5_argument_spec
try: from library.module_utils.network.f5.common import exit_json
from library.module_utils.network.f5.common import iControlUnexpectedHTTPError from library.module_utils.network.f5.common import fail_json
except ImportError: from library.module_utils.network.f5.icontrol import tmos_version
HAS_F5SDK = False
except ImportError: except ImportError:
from ansible.module_utils.network.f5.bigip import HAS_F5SDK from ansible.module_utils.network.f5.bigip import F5RestClient
from ansible.module_utils.network.f5.bigip import F5Client
from ansible.module_utils.network.f5.common import F5ModuleError from ansible.module_utils.network.f5.common import F5ModuleError
from ansible.module_utils.network.f5.common import AnsibleF5Parameters from ansible.module_utils.network.f5.common import AnsibleF5Parameters
from ansible.module_utils.network.f5.common import cleanup_tokens from ansible.module_utils.network.f5.common import cleanup_tokens
from ansible.module_utils.network.f5.common import f5_argument_spec from ansible.module_utils.network.f5.common import f5_argument_spec
try: from ansible.module_utils.network.f5.common import exit_json
from ansible.module_utils.network.f5.common import iControlUnexpectedHTTPError from ansible.module_utils.network.f5.common import fail_json
except ImportError: from ansible.module_utils.network.f5.icontrol import tmos_version
HAS_F5SDK = False
class Parameters(AnsibleF5Parameters): class Parameters(AnsibleF5Parameters):
@ -226,24 +230,33 @@ class Parameters(AnsibleF5Parameters):
} }
updatables = [ updatables = [
'partition_access', 'full_name', 'shell', 'password_credential' 'partition_access',
'full_name',
'shell',
'password_credential',
] ]
returnables = [ returnables = [
'shell', 'partition_access', 'full_name', 'username_credential' 'shell',
'partition_access',
'full_name',
'username_credential',
'password_credential',
] ]
api_attributes = [ api_attributes = [
'shell', 'partitionAccess', 'description', 'name', 'password' 'shell',
'partitionAccess',
'description',
'name',
'password',
] ]
@property @property
def partition_access(self): def partition_access(self):
"""Partition access values will require some transformation. """Partition access values will require some transformation.
This operates on both user and device returned values. This operates on both user and device returned values.
Check if the element is a string from user input in the format of Check if the element is a string from user input in the format of
name:role, if it is split it and create dictionary out of it. name:role, if it is split it and create dictionary out of it.
@ -251,11 +264,14 @@ class Parameters(AnsibleF5Parameters):
or already processed) and contains nameReference or already processed) and contains nameReference
key, delete it and append the remaining dictionary element into key, delete it and append the remaining dictionary element into
a list. a list.
If the nameReference key is removed just append the dictionary If the nameReference key is removed just append the dictionary
into the list. into the list.
:returns list of dictionaries Returns:
List of dictionaries. Each item in the list is a dictionary
which contains the ``name`` of the partition and the ``role`` to
allow on that partition.
""" """
if self._values['partition_access'] is None: if self._values['partition_access'] is None:
return return
@ -269,7 +285,7 @@ class Parameters(AnsibleF5Parameters):
result.append(access) result.append(access)
else: else:
result.append(access) result.append(access)
if isinstance(access, str): if isinstance(access, string_types):
acl = access.split(':') acl = access.split(':')
if acl[0].lower() == 'all': if acl[0].lower() == 'all':
acl[0] = 'all-partitions' acl[0] = 'all-partitions'
@ -281,25 +297,116 @@ class Parameters(AnsibleF5Parameters):
result.append(value) result.append(value)
return result return result
class ApiParameters(Parameters):
@property
def shell(self):
if self._values['shell'] in [None, 'none']:
return None
return self._values['shell']
class ModuleParameters(Parameters):
@property
def shell(self):
if self._values['shell'] in [None, 'none']:
return None
return self._values['shell']
class Changes(Parameters):
def to_return(self): def to_return(self):
result = {} result = {}
for returnable in self.returnables: for returnable in self.returnables:
result[returnable] = getattr(self, returnable) try:
result = self._filter_params(result) result[returnable] = getattr(self, returnable)
except Exception:
pass
result = self._filter_params(result)
return result return result
def api_params(self):
result = {} class UsableChanges(Changes):
for api_attribute in self.api_attributes: @property
if api_attribute in self.api_map: def password(self):
result[api_attribute] = getattr( if self._values['password_credential'] is None:
self, self.api_map[api_attribute]) return None
elif api_attribute == 'password': return self._values['password_credential']
result[api_attribute] = self._values['password_credential']
class ReportableChanges(Changes):
pass
class Difference(object):
def __init__(self, want, have=None):
self.want = want
self.have = have
def compare(self, param):
try:
result = getattr(self, param)
return result
except AttributeError:
return self.__default(param)
def __default(self, param):
attr1 = getattr(self.want, param)
try:
attr2 = getattr(self.have, param)
if attr1 != attr2:
return attr1
except AttributeError:
return attr1
@property
def password_credential(self):
if self.want.password_credential is None:
return None
if self.want.update_password in ['always']:
return self.want.password_credential
return None
@property
def shell(self):
if self.want.shell is None:
if self.have.shell is not None:
return 'none'
else: else:
result[api_attribute] = getattr(self, api_attribute) return None
result = self._filter_params(result) if self.want.shell == 'bash':
return result self._validate_shell_parameter()
if self.want.shell == self.have.shell:
return None
else:
return self.want.shell
if self.want.shell != self.have.shell:
return self.want.shell
def _validate_shell_parameter(self):
"""Method to validate shell parameters.
Raise when shell attribute is set to 'bash' with roles set to
either 'admin' or 'resource-admin'.
NOTE: Admin and Resource-Admin roles automatically enable access to
all partitions, removing any other roles that the user might have
had. There are few other roles which do that but those roles,
do not allow bash.
"""
err = "Shell access is only available to " \
"'admin' or 'resource-admin' roles."
permit = ['admin', 'resource-admin']
have = self.have.partition_access
if not any(r['role'] for r in have if r['role'] in permit):
raise F5ModuleError(err)
if self.want.partition_access is not None:
want = self.want.partition_access
if not any(r['role'] for r in want if r['role'] in permit):
raise F5ModuleError(err)
class ModuleManager(object): class ModuleManager(object):
@ -321,7 +428,7 @@ class ModuleManager(object):
if type == 'root': if type == 'root':
return RootUserManager(**self.kwargs) return RootUserManager(**self.kwargs)
elif type == 'v1': elif type == 'v1':
return UnparitionedManager(**self.kwargs) return UnpartitionedManager(**self.kwargs)
elif type == 'v2': elif type == 'v2':
return PartitionedManager(**self.kwargs) return PartitionedManager(**self.kwargs)
@ -333,7 +440,7 @@ class ModuleManager(object):
:return: Bool :return: Bool
""" """
version = self.client.api.tmos_version version = tmos_version(self.client)
if LooseVersion(version) < LooseVersion('13.0.0'): if LooseVersion(version) < LooseVersion('13.0.0'):
return True return True
else: else:
@ -350,27 +457,17 @@ class BaseManager(object):
def __init__(self, *args, **kwargs): def __init__(self, *args, **kwargs):
self.module = kwargs.get('module', None) self.module = kwargs.get('module', None)
self.client = kwargs.get('client', None) self.client = kwargs.get('client', None)
self.have = None self.want = ModuleParameters(params=self.module.params)
self.want = Parameters(params=self.module.params) self.have = ApiParameters()
self.changes = Parameters() self.changes = UsableChanges()
def exec_module(self): def _announce_deprecations(self, result):
changed = False warnings = result.pop('__warnings', [])
result = dict() for warning in warnings:
state = self.want.state self.module.deprecate(
msg=warning['msg'],
try: version=warning['version']
if state == "present": )
changed = self.present()
elif state == "absent":
changed = self.absent()
except iControlUnexpectedHTTPError as e:
raise F5ModuleError(str(e))
changes = self.changes.to_return()
result.update(**changes)
result.update(dict(changed=changed))
return result
def _set_changed_options(self): def _set_changed_options(self):
changed = {} changed = {}
@ -378,61 +475,42 @@ class BaseManager(object):
if getattr(self.want, key) is not None: if getattr(self.want, key) is not None:
changed[key] = getattr(self.want, key) changed[key] = getattr(self.want, key)
if changed: if changed:
self.changes = Parameters(params=changed) self.changes = UsableChanges(params=changed)
def _update_changed_options(self): def _update_changed_options(self):
changed = {} diff = Difference(self.want, self.have)
for key in Parameters.updatables: updatables = Parameters.updatables
if getattr(self.want, key) is not None: changed = dict()
if key == 'password_credential': for k in updatables:
new_pass = getattr(self.want, key) change = diff.compare(k)
if self.want.update_password == 'always': if change is None:
changed[key] = new_pass continue
else:
if isinstance(change, dict):
changed.update(change)
else: else:
# We set the shell parameter to 'none' when bigip does changed[k] = change
# not return it.
if self.want.shell == 'bash':
self.validate_shell_parameter()
if self.want.shell == 'none' and self.have.shell is None:
self.have.shell = 'none'
attr1 = getattr(self.want, key)
attr2 = getattr(self.have, key)
if attr1 != attr2:
changed[key] = attr1
if changed: if changed:
self.changes = Parameters(params=changed) self.changes = UsableChanges(params=changed)
return True return True
return False return False
def validate_shell_parameter(self): def exec_module(self):
"""Method to validate shell parameters. changed = False
result = dict()
Raise when shell attribute is set to 'bash' with roles set to state = self.want.state
either 'admin' or 'resource-admin'.
NOTE: Admin and Resource-Admin roles automatically enable access to
all partitions, removing any other roles that the user might have
had. There are few other roles which do that but those roles,
do not allow bash.
"""
err = "Shell access is only available to " \
"'admin' or 'resource-admin' roles"
permit = ['admin', 'resource-admin']
if self.have is not None: if state == "present":
have = self.have.partition_access changed = self.present()
if not any(r['role'] for r in have if r['role'] in permit): elif state == "absent":
raise F5ModuleError(err) changed = self.absent()
# This check is needed if we want to modify shell AND reportable = ReportableChanges(params=self.changes.to_return())
# partition_access attribute. changes = reportable.to_return()
# This check will also trigger on create. result.update(**changes)
if self.want.partition_access is not None: result.update(dict(changed=changed))
want = self.want.partition_access self._announce_deprecations(result)
if not any(r['role'] for r in want if r['role'] in permit): return result
raise F5ModuleError(err)
def present(self): def present(self):
if self.exists(): if self.exists():
@ -451,22 +529,6 @@ class BaseManager(object):
return True return True
return False return False
def validate_create_parameters(self):
"""Password credentials and partition access are mandatory,
when creating a user resource.
"""
if self.want.password_credential and \
self.want.update_password != 'on_create':
err = "The 'update_password' option " \
"needs to be set to 'on_create' when creating " \
"a resource with a password."
raise F5ModuleError(err)
if self.want.partition_access is None:
err = "The 'partition_access' option " \
"is required when creating a resource."
raise F5ModuleError(err)
def update(self): def update(self):
self.have = self.read_current_from_device() self.have = self.read_current_from_device()
if not self.should_update(): if not self.should_update():
@ -481,7 +543,7 @@ class BaseManager(object):
return True return True
self.remove_from_device() self.remove_from_device()
if self.exists(): if self.exists():
raise F5ModuleError("Failed to delete the user") raise F5ModuleError("Failed to delete the user.")
return True return True
def create(self): def create(self):
@ -494,51 +556,175 @@ class BaseManager(object):
self.create_on_device() self.create_on_device()
return True return True
def validate_shell_parameter(self):
"""Method to validate shell parameters.
class UnparitionedManager(BaseManager): Raise when shell attribute is set to 'bash' with roles set to
def create_on_device(self): either 'admin' or 'resource-admin'.
params = self.want.api_params()
self.client.api.tm.auth.users.user.create(**params)
def update_on_device(self): NOTE: Admin and Resource-Admin roles automatically enable access to
params = self.want.api_params() all partitions, removing any other roles that the user might have
result = self.client.api.tm.auth.users.user.load(name=self.want.name) had. There are few other roles which do that but those roles,
result.modify(**params) do not allow bash.
"""
err = "Shell access is only available to " \
"'admin' or 'resource-admin' roles."
permit = ['admin', 'resource-admin']
if self.want.partition_access is not None:
want = self.want.partition_access
if not any(r['role'] for r in want if r['role'] in permit):
raise F5ModuleError(err)
def validate_create_parameters(self):
"""Password credentials and partition access are mandatory,
when creating a user resource.
"""
if self.want.password_credential and \
self.want.update_password != 'on_create':
err = "The 'update_password' option " \
"needs to be set to 'on_create' when creating " \
"a resource with a password."
raise F5ModuleError(err)
if self.want.partition_access is None:
err = "The 'partition_access' option " \
"is required when creating a resource."
raise F5ModuleError(err)
def read_current_from_device(self):
tmp_res = self.client.api.tm.auth.users.user.load(name=self.want.name)
result = tmp_res.attrs
return Parameters(params=result)
class UnpartitionedManager(BaseManager):
def exists(self): def exists(self):
return self.client.api.tm.auth.users.user.exists(name=self.want.name) uri = "https://{0}:{1}/mgmt/tm/auth/user/{2}".format(
self.client.provider['server'],
self.client.provider['server_port'],
self.want.name
)
resp = self.client.api.get(uri)
try:
response = resp.json()
except ValueError:
return False
if resp.status == 404 or 'code' in response and response['code'] == 404:
return False
return True
def create_on_device(self):
params = self.changes.api_params()
params['name'] = self.want.name
uri = "https://{0}:{1}/mgmt/tm/auth/user/".format(
self.client.provider['server'],
self.client.provider['server_port']
)
resp = self.client.api.post(uri, json=params)
try:
response = resp.json()
except ValueError as ex:
raise F5ModuleError(str(ex))
if 'code' in response and response['code'] in [400, 403]:
if 'message' in response:
raise F5ModuleError(response['message'])
else:
raise F5ModuleError(resp.content)
return response['selfLink']
def update_on_device(self):
params = self.changes.api_params()
uri = "https://{0}:{1}/mgmt/tm/auth/user/{2}".format(
self.client.provider['server'],
self.client.provider['server_port'],
self.want.name
)
resp = self.client.api.patch(uri, json=params)
try:
response = resp.json()
except ValueError as ex:
raise F5ModuleError(str(ex))
if 'code' in response and response['code'] == 400:
if 'message' in response:
raise F5ModuleError(response['message'])
else:
raise F5ModuleError(resp.content)
def remove_from_device(self): def remove_from_device(self):
result = self.client.api.tm.auth.users.user.load(name=self.want.name) uri = "https://{0}:{1}/mgmt/tm/auth/user/{2}".format(
if result: self.client.provider['server'],
result.delete() self.client.provider['server_port'],
self.want.name
)
response = self.client.api.delete(uri)
if response.status == 200:
return True
raise F5ModuleError(response.content)
def read_current_from_device(self):
uri = "https://{0}:{1}/mgmt/tm/auth/user/{2}".format(
self.client.provider['server'],
self.client.provider['server_port'],
self.want.name
)
resp = self.client.api.get(uri)
try:
response = resp.json()
except ValueError as ex:
raise F5ModuleError(str(ex))
if 'code' in response and response['code'] == 400:
if 'message' in response:
raise F5ModuleError(response['message'])
else:
raise F5ModuleError(resp.content)
return ApiParameters(params=response)
class PartitionedManager(BaseManager): class PartitionedManager(BaseManager):
def exists(self):
response = self.list_users_on_device()
if 'items' in response:
collection = [x for x in response['items'] if x['name'] == self.want.name]
if len(collection) == 1:
return True
elif len(collection) == 0:
return False
else:
raise F5ModuleError(
"Multiple users with the provided name were found!"
)
return False
def create_on_device(self): def create_on_device(self):
params = self.want.api_params() params = self.changes.api_params()
self.client.api.tm.auth.users.user.create( params['name'] = self.want.name
partition=self.want.partition, **params params['partition'] = self.want.partition
uri = "https://{0}:{1}/mgmt/tm/auth/user/".format(
self.client.provider['server'],
self.client.provider['server_port']
) )
resp = self.client.api.post(uri, json=params)
try:
response = resp.json()
except ValueError as ex:
raise F5ModuleError(str(ex))
def _read_one_resource_from_collection(self): if 'code' in response and response['code'] in [400, 404, 409]:
collection = self.client.api.tm.auth.users.get_collection( if 'message' in response:
requests_params=dict( raise F5ModuleError(response['message'])
params="$filter=partition+eq+'{0}'".format(self.want.partition) else:
) raise F5ModuleError(resp.content)
) return response['selfLink']
collection = [x for x in collection if x.name == self.want.name]
def read_current_from_device(self):
response = self.list_users_on_device()
collection = [x for x in response['items'] if x['name'] == self.want.name]
if len(collection) == 1: if len(collection) == 1:
resource = collection.pop() user = collection.pop()
return resource return ApiParameters(params=user)
elif len(collection) == 0: elif len(collection) == 0:
raise F5ModuleError( raise F5ModuleError(
"No accounts with the provided name were found" "No accounts with the provided name were found."
) )
else: else:
raise F5ModuleError( raise F5ModuleError(
@ -546,43 +732,54 @@ class PartitionedManager(BaseManager):
) )
def update_on_device(self): def update_on_device(self):
params = self.want.api_params() params = self.changes.api_params()
uri = "https://{0}:{1}/mgmt/tm/auth/user/{2}".format(
self.client.provider['server'],
self.client.provider['server_port'],
self.want.name
)
resp = self.client.api.patch(uri, json=params)
try: try:
resource = self._read_one_resource_from_collection() response = resp.json()
resource.modify(**params) except ValueError as ex:
except iControlUnexpectedHTTPError as ex: raise F5ModuleError(str(ex))
# TODO: Patch this in the F5 SDK so that I dont need this check
if 'updated successfully' not in str(ex): if 'code' in response and response['code'] in [400, 404, 409]:
raise F5ModuleError( if 'message' in response:
"Failed to update the specified user" if 'updated successfully' not in response['message']:
) raise F5ModuleError(response['message'])
else:
raise F5ModuleError(resp.content)
def read_current_from_device(self): def remove_from_device(self):
resource = self._read_one_resource_from_collection() uri = "https://{0}:{1}/mgmt/tm/auth/user/{2}".format(
result = resource.attrs self.client.provider['server'],
return Parameters(params=result) self.client.provider['server_port'],
self.want.name
)
response = self.client.api.delete(uri)
if response.status == 200:
return True
raise F5ModuleError(response.content)
def exists(self): def list_users_on_device(self):
collection = self.client.api.tm.auth.users.get_collection( uri = "https://{0}:{1}/mgmt/tm/auth/user/".format(
requests_params=dict( self.client.provider['server'],
params="$filter=partition+eq+'{0}'".format(self.want.partition) self.client.provider['server_port'],
)
) )
collection = [x for x in collection if x.name == self.want.name] query = "?$filter=partition+eq+'{0}'".format(self.want.partition)
if len(collection) == 1: resp = self.client.api.get(uri + query)
result = True try:
elif len(collection) == 0: response = resp.json()
result = False except ValueError as ex:
else: raise F5ModuleError(str(ex))
raise F5ModuleError(
"Multiple users with the provided name were found!"
)
return result
def remove_from_device(self): if 'code' in response and response['code'] == 400:
resource = self._read_one_resource_from_collection() if 'message' in response:
if resource: raise F5ModuleError(response['message'])
resource.delete() else:
raise F5ModuleError(resp.content)
return response
class RootUserManager(BaseManager): class RootUserManager(BaseManager):
@ -591,19 +788,18 @@ class RootUserManager(BaseManager):
result = dict() result = dict()
state = self.want.state state = self.want.state
try: if state == "present":
if state == "present": changed = self.present()
changed = self.present() elif state == "absent":
elif state == "absent": raise F5ModuleError(
raise F5ModuleError( "You may not remove the root user."
"You may not remove the root user." )
)
except iControlUnexpectedHTTPError as e:
raise F5ModuleError(str(e))
changes = self.changes.to_return() reportable = ReportableChanges(params=self.changes.to_return())
changes = reportable.to_return()
result.update(**changes) result.update(**changes)
result.update(dict(changed=changed)) result.update(dict(changed=changed))
self._announce_deprecations(result)
return result return result
def exists(self): def exists(self):
@ -619,18 +815,29 @@ class RootUserManager(BaseManager):
content = "{0}\n{0}\n".format(self.want.password_credential) content = "{0}\n{0}\n".format(self.want.password_credential)
command = re.sub(escape_patterns, r'\\\1', content) command = re.sub(escape_patterns, r'\\\1', content)
cmd = '-c "printf \\\"{0}\\\" | tmsh modify auth password root"'.format(command) cmd = '-c "printf \\\"{0}\\\" | tmsh modify auth password root"'.format(command)
params = dict(
command='run',
utilCmdArgs=cmd
)
uri = "https://{0}:{1}/mgmt/tm/util/bash".format(
self.client.provider['server'],
self.client.provider['server_port']
)
resp = self.client.api.post(uri, json=params)
try: try:
output = self.client.api.tm.util.bash.exec_cmd( response = resp.json()
'run', if 'commandResult' in response:
utilCmdArgs=cmd if any(x for x in errors if x in response['commandResult']):
) raise F5ModuleError(response['commandResult'])
if hasattr(output, 'commandResult'): except ValueError as ex:
result = str(output.commandResult) raise F5ModuleError(str(ex))
if any(x for x in errors if x in result): if 'code' in response and response['code'] in [400, 403]:
raise F5ModuleError(result) if 'message' in response:
return True raise F5ModuleError(response['message'])
except iControlUnexpectedHTTPError: else:
return False raise F5ModuleError(resp.content)
return True
class ArgumentSpec(object): class ArgumentSpec(object):
@ -673,18 +880,17 @@ def main():
argument_spec=spec.argument_spec, argument_spec=spec.argument_spec,
supports_check_mode=spec.supports_check_mode supports_check_mode=spec.supports_check_mode
) )
if not HAS_F5SDK:
module.fail_json(msg="The python f5-sdk module is required") client = F5RestClient(**module.params)
try: try:
client = F5Client(**module.params)
mm = ModuleManager(module=module, client=client) mm = ModuleManager(module=module, client=client)
results = mm.exec_module() results = mm.exec_module()
cleanup_tokens(client) cleanup_tokens(client)
module.exit_json(**results) exit_json(module, results, client)
except F5ModuleError as ex: except F5ModuleError as ex:
cleanup_tokens(client) cleanup_tokens(client)
module.fail_json(msg=str(ex)) fail_json(module, ex, client)
if __name__ == '__main__': if __name__ == '__main__':

@ -21,7 +21,7 @@ try:
from library.modules.bigip_user import Parameters from library.modules.bigip_user import Parameters
from library.modules.bigip_user import ModuleManager from library.modules.bigip_user import ModuleManager
from library.modules.bigip_user import ArgumentSpec from library.modules.bigip_user import ArgumentSpec
from library.modules.bigip_user import UnparitionedManager from library.modules.bigip_user import UnpartitionedManager
from library.modules.bigip_user import PartitionedManager from library.modules.bigip_user import PartitionedManager
from library.module_utils.network.f5.common import F5ModuleError from library.module_utils.network.f5.common import F5ModuleError
@ -37,7 +37,7 @@ except ImportError:
from ansible.modules.network.f5.bigip_user import Parameters from ansible.modules.network.f5.bigip_user import Parameters
from ansible.modules.network.f5.bigip_user import ModuleManager from ansible.modules.network.f5.bigip_user import ModuleManager
from ansible.modules.network.f5.bigip_user import ArgumentSpec from ansible.modules.network.f5.bigip_user import ArgumentSpec
from ansible.modules.network.f5.bigip_user import UnparitionedManager from ansible.modules.network.f5.bigip_user import UnpartitionedManager
from ansible.modules.network.f5.bigip_user import PartitionedManager from ansible.modules.network.f5.bigip_user import PartitionedManager
from ansible.module_utils.network.f5.common import F5ModuleError from ansible.module_utils.network.f5.common import F5ModuleError
@ -294,7 +294,7 @@ class TestManager(unittest.TestCase):
mm.get_manager = Mock(return_value=pm) mm.get_manager = Mock(return_value=pm)
msg = "Shell access is only available to 'admin' or " \ msg = "Shell access is only available to 'admin' or " \
"'resource-admin' roles" "'resource-admin' roles."
with pytest.raises(F5ModuleError) as ex: with pytest.raises(F5ModuleError) as ex:
mm.exec_module() mm.exec_module()
@ -467,7 +467,7 @@ class TestManager(unittest.TestCase):
) )
# Override methods to force specific logic in the module to happen # Override methods to force specific logic in the module to happen
upm = UnparitionedManager(module=module, params=module.params) upm = UnpartitionedManager(module=module, params=module.params)
upm.exists = Mock(return_value=True) upm.exists = Mock(return_value=True)
upm.update_on_device = Mock(return_value=True) upm.update_on_device = Mock(return_value=True)
upm.read_current_from_device = Mock(return_value=current) upm.read_current_from_device = Mock(return_value=current)
@ -510,7 +510,7 @@ class TestManager(unittest.TestCase):
) )
# Override methods to force specific logic in the module to happen # Override methods to force specific logic in the module to happen
upm = UnparitionedManager(module=module, params=module.params) upm = UnpartitionedManager(module=module, params=module.params)
upm.exists = Mock(return_value=True) upm.exists = Mock(return_value=True)
upm.update_on_device = Mock(return_value=True) upm.update_on_device = Mock(return_value=True)
upm.read_current_from_device = Mock(return_value=current) upm.read_current_from_device = Mock(return_value=current)
@ -520,7 +520,7 @@ class TestManager(unittest.TestCase):
mm.get_manager = Mock(return_value=upm) mm.get_manager = Mock(return_value=upm)
msg = "Shell access is only available to 'admin' or " \ msg = "Shell access is only available to 'admin' or " \
"'resource-admin' roles" "'resource-admin' roles."
with pytest.raises(F5ModuleError) as ex: with pytest.raises(F5ModuleError) as ex:
mm.exec_module() mm.exec_module()
@ -550,7 +550,7 @@ class TestLegacyManager(unittest.TestCase):
) )
# Override methods to force specific logic in the module to happen # Override methods to force specific logic in the module to happen
upm = UnparitionedManager(module=module, params=module.params) upm = UnpartitionedManager(module=module, params=module.params)
upm.create_on_device = Mock(return_value=True) upm.create_on_device = Mock(return_value=True)
upm.exists = Mock(return_value=False) upm.exists = Mock(return_value=False)
@ -579,7 +579,7 @@ class TestLegacyManager(unittest.TestCase):
) )
# Override methods to force specific logic in the module to happen # Override methods to force specific logic in the module to happen
upm = UnparitionedManager(module=module, params=module.params) upm = UnpartitionedManager(module=module, params=module.params)
upm.create_on_device = Mock(return_value=True) upm.create_on_device = Mock(return_value=True)
upm.exists = Mock(return_value=False) upm.exists = Mock(return_value=False)
@ -609,7 +609,7 @@ class TestLegacyManager(unittest.TestCase):
) )
# Override methods to force specific logic in the module to happen # Override methods to force specific logic in the module to happen
upm = UnparitionedManager(module=module, params=module.params) upm = UnpartitionedManager(module=module, params=module.params)
upm.create_on_device = Mock(return_value=True) upm.create_on_device = Mock(return_value=True)
upm.exists = Mock(return_value=False) upm.exists = Mock(return_value=False)
@ -639,7 +639,7 @@ class TestLegacyManager(unittest.TestCase):
) )
# Override methods to force specific logic in the module to happen # Override methods to force specific logic in the module to happen
upm = UnparitionedManager(module=module, params=module.params) upm = UnpartitionedManager(module=module, params=module.params)
upm.create_on_device = Mock(return_value=True) upm.create_on_device = Mock(return_value=True)
upm.exists = Mock(return_value=False) upm.exists = Mock(return_value=False)
@ -673,7 +673,7 @@ class TestLegacyManager(unittest.TestCase):
) )
# Override methods to force specific logic in the module to happen # Override methods to force specific logic in the module to happen
upm = UnparitionedManager(module=module, params=module.params) upm = UnpartitionedManager(module=module, params=module.params)
upm.create_on_device = Mock(return_value=True) upm.create_on_device = Mock(return_value=True)
upm.exists = Mock(return_value=False) upm.exists = Mock(return_value=False)
@ -705,7 +705,7 @@ class TestLegacyManager(unittest.TestCase):
) )
# Override methods to force specific logic in the module to happen # Override methods to force specific logic in the module to happen
upm = UnparitionedManager(module=module, params=module.params) upm = UnpartitionedManager(module=module, params=module.params)
upm.create_on_device = Mock(return_value=True) upm.create_on_device = Mock(return_value=True)
upm.exists = Mock(return_value=False) upm.exists = Mock(return_value=False)
@ -714,7 +714,7 @@ class TestLegacyManager(unittest.TestCase):
mm.get_manager = Mock(return_value=upm) mm.get_manager = Mock(return_value=upm)
msg = "Shell access is only available to 'admin' or " \ msg = "Shell access is only available to 'admin' or " \
"'resource-admin' roles" "'resource-admin' roles."
with pytest.raises(F5ModuleError) as ex: with pytest.raises(F5ModuleError) as ex:
mm.exec_module() mm.exec_module()
@ -745,7 +745,7 @@ class TestLegacyManager(unittest.TestCase):
) )
# Override methods to force specific logic in the module to happen # Override methods to force specific logic in the module to happen
upm = UnparitionedManager(module=module, params=module.params) upm = UnpartitionedManager(module=module, params=module.params)
upm.exists = Mock(return_value=True) upm.exists = Mock(return_value=True)
upm.update_on_device = Mock(return_value=True) upm.update_on_device = Mock(return_value=True)
upm.read_current_from_device = Mock(return_value=current) upm.read_current_from_device = Mock(return_value=current)
@ -782,7 +782,7 @@ class TestLegacyManager(unittest.TestCase):
) )
# Override methods to force specific logic in the module to happen # Override methods to force specific logic in the module to happen
upm = UnparitionedManager(module=module, params=module.params) upm = UnpartitionedManager(module=module, params=module.params)
upm.exists = Mock(return_value=True) upm.exists = Mock(return_value=True)
upm.update_on_device = Mock(return_value=True) upm.update_on_device = Mock(return_value=True)
upm.read_current_from_device = Mock(return_value=current) upm.read_current_from_device = Mock(return_value=current)
@ -821,7 +821,7 @@ class TestLegacyManager(unittest.TestCase):
) )
# Override methods to force specific logic in the module to happen # Override methods to force specific logic in the module to happen
upm = UnparitionedManager(module=module, params=module.params) upm = UnpartitionedManager(module=module, params=module.params)
upm.exists = Mock(return_value=True) upm.exists = Mock(return_value=True)
upm.update_on_device = Mock(return_value=True) upm.update_on_device = Mock(return_value=True)
upm.read_current_from_device = Mock(return_value=current) upm.read_current_from_device = Mock(return_value=current)
@ -861,7 +861,7 @@ class TestLegacyManager(unittest.TestCase):
) )
# Override methods to force specific logic in the module to happen # Override methods to force specific logic in the module to happen
upm = UnparitionedManager(module=module, params=module.params) upm = UnpartitionedManager(module=module, params=module.params)
upm.exists = Mock(return_value=True) upm.exists = Mock(return_value=True)
upm.update_on_device = Mock(return_value=True) upm.update_on_device = Mock(return_value=True)
upm.read_current_from_device = Mock(return_value=current) upm.read_current_from_device = Mock(return_value=current)
@ -904,7 +904,7 @@ class TestLegacyManager(unittest.TestCase):
) )
# Override methods to force specific logic in the module to happen # Override methods to force specific logic in the module to happen
upm = UnparitionedManager(module=module, params=module.params) upm = UnpartitionedManager(module=module, params=module.params)
upm.exists = Mock(return_value=True) upm.exists = Mock(return_value=True)
upm.update_on_device = Mock(return_value=True) upm.update_on_device = Mock(return_value=True)
upm.read_current_from_device = Mock(return_value=current) upm.read_current_from_device = Mock(return_value=current)
@ -914,7 +914,7 @@ class TestLegacyManager(unittest.TestCase):
mm.get_manager = Mock(return_value=upm) mm.get_manager = Mock(return_value=upm)
msg = "Shell access is only available to 'admin' or " \ msg = "Shell access is only available to 'admin' or " \
"'resource-admin' roles" "'resource-admin' roles."
with pytest.raises(F5ModuleError) as ex: with pytest.raises(F5ModuleError) as ex:
mm.exec_module() mm.exec_module()

Loading…
Cancel
Save