diff --git a/swatch-system-conduit/src/main/java/org/candlepin/subscriptions/conduit/InventoryController.java b/swatch-system-conduit/src/main/java/org/candlepin/subscriptions/conduit/InventoryController.java index 58a1b768d0..beacbcafef 100644 --- a/swatch-system-conduit/src/main/java/org/candlepin/subscriptions/conduit/InventoryController.java +++ b/swatch-system-conduit/src/main/java/org/candlepin/subscriptions/conduit/InventoryController.java @@ -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; @@ -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*"; @@ -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)); @@ -285,7 +284,8 @@ private String normalizeUuid(String uuid) { } } - private void extractHardwareFacts(Map rhsmFacts, ConduitFacts facts) { + private void extractHardwareFacts( + Consumer consumer, Map rhsmFacts, ConduitFacts facts) { String systemUuid = rhsmFacts.get(DMI_SYSTEM_UUID); if (StringUtils.hasLength(systemUuid)) { if (systemUuid.matches(UUID_REGEX)) { @@ -295,7 +295,7 @@ private void extractHardwareFacts(Map rhsmFacts, ConduitFacts fa } else { log.info( "Consumer {} in org {} has unparseable BIOS uuid: {}", - facts.getSubscriptionManagerId(), + consumerToString(consumer), facts.getOrgId(), systemUuid); } @@ -329,7 +329,7 @@ private void extractHardwareFacts(Map 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)) { @@ -337,7 +337,8 @@ private void extractHardwareFacts(Map rhsmFacts, ConduitFacts fa } } - private void setMemoryFacts(Map rhsmFacts, ConduitFacts facts) { + private void setMemoryFacts( + Consumer consumer, Map rhsmFacts, ConduitFacts facts) { String memoryTotal = rhsmFacts.get(MEMORY_MEMTOTAL); if (StringUtils.hasLength(memoryTotal)) { try { @@ -349,14 +350,18 @@ private void setMemoryFacts(Map 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); } } } @@ -383,28 +388,29 @@ protected BigDecimal memtotalFromString(String memoryTotal) { } @SuppressWarnings("indentation") - private void extractNetworkFacts(Map rhsmFacts, ConduitFacts facts) { - String fqdn = rhsmFacts.get(NETWORK_FQDN); - if (StringUtils.hasLength(fqdn)) { + private void extractNetworkFacts( + Consumer consumer, Map rhsmFacts, ConduitFacts facts) { + String fqdn = getNetworkFqdn(rhsmFacts); + if (fqdn != null) { facts.setFqdn(fqdn); } - List networkInterfaces = populateNICs(rhsmFacts); + List 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 extractMacAddresses(Map rhsmFacts) { + private Set extractMacAddresses(Consumer consumer, Map rhsmFacts) { Set macAddresses = new HashSet<>(); rhsmFacts.entrySet().stream() .filter( @@ -413,14 +419,14 @@ protected Set extractMacAddresses(Map 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 extractIpAddresses(Map rhsmFacts) { + private Set extractIpAddresses(Consumer consumer, Map rhsmFacts) { Set ipAddresses = new HashSet<>(); rhsmFacts.entrySet().stream() .filter( @@ -431,18 +437,18 @@ protected Set extractIpAddresses(Map 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 filterIps(String s, String factKey) { + protected List filterIps(Consumer consumer, String s, String factKey) { Predicate 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 truncation = ip -> !isTruncated(ip, factKey); + Predicate truncation = ip -> !isTruncated(consumer, ip, factKey); return filterCommaDelimitedList(s, truncation.and(ipTests)); } @@ -451,10 +457,10 @@ private Predicate getIpTests() { return addr -> StringUtils.hasLength(addr) && ipValidator.isValid(addr, null); } - protected List filterMacs(String s, String factKey) { + protected List 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 truncation = mac -> !isTruncated(mac, factKey); + Predicate truncation = mac -> !isTruncated(consumer, mac, factKey); Predicate macTests = getMacTests(); return filterCommaDelimitedList(s, truncation.and(macTests)); } @@ -469,7 +475,7 @@ protected List filterCommaDelimitedList(String s, Predicate pred return items.stream().filter(predicate).toList(); } - private List populateNICs(Map rhsmFacts) { + private List populateNICs(Consumer consumer, Map rhsmFacts) { var nicSet = new ArrayList(); for (Map.Entry entry : rhsmFacts.entrySet()) { if (entry.getKey().startsWith(NIC_PREFIX) @@ -482,18 +488,21 @@ private List populateNICs(Map 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 facts, String suffix) { + Consumer consumer, + HbiNetworkInterface networkInterface, + Map facts, + String suffix) { String prefix = NIC_PREFIX + networkInterface.getName() + suffix; var ipv4List = new HashSet(); @@ -502,7 +511,7 @@ 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)); } @@ -510,7 +519,7 @@ private void mapInterfaceIps( 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)); } @@ -518,7 +527,7 @@ private void mapInterfaceIps( 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)); } @@ -533,7 +542,7 @@ private void mapInterfaceIps( } private void checkLoopbackIPs( - List networkInterfaces, Map facts) { + Consumer consumer, List networkInterfaces, Map facts) { boolean loExist = networkInterfaces.stream().anyMatch(nic -> "lo".equals(nic.getName())); var lo = new HbiNetworkInterface(); @@ -541,7 +550,8 @@ private void checkLoopbackIPs( 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") @@ -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") @@ -648,10 +659,15 @@ private Optional 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 validateConduitFactsForOrg( @@ -686,7 +702,7 @@ private Optional validateConsumer(Consumer consumer) { if (log.isInfoEnabled()) { log.info( "Consumer {} failed validation: {}", - consumer.getName(), + consumerToString(consumer), violations.stream() .map(this::buildValidationMessage) .collect(Collectors.joining("; "))); @@ -694,18 +710,46 @@ private Optional validateConsumer(Consumer consumer) { 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 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 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; diff --git a/swatch-system-conduit/src/test/java/org/candlepin/subscriptions/conduit/InventoryControllerTest.java b/swatch-system-conduit/src/test/java/org/candlepin/subscriptions/conduit/InventoryControllerTest.java index ce6ae5b746..786e18523c 100644 --- a/swatch-system-conduit/src/test/java/org/candlepin/subscriptions/conduit/InventoryControllerTest.java +++ b/swatch-system-conduit/src/test/java/org/candlepin/subscriptions/conduit/InventoryControllerTest.java @@ -45,9 +45,7 @@ import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; -import java.util.HashMap; import java.util.List; -import java.util.Map; import java.util.UUID; import java.util.stream.Collectors; import org.candlepin.subscriptions.conduit.inventory.ConduitFacts; @@ -573,17 +571,20 @@ void testBadMacIsIgnoredForNics() { @Test void testIpAddressesCollected() { - Map rhsmFacts = new HashMap<>(); - rhsmFacts.put("net.interface.eth0.ipv4_address_list", "192.168.1.1, 1.2.3.4"); - rhsmFacts.put("net.interface.eth0.ipv4_address", "192.168.1.1"); - rhsmFacts.put("net.interface.lo.ipv4_address", "127.0.0.1"); - rhsmFacts.put("net.interface.eth0.ipv6_address.link", "fe80::2323:912a:177a:d8e6"); - rhsmFacts.put("net.interface.eth0.ipv6_address.link_list", "0088::99aa:bbcc:ddee:ff33"); + Consumer consumer = new Consumer(); + consumer.setUuid(UUID.randomUUID().toString()); + consumer.getFacts().put("net.interface.eth0.ipv4_address_list", "192.168.1.1, 1.2.3.4"); + consumer.getFacts().put("net.interface.eth0.ipv4_address", "192.168.1.1"); + consumer.getFacts().put("net.interface.lo.ipv4_address", "127.0.0.1"); + consumer.getFacts().put("net.interface.eth0.ipv6_address.link", "fe80::2323:912a:177a:d8e6"); + consumer + .getFacts() + .put("net.interface.eth0.ipv6_address.link_list", "0088::99aa:bbcc:ddee:ff33"); - var results = controller.extractIpAddresses(rhsmFacts); + var result = controller.getFactsFromConsumer(consumer); assertThat( - results, + result.getIpAddresses(), Matchers.containsInAnyOrder( "192.168.1.1", "1.2.3.4", @@ -591,7 +592,7 @@ void testIpAddressesCollected() { "fe80::2323:912a:177a:d8e6", "0088::99aa:bbcc:ddee:ff33")); // testing whether the duplicates have been removed - assertEquals(5, results.size()); + assertEquals(5, result.getIpAddresses().size()); } @Test @@ -624,33 +625,36 @@ void testCanonicalFactsUuidIsNormalizedWithHyphens() { @Test void testUnknownIpsAreIgnored() { - Map rhsmFacts = new HashMap<>(); - rhsmFacts.put("net.interface.eth0.ipv4_address", "192.168.1.1"); - rhsmFacts.put("net.interface.lo.ipv4_address", "127.0.0.1"); - rhsmFacts.put("net.interface.eth0.ipv6_address.link", "fe80::2323:912a:177a:d8e6"); - rhsmFacts.put("net.interface.virbr0-nic.ipv4_address", "Unknown"); - rhsmFacts.put("net.interface.virbr0.ipv4_address", "192.168.122.1"); - rhsmFacts.put("net.interface.wlan0.ipv4_address", "Unknown"); + Consumer consumer = new Consumer(); + consumer.setUuid(UUID.randomUUID().toString()); + consumer.getFacts().put("net.interface.eth0.ipv4_address", "192.168.1.1"); + consumer.getFacts().put("net.interface.lo.ipv4_address", "127.0.0.1"); + consumer.getFacts().put("net.interface.eth0.ipv6_address.link", "fe80::2323:912a:177a:d8e6"); + consumer.getFacts().put("net.interface.virbr0-nic.ipv4_address", "Unknown"); + consumer.getFacts().put("net.interface.virbr0.ipv4_address", "192.168.122.1"); + consumer.getFacts().put("net.interface.wlan0.ipv4_address", "Unknown"); - var results = controller.extractIpAddresses(rhsmFacts); + var result = controller.getFactsFromConsumer(consumer); assertThat( - results, + result.getIpAddresses(), Matchers.containsInAnyOrder( "192.168.1.1", "127.0.0.1", "fe80::2323:912a:177a:d8e6", "192.168.122.1")); } @Test void testTruncatedIPsAreIgnored() { - Map rhsmFacts = new HashMap<>(); - rhsmFacts.put("net.interface.eth0.ipv4_address", "192.168.1.1"); - rhsmFacts.put( - "net.interface.lo.ipv4_address", "127.0.0.1, 192.168.2.1,192.168.2.2,192...,redacted"); + Consumer consumer = new Consumer(); + consumer.setUuid(UUID.randomUUID().toString()); + consumer.getFacts().put("net.interface.eth0.ipv4_address", "192.168.1.1"); + consumer + .getFacts() + .put("net.interface.lo.ipv4_address", "127.0.0.1, 192.168.2.1,192.168.2.2,192...,redacted"); - var results = controller.extractIpAddresses(rhsmFacts); - assertEquals(4, results.size()); + var result = controller.getFactsFromConsumer(consumer); + assertEquals(4, result.getIpAddresses().size()); assertThat( - results, + result.getIpAddresses(), Matchers.containsInAnyOrder("192.168.1.1", "127.0.0.1", "192.168.2.1", "192.168.2.2")); }