Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

DpdkTestpmd: Installation path cleanup #3426

Draft
wants to merge 5 commits into
base: main
Choose a base branch
from

Conversation

mcgov
Copy link
Collaborator

@mcgov mcgov commented Sep 18, 2024

The DPDK installer path has grown to be unmaintainable, this PR series cleans it up using some classes instead of just a bunch of if/else trees. The PR looks scary; but, it is just moving code to the object-oriented replacements. This means when we get to Tool.install() we can just call dpdktestpmd.installer.run_installation()

My plan is to re-use the SourceInstaller and PackageManagerInstaller logic for rdma-core, though this series only implents the classes for DPDK.

For the DPDK and rdma-core installation we have to cover multiple cases:
-install from pkg manager
-install from source

  • install from git
  • install from tar
  • (implicit) use already installed version

And we cover these cases for multiple targets:

  • debian
    • ubuntu 18.04 special casing
  • suse
  • fedora

and now need to cover all of these scenarios for both DPDK and rdma-core.

Changes:

  • Adds a generic class 'Installer' class to dpdk/common.py
  • Adds child class SourceInstall and PackageManagerInstall to dpdk/common.py
  • Adds DpdkSourceInstall and DpdkPackageManagerInstall classes to dpdk/dpdktestpmd.py
  • Adds DpdkGitInstall and DpdkTarInstall implementations of the generic DpdkSourceInstall class to dpdk/dpdktestpmd.py

This required adding some other logic:

  • a Meson tool
  • Updating the python class to allow --user pip installation
  • Ddding the -f flag to the Ln tool.

@mcgov mcgov force-pushed the mcgov/test-install branch 2 times, most recently from b0a06fa to 0eecb81 Compare September 19, 2024 00:24
@@ -60,9 +60,14 @@ def _install(self) -> bool:
self.node.os.install_packages(package_name)
return self._check_exists()

def install_packages(self, packages_name: str, install_path: str = "") -> None:
def install_packages(
self, packages_name: str, install_path: str = "", user: bool = False
Copy link
Member

Choose a reason for hiding this comment

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

user to install_to_user

if not isinstance(node.os, (Debian, Fedora, Suse)):
return False
if isinstance(node.os, Ubuntu) and node.os.information.codename == "bionic":
node.os._update_packages()
Copy link
Member

Choose a reason for hiding this comment

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

Don't call private methods out of the class.



# base class for source installs
class SourceInstall(Installer):
Copy link
Member

Choose a reason for hiding this comment

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

The sourceinstall class has no special implementation for source installation, it looks not needed.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

I'm going to leave it, even though it doesn't do much.

  • It sets _checkout_path
  • It serves as a common class for checking isinstance(installer, SourceInstall)

I'm going to re-use this code for rdma-core as well, so having common parent classes is going to be useful.


def _run_installation(self) -> None:
# define to handle any special special cases
self._setup_node()
Copy link
Member

Choose a reason for hiding this comment

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

It's called in do_installation, why call it again in _run_installation, which is called by do_installation too?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

To be honest, I wrote this one first! I'll fix it up since it probably doesn't even need to be defined now.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

fixed, thanks for taking a look!

return "meson"

@property
def exists(self) -> bool:
Copy link
Member

Choose a reason for hiding this comment

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

rewrite _check_exists is better to reuse logic.

self.packages = packages


class InstallDependencies:
Copy link
Member

Choose a reason for hiding this comment

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

define it as a function.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

I don't see an advantage of defining this as a function, I'll rename it to DependencyInstaller to make the purpose more clear.

Copy link
Member

Choose a reason for hiding this comment

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

If a class host only one function, and no data used, it should be a function. Some languages like C#, Jave are purely object oriented, so they cannot use function. But Python uses mixed functions and oo. Avoid overusing classes, so the remaining classes are easier to be understood.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

I appreciate all of that, I agree with all of what you've said. But:
The os match -> package list is data. I think it's cleaner to encapsulate these dependencies so I can pass an instance of this class to the constructor for the Dpdk___Installer later. This gives the reader all the info they need to know about what's happening.

I can make a couple of DependencyInstaller instances for source and package manager installs. In our init path I just call self._installer=DpdkGitInstaller(dependencies=dpdk_source_dependencies)
or self._installer=DpdkTarInstaller(dependencies=dpdk_source_dependencies)
or self._installer=DpdkPackageManagerInstaller(dependencies=dpdk_package_manager_dependencies)
Afterwards, in the install function I just call:

self._installer.run_installation()

If you look at the code we're replacing, this is a much more organized way to handle things. If we need to replace this later, I will go back and fix it. For now, this is how I want to handle it.

# define to handle any special special cases
self._setup_node()
# and install everything
self._install_dependencies()
Copy link
Member

Choose a reason for hiding this comment

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

It's not easy to understand, why dependencies to be installed after _run_installation. What depends on those dependencies?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Fixing this class up, I re-wrote the generic code at one point but didn't go back and fix this class to use the generic _run_installation. Will fix

if not self.git_ref:
git = self._node.tools[Git]
self.git_ref = git.get_tag(
self._source_path, filter_=r"^v.*" # starts w 'v'
Copy link
Member

Choose a reason for hiding this comment

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

It's the only deifference with the DpdkSourceInstall. The name of classes are confusing.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

We don't need to do this git ref filtering for other projects, it's project specific.

It's the only deifference with the DpdkSourceInstall.

Exactly. I only need to overload one or two functions per class, that's expected. There is a lot of shared code between the builds.

@mcgov mcgov force-pushed the mcgov/test-install branch 4 times, most recently from 51917d9 to 5468cae Compare September 19, 2024 16:48
mcgov and others added 4 commits September 19, 2024 09:52
The installer path has grown to be unmaintainable.
We cover multiple cases:
-install from pkg manager
-install from source
   - install from git
   - install from tar
- (implicit) use already installed version

for multiple targets:
- debian
   - ubuntu 18.04 special casing
- suse
- fedora

and now need to allow all of these scenarios
for both DPDK and rdma-core (and the linux kernel).

SO... It's time for a refactor.

Commit makes the 'installer' concept object-oriented.

Adds a generic class 'Installer' into common.py
Adds child class SourceInstall and PackageManagerInstall.
Adds DpdkSourceInstall and DpdkPackageManagerInstall classes.
Adds DpdkGitInstall and DpdkTarInstall implementations of the generic
parent classes.
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