short_description: Client library for rancher API
 description:
   - This module modifies a rancher 1.6 using it's API (v1).
-  - WIP, as of now it can only change a current value to a new one.
-
+  - It supports some rancher features by the virtue of a 'mode'.
+  - 'modes' hide from you some necessary cruft and expose you to the only
+    important and interestig variables wich must be set. The mode mechanism
+    makes this module more easy to use and you don't have to create an
+    unnecessary boilerplate for the API.
+  - Only a few modes are/will be implemented so far - as they are/will be
+    needed. In the future the 'raw' mode can be added to enable you to craft
+    your own API requests, but that would be on the same level of a user
+    experience as running curl commands, and because the rancher 1.6 is already
+    obsoleted by the project, it would be a wasted effort.
 options:
   rancher:
     description:
     required: true
     aliases:
       - server
-      - rancher_url
+      - rancher_server
       - rancher_api
       - api
-      - url
-  category:
+  account_key:
     description:
-      - The path in JSON API without the last element.
+      - The public and secret part of the API key-pair separated by colon.
+      - You can find all your keys in web UI.
+      - For example:
+        B1716C4133D3825051CB:3P2eb3QhokFKYUiXRNZLxvGNSRYgh6LHjuMicCHQ
     required: false
-    default: settings
-    aliases:
-      - rancher_category
-      - api_category
-  option:
+  mode:
     description:
-      - The name of the settings option.
+      - A recognized mode how to deal with some concrete configuration task
+        in rancher API to ease the usage.
+      - The implemented modes so far are:
+        'settings':
+            Many options under <api_server>/v1/settings API url and some can
+            be seen also under advanced options in the web UI.
+        'access_control':
+            It setups user and password for the account (defaults to 'admin')
+            and it enables the local authentication - so the web UI and API
+            will require username/password (UI) or apikey (API).
     required: true
     aliases:
-      - name
-      - key
+      - rancher_mode
+      - api_mode
+    choices:
       - settings
-  value:
+      - access_control
+  data:
     description:
