summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--lib/ansible/module_utils/redfish_utils.py247
-rw-r--r--lib/ansible/modules/remote_management/redfish/redfish_command.py95
2 files changed, 292 insertions, 50 deletions
diff --git a/lib/ansible/module_utils/redfish_utils.py b/lib/ansible/module_utils/redfish_utils.py
index cf80172b77..b79f1df876 100644
--- a/lib/ansible/module_utils/redfish_utils.py
+++ b/lib/ansible/module_utils/redfish_utils.py
@@ -43,7 +43,8 @@ class RedfishUtils(object):
msg = self._get_extended_message(e)
return {'ret': False,
'msg': "HTTP Error %s on GET request to '%s', extended message: '%s'"
- % (e.code, uri, msg)}
+ % (e.code, uri, msg),
+ 'status': e.code}
except URLError as e:
return {'ret': False, 'msg': "URL Error on GET request to '%s': '%s'"
% (uri, e.reason)}
@@ -66,7 +67,8 @@ class RedfishUtils(object):
msg = self._get_extended_message(e)
return {'ret': False,
'msg': "HTTP Error %s on POST request to '%s', extended message: '%s'"
- % (e.code, uri, msg)}
+ % (e.code, uri, msg),
+ 'status': e.code}
except URLError as e:
return {'ret': False, 'msg': "URL Error on POST request to '%s': '%s'"
% (uri, e.reason)}
@@ -100,7 +102,8 @@ class RedfishUtils(object):
msg = self._get_extended_message(e)
return {'ret': False,
'msg': "HTTP Error %s on PATCH request to '%s', extended message: '%s'"
- % (e.code, uri, msg)}
+ % (e.code, uri, msg),
+ 'status': e.code}
except URLError as e:
return {'ret': False, 'msg': "URL Error on PATCH request to '%s': '%s'"
% (uri, e.reason)}
@@ -110,9 +113,10 @@ class RedfishUtils(object):
'msg': "Failed PATCH request to '%s': '%s'" % (uri, to_text(e))}
return {'ret': True, 'resp': resp}
- def delete_request(self, uri, pyld):
+ def delete_request(self, uri, pyld=None):
try:
- resp = open_url(uri, data=json.dumps(pyld),
+ data = json.dumps(pyld) if pyld else None
+ resp = open_url(uri, data=data,
headers=DELETE_HEADERS, method="DELETE",
url_username=self.creds['user'],
url_password=self.creds['pswd'],
@@ -123,7 +127,8 @@ class RedfishUtils(object):
msg = self._get_extended_message(e)
return {'ret': False,
'msg': "HTTP Error %s on DELETE request to '%s', extended message: '%s'"
- % (e.code, uri, msg)}
+ % (e.code, uri, msg),
+ 'status': e.code}
except URLError as e:
return {'ret': False, 'msg': "URL Error on DELETE request to '%s': '%s'"
% (uri, e.reason)}
@@ -670,6 +675,60 @@ class RedfishUtils(object):
return response
return {'ret': True, 'changed': True}
+ def _find_account_uri(self, username=None, acct_id=None):
+ if not any((username, acct_id)):
+ return {'ret': False, 'msg':
+ 'Must provide either account_id or account_username'}
+
+ response = self.get_request(self.root_uri + self.accounts_uri)
+ if response['ret'] is False:
+ return response
+ data = response['data']
+
+ uris = [a.get('@odata.id') for a in data.get('Members', []) if
+ a.get('@odata.id')]
+ for uri in uris:
+ response = self.get_request(self.root_uri + uri)
+ if response['ret'] is False:
+ continue
+ data = response['data']
+ headers = response['headers']
+ if username:
+ if username == data.get('UserName'):
+ return {'ret': True, 'data': data,
+ 'headers': headers, 'uri': uri}
+ if acct_id:
+ if acct_id == data.get('Id'):
+ return {'ret': True, 'data': data,
+ 'headers': headers, 'uri': uri}
+
+ return {'ret': False, 'no_match': True, 'msg':
+ 'No account with the given account_id or account_username found'}
+
+ def _find_empty_account_slot(self):
+ response = self.get_request(self.root_uri + self.accounts_uri)
+ if response['ret'] is False:
+ return response
+ data = response['data']
+
+ uris = [a.get('@odata.id') for a in data.get('Members', []) if
+ a.get('@odata.id')]
+ if uris:
+ # first slot may be reserved, so move to end of list
+ uris += [uris.pop(0)]
+ for uri in uris:
+ response = self.get_request(self.root_uri + uri)
+ if response['ret'] is False:
+ continue
+ data = response['data']
+ headers = response['headers']
+ if data.get('UserName') == "" and not data.get('Enabled', True):
+ return {'ret': True, 'data': data,
+ 'headers': headers, 'uri': uri}
+
+ return {'ret': False, 'no_match': True, 'msg':
+ 'No empty account slot found'}
+
def list_users(self):
result = {}
# listing all users has always been slower than other operations, why?
@@ -684,7 +743,7 @@ class RedfishUtils(object):
result['ret'] = True
data = response['data']
- for users in data[u'Members']:
+ for users in data.get('Members', []):
user_list.append(users[u'@odata.id']) # user_list[] are URIs
# for each user, get details
@@ -703,54 +762,184 @@ class RedfishUtils(object):
result["entries"] = users_results
return result
+ def add_user_via_patch(self, user):
+ if user.get('account_id'):
+ # If Id slot specified, use it
+ response = self._find_account_uri(acct_id=user.get('account_id'))
+ else:
+ # Otherwise find first empty slot
+ response = self._find_empty_account_slot()
+
+ if not response['ret']:
+ return response
+ uri = response['uri']
+ payload = {}
+ if user.get('account_username'):
+ payload['UserName'] = user.get('account_username')
+ if user.get('account_password'):
+ payload['Password'] = user.get('account_password')
+ if user.get('account_roleid'):
+ payload['RoleId'] = user.get('account_roleid')
+ response = self.patch_request(self.root_uri + uri, payload)
+ if response['ret'] is False:
+ return response
+ return {'ret': True}
+
def add_user(self, user):
- uri = self.root_uri + self.accounts_uri + "/" + user['userid']
- username = {'UserName': user['username']}
- pswd = {'Password': user['userpswd']}
- roleid = {'RoleId': user['userrole']}
- enabled = {'Enabled': True}
- for payload in username, pswd, roleid, enabled:
- response = self.patch_request(uri, payload)
- if response['ret'] is False:
+ if not user.get('account_username'):
+ return {'ret': False, 'msg':
+ 'Must provide account_username for AddUser command'}
+
+ response = self._find_account_uri(username=user.get('account_username'))
+ if response['ret']:
+ # account_username already exists, nothing to do
+ return {'ret': True, 'changed': False}
+
+ response = self.get_request(self.root_uri + self.accounts_uri)
+ if not response['ret']:
+ return response
+ headers = response['headers']
+
+ if 'allow' in headers:
+ methods = [m.strip() for m in headers.get('allow').split(',')]
+ if 'POST' not in methods:
+ # if Allow header present and POST not listed, add via PATCH
+ return self.add_user_via_patch(user)
+
+ payload = {}
+ if user.get('account_username'):
+ payload['UserName'] = user.get('account_username')
+ if user.get('account_password'):
+ payload['Password'] = user.get('account_password')
+ if user.get('account_roleid'):
+ payload['RoleId'] = user.get('account_roleid')
+
+ response = self.post_request(self.root_uri + self.accounts_uri, payload)
+ if not response['ret']:
+ if response.get('status') == 405:
+ # if POST returned a 405, try to add via PATCH
+ return self.add_user_via_patch(user)
+ else:
return response
return {'ret': True}
def enable_user(self, user):
- uri = self.root_uri + self.accounts_uri + "/" + user['userid']
+ response = self._find_account_uri(username=user.get('account_username'),
+ acct_id=user.get('account_id'))
+ if not response['ret']:
+ return response
+ uri = response['uri']
+ data = response['data']
+
+ if data.get('Enabled'):
+ # account already enabled, nothing to do
+ return {'ret': True, 'changed': False}
+
payload = {'Enabled': True}
- response = self.patch_request(uri, payload)
+ response = self.patch_request(self.root_uri + uri, payload)
if response['ret'] is False:
return response
return {'ret': True}
- def delete_user(self, user):
- uri = self.root_uri + self.accounts_uri + "/" + user['userid']
- payload = {'UserName': ""}
- response = self.patch_request(uri, payload)
+ def delete_user_via_patch(self, user, uri=None, data=None):
+ if not uri:
+ response = self._find_account_uri(username=user.get('account_username'),
+ acct_id=user.get('account_id'))
+ if not response['ret']:
+ return response
+ uri = response['uri']
+ data = response['data']
+
+ if data and data.get('UserName') == '' and not data.get('Enabled', False):
+ # account UserName already cleared, nothing to do
+ return {'ret': True, 'changed': False}
+
+ payload = {'UserName': ''}
+ if 'Enabled' in data:
+ payload['Enabled'] = False
+ response = self.patch_request(self.root_uri + uri, payload)
if response['ret'] is False:
return response
return {'ret': True}
+ def delete_user(self, user):
+ response = self._find_account_uri(username=user.get('account_username'),
+ acct_id=user.get('account_id'))
+ if not response['ret']:
+ if response.get('no_match'):
+ # account does not exist, nothing to do
+ return {'ret': True, 'changed': False}
+ else:
+ # some error encountered
+ return response
+
+ uri = response['uri']
+ headers = response['headers']
+ data = response['data']
+
+ if 'allow' in headers:
+ methods = [m.strip() for m in headers.get('allow').split(',')]
+ if 'DELETE' not in methods:
+ # if Allow header present and DELETE not listed, del via PATCH
+ return self.delete_user_via_patch(user, uri=uri, data=data)
+
+ response = self.delete_request(self.root_uri + uri)
+ if not response['ret']:
+ if response.get('status') == 405:
+ # if DELETE returned a 405, try to delete via PATCH
+ return self.delete_user_via_patch(user, uri=uri, data=data)
+ else:
+ return response
+ return {'ret': True}
+
def disable_user(self, user):
- uri = self.root_uri + self.accounts_uri + "/" + user['userid']
+ response = self._find_account_uri(username=user.get('account_username'),
+ acct_id=user.get('account_id'))
+ if not response['ret']:
+ return response
+ uri = response['uri']
+ data = response['data']
+
+ if not data.get('Enabled'):
+ # account already disabled, nothing to do
+ return {'ret': True, 'changed': False}
+
payload = {'Enabled': False}
- response = self.patch_request(uri, payload)
+ response = self.patch_request(self.root_uri + uri, payload)
if response['ret'] is False:
return response
return {'ret': True}
def update_user_role(self, user):
- uri = self.root_uri + self.accounts_uri + "/" + user['userid']
- payload = {'RoleId': user['userrole']}
- response = self.patch_request(uri, payload)
+ if not user.get('account_roleid'):
+ return {'ret': False, 'msg':
+ 'Must provide account_roleid for UpdateUserRole command'}
+
+ response = self._find_account_uri(username=user.get('account_username'),
+ acct_id=user.get('account_id'))
+ if not response['ret']:
+ return response
+ uri = response['uri']
+ data = response['data']
+
+ if data.get('RoleId') == user.get('account_roleid'):
+ # account already has RoleId , nothing to do
+ return {'ret': True, 'changed': False}
+
+ payload = {'RoleId': user.get('account_roleid')}
+ response = self.patch_request(self.root_uri + uri, payload)
if response['ret'] is False:
return response
return {'ret': True}
def update_user_password(self, user):
- uri = self.root_uri + self.accounts_uri + "/" + user['userid']
- payload = {'Password': user['userpswd']}
- response = self.patch_request(uri, payload)
+ response = self._find_account_uri(username=user.get('account_username'),
+ acct_id=user.get('account_id'))
+ if not response['ret']:
+ return response
+ uri = response['uri']
+ payload = {'Password': user['account_password']}
+ response = self.patch_request(self.root_uri + uri, payload)
if response['ret'] is False:
return response
return {'ret': True}
diff --git a/lib/ansible/modules/remote_management/redfish/redfish_command.py b/lib/ansible/modules/remote_management/redfish/redfish_command.py
index bc06f621e6..f16e486edb 100644
--- a/lib/ansible/modules/remote_management/redfish/redfish_command.py
+++ b/lib/ansible/modules/remote_management/redfish/redfish_command.py
@@ -41,7 +41,7 @@ options:
username:
required: true
description:
- - User for authentication with OOB controller
+ - Username for authentication with OOB controller
type: str
version_added: "2.8"
password:
@@ -51,26 +51,30 @@ options:
type: str
id:
required: false
+ aliases: [ account_id ]
description:
- - ID of user to add/delete/modify
+ - ID of account to delete/modify
type: str
version_added: "2.8"
new_username:
required: false
+ aliases: [ account_username ]
description:
- - name of user to add/delete/modify
+ - Username of account to add/delete/modify
type: str
version_added: "2.8"
new_password:
required: false
+ aliases: [ account_password ]
description:
- - password of user to add/delete/modify
+ - New password of account to add/modify
type: str
version_added: "2.8"
roleid:
required: false
+ aliases: [ account_roleid ]
description:
- - role of user to add/delete/modify
+ - Role of account to add/modify
type: str
version_added: "2.8"
bootdevice:
@@ -146,26 +150,65 @@ EXAMPLES = '''
username: "{{ username }}"
password: "{{ password }}"
- - name: Add and enable user
+ - name: Add user
redfish_command:
category: Accounts
- command: AddUser,EnableUser
+ command: AddUser
baseuri: "{{ baseuri }}"
username: "{{ username }}"
password: "{{ password }}"
- id: "{{ id }}"
new_username: "{{ new_username }}"
new_password: "{{ new_password }}"
roleid: "{{ roleid }}"
- - name: Disable and delete user
+ - name: Add user using new option aliases
+ redfish_command:
+ category: Accounts
+ command: AddUser
+ baseuri: "{{ baseuri }}"
+ username: "{{ username }}"
+ password: "{{ password }}"
+ account_username: "{{ account_username }}"
+ account_password: "{{ account_password }}"
+ account_roleid: "{{ account_roleid }}"
+
+ - name: Delete user
+ redfish_command:
+ category: Accounts
+ command: DeleteUser
+ baseuri: "{{ baseuri }}"
+ username: "{{ username }}"
+ password: "{{ password }}"
+ account_username: "{{ account_username }}"
+
+ - name: Disable user
+ redfish_command:
+ category: Accounts
+ command: DisableUser
+ baseuri: "{{ baseuri }}"
+ username: "{{ username }}"
+ password: "{{ password }}"
+ account_username: "{{ account_username }}"
+
+ - name: Enable user
redfish_command:
category: Accounts
- command: ["DisableUser", "DeleteUser"]
+ command: EnableUser
baseuri: "{{ baseuri }}"
username: "{{ username }}"
password: "{{ password }}"
- id: "{{ id }}"
+ account_username: "{{ account_username }}"
+
+ - name: Add and enable user
+ redfish_command:
+ category: Accounts
+ command: AddUser,EnableUser
+ baseuri: "{{ baseuri }}"
+ username: "{{ username }}"
+ password: "{{ password }}"
+ new_username: "{{ new_username }}"
+ new_password: "{{ new_password }}"
+ roleid: "{{ roleid }}"
- name: Update user password
redfish_command:
@@ -174,8 +217,18 @@ EXAMPLES = '''
baseuri: "{{ baseuri }}"
username: "{{ username }}"
password: "{{ password }}"
- id: "{{ id }}"
- new_password: "{{ new_password }}"
+ account_username: "{{ account_username }}"
+ account_password: "{{ account_password }}"
+
+ - name: Update user role
+ redfish_command:
+ category: Accounts
+ command: UpdateUserRole
+ baseuri: "{{ baseuri }}"
+ username: "{{ username }}"
+ password: "{{ password }}"
+ account_username: "{{ account_username }}"
+ roleid: "{{ roleid }}"
- name: Clear Manager Logs with a timeout of 20 seconds
redfish_command:
@@ -220,10 +273,10 @@ def main():
baseuri=dict(required=True),
username=dict(required=True),
password=dict(required=True, no_log=True),
- id=dict(),
- new_username=dict(),
- new_password=dict(no_log=True),
- roleid=dict(),
+ id=dict(aliases=["account_id"]),
+ new_username=dict(aliases=["account_username"]),
+ new_password=dict(aliases=["account_password"], no_log=True),
+ roleid=dict(aliases=["account_roleid"]),
bootdevice=dict(),
timeout=dict(type='int', default=10),
uefi_target=dict(),
@@ -240,10 +293,10 @@ def main():
'pswd': module.params['password']}
# user to add/modify/delete
- user = {'userid': module.params['id'],
- 'username': module.params['new_username'],
- 'userpswd': module.params['new_password'],
- 'userrole': module.params['roleid']}
+ user = {'account_id': module.params['id'],
+ 'account_username': module.params['new_username'],
+ 'account_password': module.params['new_password'],
+ 'account_roleid': module.params['roleid']}
# timeout
timeout = module.params['timeout']