Skip to content

chore: cyndi replacement os display sort and dev data#2240

Open
pindo696 wants to merge 3 commits intoRedHatInsights:masterfrom
pindo696:cyndi-os-migration
Open

chore: cyndi replacement os display sort and dev data#2240
pindo696 wants to merge 3 commits intoRedHatInsights:masterfrom
pindo696:cyndi-os-migration

Conversation

@pindo696
Copy link
Contributor

@pindo696 pindo696 commented Feb 26, 2026

Cyndi migration inventory hosts system profile os to operating system RHINENG-23544

Secure Coding Practices Checklist GitHub Link

Secure Coding Checklist

  • Input Validation
  • Output Encoding
  • Authentication and Password Management
  • Session Management
  • Access Control
  • Cryptographic Practices
  • Error Handling and Logging
  • Data Protection
  • Communication Security
  • System Configuration
  • Database Security
  • File Management
  • Memory Management
  • General Coding Practices

Summary by Sourcery

Integrate the dedicated operating_system table into queries, filtering, and development data to support the Cyndi replication replacement feature flag and improve OS display and sorting.

New Features:

  • Add support for retrieving and displaying operating system information from the operating_system table when the Cyndi replication replacement feature flag is enabled.
  • Introduce a reusable operating system display format and sort definition to standardize how OS data is shown and ordered across system and CVE views.

Enhancements:

  • Extend the Cyndi join workflow to include operating_system, allowing OS-based filters and views to work with the new data model.
  • Update RHEL version filtering to work against the operating_system model under the Cyndi replication replacement flag while preserving existing behavior otherwise.

Chores:

  • Refresh development seed data to populate the operating_system table and link system_platform rows via operating_system_id, including sequence initialization for the new table.

Cyndi migration inventory hosts system profile os to operating system
RHINENG-23544
@github-actions
Copy link
Contributor

SC Environment Impact Assessment

Overall Impact: 🟢 LOW

View full report

Summary

  • Total Issues: 3
  • 🟢 Low: 3

Detailed Findings

🟢 LOW Impact

Feature flag change detected

  • File: manager/cve_handler.py
  • Category: feature_flags
  • Details:
    • Found UNLEASH in manager/cve_handler.py at line 206
    • Found UNLEASH in manager/cve_handler.py at line 278
    • Found UNLEASH in manager/cve_handler.py at line 351
  • Recommendation: Verify feature flags are properly configured for SC Environment. Test bypass options for services not available in SC Environment.

Feature flag change detected

  • File: manager/filters.py
  • Category: feature_flags
  • Details:
    • Found UNLEASH in manager/filters.py at line 458
  • Recommendation: Verify feature flags are properly configured for SC Environment. Test bypass options for services not available in SC Environment.

Feature flag change detected

  • File: manager/system_handler.py
  • Category: feature_flags
  • Details:
    • Found UNLEASH in manager/system_handler.py at line 505
    • Found UNLEASH in manager/system_handler.py at line 530
    • Found UNLEASH in manager/system_handler.py at line 834
  • Recommendation: Verify feature flags are properly configured for SC Environment. Test bypass options for services not available in SC Environment.

Required Actions

  • Review all findings above
  • Verify SC Environment compatibility for all detected changes
  • Update deployment documentation if needed
  • Coordinate with ROSA Core team or deployment timeline

This assessment was automatically generated. Please review carefully and consult with the ROSA Core team for critical/high impact changes.

@sourcery-ai
Copy link

sourcery-ai bot commented Feb 26, 2026

Reviewer's Guide

Migrates dev data and query behavior from inventory system_profile OS fields to the normalized operating_system table behind the CYNDI_REPLICATION_REPLACEMENT feature flag, adding helper methods for OS display/sort and wiring the new join into CVE and system listing queries.

ER diagram for system_platform and operating_system relationship

