summaryrefslogtreecommitdiffstats
path: root/ansible/library/rancher1_api.py
diff options
context:
space:
mode:
Diffstat (limited to 'ansible/library/rancher1_api.py')
-rw-r--r--ansible/library/rancher1_api.py244
1 files changed, 216 insertions, 28 deletions
diff --git a/ansible/library/rancher1_api.py b/ansible/library/rancher1_api.py
index 006de9ce..d49e6252 100644
--- a/ansible/library/rancher1_api.py
+++ b/ansible/library/rancher1_api.py
@@ -64,11 +64,17 @@ options:
description:
- 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.
+ - settings mode:
+ option - Option/path in JSON API (url).
+ value - A new value to replace the current one.
+ - access_control mode:
+ account_id - The unique ID of the account - the default rancher admin
+ has '1a1'. Better way would be to just create arbitrary username and
+ set credentials for that, but due to time constraints, the route with
+ an ID is simpler. The designated '1a1' could be hardcoded and hidden
+ but if the user will want to use some other account (there are many),
+ then it can be just changed to some other ID.
+ password - A new password in a plaintext.
required: true
timeout:
description:
@@ -121,19 +127,26 @@ def get_rancher_api_value(url, headers=None, timeout=default_timeout,
@_decorate_rancher_api_request
def set_rancher_api_value(url, payload, headers=None, timeout=default_timeout,
- username=None, password=None):
+ username=None, password=None, method='PUT'):
+
+ if method == 'PUT':
+ request_set_method = requests.put
+ elif method == 'POST':
+ request_set_method = requests.post
+ else:
+ raise ModeError('ERROR: Wrong request method: %s' % str(method))
if username and password:
- return requests.put(url, headers=headers,
- timeout=timeout,
- allow_redirects=False,
- data=json.dumps(payload),
- auth=(username, password))
+ return request_set_method(url, headers=headers,
+ timeout=timeout,
+ allow_redirects=False,
+ data=json.dumps(payload),
+ auth=(username, password))
else:
- return requests.put(url, headers=headers,
- timeout=timeout,
- allow_redirects=False,
- data=json.dumps(payload))
+ return request_set_method(url, headers=headers,
+ timeout=timeout,
+ allow_redirects=False,
+ data=json.dumps(payload))
def create_rancher_api_url(server, mode, option):
@@ -158,6 +171,176 @@ def get_keypair(keypair):
return None, None
+def mode_access_control(api_url, data=None, headers=None,
+ timeout=default_timeout, access_key=None,
+ secret_key=None, dry_run=False):
+
+ # returns true if local auth was enabled or false if passwd changed
+ def is_admin_enabled(json_data, password):
+ try:
+ if json_data['type'] == "localAuthConfig" and \
+ json_data['accessMode'] == "unrestricted" and \
+ json_data['username'] == "admin" and \
+ json_data['password'] == password and \
+ json_data['enabled']:
+ return True
+ except Exception:
+ pass
+
+ try:
+ if json_data['type'] == "error" and \
+ json_data['code'] == "IncorrectPassword":
+ return False
+ except Exception:
+ pass
+
+ # this should never happen
+ raise ModeError('ERROR: Unknown status of the local authentication')
+
+ def create_localauth_payload(password):
+ payload = {
+ "enabled": True,
+ "accessMode": "unrestricted",
+ "username": "admin",
+ "password": password
+ }
+
+ return payload
+
+ def get_admin_password_id():
+ # assemble request URL
+ request_url = api_url + 'accounts/' + data['account_id'].strip('/') \
+ + '/credentials'
+
+ # API get current value
+ try:
+ json_response = get_rancher_api_value(request_url,
+ username=access_key,
+ password=secret_key,
+ headers=headers,
+ timeout=timeout)
+ except requests.HTTPError as e:
+ raise ModeError(str(e))
+ except requests.Timeout as e:
+ raise ModeError(str(e))
+
+ if not json_response:
+ raise ModeError('ERROR: BAD RESPONSE (GET) - no json value in '
+ + 'the response')
+
+ try:
+ for item in json_response['data']:
+ if item['type'] == 'password' and \
+ item['accountId'] == data['account_id']:
+ return item['id']
+ except Exception:
+ pass
+
+ return None
+
+ # check if data contains all required fields
+ try:
+ if not isinstance(data['account_id'], str) or data['account_id'] == '':
+ raise ModeError("ERROR: 'account_id' must contain an id of the "
+ + "affected account")
+ except KeyError:
+ raise ModeError("ERROR: Mode 'access_control' requires the field: "
+ + "'account_id': %s" % str(data))
+ try:
+ if not isinstance(data['password'], str) or data['password'] == '':
+ raise ModeError("ERROR: 'password' must contain some password")
+ except KeyError:
+ raise ModeError("ERROR: Mode 'access_control' requires the field: "
+ + "'password': %s" % str(data))
+
+ # assemble request URL
+ request_url = api_url + 'localauthconfigs'
+
+ # API get current value
+ try:
+ json_response = get_rancher_api_value(request_url,
+ username=access_key,
+ password=secret_key,
+ headers=headers,
+ timeout=timeout)
+ except requests.HTTPError as e:
+ raise ModeError(str(e))
+ except requests.Timeout as e:
+ raise ModeError(str(e))
+
+ if not json_response:
+ raise ModeError('ERROR: BAD RESPONSE (GET) - no json value in the '
+ + 'response')
+
+ # we will check if local auth is enabled or not
+ enabled = False
+ try:
+ for item in json_response['data']:
+ if item['type'] == 'localAuthConfig' and \
+ item['accessMode'] == 'unrestricted' and \
+ item['enabled']:
+ enabled = True
+ break
+ except Exception:
+ enabled = False
+
+ if dry_run:
+ # we will not set anything, only signal potential change
+ if enabled:
+ changed = False
+ else:
+ changed = True
+ else:
+ # we will try to enable again with the same password
+ localauth_payload = create_localauth_payload(data['password'])
+ json_response = None
+ try:
+ json_response = set_rancher_api_value(request_url,
+ localauth_payload,
+ username=access_key,
+ password=secret_key,
+ headers=headers,
+ method='POST',
+ timeout=timeout)
+ except requests.HTTPError as e:
+ raise ModeError(str(e))
+ except requests.Timeout as e:
+ raise ModeError(str(e))
+
+ if not json_response:
+ raise ModeError('ERROR: BAD RESPONSE (PUT) - no json value in '
+ + 'the response')
+
+ # we check if the admin was already set or not...
+ if enabled and is_admin_enabled(json_response, data['password']):
+ # it was enabled before and password is the same - no change
+ changed = False
+ elif is_admin_enabled(json_response, data['password']):
+ # we enabled it for the first time
+ changed = True
+ # ...and reset password if needed
+ else:
+ # local auth is enabled but the password differs
+ # we must reset the admin's password
+ password_id = get_admin_password_id()
+
+ if password_id is None:
+ raise ModeError("ERROR: admin's password is set, but we "
+ + "cannot identify it")
+
+ # TODO
+ raise ModeError("TODO: Reset of the admin password is not yet "
+ + "implemented")
+
+ if changed:
+ msg = "Local authentication is enabled, admin has assigned password"
+ else:
+ msg = "Local authentication was already enabled, admin's password " \
+ + "is unchanged"
+
+ return changed, msg
+
+
def mode_settings(api_url, data=None, headers=None, timeout=default_timeout,
access_key=None, secret_key=None, dry_run=False):
@@ -201,17 +384,17 @@ def mode_settings(api_url, data=None, headers=None, timeout=default_timeout,
# 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")
+ 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))
+ 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))
+ raise ModeError("ERROR: Mode 'settings' requires the field: 'value': "
+ + "%s" % str(data))
# assemble request URL
request_url = api_url + 'settings/' + data['option'].strip('/')
@@ -229,8 +412,8 @@ def mode_settings(api_url, data=None, headers=None, timeout=default_timeout,
raise ModeError(str(e))
if not json_response:
- raise ModeError('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
@@ -238,8 +421,8 @@ def mode_settings(api_url, data=None, headers=None, timeout=default_timeout,
differs, payload = create_rancher_api_payload(json_response,
data['value'])
except ValueError:
- raise ModeError('ERROR: INVALID JSON - missing json values in \
- the response')
+ raise ModeError('ERROR: INVALID JSON - missing json values in '
+ + 'the response')
else:
valid = False
@@ -261,8 +444,8 @@ def mode_settings(api_url, data=None, headers=None, timeout=default_timeout,
raise ModeError(str(e))
if not json_response:
- raise ModeError('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:
@@ -304,7 +487,12 @@ def mode_handler(server, rancher_mode, data=None, timeout=default_timeout,
secret_key=secret_key,
dry_run=dry_run)
elif rancher_mode == 'access_control':
- msg = "SKIP: 'access_control' Not yet implemented"
+ changed, msg = mode_access_control(api_url, data=data,
+ headers=http_headers,
+ timeout=timeout,
+ access_key=access_key,
+ secret_key=secret_key,
+ dry_run=dry_run)
return changed, msg