-      - A new value to replace the current one.
+      - Dictionary with key/value pairs. The actual names and meaning of pairs
+        depends on the used mode.
+      - 'settings' mode:
+        option: Option/path in JSON API (url).
+        value: A new value to replace the current one.
+      - 'access_control' mode:
+        None - not yet implemented - placeholder only.
     required: true
   timeout:
     description:
     default: 10.0
 """
 
+default_timeout = 10.0
+
+
+class ModeError(Exception):
+    pass
+
 
 def _decorate_rancher_api_request(request_method):
 
 
 
 @_decorate_rancher_api_request
-def get_rancher_api_value(url, headers=None, timeout=10.0,
+def get_rancher_api_value(url, headers=None, timeout=default_timeout,
                           username=None, password=None):
 
     if username and password:
 
 
 @_decorate_rancher_api_request
-def set_rancher_api_value(url, payload, headers=None, timeout=10.0,
+def set_rancher_api_value(url, payload, headers=None, timeout=default_timeout,
                           username=None, password=None):
 
     if username and password:
                             data=json.dumps(payload))
 
 
-def create_rancher_api_payload(json_data, new_value):
+def create_rancher_api_url(server, mode, option):
+    request_url = server.strip('/') + '/v1/'
 
-    payload = {}
+    if mode == 'raw':
+        request_url += option.strip('/')
+    elif mode == 'settings':
+        request_url += 'settings/' + option.strip('/')
+    elif mode == 'access_control':
+        request_url += option.strip('/')
 
-    try:
-        api_id = json_data['id']
-        api_activeValue = json_data['activeValue']
-        api_name = json_data['name']
-        api_source = json_data['source']
-    except Exception:
-        raise ValueError
-
-    payload.update({"activeValue": api_activeValue,
-                    "id": api_id,
-                    "name": api_name,
-                    "source": api_source,
-                    "value": new_value})
-
-    if api_activeValue != new_value:
-        differs = True
-    else:
-        differs = False
+    return request_url
 
-    return differs, payload
 
+def get_keypair(keypair):
+    if keypair:
+        keypair = keypair.split(':')
+        if len(keypair) == 2:
+            return keypair[0], keypair[1]
 
-def is_valid_rancher_api_option(json_data):
+    return None, None
 
-    try:
-        api_activeValue = json_data['activeValue']
-        api_source = json_data['source']
-    except Exception:
-        return False
 
-    if api_activeValue is None and api_source is None:
-        return False
+def mode_settings(api_url, data=None, headers=None, timeout=default_timeout,
+                  access_key=None, secret_key=None, dry_run=False):
 
-    return True
+    def is_valid_rancher_api_option(json_data):
 
+        try:
+            api_activeValue = json_data['activeValue']
+            api_source = json_data['source']
+        except Exception:
+            return False
 
-def main():
-    module = AnsibleModule(
-        argument_spec=dict(
-            rancher=dict(type='str', required=True,
-                         aliases=['server',
-                                  'rancher_api',
-                                  'rancher_url',
-                                  'api',
-                                  'url']),
-            category=dict(type='str', default='settings',
-                          aliases=['rancher_category', 'api_category']),
-            option=dict(type='str', required=True,
-                        aliases=['name', 'key', 'settings']),
-            value=dict(type='str', required=True),
-            timeout=dict(type='float', default=10.0),
-        ),
-        supports_check_mode=True
-    )
+        if api_activeValue is None and api_source is None:
+            return False
 
-    rancher_url = module.params['rancher'].strip('/')
-    rancher_option = module.params['option'].strip('/')
-    rancher_category = module.params['category']
-    rancher_value = module.params['value']
-    rancher_timeout = module.params['timeout']
-    # cattle_access_key = ''
-    # cattle_secret_key = ''
+        return True
 
-    # Assemble API url
-    request_url = rancher_url + '/v1/' + rancher_category + '/' \
-        + rancher_option
+    def create_rancher_api_payload(json_data, new_value):
 
-    http_headers = {'Content-Type': 'application/json',
-                    'Accept': 'application/json'}
+        payload = {}
+        differs = False
+
+        try:
+            api_id = json_data['id']
+            api_activeValue = json_data['activeValue']
+            api_name = json_data['name']
+            api_source = json_data['source']
+        except Exception:
+            raise ValueError
+
+        payload.update({"activeValue": api_activeValue,
+                        "id": api_id,
+                        "name": api_name,
+                        "source": api_source,
+                        "value": new_value})
+
+        if api_activeValue != new_value:
+            differs = True
+
+        return differs, payload
+
+    # check if data contains all required fields
+    try:
+        if not isinstance(data['option'], str) or data['option'] == '':
+            raise ModeError("ERROR: 'option' must contain a name of the \
+                            option")
+    except KeyError:
+        raise ModeError("ERROR: Mode 'settings' requires the field: 'option': \
+                        %s" % str(data))
+    try:
+        if not isinstance(data['value'], str) or data['value'] == '':
+            raise ModeError("ERROR: 'value' must contain a value")
+    except KeyError:
+        raise ModeError("ERROR: Mode 'settings' requires the field: 'value': \
+                        %s" % str(data))
+
+    # assemble request URL
+    request_url = api_url + 'settings/' + data['option'].strip('/')
 
     # API get current value
     try:
         json_response = get_rancher_api_value(request_url,
-                                              headers=http_headers,
-                                              timeout=rancher_timeout)
+                                              username=access_key,
+                                              password=secret_key,
+                                              headers=headers,
+                                              timeout=timeout)
     except requests.HTTPError as e:
-        module.fail_json(msg=str(e))
+        raise ModeError(str(e))
     except requests.Timeout as e:
-        module.fail_json(msg=str(e))
+        raise ModeError(str(e))
 
     if not json_response:
-        module.fail_json(msg='ERROR: BAD RESPONSE (GET) - no json value \
-                         in the response')
+        raise ModeError('ERROR: BAD RESPONSE (GET) - no json value in the \
+                        response')
 
     if is_valid_rancher_api_option(json_response):
         valid = True
         try:
             differs, payload = create_rancher_api_payload(json_response,
-                                                          rancher_value)
+                                                          data['value'])
         except ValueError:
-            module.fail_json(msg='ERROR: INVALID JSON - missing json values \
-                             in the response')
+            raise ModeError('ERROR: INVALID JSON - missing json values in \
+                            the response')
     else:
         valid = False
 
-    if valid and differs and module.check_mode:
+    if valid and differs and dry_run:
         # ansible dry-run mode
         changed = True
     elif valid and differs:
         try:
             json_response = set_rancher_api_value(request_url,
                                                   payload,
-                                                  headers=http_headers,
-                                                  timeout=rancher_timeout)
+                                                  username=access_key,
+                                                  password=secret_key,
+                                                  headers=headers,
+                                                  timeout=timeout)
         except requests.HTTPError as e:
-            module.fail_json(msg=str(e))
+            raise ModeError(str(e))
         except requests.Timeout as e:
-            module.fail_json(msg=str(e))
+            raise ModeError(str(e))
 
         if not json_response:
-            module.fail_json(msg='ERROR: BAD RESPONSE (PUT) - no json value \
-                             in the response')
+            raise ModeError('ERROR: BAD RESPONSE (PUT) - no json value in \
+                            the response')
         else:
             changed = True
     else:
 
     if changed:
         msg = "Option '%s' is now set to the new value: %s" \
-            % (rancher_option, rancher_value)
+            % (data['option'], data['value'])
     else:
-        msg = "Option '%s' is unchanged." % (rancher_option)
+        msg = "Option '%s' is unchanged." % (data['option'])
+
+    return changed, msg
+
+
+def mode_handler(server, rancher_mode, data=None, timeout=default_timeout,
+                 account_key=None, dry_run=False):
+
+    changed = False
+    msg = 'UNKNOWN: UNAPPLICABLE MODE'
+
+    # check API key-pair
+    if account_key:
+        access_key, secret_key = get_keypair(account_key)
+        if not (access_key and secret_key):
+            raise ModeError('ERROR: INVALID API KEY-PAIR')
+
+    # all requests share these headers
+    http_headers = {'Content-Type': 'application/json',
+                    'Accept': 'application/json'}
+
+    # assemble API url
+    api_url = server.strip('/') + '/v1/'
+
+    if rancher_mode == 'settings':
+        changed, msg = mode_settings(api_url, data=data,
+                                     headers=http_headers,
+                                     timeout=timeout,
+                                     access_key=access_key,
+                                     secret_key=secret_key,
+                                     dry_run=dry_run)
+    elif rancher_mode == 'access_control':
+        msg = "SKIP: 'access_control' Not yet implemented"
+
+    return changed, msg
+
+
+def main():
+    module = AnsibleModule(
+        argument_spec=dict(
+            rancher=dict(type='str', required=True,
+                         aliases=['server',
+                                  'rancher_api',
+                                  'rancher_server',
+                                  'api']),
+            account_key=dict(type='str', required=False),
+            mode=dict(required=True,
+                      choices=['settings', 'access_control'],
+                      aliases=['api_mode']),
+            data=dict(type='dict', required=True),
+            timeout=dict(type='float', default=default_timeout),
+        ),
+        supports_check_mode=True
+    )
+
+    rancher_server = module.params['rancher']
+    rancher_account_key = module.params['account_key']
+    rancher_mode = module.params['mode']
+    rancher_data = module.params['data']
+    rancher_timeout = module.params['timeout']
+
+    try:
+        changed, msg = mode_handler(rancher_server,
+                                    rancher_mode,
+                                    data=rancher_data,
+                                    account_key=rancher_account_key,
+                                    timeout=rancher_timeout,
+                                    dry_run=module.check_mode)
+    except ModeError as e:
+        module.fail_json(msg=str(e))
 
     module.exit_json(changed=changed, msg=msg)