erDiagram
    operating_system {
        int id PK
        string name
        int major
        int minor
        string lifecycle_phase
        bool usable_for_report
        int cves_critical
        int cves_important
        int cves_moderate
        int cves_low
        int cves_unpatched_critical
        int cves_unpatched_important
        int cves_unpatched_moderate
        int cves_unpatched_low
    }

    system_platform {
        int id PK
        uuid inventory_id
        string display_name
        int rh_account_id FK
        string s3_url
        json vmaas_json
        string json_checksum
        timestamp last_upload
        timestamp last_evaluation
        timestamp first_reported
        timestamp last_updated
        timestamp unchanged_since
        timestamp advisor_evaluated
        string advisor_checksum
        timestamp advisor_unchanged_since
        timestamp when_deleted
        timestamp culled_timestamp
        bool stale
        bool opt_out
        string host_type
        int operating_system_id FK
    }

    operating_system ||--o{ system_platform : operating_system_id
Loading

Class diagram for OperatingSystem model and sorting/display helpers

classDiagram
    class BaseModel

    class OperatingSystem {
        +int id
        +string name
        +int major
        +int minor
        +string lifecycle_phase
        +bool usable_for_report
        +int cves_critical
        +int cves_important
        +int cves_moderate
        +int cves_low
        +int cves_unpatched_critical
        +int cves_unpatched_important
        +int cves_unpatched_moderate
        +int cves_unpatched_low
        +display_os_format() SQLExpression
        +sort_columns() List~Field~
    }

    class SystemPlatform {
        +int id
        +int operating_system_id
    }

    BaseModel <|-- OperatingSystem
    OperatingSystem "1" <.. "*" SystemPlatform : referenced_by
Loading

File-Level Changes

Change Details Files
Switch OS-related dev data, filtering, display, and sorting to use the normalized operating_system table when CYNDI_REPLICATION_REPLACEMENT is enabled, while preserving legacy behavior via inventory system_profile when disabled.
  • Replace system_platform test/dev seed rows to include operating_system_id and add initial operating_system seed rows with ON CONFLICT protection, plus sequence initialization for operating_system_id_seq.
  • Extend OperatingSystem peewee model with display_os_format() to build a human-readable OS label and sort_columns() to define canonical OS sort order.
  • Update _filter_system_by_rhel_version to branch on CYNDI_REPLICATION_REPLACEMENT: use OperatingSystem.major/minor/name and lifecycle_phase when enabled, otherwise keep existing InventoryHosts.system_profile JSON-based filtering.
  • In CVE and system handlers, change OS_INFO_QUERY usage so that when CYNDI_REPLICATION_REPLACEMENT is enabled the selected os column is OperatingSystem.display_os_format(), and adjust sort column mapping to use raw SQL('os') instead of OS_INFO_SORT in that mode.
  • Update cyndi_join to left-join OperatingSystem via SystemPlatform.operating_system_id when CYNDI_REPLICATION_REPLACEMENT is enabled so OS data is available to the updated queries.
database/schema/ve_db_dev_data.sql
common/peewee_model.py
manager/filters.py
manager/cve_handler.py
manager/system_handler.py
manager/base.py

Tips and commands

Interacting with Sourcery

  • Trigger a new review: Comment @sourcery-ai review on the pull request.
  • Continue discussions: Reply directly to Sourcery's review comments.
  • Generate a GitHub issue from a review comment: Ask Sourcery to create an
    issue from a review comment by replying to it. You can also reply to a
    review comment with @sourcery-ai issue to create an issue from it.
  • Generate a pull request title: Write @sourcery-ai anywhere in the pull
    request title to generate a title at any time. You can also comment
    @sourcery-ai title on the pull request to (re-)generate the title at any time.
  • Generate a pull request summary: Write @sourcery-ai summary anywhere in
    the pull request body to generate a PR summary at any time exactly where you
    want it. You can also comment @sourcery-ai summary on the pull request to
    (re-)generate the summary at any time.
  • Generate reviewer's guide: Comment @sourcery-ai guide on the pull
    request to (re-)generate the reviewer's guide at any time.
  • Resolve all Sourcery comments: Comment @sourcery-ai resolve on the
    pull request to resolve all Sourcery comments. Useful if you've already
    addressed all the comments and don't want to see them anymore.
  • Dismiss all Sourcery reviews: Comment @sourcery-ai dismiss on the pull
    request to dismiss all existing Sourcery reviews. Especially useful if you
    want to start fresh with a new review - don't forget to comment
    @sourcery-ai review to trigger a new review!

Customizing Your Experience

Access your dashboard to:

  • Enable or disable review features such as the Sourcery-generated pull request
    summary, the reviewer's guide, and others.
  • Change the review language.
  • Add, remove or edit custom review instructions.
  • Adjust other review settings.

Getting Help

Copy link

@sourcery-ai sourcery-ai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hey - I've found 3 issues, and left some high level feedback:

  • In _filter_system_by_rhel_version, OperatingSystem.major/minor are likely integer columns but you’re comparing them to raw string values from version.split, so it would be safer to cast those to integers (or explicitly cast in SQL) to avoid subtle type-mismatch issues.
  • The expr accumulator in _filter_system_by_rhel_version is initialized as None and then immediately OR-ed (expr | ...), which can break on the first iteration; consider initializing it to a false-y Peewee expression or building a list of conditions and combining them with reduce/operator.or_.
Prompt for AI Agents
Please address the comments from this code review:

## Overall Comments
- In `_filter_system_by_rhel_version`, `OperatingSystem.major/minor` are likely integer columns but you’re comparing them to raw string values from `version.split`, so it would be safer to cast those to integers (or explicitly cast in SQL) to avoid subtle type-mismatch issues.
- The `expr` accumulator in `_filter_system_by_rhel_version` is initialized as `None` and then immediately OR-ed (`expr | ...`), which can break on the first iteration; consider initializing it to a false-y Peewee expression or building a list of conditions and combining them with `reduce`/`operator.or_`.

## Individual Comments

### Comment 1
<location path="manager/cve_handler.py" line_range="206" />
<code_context>
             "updated": SQL("updated"),
             "first_reported": SQL("first_reported"),
-            "os": OS_INFO_SORT,
+            "os": ([SQL("os")] if UNLEASH.is_enabled(CYNDI_REPLICATION_REPLACEMENT) else OS_INFO_SORT),
             "advisory_available": SQL("advisory_available"),
             "remediation": SQL("remediation_type_id"),
</code_context>
<issue_to_address>
**suggestion (bug_risk):** Use `OperatingSystem.sort_columns()` when Unleash is enabled instead of `[SQL("os")]` to keep stable sort semantics.

This change switches the `os` sort key from the structured `OS_INFO_SORT` to the derived string column `SQL("os")` when Unleash is enabled, which can change ordering semantics (e.g., string vs numeric comparison, NULL handling) and tie sorting to the display format.

Since you added `OperatingSystem.sort_columns()`, you can use it here to keep the sort based on the underlying OS structure:

```python
"os": (
    OperatingSystem.sort_columns()
    if UNLEASH.is_enabled(CYNDI_REPLICATION_REPLACEMENT)
    else OS_INFO_SORT
),
```

```suggestion
            "os": (
                OperatingSystem.sort_columns()
                if UNLEASH.is_enabled(CYNDI_REPLICATION_REPLACEMENT)
                else OS_INFO_SORT
            ),
```
</issue_to_address>

### Comment 2
<location path="manager/system_handler.py" line_range="502-506" />
<code_context>
             "updated": SQL("updated"),
             "first_reported": SQL("first_reported"),
-            "os": OS_INFO_SORT,
+            "os": ([SQL("os")] if UNLEASH.is_enabled(CYNDI_REPLICATION_REPLACEMENT) else OS_INFO_SORT),
             "advisory_available": SQL("advisory_available"),
             "remediation": SQL("remediation_type_id"),
</code_context>
<issue_to_address>
**suggestion (bug_risk):** Align system list OS sorting with `OperatingSystem.sort_columns()` instead of `SQL("os")`.

Using `[SQL("os")]` when Unleash is enabled changes sorting to use the rendered OS string, which can diverge from the previous structured sort. Since `OperatingSystem.sort_columns()` now exists, you could use:

```python
"os": (
    OperatingSystem.sort_columns()
    if UNLEASH.is_enabled(CYNDI_REPLICATION_REPLACEMENT)
    else OS_INFO_SORT
),
```

to keep ordering consistent across both paths and avoid coupling sort behavior to the display expression.

```suggestion
            "display_name": SystemPlatform.display_name,
            "rules_evaluation": SystemPlatform.advisor_evaluated,
            "updated": InventoryHosts.updated,
            "os": (
                OperatingSystem.sort_columns()
                if UNLEASH.is_enabled(CYNDI_REPLICATION_REPLACEMENT)
                else OS_INFO_SORT
            ),
        }
```
</issue_to_address>

### Comment 3
<location path="manager/filters.py" line_range="458-467" />
<code_context>

     if UNLEASH.is_enabled(CYNDI_REPLICATION_REPLACEMENT):
         query = (query.switch(SystemPlatform).join(SystemGroupSet, JOIN.LEFT_OUTER))
+        query = (query.switch(SystemPlatform).join(OperatingSystem, JOIN.LEFT_OUTER,
</code_context>
<issue_to_address>
**suggestion:** Factor out duplicated `rhel_version` loop and consider using numeric types for `major`/`minor` comparisons.

The loop building `expr` is duplicated across the `UNLEASH` branches; consider extracting it into a helper that returns `expr` so both branches share the same logic. Also, in the new branch `OperatingSystem.major`/`minor` (integers) are compared to string values from `version.split('.')`. Converting these components to integers (with basic error handling) before use would avoid relying on implicit casts and make the query’s intent and types clearer.
</issue_to_address>

Sourcery is free for open source - if you like our reviews please consider sharing them ✨
Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.

"updated": SQL("updated"),
"first_reported": SQL("first_reported"),
"os": OS_INFO_SORT,
"os": ([SQL("os")] if UNLEASH.is_enabled(CYNDI_REPLICATION_REPLACEMENT) else OS_INFO_SORT),
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

suggestion (bug_risk): Use OperatingSystem.sort_columns() when Unleash is enabled instead of [SQL("os")] to keep stable sort semantics.

This change switches the os sort key from the structured OS_INFO_SORT to the derived string column SQL("os") when Unleash is enabled, which can change ordering semantics (e.g., string vs numeric comparison, NULL handling) and tie sorting to the display format.

Since you added OperatingSystem.sort_columns(), you can use it here to keep the sort based on the underlying OS structure:

"os": (
    OperatingSystem.sort_columns()
    if UNLEASH.is_enabled(CYNDI_REPLICATION_REPLACEMENT)
    else OS_INFO_SORT
),
Suggested change
"os": ([SQL("os")] if UNLEASH.is_enabled(CYNDI_REPLICATION_REPLACEMENT) else OS_INFO_SORT),
"os": (
OperatingSystem.sort_columns()
if UNLEASH.is_enabled(CYNDI_REPLICATION_REPLACEMENT)
else OS_INFO_SORT
),

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No. The outer columns from query (in this case os) will be present and it can be referenced, but not tables such as OperatingSystem that exist only inside the query (inside the CTEs). What do you think?

Comment on lines 458 to 467
if UNLEASH.is_enabled(CYNDI_REPLICATION_REPLACEMENT):
for version in args["rhel_version"]:
if "." in version:
major, minor = version.split(".", 1)
expr = expr | \
((OperatingSystem.major == major) &
(OperatingSystem.minor == minor))
else:
expr = expr | (OperatingSystem.major == version)
query = query.where((OperatingSystem.name == "RHEL") & (OperatingSystem.lifecycle_phase == "minor") & (expr))
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

suggestion: Factor out duplicated rhel_version loop and consider using numeric types for major/minor comparisons.

The loop building expr is duplicated across the UNLEASH branches; consider extracting it into a helper that returns expr so both branches share the same logic. Also, in the new branch OperatingSystem.major/minor (integers) are compared to string values from version.split('.'). Converting these components to integers (with basic error handling) before use would avoid relying on implicit casts and make the query’s intent and types clearer.

Cyndi migration inventory hosts system profile os to operating system
RHINENG-23544

if UNLEASH.is_enabled(CYNDI_REPLICATION_REPLACEMENT):
query = (query.switch(SystemPlatform).join(SystemGroupSet, JOIN.LEFT_OUTER))
query = (query.switch(SystemPlatform).join(OperatingSystem, JOIN.LEFT_OUTER,
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The join with OperatingSystem table shouldn't be done here. This is a place where permissions to see workspaces/groups are handled.

continue
expr = cond if expr is None else expr | cond
if expr is not None:
query = query.where((OperatingSystem.name == "RHEL") & (OperatingSystem.lifecycle_phase == "minor") & (expr))
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The lifecycle_phase shouldn't be restricted to minor

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants