I have been testing the proposed package for jammy against the test plan
and I'm happy to report that it passes validation! See the steps I took
below.

---

Install terraform and LXD

    sudo snap install terraform --classic
    sudo snap install lxd

Make sure you're in the `lxd` group:

    if ! getent group lxd | grep "$USER"; then
        sudo usermod -aG lxd "$USER"
        newgrp lxd
    fi

If you've not already set up LXD do so now:

    lxd init --minimal

Start up Landscape Server, enable autoregistration and get the
registration info.

Extract the benchmarking patch from the original test plan to a file
called `benchmark_patch.py`, which will add CPU profiling to package
reporter:

    def main():
        original_function = '''def _compute_packages_changes(self):'''

        replacement_function = '''def _compute_packages_changes(self):
            import cProfile
            import pstats
            from datetime import datetime
            import psutil

            profile = cProfile.Profile()
            process = psutil.Process()
            start_cpu_times = process.cpu_times()
            profile.enable()

            result = self.compute_packages_change_inner()

            end_cpu_times = process.cpu_times()

            profile.disable()

            user_time = end_cpu_times.user - start_cpu_times.user
            system_time = end_cpu_times.system - start_cpu_times.system
            total_cpu_time = user_time + system_time

            output_path = "/var/lib/landscape/client/result.txt"
            with open(output_path, "a") as fp:
                now = datetime.now()
                fp.write(f"\\n--------- Run on: {now.strftime('%Y-%m-%d 
%H:%M:%S')} ---------\\n\\n")
                stats = pstats.Stats(profile, stream=fp)
                stats.strip_dirs().sort_stats("cumulative").print_stats(10)
                fp.write(f"CPU Time: {total_cpu_time}s\\n")
            return result

        def compute_packages_change_inner(self):'''

        file_path = '/usr/lib/python3/dist-
packages/landscape/client/package/reporter.py'

        try:
            with open(file_path, 'r') as f:
                content = f.read()
            
            modified_content = content.replace(original_function, 
replacement_function)
            
            with open(file_path, 'w') as f:
                f.write(modified_content)

        except Exception as e:
            print(f"Error adding benchmarking: {str(e)}")

    if __name__ == "__main__":
        main()

Create a file called `main.tf` and paste the following, making sure to
replace the server/pro info. This will create two jammy instances, one
with the proposed version of Landscape Client and one with the current
version in the jammy archives. It will copy over the
`benchmark_patch.py` file to both instances and run it to add the
profiling to package reporter:

    module "landscape-client" {
        source             = "jansdhillon/landscape-client/lxd"
        pro_token          = "xxxxx" # replace with real pro token
        account_name       = "onward"
        landscape_root_url = "10.1.77.207"
        registration_key   = "key"
        image_alias        = "jammy"

        instances = [
        {
            client_config = {
                computer_title           = "jammy-lp2099283-proposed-validator"
                ping_interval            = 10
                exchange_interval        = 10
                urgent_exchange_interval = 10
            }

            landscape_client_package = "landscape-
client=23.02-0ubuntu1~22.04.6"

            files = [
                {
                content     = <<EOT
                    Package: landscape-client landscape-common
                    Pin: release a=jammy-proposed
                    Pin-Priority: 600
                    EOT
                target_path = "/etc/apt/preferences.d/proposed"
                },
                {
                source_path = "./benchmark_patch.py"
                target_path = "/tmp/benchmark_patch.py"
                }
            ]

            additional_cloud_init = <<EOT
                #cloud-config
                apt:
                    sources:
                        jammy-proposed:
                            source: "deb http://archive.ubuntu.com/ubuntu 
jammy-proposed main restricted universe multiverse"

                package-upgrades: true
                packages:
                    - python3-psutil
                runcmd:
                    - python3 /tmp/benchmark_patch.py && sleep 60
                EOT
            },

            {
            client_config = {
                computer_title           = "jammy-lp2099283-control-validator"
                ping_interval            = 10
                exchange_interval        = 10
                urgent_exchange_interval = 10
            }

            landscape_client_package = "landscape-
client=23.02-0ubuntu1~22.04.4"

            files = [
                {
                source_path = "./benchmark_patch.py"
                target_path = "/tmp/benchmark_patch.py"
                }
            ]

            additional_cloud_init = <<EOT
                #cloud-config
                package-upgrades: true
                packages:
                    - python3-psutil
                runcmd:
                    - python3 /tmp/benchmark_patch.py && sleep 60
                EOT
            },
        ]
    }

