Skip to content

Commit

Permalink
SWATCH-2917: Improve logging to identify system being updated in conduit
Browse files Browse the repository at this point in the history
- Uniform the logs made by InventoryController, so all the logs print the same format (including the network fdqn if exists)
- Also, include into the MDC, the org_id
  • Loading branch information
Sgitario committed Sep 17, 2024
1 parent 9852ff4 commit 43f30f6
Show file tree
Hide file tree
Showing 2 changed files with 120 additions and 72 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@
import java.util.regex.Pattern;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import lombok.extern.slf4j.Slf4j;
import org.candlepin.subscriptions.conduit.inventory.ConduitFacts;
import org.candlepin.subscriptions.conduit.inventory.InventoryService;
import org.candlepin.subscriptions.conduit.inventory.ProviderFact;
Expand All @@ -54,23 +55,21 @@
import org.candlepin.subscriptions.conduit.rhsm.client.model.InstalledProducts;
import org.candlepin.subscriptions.conduit.rhsm.client.model.Pagination;
import org.candlepin.subscriptions.exception.ExternalServiceException;
import org.candlepin.subscriptions.util.LogUtils;
import org.candlepin.subscriptions.utilization.api.model.OrgInventory;
import org.candlepin.subscriptions.validator.IpAddressValidator;
import org.candlepin.subscriptions.validator.MacAddressValidator;
import org.jetbrains.annotations.NotNull;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;

