def test_collect_failure(self, mock_listdir, mock_isdir, mock_memory_info, mock_cores_info, mock_nics_info): numa_node_dirs = ['node0', 'node1'] mock_listdir.return_value = numa_node_dirs mock_isdir.return_value = True mock_memory_info.side_effect = errors.IncompatibleNumaFormatError("") mock_cores_info.side_effect = errors.IncompatibleNumaFormatError("") mock_nics_info.side_effect = errors.IncompatibleNumaFormatError("") numa_insp.collect_numa_topology_info(self.data, self.failures) self.assertNotIn("numa_topology", self.data) self.assertFalse(self.failures)
def get_nodes_memory_info(numa_node_dirs): """Collect the NUMA nodes memory information. The information is returned in the form of:: "ram": [{"numa_node": <numa_node_id>, "size_kb": <memory_in_kb>}, ...] :param numa_node_dirs: A list of NUMA node directories :raises: IncompatibleNumaFormatError: when unexpected format data in NUMA node :return: A list of memory information with NUMA node id """ ram = [] for numa_node_dir in numa_node_dirs: numa_node_memory = {} numa_node_id = get_numa_node_id(numa_node_dir) try: with open(os.path.join(numa_node_dir, 'meminfo')) as meminfo_file: for line in meminfo_file: if 'MemTotal' in line: break else: msg = ('Memory information is not available for ' '%(node)s' % { 'node': numa_node_dir }) raise errors.IncompatibleNumaFormatError(msg) except IOError as exc: msg = ('Failed to get memory information ' 'for %(node)s: %(error)s' % { 'node': numa_node_dir, 'error': exc }) raise errors.IncompatibleNumaFormatError(msg) try: # To get memory size with unit from memory info line # Memory info sample line format 'Node 0 MemTotal: 1560000 kB' value = line.split(":")[1].strip() memory_kb = int(UNIT_CONVERTER(value).to_base_units()) except (ValueError, IndexError, pint.UndefinedUnitError) as exc: msg = ('Failed to get memory information for %(node)s: ' '%(error)s' % { 'node': numa_node_dir, 'error': exc }) raise errors.IncompatibleNumaFormatError(msg) numa_node_memory['numa_node'] = numa_node_id numa_node_memory['size_kb'] = memory_kb LOG.debug('Found memory available %d KB in NUMA node %d', memory_kb, numa_node_id) ram.append(numa_node_memory) return ram
def test_collect_no_nics_dirs(self, mock_listdir, mock_isdir, mock_memory_info, mock_cores_info, mock_nics_info): numa_node_dirs = ['node0', 'node1'] mock_listdir.return_value = numa_node_dirs mock_isdir.return_value = True mock_memory_info.return_value = [{ 'numa_node': 0, 'size_kb': 1560000 }, { 'numa_node': 1, 'size_kb': 1200000 }] mock_cores_info.return_value = [{ 'cpu': 0, 'numa_node': 0, 'thread_siblings': [0, 1, 2, 3] }, { 'cpu': 1, 'numa_node': 1, 'thread_siblings': [4, 5, 6] }] mock_nics_info.side_effect = errors.IncompatibleNumaFormatError("") mock_open = mock.mock_open() with mock.patch('six.moves.builtins.open', mock_open): numa_insp.collect_numa_topology_info(self.data, self.failures) self.assertNotIn("numa_topology", self.data)
def test_bad_nodes_thread_dirs(self, mock_node_id, mock_listdir, mock_isdir): numa_node_dirs = ['/sys/devices/system/node/node0'] mock_node_id.return_value = 0 mock_listdir.side_effect = errors.IncompatibleNumaFormatError("") mock_isdir.return_value = True self.assertRaises(errors.IncompatibleNumaFormatError, numa_insp.get_nodes_cores_info, numa_node_dirs)
def get_nodes_nics_info(nic_device_path): """Collect the NUMA nodes nics information. The information is returned in the form of:: "nics": [ {"name": "<network interface name>", "numa_node": <numa_node_id>}, ..., ] :param nic_device_path: nic device directory path :raises: IncompatibleNumaFormatError: when unexpected format data in NUMA node :return: A list of nics information with NUMA node id """ nics = [] if not os.path.isdir(nic_device_path): msg = ('Failed to get list of NIC\'s, NIC device path ' 'does not exist: %(nic_device_path)s' % { 'nic_device_path': nic_device_path }) raise errors.IncompatibleNumaFormatError(msg) for nic_dir in os.listdir(nic_device_path): if not os.path.isfile( os.path.join(nic_device_path, nic_dir, 'device', 'numa_node')): continue try: with open( os.path.join(nic_device_path, nic_dir, 'device', 'numa_node')) as nicsinfo_file: numa_node_id = int(nicsinfo_file.read().strip()) except (IOError, ValueError) as exc: msg = ('Failed to gather NIC\'s for NUMA node %(node)s: ' '%(error)s' % { 'node': nic_dir, 'error': exc }) raise errors.IncompatibleNumaFormatError(msg) numa_node_nics = {} numa_node_nics['name'] = nic_dir numa_node_nics['numa_node'] = numa_node_id LOG.debug('Found a NIC %s in NUMA node %d', nic_dir, numa_node_id) nics.append(numa_node_nics) return nics
def get_numa_node_id(numa_node_dir): """Provides the NUMA node id from NUMA node directory :param numa_node_dir: NUMA node directory :raises: IncompatibleNumaFormatError: when unexpected format data in NUMA node dir :return: NUMA node id """ try: return int(os.path.basename(numa_node_dir)[4:]) except (IOError, ValueError, IndexError) as exc: msg = ('Failed to get NUMA node id for %(node)s: ' '%(error)s' % { 'node': numa_node_dir, 'error': exc }) raise errors.IncompatibleNumaFormatError(msg)
def get_nodes_cores_info(numa_node_dirs): """Collect the NUMA nodes cpu's and thread's information. "cpus": [ { "cpu": <cpu_id>, "numa_node": <numa_node_id>, "thread_siblings": [<list of sibling threads>] }, ..., ] NUMA nodes path: /sys/devices/system/node/node<node_id> Thread dirs path: /sys/devices/system/node/node<node_id>/cpu<thread_id> CPU id file path: /sys/devices/system/node/node<node_id>/cpu<thread_id>/ topology/core_id :param numa_node_dirs: A list of NUMA node directories :raises: IncompatibleNumaFormatError: when unexpected format data in NUMA node :return: A list of cpu information with NUMA node id and thread siblings """ dict_cpus = {} for numa_node_dir in numa_node_dirs: numa_node_id = get_numa_node_id(numa_node_dir) try: thread_dirs = os.listdir(numa_node_dir) except OSError as exc: msg = ('Failed to get list of threads for %(node)s: ' '%(error)s' % { 'node': numa_node_dir, 'error': exc }) raise errors.IncompatibleNumaFormatError(msg) for thread_dir in thread_dirs: if (not os.path.isdir(os.path.join(numa_node_dir, thread_dir)) or not thread_dir.startswith("cpu")): continue try: thread_id = int(thread_dir[3:]) except (ValueError, IndexError) as exc: msg = ('Failed to get cores information for ' '%(node)s: %(error)s' % { 'node': numa_node_dir, 'error': exc }) raise errors.IncompatibleNumaFormatError(msg) try: with open( os.path.join(numa_node_dir, thread_dir, 'topology', 'core_id')) as core_id_file: cpu_id = int(core_id_file.read().strip()) except (IOError, ValueError) as exc: msg = ('Failed to gather cpu_id for thread' '%(thread)s NUMA node %(node)s: %(error)s' % { 'thread': thread_dir, 'node': numa_node_dir, 'error': exc }) raise errors.IncompatibleNumaFormatError(msg) # CPU and NUMA node together forms a unique value, as cpu_id is # specific to a NUMA node # NUMA node id and cpu id tuple is used for unique key dict_key = numa_node_id, cpu_id if dict_key in dict_cpus: if thread_id not in dict_cpus[dict_key]['thread_siblings']: dict_cpus[dict_key]['thread_siblings'].append(thread_id) else: cpu_item = {} cpu_item['thread_siblings'] = [thread_id] cpu_item['cpu'] = cpu_id cpu_item['numa_node'] = numa_node_id dict_cpus[dict_key] = cpu_item LOG.debug('Found a thread sibling %d for CPU %d in NUMA node %d', thread_id, cpu_id, numa_node_id) return list(dict_cpus.values())