fm-modulemd-resolver

Created 7 years ago
Maintained by jkaluza
Resolving dependencies between modules defined using modulemd
Members 2
Jan Kaluza committed 7 years ago

modulemd_resolver

The goal of modulemd_resolver Python library is to resolve module-level dependencies between modules the same way as DNF does for RPMs.

Basic usage

The modules metadata, and therefore also the dependencies between them, are defined using the modulemd YAML format. Therefore the modulemd_resolver works with modulemd.ModuleMetadata objects.

There are two lists into which the modulemd.ModuleMetadata objects can be added:

  • List of enabled modules - This list contains all the currently enabled modules on the system.
  • List of available modules - This list contains all available modules, which may be enabled on the system.

Once you fill-in these lists with data, you can ask modulemd_resolver to do the dependency resolving. Currently there are following methods:

  • Resolve "enable module" - returns actions which need to be done to enable module.
  • Resolve "disable module" - returns actions which need to be done to disable module.

Basic example

In this example, we create metadata for "parent" and "child" modules. Child module depends on the Parent module. We then try to get all the actions we need to do to enable the "child" module.

# Create the ModulesResolver instance.
solver = ModulesResolver()

# Create the "parent" module in version "1.0-1" in the available list.
mmd = ModuleMetadata()
mmd.name = "parent"
mmd.version = "1.0"
mmd.release = "1"
solver.add_available_mmd(mmd)

# Create the "child" module in version "1.0-1" in the available list.
mmd = ModuleMetadata()
mmd.name = "child"
mmd.version = "1.0"
mmd.release = "1"

# Set the dependency on "parent" module, minimal version 0.9.
mmd.add_requires("parent", "0.9")
solver.add_available_mmd(mmd)

# Try to get the list of all actions which need to be done to enable "child".
ret = solver.solve_enable("child")
print(ret.to_enable)

As you see here, ModulesResolver returns ModulesResolverResult object. It is described in next chapter.

ModulesResolverResult object

This object is the result of the resolving. It contains following lists:

  • to_disable - List of modules to disable to fulfill the dependencies.
  • to_enable - List of modules to enable to fulfill the dependencies.
  • to_downgrade - List of modules to dowgrade to fulfill the dependencies.
  • to_upgrade - List of modules to upgrade to fulfill the dependencies.

These lists are containing the modulemd.ModuleMetadata of modules with which you have to take the particular action to fulfill the dependencies.

In case when the resolving fails for some reason, there are one or more ModulesResolverProblem instances stored in the the problems list of ModulesResolverResult object.

ModulesResolverProblem object

This object appears in the ModulesResolverResult.problems list in case of resolving problem.

It contains basic description of the problem (desc), the name of the unknown dependency (dep) and other information about the problem.

One of the most important information is the list of solutions (solutions list). It contains the ModulesResolverSolution objects.

ModulesResolverSolution object

This object appears in the ModulesResolverProblem.solutions list in case the resolving problem has some solution.

Every ModulesResolverSolution object has following attributes:

  • desc - human-readable description of the solution
  • solution - list to pass to another call of ModulesResolver.solve_enable or ModulesResolver.solve_disable.

Example: Solve the ModulesResolverProblem

In this example, we create the child/parent modules again, but this time in the "enabled" list. This means ModulesResolver treats them as a enabled on the system.

We then try to disable "parent" module. The dependency resolving will fail, because removing "parent" also means removing "child", but we did not instruct ModulesResolver to do that.

ModulesResolver proposes us the solution - to also remove the "child". In this example, we just blindly accept this solution, but in the real application, we could just for example ask the user if he wants to remove also "child" module.

# Create the ModulesResolver instance.
solver = ModulesResolver()

# Create the "parent" module in version "1.0" in the enabled list.
mmd = ModuleMetadata()
mmd.name = "parent"
mmd.version = "1.0"
solver.add_enabled_mmd(mmd)

# Create the "child" module in version "1.0" in the enabled list.
# It also depends on the "parent" module.
mmd = ModuleMetadata()
mmd.name = "child"
mmd.version = "1.0"
mmd.add_requires("parent", "1.0")
solver.add_enabled_mmd(mmd)

# Try to disable the parent module.
ret = solver.solve_disable("parent")

# Check if we had a problem with disabling the "parent".
if len(ret.problems) != 0:
    # "package child-1.0 requires parent >= 1.0, but none of the providers can be installed"
    print ret.problems[0].desc

    if len(ret.problems[0].solutions) > 0:
        # "- allow deinstallation of child-1.0.x86_64\n"
        print ret.problems[0].solutions[0].desc

        # Apply the solution
        ret = solver.solve_disable("parent", ret.problems[0].solutions[0].solution)

        # Prints empty erray now: "[]", so success.
        print ret.problems