Skip to content

Commit

Permalink
Merge 7ac3573 into 0816b98
Browse files Browse the repository at this point in the history
  • Loading branch information
chambridge committed Feb 23, 2018
2 parents 0816b98 + 7ac3573 commit 1b3bcb2
Show file tree
Hide file tree
Showing 13 changed files with 788 additions and 22 deletions.
14 changes: 13 additions & 1 deletion quipucords/api/common/util.py
Original file line number Diff line number Diff line change
Expand Up @@ -148,8 +148,20 @@ def generate_headers(fact_list, exclude=None):
"""Generate column headers from fact list."""
headers = set()
for fact in fact_list:
fact_addon = {}
for fact_key in fact.keys():
headers.add(fact_key)
if fact_key == 'products':
prods = fact.get(fact_key)
for prod in prods:
prod_name = prod.get('name')
if prod_name:
prod_name = prod_name.lower()
headers.add(prod_name)
fact_addon[prod_name] = prod.get('presence',
'unknown')
else:
headers.add(fact_key)
fact.update(fact_addon)

if exclude and isinstance(exclude, set):
headers = headers - exclude
Expand Down
12 changes: 8 additions & 4 deletions quipucords/api/fingerprint/model.py
Original file line number Diff line number Diff line change
Expand Up @@ -154,11 +154,15 @@ def __str__(self):
class Product(models.Model):
"""Represents a product."""

PRESENT = 'present'
ABSENT = 'absent'
POTENTIAL = 'potential'
UNKNOWN = 'unknown'
PRESENCE_TYPE = (
('present', 'Present'),
('absent', 'Absent'),
('potential', 'Potential'),
('unknown', 'Unknown')
(PRESENT, 'Present'),
(ABSENT, 'Absent'),
(POTENTIAL, 'Potential'),
(UNKNOWN, 'Unknown')
)

fingerprint = models.ForeignKey(SystemFingerprint,
Expand Down
2 changes: 1 addition & 1 deletion quipucords/api/report/renderer.py
Original file line number Diff line number Diff line change
Expand Up @@ -141,7 +141,7 @@ def render(self,

headers = csv_helper.generate_headers(
systems_list, exclude=set([
'id', 'report_id', 'metadata', 'products', 'entitlements']))
'id', 'report_id', 'metadata', 'entitlements']))
csv_writer.writerow(headers)
for system in systems_list:
row = []
Expand Down
2 changes: 1 addition & 1 deletion quipucords/api/report/tests_report.py
Original file line number Diff line number Diff line change
Expand Up @@ -379,7 +379,7 @@ def test_csv_renderer(self):
csv_result = renderer.render(report)

# pylint: disable=line-too-long
expected = 'Report\r\n1\r\n\r\n\r\nReport:\r\nbios_uuid,cpu_core_count,cpu_core_per_socket,cpu_count,cpu_hyperthreading,cpu_siblings,cpu_socket_count,infrastructure_type,ip_addresses,mac_addresses,name,os_name,os_release,os_version,subscription_manager_id,system_creation_date,virtualized_is_guest,virtualized_num_guests,virtualized_num_running_guests,virtualized_type,vm_cluster,vm_datacenter,vm_dns_name,vm_host,vm_host_cpu_cores,vm_host_cpu_threads,vm_host_socket_count,vm_memory_size,vm_state,vm_uuid\r\n,2,1,2,False,1,2,virtualized,,,1.2.3.4,RHEL,RHEL 7.4,7.4,,2017-07-18,True,1,1,vmware,,,,,,,,,,\r\n,2,1,2,False,1,2,virtualized,,,1.2.3.4,RHEL,RHEL 7.4,7.4,,2017-07-18,True,1,1,vmware,,,,,,,,,,\r\n,2,1,2,False,1,2,virtualized,,,1.2.3.4,RHEL,RHEL 7.5,7.5,,2017-07-18,True,1,1,vmware,,,,,,,,,,\r\n\r\n' # noqa
expected = 'Report\r\n1\r\n\r\n\r\nReport:\r\nbios_uuid,cpu_core_count,cpu_core_per_socket,cpu_count,cpu_hyperthreading,cpu_siblings,cpu_socket_count,infrastructure_type,ip_addresses,jboss brms,jboss eap,jboss fuse,mac_addresses,name,os_name,os_release,os_version,subscription_manager_id,system_creation_date,virtualized_is_guest,virtualized_num_guests,virtualized_num_running_guests,virtualized_type,vm_cluster,vm_datacenter,vm_dns_name,vm_host,vm_host_cpu_cores,vm_host_cpu_threads,vm_host_socket_count,vm_memory_size,vm_state,vm_uuid\r\n,2,1,2,False,1,2,virtualized,,absent,absent,absent,,1.2.3.4,RHEL,RHEL 7.4,7.4,,2017-07-18,True,1,1,vmware,,,,,,,,,,\r\n,2,1,2,False,1,2,virtualized,,absent,absent,absent,,1.2.3.4,RHEL,RHEL 7.4,7.4,,2017-07-18,True,1,1,vmware,,,,,,,,,,\r\n,2,1,2,False,1,2,virtualized,,absent,absent,absent,,1.2.3.4,RHEL,RHEL 7.5,7.5,,2017-07-18,True,1,1,vmware,,,,,,,,,,\r\n\r\n' # noqa
self.assertEqual(csv_result, expected)

