Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Send additional CPU data do SCC (jsc#SUMA-406) #9736

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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)

Check failure on line 1 in java/spacewalk-java.changes.welder.scc-cpu-telemetry-data

View workflow job for this annotation

GitHub Actions / Changelog tests

SUMA-406 is not mentioned in PR title or in commit messages in file java/spacewalk-java.changes.welder.scc-cpu-telemetry-data#L1
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)

Check failure on line 1 in schema/spacewalk/susemanager-schema.changes.welder.cpu-arch-specs

View workflow job for this annotation

GitHub Actions / Changelog tests

SUMA-406 is not mentioned in PR title or in commit messages in file schema/spacewalk/susemanager-schema.changes.welder.cpu-arch-specs#L1
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 (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
Loading