Skip to content

Commit

Permalink
feat: capture and send installed products ID (RedHatInsights#25)
Browse files Browse the repository at this point in the history
as comma separated string replacing current "product" label.

The ids are retrieved from:
  `subscription-manager list --installed`

the "Product ID" field - for each listed product.

Signed-off-by: Petr Vobornik <[email protected]>
  • Loading branch information
pvoborni authored Nov 23, 2023
1 parent 219a50c commit 5f5e552
Show file tree
Hide file tree
Showing 9 changed files with 105 additions and 12 deletions.
2 changes: 1 addition & 1 deletion daemon/daemon_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -442,7 +442,7 @@ func createDaemon(t *testing.T) (*Daemon, *mockNotifier, *notify.MetricsLog, *mo
HostId: "testhost-id",
ExternalOrganization: "testorg",
SocketCount: "1",
Product: "testproduct",
Product: []string{"69"},
Support: "testsupport",
Usage: "testusage",
Billing: hostinfo.BillingInfo{
Expand Down
2 changes: 1 addition & 1 deletion hostinfo/info.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ type HostInfo struct {
HostId string
ExternalOrganization string
SocketCount string
Product string
Product []string
Support string
Usage string
ConversionsSuccess string
Expand Down
2 changes: 1 addition & 1 deletion hostinfo/info_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ func TestHostInfo(t *testing.T) {
"| HostId: 01234567-89ab-cdef-0123-456789abcdef\n" +
"| ExternalOrganization: 12345678\n" +
"| SocketCount: 3\n" +
"| Product: Red Hat Enterprise Linux Server\n" +
"| Product: [394 69]\n" +
"| Support: Premium\n" +
"| Usage: Production\n" +
"| ConversionsSuccess: true\n" +
Expand Down
60 changes: 58 additions & 2 deletions hostinfo/subscription.go
Original file line number Diff line number Diff line change
Expand Up @@ -59,8 +59,10 @@ func GetSocketCount(facts SubManValues) (string, error) {
return facts.get("cpu.cpu_socket(s)")
}

func GetProduct(facts SubManValues) (string, error) {
return facts.get("distribution.name")
func GetProduct(facts SubManValues) ([]string, error) {
output, _ := execSubManCommand("list --installed")
values := parseSubManOutputMultiVal(output)
return values.get("Product ID")
}

func GetConversionsSuccess(facts SubManValues) (string, error) {
Expand Down Expand Up @@ -169,3 +171,57 @@ func (values SubManValues) get(name string) (string, error) {

return v, nil
}

func parseSubManOutputMultiVal(output string) SubManMultiValues {
values := SubManMultiValues{}
reader := strings.NewReader(output)
scanner := bufio.NewScanner(reader)

for scanner.Scan() {
line := scanner.Text()
line = strings.TrimSpace(line)

// Skip empty lines and comments
if line == "" || strings.HasPrefix(line, ";") || strings.HasPrefix(line, "#") {
continue
}

// Parse key-value pairs
parts := strings.SplitN(line, ":", 2)

if len(parts) != 2 {
continue
}

key := strings.TrimSpace(parts[0])
value := strings.TrimSpace(parts[1])
values.add(key, value)
}

return values
}

type SubManMultiValues map[string][]string

func (values SubManMultiValues) add(name string, value string) {
key := strings.ToLower(name)
v, ok := values[key]
if !ok {
values[key] = []string{value}
return
}

values[key] = append(v, value)
}

func (values SubManMultiValues) get(name string) ([]string, error) {
v, ok := values[strings.ToLower(name)]

if !ok {
err := fmt.Errorf("`%s` not found", name)
logger.Warnf("Unable to get subscription info: %s", err.Error())
return []string{}, err
}

return v, nil
}
5 changes: 3 additions & 2 deletions hostinfo/subscription_test.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package hostinfo

import (
"reflect"
"testing"
)

Expand All @@ -10,7 +11,7 @@ func TestLoadSubManInformation(t *testing.T) {
HostId: "01234567-89ab-cdef-0123-456789abcdef",
ExternalOrganization: "12345678",
SocketCount: "3",
Product: "Red Hat Enterprise Linux Server",
Product: []string{"394", "69"},
Support: "Premium",
Usage: "Production",
ConversionsSuccess: "true",
Expand Down Expand Up @@ -69,7 +70,7 @@ func compareHostInfo(t *testing.T, hi *HostInfo, expected *HostInfo) {
t.Fatalf("an unexpected value of SocketCount: %v", hi.SocketCount)
}

if hi.Product != expected.Product {
if !reflect.DeepEqual(hi.Product, expected.Product) {
t.Fatalf("an unexpected value of Product: %v", hi.Product)
}

Expand Down
37 changes: 36 additions & 1 deletion mocks/subscription-manager
Original file line number Diff line number Diff line change
Expand Up @@ -136,6 +136,30 @@ FACTS_GCP=\
"gcp_project_number: 000000000000
gcp_instance_id: 1111111111111111111"

LIST_INSTALLED=\
"+-------------------------------------------+
Installed Product Status
+-------------------------------------------+
Product Name: Red Hat Developer Tools (for RHEL Server)
Product ID: 394
Version: 2021.3
Arch: x86_64
Status: Not Subscribed
Status Details:
Starts:
Ends:
Product Name: Red Hat Enterprise Linux Server
Product ID: 69
Version: 7.9
Arch: x86_64
Status: Not Subscribed
Status Details:
Starts:
Ends:
"

show_identity() {
# Show information about the host identity.
echo "${IDENTITY}"
Expand Down Expand Up @@ -170,6 +194,11 @@ show_facts() {
esac
}

show_list_installed() {
# Show the list of installed products.
echo "${LIST_INSTALLED}"
}

hard_coded() {
# Handle the specified subscription-manager command.
case "${command:=${1}}" in
Expand All @@ -185,8 +214,14 @@ hard_coded() {
facts)
show_facts
;;
list)
show_list_installed
;;
"list --installed")
show_list_installed
;;
*)
echo "This command is not supported." >&2
echo "Unsupported command: ${command}" >&2
exit 1
;;
esac
Expand Down
2 changes: 1 addition & 1 deletion notify/policy_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -83,7 +83,7 @@ func fullyDefinedMockHostInfo() *hostinfo.HostInfo {
HostId: "hostid",
ExternalOrganization: "externalorganization",
SocketCount: "socketcount",
Product: "product",
Product: []string{"69"},
Support: "support",
Usage: "usage",
Billing: fullyDefinedMockBillingInfo(),
Expand Down
3 changes: 2 additions & 1 deletion notify/prometheus.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import (
"fmt"
"io"
"net/http"
"strings"
"time"

"github.com/RedHatInsights/host-metering/config"
Expand Down Expand Up @@ -188,7 +189,7 @@ func hostInfo2WriteRequest(hostinfo *hostinfo.HostInfo, samples []prompb.Sample)
},
{
Name: "product",
Value: hostinfo.Product,
Value: strings.Join(hostinfo.Product, ","),
},
{
Name: "socket_count",
Expand Down
4 changes: 2 additions & 2 deletions notify/prometheus_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -312,7 +312,7 @@ func TestLabels(t *testing.T) {
createRequestAndCheckLabels(t, samples, hi)

hi.HostId = ""
hi.Product = ""
hi.Product = []string{}
hi.Support = ""
createRequestAndCheckLabels(t, samples, hi)

Expand Down Expand Up @@ -491,7 +491,7 @@ func createHostInfo() *hostinfo.HostInfo {
CpuCount: 1,
HostId: "test",
SocketCount: "1",
Product: "test product",
Product: []string{"123", "456"},
Support: "test support",
Usage: "test usage",
ConversionsSuccess: "true",
Expand Down

0 comments on commit 5f5e552

Please sign in to comment.