def test_csv_renderer_only_name(self):
Expand Down
80 changes: 65 additions & 15 deletions quipucords/fingerprinter/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,11 @@
import uuid
from datetime import datetime
import django.dispatch
from api.models import FactCollection, Source
from api.models import FactCollection, Source, Product
from api.serializers import FingerprintSerializer
from fingerprinter.jboss_eap import detect_jboss_eap
from fingerprinter.jboss_fuse import detect_jboss_fuse
from fingerprinter.jboss_brms import detect_jboss_brms

logger = logging.getLogger(__name__) # pylint: disable=invalid-name

Expand All @@ -41,8 +44,10 @@
FINGERPRINT_GLOBAL_ID_KEY = 'FINGERPRINT_GLOBAL_ID'

META_DATA_KEY = 'metadata'

ENTITLEMENTS_KEY = 'entitlements'
PRODUCTS_KEY = 'products'
NAME_KEY = 'name'
PRESENCE_KEY = 'presence'


def process_fact_collection(sender, instance, **kwargs):
Expand Down Expand Up @@ -402,6 +407,7 @@ def _create_index_for_fingerprints(id_key,
return result_by_key, key_not_found_list


# pylint: disable=too-many-branches
def _merge_fingerprint(priority_fingerprint, to_merge_fingerprint):
"""Merge two fingerprints.
Expand Down Expand Up @@ -430,8 +436,31 @@ def _merge_fingerprint(priority_fingerprint, to_merge_fingerprint):
if to_merge_fingerprint.get(ENTITLEMENTS_KEY):
if ENTITLEMENTS_KEY not in priority_fingerprint:
priority_fingerprint[ENTITLEMENTS_KEY] = []
for entitlement in to_merge_fingerprint.get(ENTITLEMENTS_KEY):
priority_fingerprint[ENTITLEMENTS_KEY].append(entitlement)
priority_fingerprint[ENTITLEMENTS_KEY] += \
to_merge_fingerprint.get(ENTITLEMENTS_KEY, [])

if to_merge_fingerprint.get(PRODUCTS_KEY):
if PRODUCTS_KEY not in priority_fingerprint:
priority_fingerprint[PRODUCTS_KEY] = \
to_merge_fingerprint.get(PRODUCTS_KEY, [])
else:
merged_products = []
priority_prod_dict = {}
priority_prod = priority_fingerprint.get(PRODUCTS_KEY, [])
to_merge_prod = to_merge_fingerprint.get(PRODUCTS_KEY, [])
for prod in priority_prod:
priority_prod_dict[prod[NAME_KEY]] = prod
for prod in to_merge_prod:
merge_prod = priority_prod_dict.get(prod[NAME_KEY])
presence = merge_prod.get(PRESENCE_KEY)
if (merge_prod and presence == Product.ABSENT and
prod.get(PRESENCE_KEY) != Product.ABSENT):
priority_prod_dict[prod[NAME_KEY]] = prod
elif merge_prod is None:
priority_prod_dict[prod[NAME_KEY]] = prod
for _, prod in priority_prod_dict:
merged_products.append(prod)
priority_fingerprint[PRODUCTS_KEY] = merged_products

return priority_fingerprint

Expand Down Expand Up @@ -475,11 +504,26 @@ def add_fact_to_fingerprint(source,
}


# pylint: disable=C0103
def add_fact_entitlements_to_fingerprint(source,
raw_fact_key,
raw_fact,
fingerprint):
def add_products_to_fingerprint(source,
raw_fact,
fingerprint):
"""Create the fingerprint products with fact and metadata.
:param source: Source used to gather raw facts.
:param raw_fact: Raw fact used used to obtain value
:param fingerprint: dict containing all fingerprint facts
this fact.
"""
eap = detect_jboss_eap(source, raw_fact)
fuse = detect_jboss_fuse(source, raw_fact)
brms = detect_jboss_brms(source, raw_fact)
fingerprint['products'] = [eap, fuse, brms]


def add_entitlements_to_fingerprint(source,
raw_fact_key,
raw_fact,
fingerprint):
"""Create the fingerprint entitlements with fact and metadata.
:param source: Source used to gather raw facts.
Expand All @@ -497,9 +541,8 @@ def add_fact_entitlements_to_fingerprint(source,
actual_fact_value = None
if raw_fact.get(raw_fact_key) is not None:
actual_fact_value = raw_fact.get(raw_fact_key)

entitlements = []
if actual_fact_value is not None and isinstance(actual_fact_value, list):
entitlements = []
for entitlement in actual_fact_value:
add = False
f_ent = {}
Expand All @@ -519,6 +562,8 @@ def add_fact_entitlements_to_fingerprint(source,
entitlements.append(f_ent)

fingerprint[ENTITLEMENTS_KEY] = entitlements
else:
fingerprint[ENTITLEMENTS_KEY] = entitlements


def _process_network_fact(source, fact):
Expand Down Expand Up @@ -633,8 +678,9 @@ def _process_network_fact(source, fact):
fact, 'virtualized_num_running_guests',
fingerprint)

add_fact_entitlements_to_fingerprint(source, 'subman_consumed',
fact, fingerprint)
add_entitlements_to_fingerprint(source, 'subman_consumed',
fact, fingerprint)
add_products_to_fingerprint(source, fact, fingerprint)

return fingerprint

Expand Down Expand Up @@ -690,6 +736,9 @@ def _process_vcenter_fact(source, fact):
add_fact_to_fingerprint(source, 'vm.cluster', fact,
'vm_cluster', fingerprint)

fingerprint[ENTITLEMENTS_KEY] = []
fingerprint[PRODUCTS_KEY] = []

return fingerprint


Expand Down Expand Up @@ -748,8 +797,9 @@ def _process_satellite_fact(source, fact):
add_fact_to_fingerprint(source, 'num_sockets', fact,
'cpu_socket_count', fingerprint)

add_fact_entitlements_to_fingerprint(source, 'entitlements',
fact, fingerprint)
add_entitlements_to_fingerprint(source, 'entitlements',
fact, fingerprint)
add_products_to_fingerprint(source, fact, fingerprint)

return fingerprint

Expand Down
147 changes: 147 additions & 0 deletions quipucords/fingerprinter/jboss_brms.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,147 @@
#
# Copyright (c) 2018 Red Hat, Inc.
#
# This software is licensed to you under the GNU General Public License,
# version 3 (GPLv3). There is NO WARRANTY for this software, express or
# implied, including the implied warranties of MERCHANTABILITY or FITNESS
# FOR A PARTICULAR PURPOSE. You should have received a copy of GPLv3
# along with this software; if not, see
# https://www.gnu.org/licenses/gpl-3.0.txt.
#

"""Ingests raw facts to determine the status of JBoss BRMS for a system."""

import os
import logging
from api.models import Product, Source
from fingerprinter.utils import (strip_prefix,
strip_suffix,
product_entitlement_found)

logger = logging.getLogger(__name__) # pylint: disable=invalid-name

PRODUCT = 'JBoss BRMS'
PRESENCE_KEY = 'presence'
RAW_FACT_KEY = 'raw_fact_key'
META_DATA_KEY = 'metadata'
BUSINESS_CENTRAL_CANDIDATES = 'business_central_candidates'
KIE_SERVER_CANDIDATES = 'kie_server_candidates'
JBOSS_BRMS_MANIFEST_MF = 'jboss_brms_manifest_mf'
JBOSS_BRMS_KIE_IN_BC = 'jboss_brms_kie_in_business_central'
JBOSS_BRMS_LOCATE_KIE_API = 'jboss_brms_locate_kie_api'
SUBMAN_CONSUMED = 'subman_consumed'
ENTITLEMENTS = 'entitlements'

BRMS_CLASSIFICATIONS = {
'6.4.0.Final-redhat-3': 'BRMS 6.3.0',
'6.3.0.Final-redhat-5': 'BRMS 6.2.0',
'6.2.0.Final-redhat-4': 'BRMS 6.1.0',
'6.0.3-redhat-6': 'BRMS 6.0.3',
'6.0.3-redhat-4': 'BRMS 6.0.2',
'6.0.2-redhat-6': 'BRMS 6.0.1',
'6.0.2-redhat-2': 'BRMS 6.0.0',
'5.3.1.BRMS': 'BRMS 5.3.1',
'5.3.0.BRMS': 'BRMS 5.3.0',
'5.2.0.BRMS': 'BRMS 5.2.0',
'5.1.0.BRMS': 'BRMS 5.1.0',
'5.0.2.BRMS': 'BRMS 5.0.2',
'5.0.1.BRMS': 'BRMS 5.0.1',
'drools-core-5.0.0': 'BRMS 5.0.0',
'6.5.0.Final': 'Drools 6.5.0'
}


def classify_kie_file(pathname):
"""Classify a kie-api-* file.
:param pathname: the path to the file
:returns: a BRMS version string, or None if not a Red Hat kie file.
"""
# os.path.basename behaves differently if the last part of the
# path ends in a /, so normalize.
if pathname.endswith('/'):
pathname = pathname[:-1]

basename = os.path.basename(pathname)

version_string = strip_suffix(
strip_prefix(basename, 'kie-api-'),
'.jar')

if version_string in BRMS_CLASSIFICATIONS:
return BRMS_CLASSIFICATIONS[version_string]

if 'redhat' in version_string:
return version_string

return None


# pylint: disable=too-many-locals
def detect_jboss_brms(source, facts):
"""Detect if JBoss BRMS is present based on system facts.
:param source: The source of the facts
:param facts: facts for a system
:returns: dictionary defining the product presence
"""
business_central_candidates = facts.get(BUSINESS_CENTRAL_CANDIDATES, [])
kie_server_candidates = facts.get(KIE_SERVER_CANDIDATES, [])
manifest_mfs = facts.get(JBOSS_BRMS_MANIFEST_MF, {})
kie_in_business_central = facts.get(JBOSS_BRMS_KIE_IN_BC, [])
locate_kie_api = facts.get(JBOSS_BRMS_LOCATE_KIE_API, [])
subman_consumed = facts.get(SUBMAN_CONSUMED, [])
entitlements = facts.get(ENTITLEMENTS, [])
base_directories = set(business_central_candidates + kie_server_candidates)
kie_files = kie_in_business_central + locate_kie_api

kie_versions_by_directory = {}
for directory in base_directories:
versions_in_dir = set()
for filename in list(kie_files):
if filename.startswith(directory):
kie_files.remove(filename)
category = classify_kie_file(filename)
if category:
versions_in_dir.add(category)
# Deliberately drop files if their category is falsey,
# because it means that they are not Red Hat files.
kie_versions_by_directory[directory] = versions_in_dir

found_redhat_brms = (
any(('Red Hat' in manifest
for _, manifest in manifest_mfs.items())) or
any((version
for _, version in kie_versions_by_directory.items())))

source_object = Source.objects.filter(id=source.get('source_id')).first()
if source_object:
source_name = source_object.name
else:
source_name = None

metadata = {
'source_id': source['source_id'],
'source_name': source_name,
'source_type': source['source_type'],
}
product_dict = {'name': PRODUCT}

if found_redhat_brms:
raw_fact_key = '{}/{}/{}'.format(JBOSS_BRMS_MANIFEST_MF,
JBOSS_BRMS_KIE_IN_BC,
JBOSS_BRMS_LOCATE_KIE_API)
metadata[RAW_FACT_KEY] = raw_fact_key
product_dict[PRESENCE_KEY] = Product.PRESENT
elif product_entitlement_found(subman_consumed, PRODUCT):
metadata[RAW_FACT_KEY] = SUBMAN_CONSUMED
product_dict[PRESENCE_KEY] = Product.POTENTIAL
elif product_entitlement_found(entitlements, PRODUCT):
metadata[RAW_FACT_KEY] = ENTITLEMENTS
product_dict[PRESENCE_KEY] = Product.POTENTIAL
else:
metadata[RAW_FACT_KEY] = ''
product_dict[PRESENCE_KEY] = Product.ABSENT

product_dict[META_DATA_KEY] = metadata
return product_dict
Loading

0 comments on commit 1b3bcb2

Please sign in to comment.