Browse Source

Merge "NFV OvS DPDK zero packet loss validations"

changes/18/801018/1
Zuul 2 months ago
committed by Gerrit Code Review
parent
commit
e7647adef8
  1. 15
      doc/source/modules/modules-check_cpus_aligned_with_dpdk_nics.rst
  2. 15
      doc/source/modules/modules-check_other_processes_pmd_usage.rst
  3. 15
      doc/source/modules/modules-convert_range_to_numbers_list.rst
  4. 15
      doc/source/modules/modules-get_dpdk_nics_numa_info.rst
  5. 15
      doc/source/modules/modules-pmd_threads_siblings_check.rst
  6. 39
      doc/source/roles/role-check_nfv_ovsdpdk_zero_packet_loss.rst
  7. 140
      library/check_cpus_aligned_with_dpdk_nics.py
  8. 162
      library/check_other_processes_pmd_usage.py
  9. 97
      library/convert_range_to_numbers_list.py
  10. 143
      library/get_dpdk_nics_numa_info.py
  11. 187
      library/pmd_threads_siblings_check.py
  12. 14
      playbooks/nfv-ovsdpdk-zero-packet-loss-check.yaml
  13. 37
      roles/check_nfv_ovsdpdk_zero_packet_loss/tasks/check_nfv_instances.yml
  14. 26
      roles/check_nfv_ovsdpdk_zero_packet_loss/tasks/check_nfv_pci_address.yml
  15. 217
      roles/check_nfv_ovsdpdk_zero_packet_loss/tasks/main.yml
  16. 21
      roles/check_nfv_ovsdpdk_zero_packet_loss/tasks/validate_dpdk_port_bandwidth.yml
  17. 91
      roles/check_nfv_ovsdpdk_zero_packet_loss/tasks/validate_instance.yml
  18. 67
      tripleo_validations/tests/library/test_check_cpus_aligned_with_dpdk_nics.py
  19. 116
      tripleo_validations/tests/library/test_check_other_processes_pmd_usage.py
  20. 45
      tripleo_validations/tests/library/test_convert_range_to_numbers_list.py
  21. 48
      tripleo_validations/tests/library/test_get_dpdk_nics_numa_info.py
  22. 120
      tripleo_validations/tests/library/test_pmd_threads_siblings_check.py

15
doc/source/modules/modules-check_cpus_aligned_with_dpdk_nics.rst

@ -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

15
doc/source/modules/modules-check_other_processes_pmd_usage.rst

@ -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

@ -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

@ -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

@ -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

@ -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

@ -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

@ -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

@ -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

@ -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

@ -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

@ -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

37
roles/check_nfv_ovsdpdk_zero_packet_loss/tasks/check_nfv_instances.yml

@ -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

26
roles/check_nfv_ovsdpdk_zero_packet_loss/tasks/check_nfv_pci_address.yml

@ -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

@ -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

21
roles/check_nfv_ovsdpdk_zero_packet_loss/tasks/validate_dpdk_port_bandwidth.yml

@ -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

91
roles/check_nfv_ovsdpdk_zero_packet_loss/tasks/validate_instance.yml

@ -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 }}"

67
tripleo_validations/tests/library/test_check_cpus_aligned_with_dpdk_nics.py

@ -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")

116
tripleo_validations/tests/library/test_check_other_processes_pmd_usage.py

@ -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: ''")

45
tripleo_validations/tests/library/test_convert_range_to_numbers_list.py

@ -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: ''")

48
tripleo_validations/tests/library/test_get_dpdk_nics_numa_info.py

@ -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")

120
tripleo_validations/tests/library/test_pmd_threads_siblings_check.py

@ -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'