mirror of
https://github.com/ansible-collections/community.general.git
synced 2024-09-14 20:13:21 +02:00
New module: pmem to configure Intel Optane Persistent Memory modules (#4162)
* Add new module: pmem This commit introduces to pmem module to configure Intel Optane Persistent Memory modules (PMem). * Add botmeta * Update plugins/modules/storage/pmem/pmem.py * Convert to snake_case options * Update related to xmltodict * Update to use list instead of string * Update to use single quote to the string * Update plugins/modules/storage/pmem/pmem.py
This commit is contained in:
parent
7b02adc57e
commit
7f793c83f1
4 changed files with 887 additions and 0 deletions
2
.github/BOTMETA.yml
vendored
2
.github/BOTMETA.yml
vendored
|
@ -979,6 +979,8 @@ files:
|
|||
maintainers: farhan7500 gautamphegde
|
||||
$modules/storage/ibm/:
|
||||
maintainers: tzure
|
||||
$modules/storage/pmem/pmem.py:
|
||||
maintainers: mizumm
|
||||
$modules/storage/vexata/:
|
||||
maintainers: vexata
|
||||
$modules/storage/zfs/:
|
||||
|
|
1
plugins/modules/pmem.py
Symbolic link
1
plugins/modules/pmem.py
Symbolic link
|
@ -0,0 +1 @@
|
|||
storage/pmem/pmem.py
|
473
plugins/modules/storage/pmem/pmem.py
Normal file
473
plugins/modules/storage/pmem/pmem.py
Normal file
|
@ -0,0 +1,473 @@
|
|||
#!/usr/bin/python
|
||||
# -*- coding: utf-8 -*-
|
||||
# Copyright (c) 2022, Masayoshi Mizuma <msys.mizuma@gmail.com>
|
||||
# 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)
|
||||
__metaclass__ = type
|
||||
|
||||
DOCUMENTATION = r'''
|
||||
---
|
||||
author:
|
||||
- Masayoshi Mizuma (@mizumm)
|
||||
module: pmem
|
||||
short_description: Configure Intel Optane Persistent Memory modules
|
||||
version_added: 4.5.0
|
||||
description:
|
||||
- This module allows Configuring Intel Optane Persistent Memory modules
|
||||
(PMem) using ipmctl and ndctl command line tools.
|
||||
requirements:
|
||||
- ipmctl and ndctl command line tools
|
||||
- xmltodict
|
||||
options:
|
||||
appdirect:
|
||||
description:
|
||||
- Percentage of the total capacity to use in AppDirect Mode (C(0)-C(100)).
|
||||
- Create AppDirect capacity utilizing hardware interleaving across the
|
||||
requested PMem modules if applicable given the specified target.
|
||||
- Total of I(appdirect), I(memorymode) and I(reserved) must be C(100)
|
||||
type: int
|
||||
appdirect_interleaved:
|
||||
description:
|
||||
- Create AppDirect capacity that is interleaved any other PMem modules.
|
||||
type: bool
|
||||
required: false
|
||||
default: true
|
||||
memorymode:
|
||||
description:
|
||||
- Percentage of the total capacity to use in Memory Mode (C(0)-C(100)).
|
||||
type: int
|
||||
reserved:
|
||||
description:
|
||||
- Percentage of the capacity to reserve (C(0)-C(100)). I(reserved) will not be mapped
|
||||
into the system physical address space and will be presented as reserved
|
||||
capacity with Show Device and Show Memory Resources Commands.
|
||||
- I(reserved) will be set automatically if this is not configured.
|
||||
type: int
|
||||
required: false
|
||||
socket:
|
||||
description:
|
||||
- This enables to set the configuration for each socket by using the socket ID.
|
||||
- Total of I(appdirect), I(memorymode) and I(reserved) must be C(100) within one socket.
|
||||
type: list
|
||||
elements: dict
|
||||
suboptions:
|
||||
id:
|
||||
description: The socket ID of the PMem module.
|
||||
type: int
|
||||
required: true
|
||||
appdirect:
|
||||
description:
|
||||
- Percentage of the total capacity to use in AppDirect Mode (C(0)-C(100)) within the socket ID.
|
||||
type: int
|
||||
required: true
|
||||
appdirect_interleaved:
|
||||
description:
|
||||
- Create AppDirect capacity that is interleaved any other PMem modules within the socket ID.
|
||||
type: bool
|
||||
required: false
|
||||
default: true
|
||||
memorymode:
|
||||
description:
|
||||
- Percentage of the total capacity to use in Memory Mode (C(0)-C(100)) within the socket ID.
|
||||
type: int
|
||||
required: true
|
||||
reserved:
|
||||
description:
|
||||
- Percentage of the capacity to reserve (C(0)-C(100)) within the socket ID.
|
||||
type: int
|
||||
'''
|
||||
|
||||
RETURN = r'''
|
||||
reboot_required:
|
||||
description: Indicates that the system reboot is required to complete the PMem configuration.
|
||||
returned: success
|
||||
type: bool
|
||||
sample: True
|
||||
result:
|
||||
description:
|
||||
- Shows the value of AppDirect, Memory Mode and Reserved size in bytes.
|
||||
- If I(socket) argument is provided, shows the values in each socket with C(socket) which contains the socket ID.
|
||||
returned: success
|
||||
type: list
|
||||
elements: dict
|
||||
contains:
|
||||
appdirect:
|
||||
description: AppDirect size in bytes.
|
||||
type: int
|
||||
memorymode:
|
||||
description: Memory Mode size in bytes.
|
||||
type: int
|
||||
reserved:
|
||||
description: Reserved size in bytes.
|
||||
type: int
|
||||
socket:
|
||||
description: The socket ID to be configured.
|
||||
type: int
|
||||
sample: [
|
||||
{
|
||||
"appdirect": 111669149696,
|
||||
"memorymode": 970662608896,
|
||||
"reserved": 3626500096,
|
||||
"socket": 0
|
||||
},
|
||||
{
|
||||
"appdirect": 111669149696,
|
||||
"memorymode": 970662608896,
|
||||
"reserved": 3626500096,
|
||||
"socket": 1
|
||||
}
|
||||
]
|
||||
'''
|
||||
|
||||
EXAMPLES = r'''
|
||||
- name: Configure the Pmem as AppDirect 10, Memory Mode 70, and the Reserved 20 percent.
|
||||
community.general.pmem:
|
||||
appdirect: 10
|
||||
memorymode: 70
|
||||
|
||||
- name: Configure the Pmem as AppDirect 10, Memory Mode 80, and the Reserved 10 percent.
|
||||
community.general.pmem:
|
||||
appdirect: 10
|
||||
memorymode: 80
|
||||
reserved: 10
|
||||
|
||||
- name: Configure the Pmem as AppDirect with not interleaved 10, Memory Mode 70, and the Reserved 20 percent.
|
||||
community.general.pmem:
|
||||
appdirect: 10
|
||||
appdirect_interleaved: False
|
||||
memorymode: 70
|
||||
|
||||
- name: Configure the Pmem each socket.
|
||||
community.general.pmem:
|
||||
socket:
|
||||
- id: 0
|
||||
appdirect: 10
|
||||
appdirect_interleaved: False
|
||||
memorymode: 70
|
||||
reserved: 20
|
||||
- id: 1
|
||||
appdirect: 10
|
||||
memorymode: 80
|
||||
reserved: 10
|
||||
'''
|
||||
|
||||
import json
|
||||
import re
|
||||
import traceback
|
||||
from ansible.module_utils.basic import AnsibleModule, missing_required_lib
|
||||
|
||||
try:
|
||||
import xmltodict
|
||||
except ImportError:
|
||||
HAS_XMLTODICT_LIBRARY = False
|
||||
XMLTODICT_LIBRARY_IMPORT_ERROR = traceback.format_exc()
|
||||
else:
|
||||
HAS_XMLTODICT_LIBRARY = True
|
||||
|
||||
|
||||
class PersistentMemory(object):
|
||||
def __init__(self):
|
||||
module = AnsibleModule(
|
||||
argument_spec=dict(
|
||||
appdirect=dict(type='int'),
|
||||
appdirect_interleaved=dict(type='bool', default=True),
|
||||
memorymode=dict(type='int'),
|
||||
reserved=dict(type='int'),
|
||||
socket=dict(
|
||||
type='list', elements='dict',
|
||||
options=dict(
|
||||
id=dict(required=True, type='int'),
|
||||
appdirect=dict(required=True, type='int'),
|
||||
appdirect_interleaved=dict(type='bool', default=True),
|
||||
memorymode=dict(required=True, type='int'),
|
||||
reserved=dict(type='int'),
|
||||
),
|
||||
),
|
||||
),
|
||||
required_together=(
|
||||
['appdirect', 'memorymode'],
|
||||
),
|
||||
required_one_of=(
|
||||
['appdirect', 'memorymode', 'socket'],
|
||||
),
|
||||
mutually_exclusive=(
|
||||
['appdirect', 'socket'],
|
||||
['memorymode', 'socket'],
|
||||
),
|
||||
)
|
||||
|
||||
if not HAS_XMLTODICT_LIBRARY:
|
||||
module.fail_json(
|
||||
msg=missing_required_lib('xmltodict'),
|
||||
exception=XMLTODICT_LIBRARY_IMPORT_ERROR)
|
||||
|
||||
self.ipmctl_exec = module.get_bin_path('ipmctl', True)
|
||||
self.ndctl_exec = module.get_bin_path('ndctl', True)
|
||||
|
||||
self.appdirect = module.params['appdirect']
|
||||
self.interleaved = module.params['appdirect_interleaved']
|
||||
self.memmode = module.params['memorymode']
|
||||
self.reserved = module.params['reserved']
|
||||
self.socket = module.params['socket']
|
||||
|
||||
self.module = module
|
||||
self.changed = False
|
||||
self.result = []
|
||||
|
||||
def pmem_run_command(self, command, returnCheck=True):
|
||||
# in case command[] has number
|
||||
cmd = [str(part) for part in command]
|
||||
|
||||
self.module.log(msg='pmem_run_command: execute: %s' % cmd)
|
||||
|
||||
rc, out, err = self.module.run_command(cmd)
|
||||
|
||||
self.module.log(msg='pmem_run_command: result: %s' % out)
|
||||
|
||||
if returnCheck and rc != 0:
|
||||
self.module.fail_json(msg='Error while running: %s' %
|
||||
cmd, rc=rc, out=out, err=err)
|
||||
|
||||
return out
|
||||
|
||||
def pmem_run_ipmctl(self, command, returnCheck=True):
|
||||
|
||||
command = [self.ipmctl_exec] + command
|
||||
|
||||
return self.pmem_run_command(command, returnCheck)
|
||||
|
||||
def pmem_run_ndctl(self, command, returnCheck=True):
|
||||
|
||||
command = [self.ndctl_exec] + command
|
||||
|
||||
return self.pmem_run_command(command, returnCheck)
|
||||
|
||||
def pmem_is_dcpmm_installed(self):
|
||||
# To check this system has dcpmm
|
||||
command = ['show', '-system', '-capabilities']
|
||||
return self.pmem_run_ipmctl(command)
|
||||
|
||||
def pmem_argument_check(self):
|
||||
def percent_check(self, appdirect, memmode, reserved=None):
|
||||
if appdirect is None or (appdirect < 0 or appdirect > 100):
|
||||
return 'appdirect percent should be from 0 to 100.'
|
||||
if memmode is None or (memmode < 0 or memmode > 100):
|
||||
return 'memorymode percent should be from 0 to 100.'
|
||||
|
||||
if reserved is None:
|
||||
if appdirect + memmode > 100:
|
||||
return 'Total percent should be less equal 100.'
|
||||
else:
|
||||
if reserved < 0 or reserved > 100:
|
||||
return 'reserved percent should be from 0 to 100.'
|
||||
if appdirect + memmode + reserved != 100:
|
||||
return 'Total percent should be 100.'
|
||||
|
||||
def socket_id_check(self):
|
||||
command = ['show', '-o', 'nvmxml', '-socket']
|
||||
out = self.pmem_run_ipmctl(command)
|
||||
sockets_dict = xmltodict.parse(out, dict_constructor=dict)['SocketList']['Socket']
|
||||
socket_ids = []
|
||||
for sl in sockets_dict:
|
||||
socket_ids.append(int(sl['SocketID'], 16))
|
||||
|
||||
for skt in self.socket:
|
||||
if skt['id'] not in socket_ids:
|
||||
return 'Invalid socket number: %d' % skt['id']
|
||||
|
||||
return None
|
||||
|
||||
if self.socket is None:
|
||||
return percent_check(self, self.appdirect, self.memmode, self.reserved)
|
||||
else:
|
||||
ret = socket_id_check(self)
|
||||
if ret is not None:
|
||||
return ret
|
||||
|
||||
for skt in self.socket:
|
||||
ret = percent_check(
|
||||
self, skt['appdirect'], skt['memorymode'], skt['reserved'])
|
||||
if ret is not None:
|
||||
return ret
|
||||
|
||||
return None
|
||||
|
||||
def pmem_remove_namespaces(self):
|
||||
command = ['list', '-N']
|
||||
out = self.pmem_run_ndctl(command)
|
||||
|
||||
# There's nothing namespaces in this system. Nothing to do.
|
||||
if not out:
|
||||
return
|
||||
|
||||
namespaces = json.loads(out)
|
||||
|
||||
# Disable and destroy all namespaces
|
||||
for ns in namespaces:
|
||||
command = ['disable-namespace', ns['dev']]
|
||||
self.pmem_run_ndctl(command)
|
||||
|
||||
command = ['destroy-namespace', ns['dev']]
|
||||
self.pmem_run_ndctl(command)
|
||||
|
||||
return
|
||||
|
||||
def pmem_delete_goal(self):
|
||||
# delete the goal request
|
||||
command = ['delete', '-goal']
|
||||
self.pmem_run_ipmctl(command)
|
||||
|
||||
def pmem_init_env(self):
|
||||
self.pmem_remove_namespaces()
|
||||
self.pmem_delete_goal()
|
||||
|
||||
def pmem_get_capacity(self, skt=None):
|
||||
command = ['show', '-d', 'Capacity', '-u', 'B', '-o', 'nvmxml', '-dimm']
|
||||
if skt:
|
||||
command += ['-socket', skt['id']]
|
||||
out = self.pmem_run_ipmctl(command)
|
||||
|
||||
dimm_list = xmltodict.parse(out, dict_constructor=dict)['DimmList']['Dimm']
|
||||
capacity = 0
|
||||
for entry in dimm_list:
|
||||
for key, v in entry.items():
|
||||
if key == 'Capacity':
|
||||
capacity += int(v.split()[0])
|
||||
|
||||
return capacity
|
||||
|
||||
def pmem_create_memory_allocation(self, skt=None):
|
||||
def build_ipmctl_creation_opts(self, skt=None):
|
||||
ipmctl_opts = []
|
||||
|
||||
if skt:
|
||||
appdirect = skt['appdirect']
|
||||
memmode = skt['memorymode']
|
||||
reserved = skt['reserved']
|
||||
socket_id = skt['id']
|
||||
ipmctl_opts += ['-socket', socket_id]
|
||||
else:
|
||||
appdirect = self.appdirect
|
||||
memmode = self.memmode
|
||||
reserved = self.reserved
|
||||
|
||||
if reserved is None:
|
||||
res = 100 - memmode - appdirect
|
||||
ipmctl_opts += ['memorymode=%d' % memmode, 'reserved=%d' % res]
|
||||
else:
|
||||
ipmctl_opts += ['memorymode=%d' % memmode, 'reserved=%d' % reserved]
|
||||
|
||||
if self.interleaved:
|
||||
ipmctl_opts += ['PersistentMemoryType=AppDirect']
|
||||
else:
|
||||
ipmctl_opts += ['PersistentMemoryType=AppDirectNotInterleaved']
|
||||
|
||||
return ipmctl_opts
|
||||
|
||||
def is_allocation_good(self, ipmctl_out, command):
|
||||
warning = re.compile('WARNING')
|
||||
error = re.compile('.*Error.*')
|
||||
ignore_error = re.compile(
|
||||
'Do you want to continue? [y/n] Error: Invalid data input.')
|
||||
|
||||
errmsg = ''
|
||||
rc = True
|
||||
for line in ipmctl_out.splitlines():
|
||||
if warning.match(line):
|
||||
errmsg = '%s (command: %s)' % (line, command)
|
||||
rc = False
|
||||
break
|
||||
elif error.match(line):
|
||||
if not ignore_error:
|
||||
errmsg = '%s (command: %s)' % (line, command)
|
||||
rc = False
|
||||
break
|
||||
|
||||
return rc, errmsg
|
||||
|
||||
def get_allocation_result(self, goal, skt=None):
|
||||
ret = {'appdirect': 0, 'memorymode': 0}
|
||||
|
||||
if skt:
|
||||
ret['socket'] = skt['id']
|
||||
|
||||
out = xmltodict.parse(goal, dict_constructor=dict)['ConfigGoalList']['ConfigGoal']
|
||||
for entry in out:
|
||||
|
||||
# Probably it's a bug of ipmctl to show the socket goal
|
||||
# which isn't specified by the -socket option.
|
||||
# Anyway, filter the noise out here:
|
||||
if skt and skt['id'] != int(entry['SocketID'], 16):
|
||||
continue
|
||||
|
||||
for key, v in entry.items():
|
||||
if key == 'MemorySize':
|
||||
ret['memorymode'] += int(v.split()[0])
|
||||
elif key == 'AppDirect1Size' or key == 'AapDirect2Size':
|
||||
ret['appdirect'] += int(v.split()[0])
|
||||
|
||||
capacity = self.pmem_get_capacity(skt)
|
||||
ret['reserved'] = capacity - ret['appdirect'] - ret['memorymode']
|
||||
|
||||
return ret
|
||||
|
||||
reboot_required = False
|
||||
|
||||
ipmctl_opts = build_ipmctl_creation_opts(self, skt)
|
||||
|
||||
# First, do dry run ipmctl create command to check the error and warning.
|
||||
command = ['create', '-goal'] + ipmctl_opts
|
||||
out = self.pmem_run_ipmctl(command, returnCheck=False)
|
||||
rc, errmsg = is_allocation_good(self, out, command)
|
||||
if rc is False:
|
||||
return reboot_required, {}, errmsg
|
||||
|
||||
# Run actual creation here
|
||||
command = ['create', '-u', 'B', '-o', 'nvmxml', '-force', '-goal'] + ipmctl_opts
|
||||
goal = self.pmem_run_ipmctl(command)
|
||||
ret = get_allocation_result(self, goal, skt)
|
||||
reboot_required = True
|
||||
|
||||
return reboot_required, ret, ''
|
||||
|
||||
|
||||
def main():
|
||||
|
||||
pmem = PersistentMemory()
|
||||
|
||||
pmem.pmem_is_dcpmm_installed()
|
||||
|
||||
error = pmem.pmem_argument_check()
|
||||
if error:
|
||||
pmem.module.fail_json(msg=error)
|
||||
|
||||
pmem.pmem_init_env()
|
||||
pmem.changed = True
|
||||
|
||||
if pmem.socket is None:
|
||||
reboot_required, ret, errmsg = pmem.pmem_create_memory_allocation()
|
||||
if errmsg:
|
||||
pmem.module.fail_json(msg=errmsg)
|
||||
pmem.result.append(ret)
|
||||
else:
|
||||
for skt in pmem.socket:
|
||||
skt_reboot_required, skt_ret, skt_errmsg = pmem.pmem_create_memory_allocation(skt)
|
||||
|
||||
if skt_errmsg:
|
||||
pmem.module.fail_json(msg=skt_errmsg)
|
||||
|
||||
if skt_reboot_required:
|
||||
reboot_required = True
|
||||
|
||||
pmem.result.append(skt_ret)
|
||||
|
||||
pmem.module.exit_json(
|
||||
changed=pmem.changed,
|
||||
reboot_required=reboot_required,
|
||||
result=pmem.result
|
||||
)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
411
tests/unit/plugins/modules/storage/pmem/test_pmem.py
Normal file
411
tests/unit/plugins/modules/storage/pmem/test_pmem.py
Normal file
|
@ -0,0 +1,411 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
# Copyright (c) 2022, Masayoshi Mizuma <msys.mizuma@gmail.com>
|
||||
# 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)
|
||||
__metaclass__ = type
|
||||
|
||||
import pytest
|
||||
|
||||
pytest.importorskip('xmltodict')
|
||||
|
||||
from ansible_collections.community.general.tests.unit.plugins.modules.utils import ModuleTestCase, set_module_args, AnsibleFailJson, AnsibleExitJson
|
||||
from ansible_collections.community.general.tests.unit.compat.mock import patch
|
||||
|
||||
from ansible_collections.community.general.plugins.modules.storage.pmem import pmem as pmem_module
|
||||
|
||||
# goal_plain: the mock return value of pmem_run_command with:
|
||||
# impctl create -goal MemoryMode=70 Reserved=20 PersistentMemoryType=AppDirect
|
||||
goal_plain = """The following configuration will be applied:
|
||||
SocketID | DimmID | MemorySize | AppDirect1Size | AppDirect2Size
|
||||
==================================================================
|
||||
0x0000 | 0x0001 | 88.000 GiB | 12.000 GiB | 0.000 GiB
|
||||
0x0000 | 0x0011 | 88.000 GiB | 12.000 GiB | 0.000 GiB
|
||||
0x0001 | 0x1001 | 88.000 GiB | 12.000 GiB | 0.000 GiB
|
||||
0x0001 | 0x1011 | 88.000 GiB | 12.000 GiB | 0.000 GiB
|
||||
Do you want to continue? [y/n] Error: Invalid data input."""
|
||||
|
||||
# goal_plain_sk0: the mock return value of pmem_run_command with:
|
||||
# ipmctl create -goal -socket 0 MemoryMode=70 Reserved=20 PersistentMemoryType=AppDirect
|
||||
goal_plain_sk0 = """The following configuration will be applied:
|
||||
SocketID | DimmID | MemorySize | AppDirect1Size | AppDirect2Size
|
||||
==================================================================
|
||||
0x0000 | 0x0001 | 88.000 GiB | 12.000 GiB | 0.000 GiB
|
||||
0x0000 | 0x0011 | 88.000 GiB | 12.000 GiB | 0.000 GiB
|
||||
Do you want to continue? [y/n] Error: Invalid data input."""
|
||||
|
||||
# goal_plain_sk1: the mock return value of pmem_run_command with:
|
||||
# ipmctl create -goal -socket 1 MemoryMode=70 Reserved=20 PersistentMemoryType=AppDirect
|
||||
goal_plain_sk1 = """The following configuration will be applied:
|
||||
SocketID | DimmID | MemorySize | AppDirect1Size | AppDirect2Size
|
||||
==================================================================
|
||||
0x0001 | 0x1001 | 88.000 GiB | 12.000 GiB | 0.000 GiB
|
||||
0x0001 | 0x1011 | 88.000 GiB | 12.000 GiB | 0.000 GiB
|
||||
Do you want to continue? [y/n] Error: Invalid data input."""
|
||||
|
||||
# goal: the mock return value of pmem_run_command with:
|
||||
# ipmctl create -u B -o nvmxml -force -goal -socket 0 MemoryMode=70 Reserved=20 PersistentMemoryType=AppDirect
|
||||
goal = """<?xml version="1.0"?>
|
||||
<ConfigGoalList>
|
||||
<ConfigGoal>
|
||||
<SocketID>0x0000</SocketID>
|
||||
<DimmID>0x0001</DimmID>
|
||||
<MemorySize>94489280512 B</MemorySize>
|
||||
<AppDirect1Size>12884901888 B</AppDirect1Size>
|
||||
<AppDirect2Size>0 B</AppDirect2Size>
|
||||
</ConfigGoal>
|
||||
<ConfigGoal>
|
||||
<SocketID>0x0000</SocketID>
|
||||
<DimmID>0x0011</DimmID>
|
||||
<MemorySize>94489280512 B</MemorySize>
|
||||
<AppDirect1Size>12884901888 B</AppDirect1Size>
|
||||
<AppDirect2Size>0 B</AppDirect2Size>
|
||||
</ConfigGoal>
|
||||
</ConfigGoalList>"""
|
||||
|
||||
# goal_sk0: the mock return value of pmem_run_command with:
|
||||
# ipmctl create -u B -o nvmxml -force -goal -socket 0 MemoryMode=70 Reserved=20 PersistentMemoryType=AppDirect
|
||||
goal_sk0 = """<?xml version="1.0"?>
|
||||
<ConfigGoalList>
|
||||
<ConfigGoal>
|
||||
<SocketID>0x0000</SocketID>
|
||||
<DimmID>0x0001</DimmID>
|
||||
<MemorySize>94489280512 B</MemorySize>
|
||||
<AppDirect1Size>12884901888 B</AppDirect1Size>
|
||||
<AppDirect2Size>0 B</AppDirect2Size>
|
||||
</ConfigGoal>
|
||||
<ConfigGoal>
|
||||
<SocketID>0x0001</SocketID>
|
||||
<DimmID>0x0011</DimmID>
|
||||
<MemorySize>94489280512 B</MemorySize>
|
||||
<AppDirect1Size>12884901888 B</AppDirect1Size>
|
||||
<AppDirect2Size>0 B</AppDirect2Size>
|
||||
</ConfigGoal>
|
||||
</ConfigGoalList>"""
|
||||
|
||||
# goal_sk1: the mock return value of pmem_run_command with:
|
||||
# ipmctl create -u B -o nvmxml -force -goal -socket 1 MemoryMode=70 Reserved=20 PersistentMemoryType=AppDirect
|
||||
goal_sk1 = """<?xml version="1.0"?>
|
||||
<ConfigGoalList>
|
||||
<ConfigGoal>
|
||||
<SocketID>0x0001</SocketID>
|
||||
<DimmID>0x1001</DimmID>
|
||||
<MemorySize>94489280512 B</MemorySize>
|
||||
<AppDirect1Size>12884901888 B</AppDirect1Size>
|
||||
<AppDirect2Size>0 B</AppDirect2Size>
|
||||
</ConfigGoal>
|
||||
<ConfigGoal>
|
||||
<SocketID>0x0001</SocketID>
|
||||
<DimmID>0x1011</DimmID>
|
||||
<MemorySize>94489280512 B</MemorySize>
|
||||
<AppDirect1Size>12884901888 B</AppDirect1Size>
|
||||
<AppDirect2Size>0 B</AppDirect2Size>
|
||||
</ConfigGoal>
|
||||
</ConfigGoalList>"""
|
||||
|
||||
# dimmlist: the mock return value of pmem_run_command with:
|
||||
# ipmctl show -d Capacity -u B -o nvmxml -dimm
|
||||
dimmlist = """<?xml version="1.0"?>
|
||||
<DimmList>
|
||||
<Dimm>
|
||||
<DimmID>0x0001</DimmID>
|
||||
<Capacity>135744782336 B</Capacity>
|
||||
</Dimm>
|
||||
<Dimm> <DimmID>0x0011</DimmID>
|
||||
<Capacity>135744782336 B</Capacity>
|
||||
</Dimm>
|
||||
<Dimm>
|
||||
<DimmID>0x1001</DimmID>
|
||||
<Capacity>135744782336 B</Capacity>
|
||||
</Dimm>
|
||||
<Dimm> <DimmID>0x1011</DimmID>
|
||||
<Capacity>135744782336 B</Capacity>
|
||||
</Dimm>
|
||||
</DimmList>"""
|
||||
|
||||
# dimmlist_sk0: the mock return value of pmem_run_command with:
|
||||
# ipmctl show -d Capacity -u B -o nvmxml -dimm -socket 0
|
||||
dimmlist_sk0 = """<?xml version="1.0"?>
|
||||
<DimmList>
|
||||
<Dimm>
|
||||
<DimmID>0x0001</DimmID>
|
||||
<Capacity>135744782336 B</Capacity>
|
||||
</Dimm>
|
||||
<Dimm> <DimmID>0x0011</DimmID>
|
||||
<Capacity>135744782336 B</Capacity>
|
||||
</Dimm>
|
||||
</DimmList>"""
|
||||
|
||||
# dimmlist_sk1: the mock return value of pmem_run_command with:
|
||||
# ipmctl show -d Capacity -u B -o nvmxml -dimm -socket 1
|
||||
dimmlist_sk1 = """<?xml version="1.0"?>
|
||||
<DimmList>
|
||||
<Dimm>
|
||||
<DimmID>0x1001</DimmID>
|
||||
<Capacity>135744782336 B</Capacity>
|
||||
</Dimm>
|
||||
<Dimm> <DimmID>0x1011</DimmID>
|
||||
<Capacity>135744782336 B</Capacity>
|
||||
</Dimm>
|
||||
</DimmList>"""
|
||||
|
||||
# show_skt: the mock return value of pmem_run_command with:
|
||||
# ipmctl show -o nvmxml -socket
|
||||
show_skt = """<?xml version="1.0"?>
|
||||
<SocketList>
|
||||
<Socket>
|
||||
<SocketID>0x0000</SocketID>
|
||||
<MappedMemoryLimit>1024.000 GiB</MappedMemoryLimit>
|
||||
<TotalMappedMemory>400.000 GiB</TotalMappedMemory>
|
||||
</Socket>
|
||||
<Socket>
|
||||
<SocketID>0x0001</SocketID>
|
||||
<MappedMemoryLimit>1024.000 GiB</MappedMemoryLimit>
|
||||
<TotalMappedMemory>400.000 GiB</TotalMappedMemory>
|
||||
</Socket>
|
||||
</SocketList>"""
|
||||
|
||||
|
||||
class TestPmem(ModuleTestCase):
|
||||
def setUp(self):
|
||||
super(TestPmem, self).setUp()
|
||||
self.module = pmem_module
|
||||
|
||||
self.mock_run_command = (patch('ansible.module_utils.basic.AnsibleModule.run_command'))
|
||||
self.mock_get_bin_path = (patch('ansible.module_utils.basic.AnsibleModule.get_bin_path'))
|
||||
|
||||
self.run_command = self.mock_run_command.start()
|
||||
self.get_bin_path = self.mock_get_bin_path.start()
|
||||
|
||||
self.mock_pmem_is_dcpmm_installed = (patch(
|
||||
'ansible_collections.community.general.plugins.modules.storage.pmem.pmem.PersistentMemory.pmem_is_dcpmm_installed', return_value=""))
|
||||
self.mock_pmem_init_env = (patch(
|
||||
'ansible_collections.community.general.plugins.modules.storage.pmem.pmem.PersistentMemory.pmem_init_env', return_value=""))
|
||||
|
||||
self.pmem_is_dcpmm_installed = self.mock_pmem_is_dcpmm_installed.start()
|
||||
self.pmem_init_env = self.mock_pmem_init_env.start()
|
||||
|
||||
def tearDown(self):
|
||||
super(TestPmem, self).tearDown()
|
||||
self.mock_get_bin_path.stop()
|
||||
self.mock_run_command.stop()
|
||||
self.mock_pmem_is_dcpmm_installed.stop()
|
||||
self.mock_pmem_init_env.stop()
|
||||
|
||||
def result_check(self, result, socket, appdirect, memmode, reserved):
|
||||
self.assertTrue(result.exception.args[0]['changed'])
|
||||
self.assertTrue(result.exception.args[0]['reboot_required'])
|
||||
|
||||
test_result = result.exception.args[0]['result']
|
||||
|
||||
if socket:
|
||||
maxIndex = 1
|
||||
else:
|
||||
maxIndex = 0
|
||||
|
||||
for i in range(0, maxIndex):
|
||||
self.assertAlmostEqual(test_result[i]['appdirect'], appdirect[i])
|
||||
self.assertAlmostEqual(test_result[i]['memorymode'], memmode[i])
|
||||
self.assertAlmostEqual(test_result[i]['reserved'], reserved[i])
|
||||
if socket:
|
||||
self.assertAlmostEqual(test_result[i]['socket'], i)
|
||||
|
||||
def test_fail_when_required_args_missing(self):
|
||||
with self.assertRaises(AnsibleFailJson):
|
||||
set_module_args({})
|
||||
pmem_module.main()
|
||||
|
||||
def test_fail_when_appdirect_only(self):
|
||||
with self.assertRaises(AnsibleFailJson):
|
||||
set_module_args({
|
||||
'appdirect': 10,
|
||||
})
|
||||
pmem_module.main()
|
||||
|
||||
def test_fail_when_MemosyMode_only(self):
|
||||
with self.assertRaises(AnsibleFailJson):
|
||||
set_module_args({
|
||||
'memorymode': 70,
|
||||
})
|
||||
pmem_module.main()
|
||||
|
||||
def test_fail_when_reserved_only(self):
|
||||
with self.assertRaises(AnsibleFailJson):
|
||||
set_module_args({
|
||||
'reserved': 10,
|
||||
})
|
||||
pmem_module.main()
|
||||
|
||||
def test_fail_when_appdirect_memorymode_reserved_total_not_100(self):
|
||||
with self.assertRaises(AnsibleFailJson):
|
||||
set_module_args({
|
||||
'appdirect': 10,
|
||||
'memorymode': 70,
|
||||
'reserved': 10,
|
||||
})
|
||||
pmem_module.main()
|
||||
|
||||
def test_when_appdirect_memorymode(self):
|
||||
set_module_args({
|
||||
'appdirect': 10,
|
||||
'memorymode': 70,
|
||||
})
|
||||
with patch(
|
||||
'ansible_collections.community.general.plugins.modules.storage.pmem.pmem.PersistentMemory.pmem_run_command',
|
||||
side_effect=[goal_plain, goal, dimmlist]):
|
||||
with self.assertRaises(AnsibleExitJson) as result:
|
||||
pmem_module.main()
|
||||
self.result_check(result, False, [25769803776], [188978561024], [328230764544])
|
||||
|
||||
def test_when_appdirect_memorymode_reserved(self):
|
||||
set_module_args({
|
||||
'appdirect': 10,
|
||||
'memorymode': 70,
|
||||
'reserved': 20,
|
||||
})
|
||||
with patch(
|
||||
'ansible_collections.community.general.plugins.modules.storage.pmem.pmem.PersistentMemory.pmem_run_command',
|
||||
side_effect=[goal_plain, goal, dimmlist]):
|
||||
with self.assertRaises(AnsibleExitJson) as result:
|
||||
pmem_module.main()
|
||||
self.result_check(result, False, [25769803776], [188978561024], [328230764544])
|
||||
|
||||
def test_when_appdirect_notinterleaved_memorymode_reserved(self):
|
||||
set_module_args({
|
||||
'appdirect': 10,
|
||||
'appdirect_interleaved': False,
|
||||
'memorymode': 70,
|
||||
'reserved': 20,
|
||||
})
|
||||
with patch(
|
||||
'ansible_collections.community.general.plugins.modules.storage.pmem.pmem.PersistentMemory.pmem_run_command',
|
||||
side_effect=[goal_plain, goal, dimmlist]):
|
||||
with self.assertRaises(AnsibleExitJson) as result:
|
||||
pmem_module.main()
|
||||
self.result_check(result, False, [25769803776], [188978561024], [328230764544])
|
||||
|
||||
def test_fail_when_socket_id_appdirect(self):
|
||||
with self.assertRaises(AnsibleFailJson):
|
||||
set_module_args({
|
||||
'socket': [
|
||||
{
|
||||
'id': 0,
|
||||
'appdirect': 10,
|
||||
},
|
||||
{
|
||||
'id': 1,
|
||||
'appdirect': 10,
|
||||
},
|
||||
],
|
||||
})
|
||||
pmem_module.main()
|
||||
|
||||
def test_fail_when_socket0_id_memorymode_socket1_id_appdirect(self):
|
||||
with self.assertRaises(AnsibleFailJson):
|
||||
set_module_args({
|
||||
'socket': [
|
||||
{
|
||||
'id': 0,
|
||||
' memorymode': 70,
|
||||
},
|
||||
{
|
||||
'id': 1,
|
||||
'appdirect': 10,
|
||||
},
|
||||
],
|
||||
})
|
||||
pmem_module.main()
|
||||
|
||||
def test_fail_when_socket0_without_id(self):
|
||||
with self.assertRaises(AnsibleFailJson):
|
||||
set_module_args({
|
||||
'socket': [
|
||||
{
|
||||
'appdirect': 10,
|
||||
'memorymode': 70,
|
||||
},
|
||||
{
|
||||
'id': 1,
|
||||
'appdirect': 10,
|
||||
'memorymode': 70,
|
||||
},
|
||||
],
|
||||
})
|
||||
pmem_module.main()
|
||||
|
||||
def test_when_socket0_and_1_appdirect_memorymode(self):
|
||||
set_module_args({
|
||||
'socket': [
|
||||
{
|
||||
'id': 0,
|
||||
'appdirect': 10,
|
||||
'memorymode': 70,
|
||||
},
|
||||
{
|
||||
'id': 1,
|
||||
'appdirect': 10,
|
||||
'memorymode': 70,
|
||||
},
|
||||
],
|
||||
})
|
||||
with patch(
|
||||
'ansible_collections.community.general.plugins.modules.storage.pmem.pmem.PersistentMemory.pmem_run_command',
|
||||
side_effect=[
|
||||
show_skt, goal_plain_sk0, goal_sk0, dimmlist_sk0, goal_plain_sk1, goal_sk1, dimmlist_sk1]):
|
||||
with self.assertRaises(AnsibleExitJson) as result:
|
||||
pmem_module.main()
|
||||
self.result_check(
|
||||
result, True, [12884901888, 12884901888], [94489280512, 94489280512], [164115382272, 164115382272])
|
||||
|
||||
def test_when_socket0_and_1_appdirect_memorymode_reserved(self):
|
||||
set_module_args({
|
||||
'socket': [
|
||||
{
|
||||
'id': 0,
|
||||
'appdirect': 10,
|
||||
'memorymode': 70,
|
||||
'reserved': 20,
|
||||
},
|
||||
{
|
||||
'id': 1,
|
||||
'appdirect': 10,
|
||||
'memorymode': 70,
|
||||
'reserved': 20,
|
||||
},
|
||||
],
|
||||
})
|
||||
with patch(
|
||||
'ansible_collections.community.general.plugins.modules.storage.pmem.pmem.PersistentMemory.pmem_run_command',
|
||||
side_effect=[
|
||||
show_skt, goal_plain_sk0, goal_sk0, dimmlist_sk0, goal_plain_sk1, goal_sk1, dimmlist_sk1]):
|
||||
with self.assertRaises(AnsibleExitJson) as result:
|
||||
pmem_module.main()
|
||||
self.result_check(
|
||||
result, True, [12884901888, 12884901888], [94489280512, 94489280512], [164115382272, 164115382272])
|
||||
|
||||
def test_when_socket0_appdirect_notinterleaved_memorymode_reserved_socket1_appdirect_memorymode_reserved(self):
|
||||
set_module_args({
|
||||
'socket': [
|
||||
{
|
||||
'id': 0,
|
||||
'appdirect': 10,
|
||||
'appdirect_interleaved': False,
|
||||
'memorymode': 70,
|
||||
'reserved': 20,
|
||||
},
|
||||
{
|
||||
'id': 1,
|
||||
'appdirect': 10,
|
||||
'memorymode': 70,
|
||||
'reserved': 20,
|
||||
},
|
||||
],
|
||||
})
|
||||
with patch(
|
||||
'ansible_collections.community.general.plugins.modules.storage.pmem.pmem.PersistentMemory.pmem_run_command',
|
||||
side_effect=[
|
||||
show_skt, goal_plain_sk0, goal_sk0, dimmlist_sk0, goal_plain_sk1, goal_sk1, dimmlist_sk1]):
|
||||
with self.assertRaises(AnsibleExitJson) as result:
|
||||
pmem_module.main()
|
||||
self.result_check(
|
||||
result, True, [12884901888, 12884901888], [94489280512, 94489280512], [164115382272, 164115382272])
|
Loading…
Reference in a new issue