NFV OvS DPDK zero packet loss validations
This changes to add the validations for NFV OvS DPDK zero packet loss rules. Change-Id: I70e60b8039eb99af26c8e39e30c7cdfd93f5a3c0
This commit is contained in:
parent
f6144ea95f
commit
46d9b751fd
@ -0,0 +1,15 @@
|
||||
==========================================
|
||||
Module - check_cpus_aligned_with_dpdk_nics
|
||||
==========================================
|
||||
|
||||
|
||||
This module provides for the following ansible plugin:
|
||||
|
||||
* check_cpus_aligned_with_dpdk_nics
|
||||
|
||||
|
||||
.. ansibleautoplugin::
|
||||
:module: library/check_cpus_aligned_with_dpdk_nics.py
|
||||
:documentation: true
|
||||
:examples: true
|
||||
|
@ -0,0 +1,15 @@
|
||||
========================================
|
||||
Module - check_other_processes_pmd_usage
|
||||
========================================
|
||||
|
||||
|
||||
This module provides for the following ansible plugin:
|
||||
|
||||
* check_other_processes_pmd_usage
|
||||
|
||||
|
||||
.. ansibleautoplugin::
|
||||
:module: library/check_other_processes_pmd_usage.py
|
||||
:documentation: true
|
||||
:examples: true
|
||||
|
15
doc/source/modules/modules-convert_range_to_numbers_list.rst
Normal file
15
doc/source/modules/modules-convert_range_to_numbers_list.rst
Normal file
@ -0,0 +1,15 @@
|
||||
======================================
|
||||
Module - convert_range_to_numbers_list
|
||||
======================================
|
||||
|
||||
|
||||
This module provides for the following ansible plugin:
|
||||
|
||||
* convert_range_to_numbers_list
|
||||
|
||||
|
||||
.. ansibleautoplugin::
|
||||
:module: library/convert_range_to_numbers_list.py
|
||||
:documentation: true
|
||||
:examples: true
|
||||
|
15
doc/source/modules/modules-get_dpdk_nics_numa_info.rst
Normal file
15
doc/source/modules/modules-get_dpdk_nics_numa_info.rst
Normal file
@ -0,0 +1,15 @@
|
||||
================================
|
||||
Module - get_dpdk_nics_numa_info
|
||||
================================
|
||||
|
||||
|
||||
This module provides for the following ansible plugin:
|
||||
|
||||
* get_dpdk_nics_numa_info
|
||||
|
||||
|
||||
.. ansibleautoplugin::
|
||||
:module: library/get_dpdk_nics_numa_info.py
|
||||
:documentation: true
|
||||
:examples: true
|
||||
|
15
doc/source/modules/modules-pmd_threads_siblings_check.rst
Normal file
15
doc/source/modules/modules-pmd_threads_siblings_check.rst
Normal file
@ -0,0 +1,15 @@
|
||||
===================================
|
||||
Module - pmd_threads_siblings_check
|
||||
===================================
|
||||
|
||||
|
||||
This module provides for the following ansible plugin:
|
||||
|
||||
* pmd_threads_siblings_check
|
||||
|
||||
|
||||
.. ansibleautoplugin::
|
||||
:module: library/pmd_threads_siblings_check.py
|
||||
:documentation: true
|
||||
:examples: true
|
||||
|
39
doc/source/roles/role-check_nfv_ovsdpdk_zero_packet_loss.rst
Normal file
39
doc/source/roles/role-check_nfv_ovsdpdk_zero_packet_loss.rst
Normal file
@ -0,0 +1,39 @@
|
||||
==================================
|
||||
check_nfv_ovsdpdk_zero_packet_loss
|
||||
==================================
|
||||
--------------
|
||||
About the role
|
||||
--------------
|
||||
This role validates the NFV OvS DPDK zero packet loss rules on OvS DPDK Compute nodes to find out the issues with NFV OvS Dpdk configuration.
|
||||
Requirements
|
||||
============
|
||||
- Validates PMD threads configuration.
|
||||
- Validates PMD threads included as part of isolcpus.
|
||||
- Checks any interrupts on Isolated CPU's.
|
||||
- Validates all the data paths are same on the server if ovs user bridge is used.
|
||||
- Validates bandwidth of the PCI slots.
|
||||
- Validates hugepages, CPU pinning, emulatorpin threads and libvirt queue size configuration on NFV instances.
|
||||
Dependencies
|
||||
============
|
||||
- Expects all the configuration files that are passed.
|
||||
Example Playbook
|
||||
================
|
||||
.. code-block:: yaml
|
||||
|
||||
- hosts: servers
|
||||
roles:
|
||||
|
||||
- { role: check_nfv_ovsdpdk_zero_packet_loss }
|
||||
|
||||
License
|
||||
=======
|
||||
Apache
|
||||
Author Information
|
||||
=================
|
||||
**Red Hat TripleO DFG:NFV Integration**
|
||||
----------------
|
||||
Full Description
|
||||
----------------
|
||||
|
||||
.. ansibleautoplugin::
|
||||
:role: roles/check_nfv_ovsdpdk_zero_packet_loss
|
140
library/check_cpus_aligned_with_dpdk_nics.py
Normal file
140
library/check_cpus_aligned_with_dpdk_nics.py
Normal file
@ -0,0 +1,140 @@
|
||||
#!/usr/bin/env python
|
||||
|
||||
# -*- coding: utf-8 -*-
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||
# implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
"""check_cpus_aligned_with_dpdk_nics module
|
||||
Used by the `check_nfv_ovsdpdk_zero_packet_loss` role.
|
||||
"""
|
||||
|
||||
from ansible.module_utils.basic import AnsibleModule
|
||||
from yaml import safe_load as yaml_safe_load
|
||||
|
||||
import json
|
||||
import yaml
|
||||
|
||||
DOCUMENTATION = '''
|
||||
---
|
||||
module: OVS DPDK PMD CPU's check
|
||||
short_description: Run PMD CPU's from all the NUMA nodes check
|
||||
description:
|
||||
- Run PMD CPU's from all the NUMA nodes check
|
||||
- Owned by the DFG:NFV Integration
|
||||
options:
|
||||
cpus:
|
||||
required: true
|
||||
description:
|
||||
- The CPU's list
|
||||
type: str
|
||||
numa_node:
|
||||
required: true
|
||||
description:
|
||||
- The NUMA node
|
||||
type: int
|
||||
dpdk_nics_numa_info:
|
||||
required: true
|
||||
description:
|
||||
- The DPDK NIC's NUMA details
|
||||
type: list
|
||||
author: "Jaganathan Palanisamy"
|
||||
'''
|
||||
|
||||
EXAMPLES = '''
|
||||
# Call this module from TripleO Ansible Validations
|
||||
|
||||
- name: Check CPU's aligned with DPDK NIC's NUMA
|
||||
become: true
|
||||
check_cpus_aligned_with_dpdk_nics:
|
||||
cpus: "2,3,4,5"
|
||||
numa_node: "0"
|
||||
dpdk_nics_numa_info: [{"numa_node": 0, "mac": "mac1", "pci": "pci1"},
|
||||
{"numa_node": 0, "mac": "mac2", "pci": "pci2"}]
|
||||
register: valid_cpus
|
||||
'''
|
||||
|
||||
|
||||
def get_nodes_cpus_info(module):
|
||||
"""Gets the logical cpus info for all numa nodes."""
|
||||
|
||||
dict_cpus = {}
|
||||
# Gets numa node and cpu details
|
||||
cmd = "lscpu -p=NODE,CPU"
|
||||
result = module.run_command(cmd)
|
||||
if (not result or (result[0] != 0) or not (str(result[1]).strip(' '))):
|
||||
err = "Unable to determine NUMA cpus"
|
||||
module.fail_json(msg=err)
|
||||
else:
|
||||
output = str(result[1])
|
||||
try:
|
||||
for line in output.split('\n'):
|
||||
if line and '#' not in line:
|
||||
cpu_info = line.split(',')
|
||||
node = int(cpu_info[0])
|
||||
thread = int(cpu_info[1])
|
||||
if node in dict_cpus:
|
||||
if thread not in dict_cpus[node]:
|
||||
dict_cpus[node].append(thread)
|
||||
else:
|
||||
dict_cpus[node] = [thread]
|
||||
except (IndexError, ValueError):
|
||||
err = "Unable to determine NUMA cpus"
|
||||
module.fail_json(msg=err)
|
||||
return dict_cpus
|
||||
|
||||
|
||||
def check_cpus_aligned_with_dpdk_nics(module, cpus, numa_node, dpdk_nics_numa_info):
|
||||
"""Checks cpus aligned with NUMA with DPDK NIC's."""
|
||||
|
||||
result = dict(
|
||||
changed=False,
|
||||
valid_cpus=False,
|
||||
message=''
|
||||
)
|
||||
nodes = []
|
||||
valid_numa = False
|
||||
invalid_cpus = []
|
||||
nodes_cpus = get_nodes_cpus_info(module)
|
||||
for dpdk_nics_numa in dpdk_nics_numa_info:
|
||||
if (dpdk_nics_numa['numa_node'] == numa_node):
|
||||
valid_numa = True
|
||||
break
|
||||
if not valid_numa:
|
||||
err = "NFV instance is not aligned with DPDK NIC's NUMA."
|
||||
module.fail_json(msg=err)
|
||||
for cpu in cpus.split(','):
|
||||
if not int(cpu) in nodes_cpus[numa_node]:
|
||||
invalid_cpus.append(cpu)
|
||||
if invalid_cpus:
|
||||
err = "CPU's are not aligned with DPDK NIC's NUMA, Invalid CPU's: "+','.join(invalid_cpus)
|
||||
result['message'] = err
|
||||
result['valid_cpus'] = False
|
||||
module.fail_json(msg=err)
|
||||
else:
|
||||
result['message'] = "CPU's configured correctly: " + cpus
|
||||
result['valid_cpus'] = True
|
||||
module.exit_json(**result)
|
||||
|
||||
|
||||
def main():
|
||||
module = AnsibleModule(
|
||||
argument_spec=yaml_safe_load(DOCUMENTATION)['options']
|
||||
)
|
||||
|
||||
check_cpus_aligned_with_dpdk_nics(module,
|
||||
module.params.get('cpus'),
|
||||
module.params.get('numa_node'),
|
||||
module.params.get('dpdk_nics_numa_info'))
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
162
library/check_other_processes_pmd_usage.py
Normal file
162
library/check_other_processes_pmd_usage.py
Normal file
@ -0,0 +1,162 @@
|
||||
#!/usr/bin/env python
|
||||
|
||||
# -*- coding: utf-8 -*-
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||
# implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
"""check_other_processes_pmd_usage module
|
||||
Used by the `check_nfv_ovsdpdk_zero_packet_loss` role.
|
||||
"""
|
||||
|
||||
from ansible.module_utils.basic import AnsibleModule
|
||||
from yaml import safe_load as yaml_safe_load
|
||||
|
||||
DOCUMENTATION = '''
|
||||
---
|
||||
module: Check OVS DPDK PMD threads used by other processes or not
|
||||
short_description: Run PMD threads used by other processes or not check
|
||||
description:
|
||||
- Run PMD threads used by other processes or not check
|
||||
- Owned by the DFG:NFV Integration
|
||||
options:
|
||||
pmd_cpus:
|
||||
required: true
|
||||
description:
|
||||
- The pmd cpus list
|
||||
type: list
|
||||
exclude_processes_pid:
|
||||
required: false
|
||||
description:
|
||||
- The processes pid list which need to be excluded.
|
||||
- This option is optional.
|
||||
default: []
|
||||
type: list
|
||||
|
||||
author: "Jaganathan Palanisamy"
|
||||
'''
|
||||
|
||||
EXAMPLES = '''
|
||||
# Call this module from TripleO Ansible Validations
|
||||
|
||||
- name: Run PMD threads used by other processes or not check
|
||||
become: true
|
||||
check_other_processes_pmd_usage:
|
||||
pmd_cpus: [6, 7, 9, 11]
|
||||
register: pmd_interrupts
|
||||
|
||||
- name: Run PMD threads used by other processes or not with exclude processes
|
||||
become: true
|
||||
check_other_processes_pmd_usage:
|
||||
pmd_cpus: [6, 7, 9, 11]
|
||||
exclude_processes_pid: ['24', '26']
|
||||
register: pmd_interrupts
|
||||
'''
|
||||
|
||||
|
||||
def check_current_process_pmd_usage(module, pmd_cpus, process_id, range_list):
|
||||
"""Check pmd usage in current process cpus range list."""
|
||||
|
||||
messages = []
|
||||
num_list = []
|
||||
exclude_num_list = []
|
||||
threads_used = []
|
||||
try:
|
||||
for val in range_list.split(','):
|
||||
val = val.strip(' ')
|
||||
if '^' in val:
|
||||
exclude_num_list.append(int(val[1:]))
|
||||
elif '-' in val:
|
||||
split_list = val.split("-")
|
||||
range_min = int(split_list[0])
|
||||
range_max = int(split_list[1])
|
||||
num_list.extend(range(range_min, (range_max + 1)))
|
||||
else:
|
||||
num_list.append(int(val))
|
||||
except ValueError as exc:
|
||||
err = ("Invalid number in input param "
|
||||
"'range_list': %s" % exc)
|
||||
module.fail_json(msg=err)
|
||||
# here, num_list is a list of integers
|
||||
threads_list = [str(num) for num in num_list if num not in exclude_num_list]
|
||||
for thread in threads_list:
|
||||
if thread in pmd_cpus:
|
||||
if threads_used:
|
||||
threads_used.append(thread)
|
||||
else:
|
||||
threads_used = [thread]
|
||||
if threads_used:
|
||||
messages.append("pmd threads: " + ','.join(threads_used) + " used in process: " + process_id)
|
||||
return list(messages)
|
||||
|
||||
|
||||
def check_other_processes_pmd_usage(module, pmd_cpus, exclude_processes_pid):
|
||||
"""Checks PMD threads used in any other process or not"""
|
||||
|
||||
output = dict(
|
||||
pmd_interrupts=False,
|
||||
messages=[]
|
||||
)
|
||||
messages = []
|
||||
threads_used = {}
|
||||
current_processes = []
|
||||
|
||||
# Gets all the processes and corresponding threads usage
|
||||
# except processes mentioned in exclude_processes_pid list
|
||||
# processes pid and threads information
|
||||
cmd = ("find -L /proc/[0-9]*/exe ! -type l | cut -d / -f3 | "
|
||||
"xargs -l -i sh -c 'ps -p {} -o comm=; taskset -acp {}' | "
|
||||
"grep -vE '" + '|'.join(exclude_processes_pid) + "' | "
|
||||
"awk '{printf \"%s %s\\n\", $2, $6}'")
|
||||
result = module.run_command(cmd, use_unsafe_shell=True)
|
||||
if (not result or (result[0] != 0) or not (str(result[1]).strip(' '))):
|
||||
err = "Unable to determine current processes"
|
||||
module.fail_json(msg=err)
|
||||
else:
|
||||
current_processes = str(result[1]).split('\n')
|
||||
|
||||
pmd_threads_processes = []
|
||||
# Gets processes associated to PMD and corresponding threads usage
|
||||
# proceses pid and threads information
|
||||
cmd = ("ps -T -o spid,comm -p $(pidof ovs-vswitchd) |grep '\<pmd' | "
|
||||
"while read spid name; do echo $(taskset -p -c $spid) | "
|
||||
"awk '{printf \"%s %s\\n\", $2, $6}'; done")
|
||||
result = module.run_command(cmd, use_unsafe_shell=True)
|
||||
if (not result or (result[0] != 0) or not (str(result[1]).strip(' '))):
|
||||
err = "Unable to determine PMD threads processes"
|
||||
module.fail_json(msg=err)
|
||||
else:
|
||||
pmd_threads_processes = str(result[1]).split('\n')
|
||||
|
||||
for process in current_processes:
|
||||
process = process.strip(' ')
|
||||
if process and process not in pmd_threads_processes:
|
||||
process_params = process.split(' ')
|
||||
if len(process_params) == 2:
|
||||
process_id = process_params[0].strip("'s")
|
||||
messages.extend(
|
||||
check_current_process_pmd_usage(module, pmd_cpus, process_id, process_params[1]))
|
||||
output['messages'] = messages
|
||||
if messages:
|
||||
output['pmd_interrupts'] = True
|
||||
module.exit_json(**output)
|
||||
|
||||
|
||||
def main():
|
||||
module = AnsibleModule(
|
||||
argument_spec=yaml_safe_load(DOCUMENTATION)['options']
|
||||
)
|
||||
|
||||
check_other_processes_pmd_usage(module,
|
||||
module.params.get('pmd_cpus'),
|
||||
module.params.get('exclude_processes_pid'))
|
||||
if __name__ == '__main__':
|
||||
main()
|
97
library/convert_range_to_numbers_list.py
Normal file
97
library/convert_range_to_numbers_list.py
Normal file
@ -0,0 +1,97 @@
|
||||
#!/usr/bin/env python
|
||||
|
||||
# -*- coding: utf-8 -*-
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||
# implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
"""convert_range_to_numbers_list module
|
||||
Used by the `check_nfv_ovsdpdk_zero_packet_loss` role.
|
||||
"""
|
||||
|
||||
from ansible.module_utils.basic import AnsibleModule
|
||||
from yaml import safe_load as yaml_safe_load
|
||||
|
||||
DOCUMENTATION = '''
|
||||
---
|
||||
module: Converts the CPU's range list into numbers list
|
||||
short_description: Converts the CPU's range list into numbers list
|
||||
description:
|
||||
- Converts CPU's range list into numbers list
|
||||
- Owned by the DFG:NFV Integration
|
||||
options:
|
||||
range_list:
|
||||
required: true
|
||||
description:
|
||||
- The cpus range list
|
||||
type: str
|
||||
author: "Jaganathan Palanisamy"
|
||||
'''
|
||||
|
||||
EXAMPLES = '''
|
||||
# Call this module from TripleO Ansible Validations
|
||||
|
||||
- name: Converts CPU's range list into numbers list
|
||||
convert_range_to_numbers_list:
|
||||
range_list: "12-14,17"
|
||||
register: numbers_list
|
||||
'''
|
||||
|
||||
|
||||
def convert_range_to_numbers_list(module, range_list):
|
||||
"""Converts range list into number list
|
||||
here input parameter and return value as list
|
||||
example: "12-14,17" into [12, 13, 14, 17]
|
||||
"""
|
||||
|
||||
num_list = []
|
||||
exclude_num_list = []
|
||||
try:
|
||||
for val in range_list.split(','):
|
||||
val = val.strip(' ')
|
||||
if '^' in val:
|
||||
exclude_num_list.append(int(val[1:]))
|
||||
elif '-' in val:
|
||||
split_list = val.split("-")
|
||||
range_min = int(split_list[0])
|
||||
range_max = int(split_list[1])
|
||||
num_list.extend(range(range_min, (range_max + 1)))
|
||||
else:
|
||||
num_list.append(int(val))
|
||||
except ValueError as exc:
|
||||
err = ("Invalid number in input param "
|
||||
"'range_list': %s" % exc)
|
||||
module.fail_json(msg=err)
|
||||
# here, num_list is a list of integers
|
||||
number_list = [num for num in num_list if num not in exclude_num_list]
|
||||
return number_list
|
||||
|
||||
|
||||
def get_number_list(module, range_list):
|
||||
"""Checks PMD threads used in any other process or not."""
|
||||
|
||||
result = dict(
|
||||
number_list=[]
|
||||
)
|
||||
result['number_list'] = convert_range_to_numbers_list(module, range_list)
|
||||
module.exit_json(**result)
|
||||
|
||||
|
||||
def main():
|
||||
module = AnsibleModule(
|
||||
argument_spec=yaml_safe_load(DOCUMENTATION)['options']
|
||||
)
|
||||
|
||||
get_number_list(module, module.params.get('range_list'))
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
143
library/get_dpdk_nics_numa_info.py
Normal file
143
library/get_dpdk_nics_numa_info.py
Normal file
@ -0,0 +1,143 @@
|
||||
#!/usr/bin/env python
|
||||
|
||||
# -*- coding: utf-8 -*-
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||
# implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
"""get_dpdk_nics_numa_info module
|
||||
Used by the `check_nfv_ovsdpdk_zero_packet_loss` role.
|
||||
"""
|
||||
|
||||
from ansible.module_utils.basic import AnsibleModule
|
||||
from yaml import safe_load as yaml_safe_load
|
||||
|
||||
import json
|
||||
import yaml
|
||||
|
||||
DOCUMENTATION = '''
|
||||
---
|
||||
module: DPDK NIC's NUMA details
|
||||
short_description: Gets the DPDK NIC's NUMA details
|
||||
description:
|
||||
- Gets the DPDK NIC's NUMA details
|
||||
- Owned by the DFG:NFV Integration
|
||||
options:
|
||||
dpdk_mapping_file:
|
||||
required: true
|
||||
description:
|
||||
- The DPDK mapping file path
|
||||
type: str
|
||||
|
||||
author: "Jaganathan Palanisamy"
|
||||
'''
|
||||
|
||||
EXAMPLES = '''
|
||||
# Call this module from TripleO Ansible Validations
|
||||
|
||||
- name: Get DPDK NIC's NUMA info
|
||||
become: true
|
||||
get_dpdk_nics_numa_info:
|
||||
dpdk_mapping_file: /var/lib/os-net-config/dpdk_mapping.yaml
|
||||
register: dpdk_nics_numa
|
||||
'''
|
||||
|
||||
|
||||
def get_dpdk_nics_mapping(module, dpdk_mapping_file, mac):
|
||||
"""Gets the DPDK NIC's mapping with NIC physical name and
|
||||
driver info for the given MAC."""
|
||||
|
||||
# Reads dpdk mapping file
|
||||
cmd = "cat " + dpdk_mapping_file
|
||||
result = module.run_command(cmd)
|
||||
if (not result or (result[0] != 0) or not (str(result[1]).strip(' '))):
|
||||
err = "Unable to determine DPDK NIC's details"
|
||||
module.fail_json(msg=err)
|
||||
else:
|
||||
dpdk_nics_map = yaml.load(str(result[1]))
|
||||
for dpdk_nic_map in dpdk_nics_map:
|
||||
if dpdk_nic_map['mac_address'] == mac:
|
||||
return dpdk_nic_map
|
||||
else:
|
||||
err = ("Unable to determine DPDK NIC Mapping for MAC: '%(mac)s'" % {'mac': mac})
|
||||
module.fail_json(msg=err)
|
||||
|
||||
|
||||
def get_dpdk_nics_info(module, dpdk_mapping_file):
|
||||
"""Gets the DPDK NIC's information like interface name, pci,
|
||||
mac and mtu etc.. using dpdk mapping file."""
|
||||
|
||||
dpdk_nics_info = []
|
||||
dpdk_nics = []
|
||||
# Gets the DPDK interfaces details
|
||||
cmd = "ovs-vsctl --columns=name,type,admin_state --format=json list interface"
|
||||
result = module.run_command(cmd)
|
||||
if (not result or (result[0] != 0) or not (str(result[1]).strip(' '))):
|
||||
err = "Unable to determine DPDK NIC's details"
|
||||
module.fail_json(msg=err)
|
||||
else:
|
||||
nics = json.loads(str(result[1]))
|
||||
for nic in nics.get('data', []):
|
||||
if nic and str(nic[1]) == 'dpdk' and str(nic[2]) == 'up':
|
||||
dpdk_nics.append(str(nic[0]))
|
||||
if dpdk_nics:
|
||||
# Gets the mac, mtu and status information of all the DPDK interfaces
|
||||
cmd = ("ovs-vsctl --column=mac-in-use,mtu,status --format=json "
|
||||
"list interface " + ' '.join(dpdk_nics))
|
||||
result = module.run_command(cmd)
|
||||
if (not result or (result[0] != 0) or not (str(result[1]).strip(' '))):
|
||||
err = "Unable to determine DPDK NIC's details"
|
||||
module.fail_json(msg=err)
|
||||
else:
|
||||
nics_info = json.loads(str(result[1]))
|
||||
for nic_info in nics_info.get('data', []):
|
||||
data = {}
|
||||
data['mac'] = nic_info[0]
|
||||
data['mtu'] = nic_info[1]
|
||||
for field in nic_info[2][1]:
|
||||
if field[0] == 'numa_id':
|
||||
data['numa_node'] = int(field[1])
|
||||
dpdk_nic_map = get_dpdk_nics_mapping(module, dpdk_mapping_file, nic_info[0])
|
||||
#data['nic'] = dpdk_nic_map['name']
|
||||
data['pci'] = dpdk_nic_map['pci_address']
|
||||
dpdk_nics_info.append(data)
|
||||
return dpdk_nics_info
|
||||
|
||||
|
||||
def get_dpdk_nics_numa_info(module, dpdk_mapping_file):
|
||||
"""Gets the DPDK NIC's NUMA info."""
|
||||
|
||||
result = dict(
|
||||
changed=False,
|
||||
dpdk_nics_numa_info=[],
|
||||
message=''
|
||||
)
|
||||
dpdk_nics_info = get_dpdk_nics_info(module, dpdk_mapping_file)
|
||||
if not dpdk_nics_info:
|
||||
err = "Unable to determine DPDK NIC's NUMA info"
|
||||
module.fail_json(msg=err)
|
||||
else:
|
||||
result['message'] = "DPDK NIC's NUMA info"
|
||||
result['dpdk_nics_numa_info'] = dpdk_nics_info
|
||||
module.exit_json(**result)
|
||||
|
||||
|
||||
def main():
|
||||
module = AnsibleModule(
|
||||
argument_spec=yaml_safe_load(DOCUMENTATION)['options']
|
||||
)
|
||||
|
||||
get_dpdk_nics_numa_info(module,
|
||||
module.params.get('dpdk_mapping_file'))
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
187
library/pmd_threads_siblings_check.py
Normal file
187
library/pmd_threads_siblings_check.py
Normal file
@ -0,0 +1,187 @@
|
||||
#!/usr/bin/env python
|
||||
|
||||
# -*- coding: utf-8 -*-
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||
# implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
"""pmd_threads_siblings_check module
|
||||
Used by the `check_nfv_ovsdpdk_zero_packet_loss` role.
|
||||
"""
|
||||
|
||||
from ansible.module_utils.basic import AnsibleModule
|
||||
from yaml import safe_load as yaml_safe_load
|
||||
|
||||
DOCUMENTATION = '''
|
||||
---
|
||||
module: OVS DPDK PMD CPU's check
|
||||
short_description: Run PMD CPU's from all the NUMA nodes check
|
||||
description:
|
||||
- Run PMD CPU's from all the NUMA nodes check
|
||||
- Owned by the DFG:NFV Integration
|
||||
options:
|
||||
pmd_cpu_mask:
|
||||
required: true
|
||||
description:
|
||||
- The pmd cpu mask value
|
||||
type: str
|
||||
author: "Jaganathan Palanisamy"
|
||||
'''
|
||||
|
||||
EXAMPLES = '''
|
||||
# Call this module from TripleO Ansible Validations
|
||||
|
||||
- name: Run PMD CPU's check
|
||||
become: true
|
||||
ovs_dpdk_pmd_cpus_check:
|
||||
pmd_cpu_mask: 1010010000000001
|
||||
register: pmd_cpus
|
||||
'''
|
||||
|
||||
|
||||
def get_cpus_list_from_mask_value(mask_val):
|
||||
"""Gets CPU's list from the mask value
|
||||
|
||||
:return: comma separated CPU's list
|
||||
"""
|
||||
|
||||
mask_val = mask_val.strip('\\"')
|
||||
cpus_list = []
|
||||
int_mask_val = int(mask_val, 16)
|
||||
bin_mask_val = bin(int_mask_val)
|
||||
bin_mask_val = str(bin_mask_val).replace('0b', '')
|
||||
rev_bin_mask_val = bin_mask_val[::-1]
|
||||
thread = 0
|
||||
for bin_val in rev_bin_mask_val:
|
||||
if bin_val == '1':
|
||||
cpus_list.append(thread)
|
||||
thread += 1
|
||||
return [str(cpu) for cpu in sorted(cpus_list)]
|
||||
|
||||
|
||||
def get_nodes_cores_info(module):
|
||||
"""Gets the distinct numa nodes, physical and logical cpus info
|
||||
for all numa nodes.
|
||||
"""
|
||||
|
||||
dict_cpus = {}
|
||||
numa_nodes = []
|
||||
# Gets NUMA nodes core informations
|
||||
cmd = "lscpu -p=NODE,CORE,CPU"
|
||||
result = module.run_command(cmd)
|
||||
if (not result or (result[0] != 0) or not (str(result[1]).strip(' '))):
|
||||
err = "Unable to determine physical and logical cpus."
|
||||
module.fail_json(msg=err)
|
||||
else:
|
||||
for line in str(result[1]).split('\n'):
|
||||
if (line.strip(' ') and not line.strip(' ').startswith('#')):
|
||||
cpu_info = line.strip(' ').split(',')
|
||||
try:
|
||||
node = int(cpu_info[0])
|
||||
cpu = int(cpu_info[1])
|
||||
thread = int(cpu_info[2])
|
||||
if node not in numa_nodes:
|
||||
numa_nodes.append(node)
|
||||
# CPU and NUMA node together forms a unique value,
|
||||
# as cpu is specific to a NUMA node
|
||||
# NUMA node id and cpu id tuple is used for unique key
|
||||
key = node, cpu
|
||||
if key in dict_cpus:
|
||||
if thread not in dict_cpus[key]['thread_siblings']:
|
||||
dict_cpus[key]['thread_siblings'].append(thread)
|
||||
else:
|
||||
cpu_item = {}
|
||||
cpu_item['thread_siblings'] = [thread]
|
||||
cpu_item['cpu'] = cpu
|
||||
cpu_item['numa_node'] = node
|
||||
dict_cpus[key] = cpu_item
|
||||
except (IndexError, ValueError):
|
||||
err = "Unable to determine physical and logical cpus."
|
||||
module.fail_json(msg=err)
|
||||
return (numa_nodes, list(dict_cpus.values()))
|
||||
|
||||
|
||||
def get_thread_siblings(module, numa, cpu):
|
||||
# Gets the threads siblings information
|
||||
cmd = ("cat /sys/devices/system/node/node"+str(numa)+"/"
|
||||
"cpu"+str(cpu)+"/topology/thread_siblings_list")
|
||||
result = module.run_command(cmd)
|
||||
if (not result or (result[0] != 0) or not (str(result[1]).strip(' '))):
|
||||
err = "Unable to determine thread sibling"
|
||||
module.fail_json(msg=err)
|
||||
else:
|
||||
core = str(result[1]).split(',')
|
||||
return [cpu for cpu in core if not str(cpu) in core]
|
||||
|
||||
|
||||
def validate_pmd_cpus(module, pmd_cpu_mask):
|
||||
result = dict(
|
||||
changed=False,
|
||||
pmd_cpus_list=[],
|
||||
message=''
|
||||
)
|
||||
pmd_cpu_list = get_cpus_list_from_mask_value(pmd_cpu_mask)
|
||||
cpus = []
|
||||
numa_nodes = []
|
||||
numa_nodes, cpus = get_nodes_cores_info(module)
|
||||
|
||||
# Ensure all thread siblings included in PMD CPU's
|
||||
pmd_cpu_numa = 0
|
||||
for pmd_cpu in pmd_cpu_list:
|
||||
core_id = -1
|
||||
for cpu in cpus:
|
||||
if (int(pmd_cpu) in cpu['thread_siblings']):
|
||||
pmd_cpu_numa = cpu['numa_node']
|
||||
core_id = cpu['cpu']
|
||||
break
|
||||
if core_id == -1:
|
||||
err = "Invalid PMD CPU: " + pmd_cpu
|
||||
module.fail_json(msg=err)
|
||||
thread_siblings = get_thread_siblings(module, pmd_cpu_numa, core_id)
|
||||
for thread in thread_siblings:
|
||||
if thread not in pmd_cpu_list:
|
||||
err = "Invalid PMD CPU's, thread siblings missed"
|
||||
module.fail_json(msg=err)
|
||||
|
||||
# Ensure PMD CPU's used for all NUMA nodes
|
||||
valid_numa_nodes = {}
|
||||
for numa_node in numa_nodes:
|
||||
valid_numa_nodes[str(numa_node)] = False
|
||||
for cpu in cpus:
|
||||
if cpu['numa_node'] == numa_node:
|
||||
if True in [int(pmd_cpu) in cpu['thread_siblings']
|
||||
for pmd_cpu in pmd_cpu_list]:
|
||||
valid_numa_nodes[str(numa_node)] = True
|
||||
invalid_numa_nodes = [node for node, val in valid_numa_nodes.items()
|
||||
if not val]
|
||||
if invalid_numa_nodes:
|
||||
failed_nodes = ','.join(invalid_numa_nodes)
|
||||
err = ("Invalid PMD CPU's, cpu is not used from "
|
||||
"NUMA node(s): %(node)s." % {'node': failed_nodes})
|
||||
module.fail_json(msg=err)
|
||||
else:
|
||||
pmd_cpus = ','.join(pmd_cpu_list)
|
||||
result['message'] = "PMD CPU's configured correctly: " + pmd_cpus
|
||||
result['pmd_cpus_list'] = pmd_cpu_list
|
||||
module.exit_json(**result)
|
||||
|
||||
|
||||
def main():
|
||||
module = AnsibleModule(
|
||||
argument_spec=yaml_safe_load(DOCUMENTATION)['options']
|
||||
)
|
||||
|
||||
validate_pmd_cpus(module,
|
||||
module.params.get('pmd_cpu_mask'))
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
14
playbooks/nfv-ovsdpdk-zero-packet-loss-check.yaml
Normal file
14
playbooks/nfv-ovsdpdk-zero-packet-loss-check.yaml
Normal file
@ -0,0 +1,14 @@
|
||||
---
|
||||
- hosts: "{{ compute_ovsdpdk_rolename | default('ComputeOvsDpdk') }}"
|
||||
vars:
|
||||
metadata:
|
||||
name: NFV OvS DPDK Zero Packet Loss Validations
|
||||
description: |
|
||||
Run `check-nfv-ovsdpdk-zero-packet-loss` on the compute ovsdpdk nodes
|
||||
to find out the issues with NFV OvS Dpdk configuration.
|
||||
The tool expects all the configuration files that are passed.
|
||||
groups:
|
||||
- post-deployment
|
||||
|
||||
roles:
|
||||
- check_nfv_ovsdpdk_zero_packet_loss
|
@ -0,0 +1,37 @@
|
||||
---
|
||||
# Gets only NFV instances and validates all the NFV instances.
|
||||
- name: Get instance {{ vm_id }} xml
|
||||
become: true
|
||||
shell: >-
|
||||
"{{ container_cli }}" exec -u root nova_libvirt virsh dumpxml "{{ vm_id }}"
|
||||
register: instance_xml
|
||||
|
||||
- name: Set instance {{ vm_id }} xml data
|
||||
set_fact:
|
||||
instance_xml_data: "{{ instance_xml.stdout }}"
|
||||
|
||||
- name: Get interfaces from xml string
|
||||
xml:
|
||||
xmlstring: "{{ instance_xml_data }}"
|
||||
xpath: /domain/devices/interface
|
||||
content: attribute
|
||||
attribute: type
|
||||
register: xmlinterfacetype
|
||||
|
||||
- name: Set default no valid nfv instance {{ vm_id }}
|
||||
set_fact:
|
||||
valid_nfv_instance: false
|
||||
|
||||
- name: Check whether valid nfv instance {{ vm_id }}
|
||||
set_fact:
|
||||
valid_nfv_instance: true
|
||||
when: "{{ interface_type.interface.type == 'vhostuser' }}"
|
||||
loop: "{{ xmlinterfacetype.matches}}"
|
||||
loop_control:
|
||||
loop_var: interface_type
|
||||
|
||||
- name: Validate NFV instance
|
||||
vars:
|
||||
instance_id: "{{ vm_id }}"
|
||||
import_tasks: validate_instance.yml
|
||||
when: valid_nfv_instance
|
@ -0,0 +1,26 @@
|
||||
---
|
||||
- name: Initialize PCI bandwidth default value
|
||||
set_fact:
|
||||
pci_bw: 1
|
||||
|
||||
- name: Check pci addresses bandwidth
|
||||
become: true
|
||||
shell: "lspci -s {{ pci }} -vvnn | grep -i width"
|
||||
register: pci_bandwidth
|
||||
|
||||
- name: Compute bandwidth on pci address
|
||||
set_fact:
|
||||
pci_bw: "{{ pci_bw | int * ((bw_param.split(' ')[1] | replace('x', '')) | replace('GT/s', '') | int) }}"
|
||||
when: "{{ 'Speed' in bw_param or 'Width' in bw_param }}"
|
||||
loop: "{{ pci_bandwidth.stdout.split('\n')[0].split(', ') }}"
|
||||
loop_control:
|
||||
loop_var: bw_param
|
||||
|
||||
- name: Get interface bandwidth
|
||||
vars:
|
||||
dpdk_pci_bw: "{{ pci_bw }}"
|
||||
dpdk_port_name: "{{ dpdk_port }}"
|
||||
include_tasks: validate_dpdk_port_bandwidth.yml
|
||||
loop: "{{ dpdk_ports_list }}"
|
||||
loop_control:
|
||||
loop_var: dpdk_port
|
217
roles/check_nfv_ovsdpdk_zero_packet_loss/tasks/main.yml
Normal file
217
roles/check_nfv_ovsdpdk_zero_packet_loss/tasks/main.yml
Normal file
@ -0,0 +1,217 @@
|
||||
---
|
||||
- name: Initialize validation message list
|
||||
set_fact:
|
||||
validation_msg: []
|
||||
pmd_isolated: true
|
||||
|
||||
# Gets applied PMD cpus list
|
||||
- name: Get OVS DPDK PMD cores mask value
|
||||
become: true
|
||||
register: pmd_cpu_mask
|
||||
command: ovs-vsctl --no-wait get Open_vSwitch . other_config:pmd-cpu-mask
|
||||
changed_when: false
|
||||
|
||||
- name: Check OVS DPDK PMD cores thread siblings
|
||||
become: true
|
||||
pmd_threads_siblings_check:
|
||||
pmd_cpu_mask: "{{ pmd_cpu_mask.stdout }}"
|
||||
register: pmd_cpus_list
|
||||
|
||||
- name: Set PMD cpus
|
||||
set_fact:
|
||||
pmd_cpus: "{{ pmd_cpus_list.pmd_cpus_list }}"
|
||||
when: pmd_cpus_list.pmd_cpus_list is defined
|
||||
|
||||
# Validates datapath's are mixed or not.
|
||||
- name: Verify system and netdev datapath's are not mixed on compute node
|
||||
block:
|
||||
- name: Check bridge datapath type
|
||||
become: true
|
||||
shell: "ovs-vsctl list bridge | grep datapath_type"
|
||||
register: bridge_data_path_type
|
||||
|
||||
- name: Check if any datapath type is netdev
|
||||
set_fact:
|
||||
datapath_type_list: "{{ bridge_data_path_type.stdout.split('\n') }}"
|
||||
when: "{{ 'netdev' in bridge_data_path_type.stdout }}"
|
||||
|
||||
- name: Check if all the datapath type netdev
|
||||
set_fact:
|
||||
validation_msg: "{{ validation_msg }} + {{ ['Mixed system and netdev datapath's on the same compute node.'] }}"
|
||||
when: "{{ not 'netdev' in datapath }}"
|
||||
loop: "{{ datapath_type_list }}"
|
||||
loop_control:
|
||||
loop_var: datapath
|
||||
|
||||
- name: Get DPDK NIC's NUMA info
|
||||
become: true
|
||||
get_dpdk_nics_numa_info:
|
||||
dpdk_mapping_file: /var/lib/os-net-config/dpdk_mapping.yaml
|
||||
register: dpdk_nics_numa
|
||||
|
||||
- name: Set DPDK NIC's NUMA info
|
||||
set_fact:
|
||||
dpdk_nics_numa_info: "{{ dpdk_nics_numa.dpdk_nics_numa_info }}"
|
||||
when:
|
||||
- dpdk_nics_numa is defined
|
||||
|
||||
- name: Get nova_libvirt_launcher Process
|
||||
become: true
|
||||
shell: |-
|
||||
ps -Leaf | grep nova_libvirt_launcher.sh | grep -v pts | awk '{printf "%s", $2}'
|
||||
register: nova_libvirt_launcher
|
||||
|
||||
- name: Get nova libvirt namespace processes
|
||||
become: true
|
||||
shell: |-
|
||||
pgrep --ns {{ nova_libvirt_launcher.stdout }}
|
||||
register: nova_libvirt_proceses
|
||||
|
||||
- name: Update nova libvirt namespace processes pid
|
||||
set_fact:
|
||||
nova_libvirt_proceses_pid: "{{ nova_libvirt_proceses.stdout.split('\n') | join('|') }}"
|
||||
|
||||
- name: Get nova libvirt launch processes id
|
||||
become: true
|
||||
shell: |-
|
||||
ps -Leaf | grep -E '{{ nova_libvirt_proceses_pid }}' | grep -v pts | awk '{printf "%s\n", $4}'
|
||||
register: nova_libvirt_launch_pids
|
||||
|
||||
- name: Update nova libvirt launch processes pid
|
||||
set_fact:
|
||||
nova_lib_launch_proceses_pid: "{{ nova_libvirt_launch_pids.stdout.split('\n') }}"
|
||||
|
||||
- name: Check pmd cpus used in any other processes
|
||||
become: true
|
||||
check_other_processes_pmd_usage:
|
||||
pmd_cpus: "{{ pmd_cpus | list }}"
|
||||
exclude_processes_pid: "{{ nova_lib_launch_proceses_pid | list }}"
|
||||
register: pmd_threads_usage
|
||||
|
||||
- name: Update validation message if any PMD threads usage by other processes message
|
||||
set_fact:
|
||||
validation_msg: "{{ validation_msg }} + {{ pmd_threads_usage.messages }}"
|
||||
when:
|
||||
- pmd_threads_usage.pmd_interrupts
|
||||
|
||||
# Validates PMD cpus are isolated or not.
|
||||
- name: Check PMD cores should be isolated
|
||||
become: true
|
||||
shell: "cat /proc/cmdline"
|
||||
register: kernel_args
|
||||
|
||||
- name: Get isolcpus using kernel args
|
||||
set_fact:
|
||||
isol_cpus: "{{ kernel_arg.split('=')[1] }}"
|
||||
when: "{{ 'isolcpus' == kernel_arg.split('=')[0] }}"
|
||||
loop: "{{ kernel_args.stdout.split(' ') }}"
|
||||
loop_control:
|
||||
loop_var: kernel_arg
|
||||
|
||||
- name: Convert isolcpus range list into number list
|
||||
convert_range_to_numbers_list:
|
||||
range_list: "{{ isol_cpus }}"
|
||||
register: isol_cpus_list
|
||||
|
||||
- name: check PMD threads isolated or not
|
||||
set_fact:
|
||||
pmd_isolated: false
|
||||
when: "{{ not pmd_thread | int in isol_cpus_list.number_list }}"
|
||||
loop: "{{ pmd_cpus }}"
|
||||
loop_control:
|
||||
loop_var: pmd_thread
|
||||
|
||||
- name: Set message if pmd threads are not isolated
|
||||
set_fact:
|
||||
validation_msg: "{{ validation_msg }} + ['PMD threads are not isolated.']"
|
||||
when:
|
||||
- not pmd_isolated
|
||||
|
||||
# Validates any interuppts happened on isolcpus list.
|
||||
- name: Set isol cpus required columns
|
||||
set_fact:
|
||||
cpu_columns_format: "{{ (cpu_columns_format | default('')) + '%s,' }}"
|
||||
cpu_columns: "{{ (cpu_columns | default('')) + '$'+ ((cpu | int + 2) | string) + ',' }}"
|
||||
loop: "{{ isol_cpus_list.number_list | list }}"
|
||||
loop_control:
|
||||
loop_var: cpu
|
||||
|
||||
- name: Update cpu columns in required format
|
||||
set_fact:
|
||||
cpu_columns_format: "{{ cpu_columns_format | regex_replace(',$', '') }}"
|
||||
cpu_columns: "{{ cpu_columns | regex_replace(',$', '') }}"
|
||||
|
||||
- name: Check interrupts on isolcpus list
|
||||
become: true
|
||||
shell: |-
|
||||
cat /proc/interrupts | awk '{printf "%s-{{ cpu_columns_format }}\n", $1,{{ cpu_columns }} }' | grep -v [A-Za-z]
|
||||
register: isolcpus_interrupts
|
||||
|
||||
- name: Isol CPU's interrupts
|
||||
set_fact:
|
||||
validation_msg: "{{ validation_msg }} + {{ ['Interrupts exist in Isol cpus ' + ( isol_cpus_list.number_list | join(',')) +': ' + interrupts_line ] }}"
|
||||
when: "{{ 'CPU' not in interrupts_line and interrupts_line.split('-')[1].replace('0', '').split(',') is any }}"
|
||||
loop: "{{ isolcpus_interrupts.stdout.split('\n') }}"
|
||||
loop_control:
|
||||
loop_var: interrupts_line
|
||||
|
||||
- name: Get list of dpdk ports
|
||||
become: true
|
||||
shell: "ovs-appctl dpctl/show | grep 'dpdk: configured'"
|
||||
register: ovs_dpdk_ports
|
||||
|
||||
- name: Get list of dpdk ports name
|
||||
set_fact:
|
||||
dpdk_ports: "{{ dpdk_ports | default([]) }} + {{ [dpdk_port_line.split(': ')[1].split(' ')[0]] }}"
|
||||
loop: "{{ ovs_dpdk_ports.stdout.split('\n') }}"
|
||||
loop_control:
|
||||
loop_var: dpdk_port_line
|
||||
|
||||
- name: Get DPDK NIC's PCI addresses
|
||||
set_fact:
|
||||
dpdk_pci_list: |-
|
||||
{{ (dpdk_pci_list | default([])) }} + {{ [dpdk_nic_info.pci] }}
|
||||
loop: "{{ dpdk_nics_numa_info }}"
|
||||
loop_control:
|
||||
loop_var: dpdk_nic_info
|
||||
|
||||
- name: Check pci addresses bandwidth
|
||||
vars:
|
||||
pci: "{{ dpdk_pci }}"
|
||||
dpdk_ports_list: "{{ dpdk_ports }}"
|
||||
include_tasks: check_nfv_pci_address.yml
|
||||
loop: "{{ dpdk_pci_list }}"
|
||||
loop_control:
|
||||
loop_var: dpdk_pci
|
||||
|
||||
# validates the NFV instances on OvS DPDK node.
|
||||
- name: Set container_cli fact from the inventory
|
||||
set_fact:
|
||||
container_cli: "{{ hostvars[inventory_hostname].container_cli | default('podman', true) }}"
|
||||
when: container_cli is not defined
|
||||
|
||||
- name: Get instances list on node
|
||||
become: true
|
||||
shell: >-
|
||||
"{{ container_cli }}" exec -u root nova_libvirt virsh list --all | awk 'NR > 2 { printf $1 "\n"}'
|
||||
register: instances_list
|
||||
|
||||
- name: Get instances id list
|
||||
set_fact:
|
||||
vm_list: "{{ instances_list.stdout.split('\n') }}"
|
||||
|
||||
# Validate all the instances using instances xml
|
||||
- name: Loop on all instances and validate xml
|
||||
include_tasks: check_nfv_instances.yml
|
||||
when: "{{ vm_id }}"
|
||||
loop: "{{ vm_list }}"
|
||||
loop_control:
|
||||
loop_var: vm_id
|
||||
|
||||
# Prints all the validation errors if found.
|
||||
- name: Validation errors
|
||||
fail:
|
||||
msg: "Failed NFV zero packet loss rules:\n{{ validation_msg | join('\n') }}"
|
||||
when:
|
||||
- validation_msg is defined
|
||||
- validation_msg | length > 0
|
@ -0,0 +1,21 @@
|
||||
---
|
||||
- name: Get DPDK port bandwidth
|
||||
become: true
|
||||
shell: "ovs-vsctl list interface {{ dpdk_port_name }} | grep -e link_speed= -e dpdk-devargs="
|
||||
register: dpdk_port_bw
|
||||
|
||||
- name: Check dpdk port matching pci or not
|
||||
set_fact:
|
||||
inf_bw: "{{ dpdk_port_param.split('=')[1] | replace('Gbps', '') }}"
|
||||
when: "{{ (dpdk_port_bw is defined) and (pci in dpdk_port_bw.stdout) and ('link_speed=' in dpdk_port_param) }}"
|
||||
loop: "{{ dpdk_port_bw.stdout.split(', ') }}"
|
||||
loop_control:
|
||||
loop_var: dpdk_port_param
|
||||
|
||||
- name: Update invalid bandwidth validation message
|
||||
set_fact:
|
||||
validation_msg: "{{ validation_msg }} + {{ ['PCI bandwidth configured less than interface link speed.'] }}"
|
||||
when:
|
||||
- dpdk_port_bw is defined
|
||||
- pci in dpdk_port_bw.stdout
|
||||
- inf_bw | int > dpdk_pci_bw | int
|
@ -0,0 +1,91 @@
|
||||
---
|
||||
- name: Get instance numa node from xml string
|
||||
xml:
|
||||
xmlstring: "{{ instance_xml_data }}"
|
||||
xpath: /domain/numatune/memory
|
||||
content: attribute
|
||||
attribute: nodeset
|
||||
register: xml_instance_node
|
||||
|
||||
- name: Get instance associated numa nodes
|
||||
set_fact:
|
||||
instance_numa: "{{ xml_instance_node.matches[0].memory.nodeset }}"
|
||||
|
||||
# Validates the instance vcpus list.
|
||||
- name: Get vcpu list from xml string
|
||||
xml:
|
||||
xmlstring: "{{ instance_xml_data }}"
|
||||
xpath: /domain/cputune/vcpupin
|
||||
content: attribute
|
||||
attribute: cpuset
|
||||
register: xml_vcpus
|
||||
|
||||
- name: Get instance vcpus list
|
||||
set_fact:
|
||||
vcpus_list: |-
|
||||
{{ (vcpus_list | default([])) }} + {{ [vcpu.vcpupin.cpuset] }}
|
||||
loop: "{{ xml_vcpus.matches }}"
|
||||
loop_control:
|
||||
loop_var: vcpu
|
||||
|
||||
- name: Check vcpu's aligned with DPDK NIC's NUMA
|
||||
become: true
|
||||
check_cpus_aligned_with_dpdk_nics:
|
||||
cpus: "{{ vcpus_list | join(',') }}"
|
||||
numa_node: "{{ instance_numa | int }}"
|
||||
dpdk_nics_numa_info: "{{ dpdk_nics_numa_info }}"
|
||||
register: valid_cpus
|
||||
|
||||
- name: Check vcpu's valid or not
|
||||
set_fact:
|
||||
validation_msg: "{{ validation_msg }} + {{ [valid_cpus.message] }}"
|
||||
when:
|
||||
- not valid_cpus.valid_cpus
|
||||
|
||||
# Validates instance emulatorpin threads
|
||||
- name: Get emulatorpin list from xml string
|
||||
xml:
|
||||
xmlstring: "{{ instance_xml_data }}"
|
||||
xpath: /domain/cputune/emulatorpin
|
||||
content: attribute
|
||||
attribute: cpuset
|
||||
register: xml_emulatorpin
|
||||
|
||||
- name: Check emulatorpin valid or not
|
||||
set_fact:
|
||||
validation_msg: "{{ validation_msg }} + {{ ['Invalid emulatorpin configured for instance ' \
|
||||
+ instance_id + ': ' + emulatorpin.emulatorpin.cpuset] }}"
|
||||
when: "{{ not emulatorpin.emulatorpin.cpuset in vcpus_list | list }}"
|
||||
loop: "{{ xml_emulatorpin.matches }}"
|
||||
loop_control:
|
||||
loop_var: emulatorpin
|
||||
|
||||
# Validates instance huge page size length is greater than or equal to 6.
|
||||
- name: Get hugepages from xml string
|
||||
xml:
|
||||
xmlstring: "{{ instance_xml_data }}"
|
||||
xpath: /domain/memoryBacking/hugepages/page
|
||||
content: attribute
|
||||
attribute: size
|
||||
register: xmlhugepages
|
||||
|
||||
- name: Set instance {{ vm_id }} hugepages details
|
||||
set_fact:
|
||||
msg: |-
|
||||
Huge page size '{{ xmlhugepages.matches[0].page.size }}'
|
||||
when: "{{ xmlhugepages.matches[0].page.size | length >= 6 }}"
|
||||
|
||||
# Validates instance tx rx queue sizes and should be greater than or equal to 1024.
|
||||
- name: Get {{ vm_id }} libvirt tx | rx queue sizes from xml string
|
||||
xml:
|
||||
xmlstring: "{{ instance_xml_data }}"
|
||||
xpath: /domain/devices/interface/driver
|
||||
content: attribute
|
||||
register: xmlqueues
|
||||
|
||||
- name: Set instance {{ vm_id }} devices tx and rx queue size details
|
||||
set_fact:
|
||||
validation_msg: "{{ validation_msg }} + {{ ['Invalid tx/rx queues configured for instance '
|
||||
+ instance_id + ', tx queue size: ' + xmlqueues.matches[0].driver.tx_queue_size + ' \
|
||||
& rx queue size: ' + xmlqueues.matches[0].driver.rx_queue_size] }}"
|
||||
when: "{{ xmlqueues.matches[0].driver.tx_queue_size | int < 1024 and xmlqueues.matches[0].driver.rx_queue_size | int < 1024 }}"
|
@ -0,0 +1,67 @@
|
||||
# Copyright 2016 Red Hat, Inc.
|
||||
# All Rights Reserved.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||
# not use this file except in compliance with the License. You may obtain
|
||||
# a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
from unittest.mock import MagicMock
|
||||
from unittest.mock import patch
|
||||
|
||||
import library.check_cpus_aligned_with_dpdk_nics as validation
|
||||
from tripleo_validations.tests import base
|
||||
|
||||
|
||||
class TestCpusAlignedWithDpdkNicsCheck(base.TestCase):
|
||||
|
||||
def setUp(self):
|
||||
super(TestCpusAlignedWithDpdkNicsCheck, self).setUp()
|
||||
self.module = MagicMock()
|
||||
|
||||
@patch('library.check_cpus_aligned_with_dpdk_nics.'
|
||||
'get_nodes_cpus_info')
|
||||
def test_validate_valid_cpus_aligned_with_dpdk_nics_numa(self, mock_nodes_cpus):
|
||||
mock_nodes_cpus.return_value = {0: [0, 1, 2, 3], 1: [4, 5, 6, 7]}
|
||||
dpdk_nics_numa_info = [{"numa_node": 0, "mac": "mac1", "pci": "pci1"}]
|
||||
cpus = "2,3"
|
||||
numa_node = 0
|
||||
validation.check_cpus_aligned_with_dpdk_nics(self.module, cpus,
|
||||
numa_node, dpdk_nics_numa_info)
|
||||
self.module.exit_json.assert_called_with(
|
||||
changed=False,
|
||||
message="CPU's configured correctly: 2,3",
|
||||
valid_cpus=True)
|
||||
|
||||
@patch('library.check_cpus_aligned_with_dpdk_nics.'
|
||||
'get_nodes_cpus_info')
|
||||
def test_validate_invalid_cpus_aligned_with_dpdk_nics_numa(self, mock_nodes_cpus):
|
||||
mock_nodes_cpus.return_value = {0: [0, 1, 2, 3], 1: [4, 5, 6, 7]}
|
||||
dpdk_nics_numa_info = [{"numa_node": 0, "mac": "mac1", "pci": "pci1"}]
|
||||
cpus = "2,3,4,5"
|
||||
numa_node = 0
|
||||
validation.check_cpus_aligned_with_dpdk_nics(self.module, cpus,
|
||||
numa_node, dpdk_nics_numa_info)
|
||||
self.module.fail_json.assert_called_with(
|
||||
msg="CPU's are not aligned with DPDK NIC's NUMA, Invalid CPU's: 4,5")
|
||||
|
||||
def test_valid_get_nodes_cpus_info(self):
|
||||
lines = "# format\n0,0\n 0,2\n1,1\n1,3"
|
||||
self.module.run_command.return_value = [0, lines, ""]
|
||||
expected_value = {0: [0, 2], 1: [1, 3]}
|
||||
result = validation.get_nodes_cpus_info(self.module)
|
||||
self.assertEqual(result, expected_value)
|
||||
|
||||
def test_invalid_missing_val_get_nodes_cpus_info(self):
|
||||
lines = "# format\n,0\n0,2\n1,1\n1,3"
|
||||
self.module.run_command.return_value = [0, lines, ""]
|
||||
validation.get_nodes_cpus_info(self.module)
|
||||
self.module.fail_json.assert_called_with(
|
||||
msg="Unable to determine NUMA cpus")
|
@ -0,0 +1,116 @@
|
||||
# Copyright 2016 Red Hat, Inc.
|
||||
# All Rights Reserved.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||
# not use this file except in compliance with the License. You may obtain
|
||||
# a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
from unittest.mock import MagicMock
|
||||
from unittest.mock import patch
|
||||
|
||||
import library.check_other_processes_pmd_usage as validation
|
||||
from tripleo_validations.tests import base
|
||||
|
||||
|
||||
class TestOtherProcessesPmdusageCheck(base.TestCase):
|
||||
|
||||
def setUp(self):
|
||||
super(TestOtherProcessesPmdusageCheck, self).setUp()
|
||||
self.module = MagicMock()
|
||||
|
||||
@patch('library.check_other_processes_pmd_usage.'
|
||||
'check_current_process_pmd_usage')
|
||||
def test_validate_no_other_processes_pmd_usage(self, mock_number_list):
|
||||
mock_number_list.side_effect = [[], []]
|
||||
pmd_cpus = ["2", "3"]
|
||||
current_processes = "21's 4-7\n22's 4-9\n24's 4-5"
|
||||
pmd_processes = "22's 4-9"
|
||||
self.module.run_command.side_effect = [[0, current_processes, ""],
|
||||
[0, pmd_processes, ""]]
|
||||
exclude_processes_pid = ["24"]
|
||||
validation.check_other_processes_pmd_usage(self.module, pmd_cpus,
|
||||
exclude_processes_pid)
|
||||
self.module.exit_json.assert_called_with(
|
||||
messages=[],
|
||||
pmd_interrupts=False)
|
||||
|
||||
@patch('library.check_other_processes_pmd_usage.'
|
||||
'check_current_process_pmd_usage')
|
||||
def test_validate_with_no_current_processes(self, mock_number_list):
|
||||
mock_number_list.side_effect = [[], []]
|
||||
pmd_cpus = ["2", "3"]
|
||||
current_processes = ""
|
||||
pmd_processes = "22's 4-9"
|
||||
self.module.run_command.side_effect = [None,
|
||||
[0, pmd_processes, ""]]
|
||||
exclude_processes_pid = ["24"]
|
||||
validation.check_other_processes_pmd_usage(self.module, pmd_cpus,
|
||||
exclude_processes_pid)
|
||||
self.module.fail_json.assert_called_with(
|
||||
msg="Unable to determine current processes")
|
||||
|
||||
@patch('library.check_other_processes_pmd_usage.'
|
||||
'check_current_process_pmd_usage')
|
||||
def test_validate_with_no_pmd_processes(self, mock_number_list):
|
||||
mock_number_list.return_value = []
|
||||
pmd_cpus = ["2", "3"]
|
||||
current_processes = "21's 2-5\n22's 4-9\n24's 4-5"
|
||||
pmd_processes = ""
|
||||
self.module.run_command.side_effect = [[0, current_processes, ""],
|
||||
None]
|
||||
exclude_processes_pid = ["24"]
|
||||
validation.check_other_processes_pmd_usage(self.module, pmd_cpus,
|
||||
exclude_processes_pid)
|
||||
self.module.fail_json.assert_called_with(
|
||||
msg="Unable to determine PMD threads processes")
|
||||
|
||||
@patch('library.check_other_processes_pmd_usage.'
|
||||
'check_current_process_pmd_usage')
|
||||
def test_validate_other_processes_pmd_usage(self, mock_number_list):
|
||||
mock_number_list.side_effect = [["pmd threads: 2,3 used in process: 21"], []]
|
||||
pmd_cpus = ["2", "3"]
|
||||
current_processes = "21's 2-5\n22's 4-9\n24's 4-5"
|
||||
pmd_processes = "22's 4-9"
|
||||
self.module.run_command.side_effect = [[0, current_processes, ""],
|
||||
[0, pmd_processes, ""]]
|
||||
exclude_processes_pid = ["24"]
|
||||
validation.check_other_processes_pmd_usage(self.module, pmd_cpus,
|
||||
exclude_processes_pid)
|
||||
self.module.exit_json.assert_called_with(
|
||||
messages=["pmd threads: 2,3 used in process: 21"],
|
||||
pmd_interrupts=True)
|
||||
|
||||
def test_check_current_process_pmd_usage(self):
|
||||
pmd_cpus = ["2", "3"]
|
||||
process_id = "21"
|
||||
range_list = "2-5,8-11"
|
||||
expected_value = ["pmd threads: 2,3 used in process: 21"]
|
||||
result = validation.check_current_process_pmd_usage(self.module, pmd_cpus,
|
||||
process_id, range_list)
|
||||
self.assertEqual(result, expected_value)
|
||||
|
||||
def test_check_current_process_pmd_usage_with_exclude_value(self):
|
||||
pmd_cpus = ["2", "3"]
|
||||
process_id = "21"
|
||||
range_list = "2-5,8-11,^8"
|
||||
expected_value = ["pmd threads: 2,3 used in process: 21"]
|
||||
result = validation.check_current_process_pmd_usage(self.module, pmd_cpus,
|
||||
process_id, range_list)
|
||||
self.assertEqual(result, expected_value)
|
||||
|
||||
def test_check_current_process_pmd_usage_with_invalid_range(self):
|
||||
pmd_cpus = ["2", "3"]
|
||||
process_id = "21"
|
||||
range_list = "2-5,-"
|
||||
result = validation.check_current_process_pmd_usage(self.module, pmd_cpus,
|
||||
process_id, range_list)
|
||||
self.module.fail_json.assert_called_with(
|
||||
msg="Invalid number in input param 'range_list': invalid literal for int() with base 10: ''")
|
@ -0,0 +1,45 @@
|
||||
# Copyright 2016 Red Hat, Inc.
|
||||
# All Rights Reserved.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||
# not use this file except in compliance with the License. You may obtain
|
||||
# a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
from unittest.mock import MagicMock
|
||||
from unittest.mock import patch
|
||||
|
||||
import library.convert_range_to_numbers_list as validation
|
||||
from tripleo_validations.tests import base
|
||||
|
||||
|
||||
class TestConvertRangeToNumbersList(base.TestCase):
|
||||
|
||||
def setUp(self):
|
||||
super(TestConvertRangeToNumbersList, self).setUp()
|
||||
self.module = MagicMock()
|
||||
|
||||
def test_valid_convert_range_to_numbers_list(self):
|
||||
range_list = "2-5,8-11"
|
||||
expected_value = [2, 3, 4, 5, 8, 9, 10, 11]
|
||||
result = validation.convert_range_to_numbers_list(self.module, range_list)
|
||||
self.assertEqual(result, expected_value)
|
||||
|
||||
def test_valid_convert_range_to_numbers_list_with_exclude_value(self):
|
||||
range_list = "2-5,8-11,^8"
|
||||
expected_value = [2, 3, 4, 5, 9, 10, 11]
|
||||
result = validation.convert_range_to_numbers_list(self.module, range_list)
|
||||
self.assertEqual(result, expected_value)
|
||||
|
||||
def test_invalid_convert_range_to_numbers_list(self):
|
||||
range_list = "2-5,-"
|
||||
validation.convert_range_to_numbers_list(self.module, range_list)
|
||||
self.module.fail_json.assert_called_with(
|
||||
msg="Invalid number in input param 'range_list': invalid literal for int() with base 10: ''")
|
@ -0,0 +1,48 @@
|
||||
# Copyright 2016 Red Hat, Inc.
|
||||
# All Rights Reserved.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||
# not use this file except in compliance with the License. You may obtain
|
||||
# a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
from unittest.mock import MagicMock
|
||||
from unittest.mock import patch
|
||||
|
||||
import library.get_dpdk_nics_numa_info as validation
|
||||
from tripleo_validations.tests import base
|
||||
|
||||
|
||||
class TestGetDpdkNicsNumaInfo(base.TestCase):
|
||||
|
||||
def setUp(self):
|
||||
super(TestGetDpdkNicsNumaInfo, self).setUp()
|
||||
self.module = MagicMock()
|
||||
|
||||
@patch('library.get_dpdk_nics_numa_info.'
|
||||
'get_dpdk_nics_info')
|
||||
def test_get_dpdk_nics_numa_info(self, mock_dpdk_nics_info):
|
||||
dpdk_nics_numa_info = [{"numa_node": 0, "mac": "mac1", "pci": "pci1"}]
|
||||
mock_dpdk_nics_info.return_value = dpdk_nics_numa_info
|
||||
dpdk_mapping_file = "/var/lib/os-net-config/dpdk_mapping.yaml"
|
||||
validation.get_dpdk_nics_numa_info(self.module, dpdk_mapping_file)
|
||||
self.module.exit_json.assert_called_with(
|
||||
changed=False,
|
||||
message="DPDK NIC's NUMA info",
|
||||
dpdk_nics_numa_info=dpdk_nics_numa_info)
|
||||
|
||||
@patch('library.get_dpdk_nics_numa_info.'
|
||||
'get_dpdk_nics_info')
|
||||
def test_no_dpdk_nics_numa_info(self, mock_dpdk_nics_info):
|
||||
mock_dpdk_nics_info.return_value = []
|
||||
dpdk_mapping_file = "/var/lib/os-net-config/dpdk_mapping.yaml"
|
||||
validation.get_dpdk_nics_numa_info(self.module, dpdk_mapping_file)
|
||||
self.module.fail_json.assert_called_with(
|
||||
msg="Unable to determine DPDK NIC's NUMA info")
|
@ -0,0 +1,120 @@
|
||||
# Copyright 2021 Red Hat, Inc.
|
||||
# All Rights Reserved.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||
# not use this file except in compliance with the License. You may obtain
|
||||
# a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
from unittest.mock import MagicMock
|
||||
from unittest.mock import patch
|
||||
|
||||
import library.pmd_threads_siblings_check as validation
|
||||
from tripleo_validations.tests import base
|
||||
|
||||
|
||||
class TestPmdThreadsSiblingsCheck(base.TestCase):
|
||||
|
||||
def setUp(self):
|
||||
super(TestPmdThreadsSiblingsCheck, self).setUp()
|
||||
self.module = MagicMock()
|
||||
|
||||
@patch('library.pmd_threads_siblings_check.'
|
||||
'get_cpus_list_from_mask_value')
|
||||
@patch('library.pmd_threads_siblings_check.'
|
||||
'get_nodes_cores_info')
|
||||
@patch('library.pmd_threads_siblings_check.'
|
||||
'get_thread_siblings')
|
||||
def test_validate_valid_pmd_cpus(self, mock_pmd_cpus, mock_cpus,
|
||||
mock_threads_siblings):
|
||||
mock_pmd_cpus.return_value = ['0', '2']
|
||||
mock_cpus.return_value = (
|
||||
[0],
|
||||
[{'numa_node': 0, 'thread_siblings': [0, 2], 'cpu': 0},
|
||||
{'numa_node': 0, 'thread_siblings': [4, 6], 'cpu': 4},
|
||||
{'numa_node': 0, 'thread_siblings': [8, 10], 'cpu': 8},
|
||||
{'numa_node': 1, 'thread_siblings': [1, 3], 'cpu': 1},
|
||||
{'numa_node': 1, 'thread_siblings': [5, 7], 'cpu': 5},
|
||||
{'numa_node': 1, 'thread_siblings': [9, 11], 'cpu': 9}])
|
||||
mock_threads_siblings.return_value = ['0', '2']
|
||||
|
||||
validation.validate_pmd_cpus(self.module, '"3"')
|
||||
self.module.exit_json.assert_called_with(
|
||||
changed=False,
|
||||
message="PMD CPU's configured correctly: 0,2",
|
||||
pmd_cpus_list=['0', '2'])
|
||||
|
||||
@patch('library.pmd_threads_siblings_check.'
|
||||
'get_cpus_list_from_mask_value')
|
||||
@patch('library.pmd_threads_siblings_check.'
|
||||
'get_nodes_cores_info')
|
||||
@patch('library.pmd_threads_siblings_check.'
|
||||
'get_thread_siblings')
|
||||
def test_validate_invalid_pmd_cpus(self, mock_pmd_cpus, mock_cpus,
|
||||
mock_threads_siblings):
|
||||
mock_pmd_cpus.return_value = ['0', '1']
|
||||
mock_cpus.return_value = (
|
||||
[0],
|
||||
[{'numa_node': 0, 'thread_siblings': [0, 2], 'cpu': 0},
|
||||
{'numa_node': 0, 'thread_siblings': [4, 6], 'cpu': 4},
|
||||
{'numa_node': 0, 'thread_siblings': [8, 10], 'cpu': 8},
|
||||
{'numa_node': 1, 'thread_siblings': [1, 3], 'cpu': 1},
|
||||
{'numa_node': 1, 'thread_siblings': [5, 7], 'cpu': 5},
|
||||
{'numa_node': 1, 'thread_siblings': [9, 11], 'cpu': 9}])
|
||||
mock_threads_siblings.return_value = ['0', '2']
|
||||
|
||||
validation.validate_pmd_cpus(self.module, '"5"')
|
||||
self.module.fail_json.assert_called_with(
|
||||
msg="Invalid PMD CPU's, thread siblings missed")
|
||||
|
||||
def test_get_cpus_list_from_mask_value(self):
|
||||
cpu_mask_val = '"3"'
|
||||
expected_value = ['0', '1']
|
||||
result = validation.get_cpus_list_from_mask_value(cpu_mask_val)
|
||||
self.assertEqual(result, expected_value)
|
||||
|
||||
def test_valid_get_nodes_cores_info(self):
|
||||
lines = "# format\n0,0,0\n 0,0,2\n1,1,1\n1,1,3"
|
||||
self.module.run_command.return_value = [0, lines, ""]
|
||||
|
||||
expected_value = (
|
||||
[0, 1],
|
||||
[{'numa_node': 0, 'thread_siblings': [0, 2], 'cpu': 0},
|
||||
{'numa_node': 1, 'thread_siblings': [1, 3], 'cpu': 1}])
|
||||
result = validation.get_nodes_cores_info(self.module)
|
||||
self.assertListEqual(result[0], expected_value[0])
|
||||
self.assertListEqual(result[1], expected_value[1])
|
||||
|
||||
def test_invalid_missing_val_get_nodes_cores_info(self):
|
||||
lines = "# format\n,0,0\n 0,0,2\n1,1,1\n1,1,3"
|
||||
self.module.run_command.return_value = [0, lines, ""]
|
||||
validation.get_nodes_cores_info(self.module)
|
||||
self.module.fail_json.assert_called_with(
|
||||
msg="Unable to determine physical and logical cpus.")
|
||||
|
||||
def test_invalid_missing_field_get_nodes_cores_info(self):
|
||||
lines = "# format\n0,0\n 0,0,2\n1,1,1\n1,1,3"
|
||||
self.module.run_command.return_value = [0, lines, ""]
|
||||
validation.get_nodes_cores_info(self.module)
|
||||
self.module.fail_json.assert_called_with(
|
||||
msg="Unable to determine physical and logical cpus.")
|
||||
|
||||
def test_invalid_incorrect_value_get_nodes_cores_info(self):
|
||||
lines = "# format\nab,0,0\n0,0,2\n1,1,1\n1,1,3"
|
||||
self.module.run_command.return_value = [0, lines, ""]
|
||||
validation.get_nodes_cores_info(self.module)
|
||||
self.module.fail_json.assert_called_with(
|
||||
msg="Unable to determine physical and logical cpus.")
|
||||
|
||||
def test_invalid_command_result_get_nodes_cores_info(self):
|
||||
self.module.run_command.return_value = []
|
||||
validation.get_nodes_cores_info(self.module)
|
||||
self.module.fail_json.assert_called_with(
|
||||
msg="Unable to determine physical and logical cpus.")
|
Loading…
x
Reference in New Issue
Block a user