Skip to content

Commit

Permalink
Send additional CPU data do SCC
Browse files Browse the repository at this point in the history
  • Loading branch information
wweellddeerr committed Feb 11, 2025
1 parent 16b41e1 commit 0caacc9
Show file tree
Hide file tree
Showing 17 changed files with 289 additions and 4 deletions.
4 changes: 4 additions & 0 deletions java/code/src/com/redhat/rhn/domain/server/CPU.hbm.xml
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,10 @@ PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
<property name="nrCore" column="nrcore" type="long"/>
<property name="nrThread" column="nrthread" type="long"/>

<property name="archSpecs" type="text">
<column name="arch_specs" sql-type="jsonb" write="?::jsonb" />
</property>

<property name="created" column="created" type="timestamp"
insert="false" update="false"/>
<property name="modified" column="modified" type="timestamp"
Expand Down
20 changes: 20 additions & 0 deletions java/code/src/com/redhat/rhn/domain/server/CPU.java
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,12 @@ public class CPU extends BaseDomainHelper {
private String chipSet;
private CPUArch arch;

/**
* This field stores CPU architecture-specific information. Although the corresponding database field is of type
* JSONB, it is mapped here as a String due to limitations in XML mapping for JSON types. Since it is currently
* intended only for sending to SCC, parsing is not necessary.
*/
private String archSpecs;

/**
* @return Returns the acpiVersion.
Expand Down Expand Up @@ -331,6 +337,20 @@ public String getArchName() {
return arch.getName();
}

/**
* @return Returns the archSpecs.
*/
public String getArchSpecs() {
return archSpecs;
}

/**
* @param archSpecsIn The archSpecs to set.
*/
public void setArchSpecs(String archSpecsIn) {
this.archSpecs = archSpecsIn;
}

@Override
public String toString() {
return "CPU{" +
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,9 @@
import com.suse.manager.webui.utils.salt.custom.SumaUtil;
import com.suse.salt.netapi.calls.modules.Network;

import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;

import org.apache.commons.lang3.ObjectUtils;
import org.apache.commons.lang3.RandomStringUtils;
import org.apache.commons.lang3.StringUtils;
Expand Down Expand Up @@ -118,9 +121,10 @@ public long getTotalSwapMemory() {
/**
* Store CPU information given as a {@link ValueMap}.
*
* @param cpuinfo Salt returns /proc/cpuinfo data
* @param cpuinfo Salt returns /proc/cpuinfo data
* @param cpuArchSpecsIn CPU architecture specific data
*/
public void mapCpuInfo(ValueMap cpuinfo) {
public void mapCpuInfo(ValueMap cpuinfo, Map<String, Object> cpuArchSpecsIn) {
final CPU cpu = Optional.ofNullable(server.getCpu()).orElseGet(CPU::new);

// os.uname[4]
Expand Down Expand Up @@ -199,6 +203,13 @@ else if (CpuArchUtil.isAarch64(cpuarch)) {
// On s390x this number of active and actual CPUs can be different.
cpu.setNrCPU(grains.getValueAsLong("total_num_cpus").orElse(0L));

try {
cpu.setArchSpecs(new ObjectMapper().writeValueAsString(cpuArchSpecsIn));
}
catch (JsonProcessingException e) {
LOG.warn("Failed to serialize CPU arch specs, ignoring results.", e);
}

if (arch != null) {
cpu.setServer(server);
server.setCpu(cpu);
Expand Down
2 changes: 1 addition & 1 deletion java/code/src/com/suse/manager/utils/SaltUtils.java
Original file line number Diff line number Diff line change
Expand Up @@ -1833,7 +1833,7 @@ private static void handleHardwareProfileUpdate(MinionServer server,

HardwareMapper hwMapper = new HardwareMapper(server,
new ValueMap(result.getGrains()));
hwMapper.mapCpuInfo(new ValueMap(result.getCpuInfo()));
hwMapper.mapCpuInfo(new ValueMap(result.getCpuInfo()), result.getCpuArchSpecs());
server.setRam(hwMapper.getTotalMemory());
server.setSwap(hwMapper.getTotalSwapMemory());
if (CpuArchUtil.isDmiCapable(hwMapper.getCpuArch())) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,9 @@ public class HwProfileUpdateSlsResult {
@SerializedName("mgrcompat_|-cpuinfo_|-status.cpuinfo_|-module_run")
private StateApplyResult<Ret<Map<String, Object>>> cpuInfo;

@SerializedName("mgrcompat_|-cpu_arch_specs_|-cpuinfo.arch_specs_|-module_run")
private StateApplyResult<Ret<Map<String, Object>>> cpuArchSpecs;

@SerializedName(value = "mgrcompat_|-udev_|-udev.exportdb_|-module_run",
alternate = {"mgrcompat_|-udevdb_|-udevdb.exportdb_|-module_run"})
private StateApplyResult<Ret<List<Map<String, Object>>>> udevdb;
Expand Down Expand Up @@ -278,4 +281,15 @@ public String getContainerRuntime() {
public String getUname() {
return uname.map(ret -> ret.getChanges().getStdout()).orElse(null);
}

/**
* Get the CPU architecture specific information
* @return the specs Map
*/
public Map<String, Object> getCpuArchSpecs() {
if (cpuArchSpecs == null) {
return Collections.emptyMap();
}
return cpuArchSpecs.getChanges().getRet();
}
}
10 changes: 10 additions & 0 deletions java/code/src/com/suse/scc/model/SCCHwInfoJson.java
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@

import com.google.gson.annotations.SerializedName;

import java.util.Map;
import java.util.Set;

/**
Expand All @@ -42,6 +43,8 @@ public class SCCHwInfoJson {
private String cloudProvider;

private Set<SAPJson> sap;
@SerializedName("arch_specs")
private Map<String, Object> archSpecs;

public int getCpus() {
return cpus;
Expand Down Expand Up @@ -122,4 +125,11 @@ public String getContainerRuntime() {
public void setContainerRuntime(String containerRuntimeIn) {
containerRuntime = containerRuntimeIn;
}
public Map<String, Object> getArchSpecs() {
return archSpecs;
}

public void setArchSpecs(Map<String, Object> archSpecsIn) {
archSpecs = archSpecsIn;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -31,12 +31,17 @@
import com.suse.scc.model.SCCMinProductJson;
import com.suse.scc.model.SCCRegisterSystemJson;

import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.ObjectMapper;

import org.apache.commons.lang3.RandomStringUtils;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;

import java.security.SecureRandom;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Optional;
import java.util.stream.Collectors;
Expand Down Expand Up @@ -97,6 +102,20 @@ private Optional<SCCRegisterSystemJson> getPayload(SCCRegCacheItem rci) {
Optional<CPU> cpu = ofNullable(srv.getCpu());
cpu.flatMap(c -> ofNullable(c.getNrCPU())).ifPresent(c -> hwInfo.setCpus(c.intValue()));
cpu.flatMap(c -> ofNullable(c.getNrsocket())).ifPresent(c -> hwInfo.setSockets(c.intValue()));
cpu.ifPresent(
c -> {
try {
var archSpecs = new ObjectMapper()
.readValue(c.getArchSpecs(), new TypeReference<Map<String, Object>>() {});
hwInfo.setArchSpecs(archSpecs);
}
catch (JsonProcessingException e) {
LOG.warn("Failed to parse archSpecs from CPU entity. Field will be ignored.", e);
LOG.debug("Raw archSpecs JSON stored in the database: {}", c.getArchSpecs());
}
}
);

hwInfo.setArch(srv.getServerArch().getLabel().split("-")[0]);
if (srv.isVirtualGuest()) {
VirtualInstance virtualInstance = srv.getVirtualInstance();
Expand Down
1 change: 1 addition & 0 deletions java/spacewalk-java.changes.welder.scc-cpu-telemetry-data
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
- Send CPU architecture specific data to SCC (jsc#SUMA-406)
1 change: 1 addition & 0 deletions schema/spacewalk/common/tables/rhnCpu.sql
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@ CREATE TABLE rhnCpu
apic VARCHAR(32),
apmVersion VARCHAR(32),
chipset VARCHAR(64),
arch_specs JSONB,
created TIMESTAMPTZ
DEFAULT (current_timestamp) NOT NULL,
modified TIMESTAMPTZ
Expand Down
2 changes: 1 addition & 1 deletion schema/spacewalk/postgres/triggers/rhnCpu.sql
Original file line number Diff line number Diff line change
Expand Up @@ -29,5 +29,5 @@ create trigger
rhn_cpu_up_trig
after update on rhnCpu
for each row
when (OLD.nrcpu is distinct from NEW.nrcpu OR OLD.nrsocket is distinct from NEW.nrsocket OR OLD.nrcore is distinct from NEW.nrcore OR OLD.nrthread is distinct from NEW.nrthread)
when (OLD.nrcpu is distinct from NEW.nrcpu OR OLD.nrsocket is distinct from NEW.nrsocket OR OLD.nrcore is distinct from NEW.nrcore OR OLD.nrthread is distinct from NEW.nrthread OR OLD.arch_specs is distinct from NEW.arch_specs)
execute procedure rhn_cpu_up_trig_fun();
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
- Store CPU architecture specific data (jsc#SUMA-406)
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
ALTER TABLE rhnCpu ADD COLUMN IF NOT EXISTS arch_specs JSONB;
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
drop trigger if exists rhn_cpu_up_trig on rhnCpu;

create trigger
rhn_cpu_up_trig
after update on rhnCpu
for each row
when (OLD.nrcpu is distinct from NEW.nrcpu OR OLD.nrsocket is distinct from NEW.nrsocket OR OLD.nrcore is distinct from NEW.nrcore OR OLD.nrthread is distinct from NEW.nrthread OR OLD.arch_specs is distinct from NEW.arch_specs)
execute procedure rhn_cpu_up_trig_fun();
Original file line number Diff line number Diff line change
Expand Up @@ -188,6 +188,10 @@ uname:
container_runtime:
mgrcompat.module_run:
- name: container_runtime.get_container_runtime

cpu_arch_specs:
mgrcompat.module_run:
- name: cpuinfo.arch_specs
- require:
{%- if grains.get('__suse_reserved_saltutil_states_support', False) %}
- saltutil: sync_modules
Expand Down
114 changes: 114 additions & 0 deletions susemanager-utils/susemanager-sls/src/modules/cpuinfo.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,114 @@
"""
CPU Architecture Metadata Detection Module for SaltStack
This module provides functionality to collect extended CPU architecture-specific metadata from the target system.
It detects the system architecture and gathers relevant hardware details based on the architecture, such as information
for PowerPC (ppc64), ARM (arm64), and IBM Z (s390) systems.
References:
- Most of the code was ported from
https://github.com/SUSE/connect-ng/blob/main/internal/collectors/cpu.go
"""

import logging
import re
import subprocess

log = logging.getLogger(__name__)

def arch_specs():
"""
Collect extended CPU architecture-specific metadata.
"""
specs = {}
arch = _get_architecture()

if arch in ["ppc64", "ppc64le"]:
_add_ppc64_extras(specs)
elif arch in ["arm64", "aarch64"]:
_add_arm64_extras(specs)
elif arch.startswith("s390"):
_add_z_systems_extras(specs)

return specs

def _get_architecture():
"""
Detect the system architecture.
"""
try:
return subprocess.check_output(["uname", "-m"], stderr=subprocess.PIPE).decode().strip()
except (FileNotFoundError, subprocess.CalledProcessError, UnicodeDecodeError):
log.warning("Failed to determine system architecture. Falling back to 'unknown'.", exc_info=True)
return "unknown"

def _add_ppc64_extras(specs):
_add_device_tree(specs)

lparcfg_content = _read_file("/proc/ppc64/lparcfg")
if lparcfg_content:
match = re.search(r"shared_processor_mode\s*=\s*(\d+)", lparcfg_content)
if match:
specs["lpar_mode"] = "shared" if match.group(1) == "1" else "dedicated"

def _add_arm64_extras(specs):
_add_device_tree(specs)
try:
output = subprocess.check_output(["dmidecode", "-t", "processor"], stderr=subprocess.PIPE).decode()
specs["family"] = _exact_string_match("Family", output)
specs["manufacturer"] = _exact_string_match("Manufacturer", output)
specs["signature"] = _exact_string_match("Signature", output)
except (FileNotFoundError, OSError):
log.warning("Failed to retrieve arm64 CPU details.", exc_info=True)

def _add_z_systems_extras(specs):
"""
Collect extended metadata for z systems based on `read_values -s`.
"""
try:
output = subprocess.check_output(["read_values", "-s"], stderr=subprocess.PIPE).decode()
if "VM00" in output:
specs["hypervisor"] = "zvm"
specs["type"] = _exact_string_match("Type", output)
specs["layer_type"] = _exact_string_match("VM00 Name", output)
elif "LPAR" in output:
specs["hypervisor"] = "lpar"
specs["type"] = _exact_string_match("Type", output)
specs["layer_type"] = _exact_string_match("LPAR Name", output)

specs["sockets"] = _exact_string_match("Sockets", output)

except (FileNotFoundError, subprocess.CalledProcessError):
log.warning("Failed to retrieve z system CPU details.", exc_info=True)

def _add_device_tree(specs):
"""
Attempts to read the device tree from predefined paths.
"""
device_tree_paths = [
"/sys/firmware/devicetree/base/compatible",
"/sys/firmware/devicetree/base/hypervisor/compatible",
]
for path in device_tree_paths:
content = _read_file(path)
if content:
specs["device_tree"] = content.replace("\x00", "").strip()
break

def _exact_string_match(key, text):
"""
Extract a value based on a key in the text.
"""
match = re.search(rf"{key}\s*:\s*(.*)", text)
return match.group(1).strip() if match else ""

def _read_file(path):
"""
Helper to read a file and return its content.
"""
try:
with open(path, "r", errors="replace") as f:
return f.read()
except FileNotFoundError:
return ""
Loading

0 comments on commit 0caacc9

Please sign in to comment.