Initialize and apply the terraform plan:

    terraform init && terraform apply -auto-approve

Once both machines have registered and run their benchmarks (apply
finishes), view the results:

    printf "\njammy-lp2099283-proposed-validator: \n"
    lxc exec jammy-lp2099283-proposed-validator -- cat 
/var/lib/landscape/client/result.txt

    printf "\njammy-lp2099283-control-validator (currently in archives): \n"
    lxc exec jammy-lp2099283-control-validator -- cat 
/var/lib/landscape/client/result.txt

Example output:

    jammy-lp2099283-proposed-validator:

    --------- Run on: 2025-09-16 00:40:25 ---------

            2892510 function calls (2892506 primitive calls) in 3.308
seconds

    Ordered by: cumulative time
    List reduced from 86 to 10 due to restriction <10>

    ncalls  tottime  percall  cumtime  percall filename:lineno(function)
            1    0.245    0.245    3.308    3.308 
reporter.py:626(compute_packages_change_inner)
    123190    0.083    0.000    2.424    0.000 store.py:146(get_hash_id)
    123196    0.144    0.000    2.333    0.000 store.py:19(inner)
    123190    0.101    0.000    2.137    0.000 store.py:49(get_hash_id)
    123196    2.024    0.000    2.024    0.000 {method 'execute' of 
'sqlite3.Cursor' objects}
            1    0.000    0.000    0.394    0.394 
facade.py:183(get_locked_packages)
            1    0.025    0.025    0.394    0.394 facade.py:188(<listcomp>)
    123647    0.054    0.000    0.369    0.000 
facade.py:446(is_package_installed)
    129593    0.030    0.000    0.300    0.000 package.py:453(__eq__)
    129593    0.141    0.000    0.270    0.000 package.py:423(_cmp)


    CPU Time: 3.240000000000001s

    ...

    jammy-lp2099283-control-validator (currently in archives):

    --------- Run on: 2025-09-16 00:40:46 ---------

            2970716 function calls (2970712 primitive calls) in 33.562
seconds

    Ordered by: cumulative time
    List reduced from 137 to 10 due to restriction <10>

    ncalls  tottime  percall  cumtime  percall filename:lineno(function)
            1    0.226    0.226   33.562   33.562 
reporter.py:626(compute_packages_change_inner)
    117326    0.200    0.000   30.032    0.000 package.py:726(origins)
    159154    0.238    0.000   29.812    0.000 package.py:316(__init__)
    159154   29.574    0.000   29.574    0.000 {method 'find_index' of 
'apt_pkg.SourceList' objects}
    116412    0.098    0.000    2.659    0.000 store.py:146(get_hash_id)
    116418    0.166    0.000    2.549    0.000 store.py:19(inner)
    116412    0.114    0.000    2.316    0.000 store.py:49(get_hash_id)
    116418    2.188    0.000    2.188    0.000 {method 'execute' of 
'sqlite3.Cursor' objects}
            1    0.000    0.000    0.377    0.377 
facade.py:183(get_locked_packages)
            1    0.024    0.024    0.377    0.377 facade.py:188(<listcomp>)

    CPU Time: 32.94s

These findings validate those in the original test plan, as we see a
performance improvement of over 10x in the proposed version in this
sample run.

Additionally, I was able to install a package, remove a package and
upgrade all packages on the proposed version, through Landscape Server.

Cleanup:

    terraform destroy -auto-approve

TF module used: https://github.com/jansdhillon/terraform-lxd-landscape-
client/tree/main/examples/benchmark


** Tags removed: verification-needed-jammy
** Tags added: verification-done-jammy

-- 
You received this bug notification because you are a member of Ubuntu
Bugs, which is subscribed to Ubuntu.
https://bugs.launchpad.net/bugs/2099283

Title:
  [SRU] Update Focal, Jammy, Noble, Oracular, Plucky, Questing to reduce
  CPU usage of landscape-package-reporter

To manage notifications about this bug go to:
https://bugs.launchpad.net/landscape-client/+bug/2099283/+subscriptions


-- 
ubuntu-bugs mailing list
[email protected]
https://lists.ubuntu.com/mailman/listinfo/ubuntu-bugs

Reply via email to