/** Controller used to interact with the Inventory service. */
@Slf4j
@Component
public class InventoryController {

private static final Logger log = LoggerFactory.getLogger(InventoryController.class);

private static final BigDecimal KIBIBYTES_PER_GIBIBYTE = BigDecimal.valueOf(1048576);
private static final BigDecimal BYTES_PER_KIBIBYTE = BigDecimal.valueOf(1024);
private static final String COMMA_REGEX = ",\\s*";
Expand Down Expand Up @@ -201,8 +200,8 @@ protected ConduitFacts getFactsFromConsumer(Consumer consumer) {
facts.setBillingModel(rhsmFacts.get(OCM_BILLING_MODEL));
extractConversionsActivity(rhsmFacts, facts);

extractNetworkFacts(rhsmFacts, facts);
extractHardwareFacts(rhsmFacts, facts);
extractNetworkFacts(consumer, rhsmFacts, facts);
extractHardwareFacts(consumer, rhsmFacts, facts);
extractVirtualizationFacts(consumer, rhsmFacts, facts);
extractProviderFacts(rhsmFacts, facts);
facts.setCloudProvider(extractCloudProvider(rhsmFacts));
Expand Down Expand Up @@ -285,7 +284,8 @@ private String normalizeUuid(String uuid) {
}
}

private void extractHardwareFacts(Map<String, String> rhsmFacts, ConduitFacts facts) {
private void extractHardwareFacts(
Consumer consumer, Map<String, String> rhsmFacts, ConduitFacts facts) {
String systemUuid = rhsmFacts.get(DMI_SYSTEM_UUID);
if (StringUtils.hasLength(systemUuid)) {
if (systemUuid.matches(UUID_REGEX)) {
Expand All @@ -295,7 +295,7 @@ private void extractHardwareFacts(Map<String, String> rhsmFacts, ConduitFacts fa
} else {
log.info(
"Consumer {} in org {} has unparseable BIOS uuid: {}",
facts.getSubscriptionManagerId(),
consumerToString(consumer),
facts.getOrgId(),
systemUuid);
}
Expand Down Expand Up @@ -329,15 +329,16 @@ private void extractHardwareFacts(Map<String, String> rhsmFacts, ConduitFacts fa
facts.setThreadsPerCore(Integer.parseInt(threadsPerCore));
}

setMemoryFacts(rhsmFacts, facts);
setMemoryFacts(consumer, rhsmFacts, facts);

String architecture = rhsmFacts.get(UNAME_MACHINE);
if (StringUtils.hasLength(architecture)) {
facts.setArchitecture(architecture);
}
}

private void setMemoryFacts(Map<String, String> rhsmFacts, ConduitFacts facts) {
private void setMemoryFacts(
Consumer consumer, Map<String, String> rhsmFacts, ConduitFacts facts) {
String memoryTotal = rhsmFacts.get(MEMORY_MEMTOTAL);
if (StringUtils.hasLength(memoryTotal)) {
try {
Expand All @@ -349,14 +350,18 @@ private void setMemoryFacts(Map<String, String> rhsmFacts, ConduitFacts facts) {
long systemMemoryBytes = memoryBytes.multiply(BYTES_PER_KIBIBYTE).longValue();
if (systemMemoryBytes > MAX_ALLOWED_SYSTEM_MEMORY_BYTES) {
log.warn(
"System memory bytes value {} greater than max allowed value of {}. Setting to null.",
"For consumer {}: System memory bytes value {} greater than max allowed value of {}. Setting to null.",
consumerToString(consumer),
systemMemoryBytes,
MAX_ALLOWED_SYSTEM_MEMORY_BYTES);
} else {
facts.setSystemMemoryBytes(systemMemoryBytes);
}
} catch (NumberFormatException e) {
log.info("Bad memory.memtotal value: {}", memoryTotal);
log.info(
"For consumer {}: Bad memory.memtotal value: {}",
consumerToString(consumer),
memoryTotal);
}
}
}
Expand All @@ -383,28 +388,29 @@ protected BigDecimal memtotalFromString(String memoryTotal) {
}

@SuppressWarnings("indentation")
private void extractNetworkFacts(Map<String, String> rhsmFacts, ConduitFacts facts) {
String fqdn = rhsmFacts.get(NETWORK_FQDN);
if (StringUtils.hasLength(fqdn)) {
private void extractNetworkFacts(
Consumer consumer, Map<String, String> rhsmFacts, ConduitFacts facts) {
String fqdn = getNetworkFqdn(rhsmFacts);
if (fqdn != null) {
facts.setFqdn(fqdn);
}

List<HbiNetworkInterface> networkInterfaces = populateNICs(rhsmFacts);
List<HbiNetworkInterface> networkInterfaces = populateNICs(consumer, rhsmFacts);
if (!networkInterfaces.isEmpty()) {
facts.setNetworkInterfaces(networkInterfaces);
}

var macAddresses = extractMacAddresses(rhsmFacts);
var macAddresses = extractMacAddresses(consumer, rhsmFacts);
if (!macAddresses.isEmpty()) {
facts.setMacAddresses(new ArrayList<>(macAddresses));
}
var ipAddresses = extractIpAddresses(rhsmFacts);
var ipAddresses = extractIpAddresses(consumer, rhsmFacts);
if (!ipAddresses.isEmpty()) {
facts.setIpAddresses(new ArrayList<>(ipAddresses));
}
}

protected Set<String> extractMacAddresses(Map<String, String> rhsmFacts) {
private Set<String> extractMacAddresses(Consumer consumer, Map<String, String> rhsmFacts) {
Set<String> macAddresses = new HashSet<>();
rhsmFacts.entrySet().stream()
.filter(
Expand All @@ -413,14 +419,14 @@ protected Set<String> extractMacAddresses(Map<String, String> rhsmFacts) {
entry -> {
var macString = entry.getValue();
var macFact = entry.getKey();
var splitMacs = filterMacs(macString, macFact);
var splitMacs = filterMacs(consumer, macString, macFact);
macAddresses.addAll(splitMacs);
});

return macAddresses;
}

protected Set<String> extractIpAddresses(Map<String, String> rhsmFacts) {
private Set<String> extractIpAddresses(Consumer consumer, Map<String, String> rhsmFacts) {
Set<String> ipAddresses = new HashSet<>();
rhsmFacts.entrySet().stream()
.filter(
Expand All @@ -431,18 +437,18 @@ protected Set<String> extractIpAddresses(Map<String, String> rhsmFacts) {
entry -> {
var addrString = entry.getValue();
var addrFact = entry.getKey();
var splitAddrs = filterIps(addrString, addrFact);
var splitAddrs = filterIps(consumer, addrString, addrFact);
ipAddresses.addAll(splitAddrs);
});

return ipAddresses;
}

protected List<String> filterIps(String s, String factKey) {
protected List<String> filterIps(Consumer consumer, String s, String factKey) {
Predicate<String> ipTests = getIpTests();
// A truncated IP would fail the validator, but we check it separately and
// before the validator so that we can log that the fact is truncated.
Predicate<String> truncation = ip -> !isTruncated(ip, factKey);
Predicate<String> truncation = ip -> !isTruncated(consumer, ip, factKey);
return filterCommaDelimitedList(s, truncation.and(ipTests));
}

Expand All @@ -451,10 +457,10 @@ private Predicate<String> getIpTests() {
return addr -> StringUtils.hasLength(addr) && ipValidator.isValid(addr, null);
}

protected List<String> filterMacs(String s, String factKey) {
protected List<String> filterMacs(Consumer consumer, String s, String factKey) {
// A truncated MAC would fail the validator, but we check it separately and
// before the validator so that we can log that the fact is truncated.
Predicate<String> truncation = mac -> !isTruncated(mac, factKey);
Predicate<String> truncation = mac -> !isTruncated(consumer, mac, factKey);
Predicate<String> macTests = getMacTests();
return filterCommaDelimitedList(s, truncation.and(macTests));
}
Expand All @@ -469,7 +475,7 @@ protected List<String> filterCommaDelimitedList(String s, Predicate<String> pred
return items.stream().filter(predicate).toList();
}

private List<HbiNetworkInterface> populateNICs(Map<String, String> rhsmFacts) {
private List<HbiNetworkInterface> populateNICs(Consumer consumer, Map<String, String> rhsmFacts) {
var nicSet = new ArrayList<HbiNetworkInterface>();
for (Map.Entry<String, String> entry : rhsmFacts.entrySet()) {
if (entry.getKey().startsWith(NIC_PREFIX)
Expand All @@ -482,18 +488,21 @@ private List<HbiNetworkInterface> populateNICs(Map<String, String> rhsmFacts) {
var networkInterface = new HbiNetworkInterface();
networkInterface.setName(nicsName[2]);
networkInterface.setMacAddress(mac);
mapInterfaceIps(networkInterface, rhsmFacts, ".ipv4");
mapInterfaceIps(networkInterface, rhsmFacts, ".ipv6");
mapInterfaceIps(consumer, networkInterface, rhsmFacts, ".ipv4");
mapInterfaceIps(consumer, networkInterface, rhsmFacts, ".ipv6");
nicSet.add(networkInterface);
}
}
// creates a lo interface if ips exist for it, but no mac was given
checkLoopbackIPs(nicSet, rhsmFacts);
checkLoopbackIPs(consumer, nicSet, rhsmFacts);
return nicSet;
}

private void mapInterfaceIps(
HbiNetworkInterface networkInterface, Map<String, String> facts, String suffix) {
Consumer consumer,
HbiNetworkInterface networkInterface,
Map<String, String> facts,
String suffix) {
String prefix = NIC_PREFIX + networkInterface.getName() + suffix;

var ipv4List = new HashSet<String>();
Expand All @@ -502,23 +511,23 @@ private void mapInterfaceIps(
var fact = prefix + "_address";
var listFact = prefix + "_address_list";
if (suffix.equalsIgnoreCase(".ipv4") && facts.containsKey(listFact)) {
ipv4List.addAll(filterIps(facts.get(listFact), fact));
ipv4List.addAll(filterIps(consumer, facts.get(listFact), fact));
} else if (facts.containsKey(fact) && getIpTests().test(facts.get(fact))) {
ipv4List.add(facts.get(fact));
}

fact = prefix + "_address.global";
listFact = prefix + "_address.global_list";
if (facts.containsKey(listFact)) {
ipv6List.addAll(filterIps(facts.get(listFact), fact));
ipv6List.addAll(filterIps(consumer, facts.get(listFact), fact));
} else if (facts.containsKey(fact) && getIpTests().test(facts.get(fact))) {
ipv6List.add(facts.get(fact));
}

fact = prefix + "_address.link";
listFact = prefix + "_address.link_list";
if (facts.containsKey(listFact)) {
ipv6List.addAll(filterIps(facts.get(listFact), fact));
ipv6List.addAll(filterIps(consumer, facts.get(listFact), fact));
} else if (facts.containsKey(fact) && getIpTests().test(facts.get(fact))) {
ipv6List.add(facts.get(fact));
}
Expand All @@ -533,15 +542,16 @@ private void mapInterfaceIps(
}

private void checkLoopbackIPs(
List<HbiNetworkInterface> networkInterfaces, Map<String, String> facts) {
Consumer consumer, List<HbiNetworkInterface> networkInterfaces, Map<String, String> facts) {
boolean loExist = networkInterfaces.stream().anyMatch(nic -> "lo".equals(nic.getName()));
var lo = new HbiNetworkInterface();

if (!loExist && facts.containsKey(NET_INTERFACE_LO_IPV4_ADDRESS)) {
lo.setName("lo");
lo.setMacAddress("00:00:00:00:00:00");
var splitAddrs =
filterIps(facts.get(NET_INTERFACE_LO_IPV4_ADDRESS), NET_INTERFACE_LO_IPV4_ADDRESS);
filterIps(
consumer, facts.get(NET_INTERFACE_LO_IPV4_ADDRESS), NET_INTERFACE_LO_IPV4_ADDRESS);
lo.setIpv4Addresses(
CollectionUtils.isEmpty(splitAddrs)
? List.of("127.0.0.1")
Expand All @@ -551,7 +561,8 @@ private void checkLoopbackIPs(
lo.setName("lo");
lo.setMacAddress("00:00:00:00:00:00");
var splitAddrs =
filterIps(facts.get(NET_INTERFACE_LO_IPV6_ADDRESS), NET_INTERFACE_LO_IPV6_ADDRESS);
filterIps(
consumer, facts.get(NET_INTERFACE_LO_IPV6_ADDRESS), NET_INTERFACE_LO_IPV6_ADDRESS);
lo.setIpv6Addresses(
CollectionUtils.isEmpty(splitAddrs)
? List.of("::1")
Expand Down Expand Up @@ -648,10 +659,15 @@ private Optional<String> getNextOffset(

public OrgInventory getInventoryForOrg(String orgId, String offset)
throws ExternalServiceException {
org.candlepin.subscriptions.conduit.rhsm.client.model.OrgInventory feedPage =
getConsumerFeed(orgId, offset);
return inventoryService.getInventoryForOrgConsumers(
validateConduitFactsForOrg(feedPage).toList());
try {
LogUtils.addOrgIdToMdc(orgId);
org.candlepin.subscriptions.conduit.rhsm.client.model.OrgInventory feedPage =
getConsumerFeed(orgId, offset);
return inventoryService.getInventoryForOrgConsumers(
validateConduitFactsForOrg(feedPage).toList());
} finally {
LogUtils.clearOrgIdFromMdc();
}
}

private Stream<ConduitFacts> validateConduitFactsForOrg(
Expand Down Expand Up @@ -686,26 +702,54 @@ private Optional<ConduitFacts> validateConsumer(Consumer consumer) {
if (log.isInfoEnabled()) {
log.info(
"Consumer {} failed validation: {}",
consumer.getName(),
consumerToString(consumer),
violations.stream()
.map(this::buildValidationMessage)
.collect(Collectors.joining("; ")));
}
return Optional.empty();
}
} catch (Exception e) {
log.warn(String.format("Skipping consumer %s due to exception", consumer.getUuid()), e);
log.warn("Skipping consumer {} due to exception", consumerToString(consumer), e);
return Optional.empty();
}
}

private String getNetworkFqdn(Map<String, String> rhsmFacts) {
String networkFqdn = rhsmFacts.get(NETWORK_FQDN);
if (StringUtils.hasLength(networkFqdn)) {
return networkFqdn;
}

return null;
}

private String consumerToString(Consumer consumer) {
StringBuilder sb = new StringBuilder();
sb.append(consumer.getId());
try {
String fqdn = getNetworkFqdn(consumer.getFacts());
if (fqdn != null) {
sb.append(String.format(" with fqdn='%s'", fqdn));
}
} catch (Exception ex) {
log.warn("Error loading the facts for the consumer {}", consumer.getId(), ex);
}

return sb.toString();
}

private String buildValidationMessage(ConstraintViolation<ConduitFacts> x) {
return String.format("%s: %s: %s", x.getPropertyPath(), x.getMessage(), x.getInvalidValue());
}

private boolean isTruncated(String toCheck, String factKey) {
private boolean isTruncated(Consumer consumer, String toCheck, String factKey) {
if (toCheck != null && toCheck.endsWith("...")) {
log.info("Consumer fact value was truncated. Skipping value: {}:{}", factKey, toCheck);
log.info(
"For consumer {}, fact value was truncated. Skipping value: {}:{}",
consumerToString(consumer),
factKey,
toCheck);
return true;
}
return false;
Expand Down
Loading

0 comments on commit 43f30f6

Please sign in to comment.