diff --git a/lorax-composer/.buildinfo b/lorax-composer/.buildinfo index adfbedc3..130de2d2 100644 --- a/lorax-composer/.buildinfo +++ b/lorax-composer/.buildinfo @@ -1,4 +1,4 @@ # Sphinx build info version 1 # This file hashes the configuration used when building these files. When it is not found, a full rebuild will be done. -config: 747437aa7a4b09fa19b6c379d5c03d4c +config: 66d7c44afa220f9e7a6f61390f7186ec tags: fbb0d17656682115ca4d033fb2f83ba1 diff --git a/lorax-composer/.doctrees/composer-cli.doctree b/lorax-composer/.doctrees/composer-cli.doctree new file mode 100644 index 00000000..c5e4f7f9 Binary files /dev/null and b/lorax-composer/.doctrees/composer-cli.doctree differ diff --git a/lorax-composer/.doctrees/composer.cli.doctree b/lorax-composer/.doctrees/composer.cli.doctree index d9d21050..3a8e3b49 100644 Binary files a/lorax-composer/.doctrees/composer.cli.doctree and b/lorax-composer/.doctrees/composer.cli.doctree differ diff --git a/lorax-composer/.doctrees/composer.doctree b/lorax-composer/.doctrees/composer.doctree index 969e46c6..46f0423b 100644 Binary files a/lorax-composer/.doctrees/composer.doctree and b/lorax-composer/.doctrees/composer.doctree differ diff --git a/lorax-composer/.doctrees/environment.pickle b/lorax-composer/.doctrees/environment.pickle index 6859ce3c..73e5f721 100644 Binary files a/lorax-composer/.doctrees/environment.pickle and b/lorax-composer/.doctrees/environment.pickle differ diff --git a/lorax-composer/.doctrees/index.doctree b/lorax-composer/.doctrees/index.doctree index abf17588..00292331 100644 Binary files a/lorax-composer/.doctrees/index.doctree and b/lorax-composer/.doctrees/index.doctree differ diff --git a/lorax-composer/.doctrees/intro.doctree b/lorax-composer/.doctrees/intro.doctree index 74dca9e5..ae15758e 100644 Binary files a/lorax-composer/.doctrees/intro.doctree and b/lorax-composer/.doctrees/intro.doctree differ diff --git a/lorax-composer/.doctrees/livemedia-creator.doctree b/lorax-composer/.doctrees/livemedia-creator.doctree index 96d09883..01cd323b 100644 Binary files a/lorax-composer/.doctrees/livemedia-creator.doctree and b/lorax-composer/.doctrees/livemedia-creator.doctree differ diff --git a/lorax-composer/.doctrees/lorax-composer.doctree b/lorax-composer/.doctrees/lorax-composer.doctree index bc65bfbe..dbc6176c 100644 Binary files a/lorax-composer/.doctrees/lorax-composer.doctree and b/lorax-composer/.doctrees/lorax-composer.doctree differ diff --git a/lorax-composer/.doctrees/lorax.doctree b/lorax-composer/.doctrees/lorax.doctree index c4730d86..c87f93db 100644 Binary files a/lorax-composer/.doctrees/lorax.doctree and b/lorax-composer/.doctrees/lorax.doctree differ diff --git a/lorax-composer/.doctrees/modules.doctree b/lorax-composer/.doctrees/modules.doctree index 5560675b..01a71d71 100644 Binary files a/lorax-composer/.doctrees/modules.doctree and b/lorax-composer/.doctrees/modules.doctree differ diff --git a/lorax-composer/.doctrees/product-images.doctree b/lorax-composer/.doctrees/product-images.doctree index 494ffddb..10c47522 100644 Binary files a/lorax-composer/.doctrees/product-images.doctree and b/lorax-composer/.doctrees/product-images.doctree differ diff --git a/lorax-composer/.doctrees/pylorax.api.doctree b/lorax-composer/.doctrees/pylorax.api.doctree index bea12f12..367b13dc 100644 Binary files a/lorax-composer/.doctrees/pylorax.api.doctree and b/lorax-composer/.doctrees/pylorax.api.doctree differ diff --git a/lorax-composer/.doctrees/pylorax.doctree b/lorax-composer/.doctrees/pylorax.doctree index 8ff22e8c..9322d0eb 100644 Binary files a/lorax-composer/.doctrees/pylorax.doctree and b/lorax-composer/.doctrees/pylorax.doctree differ diff --git a/lorax-composer/_modules/composer/cli.html b/lorax-composer/_modules/composer/cli.html index 0acf770d..965dad1b 100644 --- a/lorax-composer/_modules/composer/cli.html +++ b/lorax-composer/_modules/composer/cli.html @@ -8,7 +8,7 @@ - composer.cli — Lorax 19.7.15 documentation + composer.cli — Lorax 19.7.18 documentation @@ -16,7 +16,7 @@ - + @@ -38,7 +38,7 @@
  • modules |
  • -
  • Lorax 19.7.15 documentation »
  • +
  • Lorax 19.7.18 documentation »
  • Module code »
  • @@ -49,7 +49,6 @@

    Source code for composer.cli

    -#!/usr/bin/python
     #
     # composer-cli
     #
    @@ -75,12 +74,14 @@
     from composer.cli.modules import modules_cmd
     from composer.cli.projects import projects_cmd
     from composer.cli.compose import compose_cmd
    +from composer.cli.sources import sources_cmd
     
     command_map = {
         "blueprints": blueprints_cmd,
         "modules": modules_cmd,
         "projects": projects_cmd,
    -    "compose": compose_cmd
    +    "compose": compose_cmd,
    +    "sources": sources_cmd
         }
     
     
    @@ -90,15 +91,12 @@
         :param opts: Cmdline arguments
         :type opts: argparse.Namespace
         """
    -    if len(opts.args) == 0:
    -        log.error("Missing command")
    -        return 1
    -    elif opts.args[0] not in command_map:
    +
    +    # Making sure opts.args is not empty (thus, has a command and subcommand)
    +    # is already handled in src/bin/composer-cli.
    +    if opts.args[0] not in command_map:
             log.error("Unknown command %s", opts.args[0])
             return 1
    -    if len(opts.args) == 1:
    -        log.error("Missing %s sub-command", opts.args[0])
    -        return 1
         else:
             try:
                 return command_map[opts.args[0]](opts)
    @@ -138,7 +136,7 @@
             
  • modules |
  • -
  • Lorax 19.7.15 documentation »
  • +
  • Lorax 19.7.18 documentation »
  • Module code »
  • diff --git a/lorax-composer/_modules/composer/cli/blueprints.html b/lorax-composer/_modules/composer/cli/blueprints.html index 3d61a4eb..3316c123 100644 --- a/lorax-composer/_modules/composer/cli/blueprints.html +++ b/lorax-composer/_modules/composer/cli/blueprints.html @@ -8,7 +8,7 @@ - composer.cli.blueprints — Lorax 19.7.15 documentation + composer.cli.blueprints — Lorax 19.7.18 documentation @@ -16,7 +16,7 @@ - + @@ -38,7 +38,7 @@
  • modules |
  • -
  • Lorax 19.7.15 documentation »
  • +
  • Lorax 19.7.18 documentation »
  • Module code »
  • composer.cli »
  • @@ -73,6 +73,7 @@ import json from composer import http_client as client +from composer.cli.help import blueprints_help from composer.cli.utilities import argify, frozen_toml_filename, toml_filename, handle_api_result from composer.cli.utilities import packageNEVRA @@ -100,7 +101,10 @@ "undo": blueprints_undo, "workspace": blueprints_workspace } - if opts.args[1] not in cmd_map: + if opts.args[1] == "help" or opts.args[1] == "--help": + print(blueprints_help) + return 0 + elif opts.args[1] not in cmd_map: log.error("Unknown blueprints command: %s", opts.args[1]) return 1 @@ -603,7 +607,7 @@
  • modules |
  • -
  • Lorax 19.7.15 documentation »
  • +
  • Lorax 19.7.18 documentation »
  • Module code »
  • composer.cli »
  • diff --git a/lorax-composer/_modules/composer/cli/compose.html b/lorax-composer/_modules/composer/cli/compose.html index 5b3a56ba..da427331 100644 --- a/lorax-composer/_modules/composer/cli/compose.html +++ b/lorax-composer/_modules/composer/cli/compose.html @@ -8,7 +8,7 @@ - composer.cli.compose — Lorax 19.7.15 documentation + composer.cli.compose — Lorax 19.7.18 documentation @@ -16,7 +16,7 @@ - + @@ -38,7 +38,7 @@
  • modules |
  • -
  • Lorax 19.7.15 documentation »
  • +
  • Lorax 19.7.18 documentation »
  • Module code »
  • composer.cli »
  • @@ -73,6 +73,7 @@ import json from composer import http_client as client +from composer.cli.help import compose_help from composer.cli.utilities import argify, handle_api_result, packageNEVRA
    [docs]def compose_cmd(opts): @@ -98,7 +99,10 @@ "logs": compose_logs, "image": compose_image, } - if opts.args[1] not in cmd_map: + if opts.args == "help" or opts.args == "--help": + print(compose_help) + return 0 + elif opts.args[1] not in cmd_map: log.error("Unknown compose command: %s", opts.args[1]) return 1 @@ -540,7 +544,7 @@
  • modules |
  • -
  • Lorax 19.7.15 documentation »
  • +
  • Lorax 19.7.18 documentation »
  • Module code »
  • composer.cli »
  • diff --git a/lorax-composer/_modules/composer/cli/modules.html b/lorax-composer/_modules/composer/cli/modules.html index 9854d93c..802cdcec 100644 --- a/lorax-composer/_modules/composer/cli/modules.html +++ b/lorax-composer/_modules/composer/cli/modules.html @@ -8,7 +8,7 @@ - composer.cli.modules — Lorax 19.7.15 documentation + composer.cli.modules — Lorax 19.7.18 documentation @@ -16,7 +16,7 @@ - + @@ -38,7 +38,7 @@
  • modules |
  • -
  • Lorax 19.7.15 documentation »
  • +
  • Lorax 19.7.18 documentation »
  • Module code »
  • composer.cli »
  • @@ -72,6 +72,7 @@ import json from composer import http_client as client +from composer.cli.help import modules_help
    [docs]def modules_cmd(opts): """Process modules commands @@ -81,7 +82,10 @@ :returns: Value to return from sys.exit() :rtype: int """ - if opts.args[1] != "list": + if opts.args[1] == "help" or opts.args[1] == "--help": + print(modules_help) + return 0 + elif opts.args[1] != "list": log.error("Unknown modules command: %s", opts.args[1]) return 1 @@ -127,7 +131,7 @@
  • modules |
  • -
  • Lorax 19.7.15 documentation »
  • +
  • Lorax 19.7.18 documentation »
  • Module code »
  • composer.cli »
  • diff --git a/lorax-composer/_modules/composer/cli/projects.html b/lorax-composer/_modules/composer/cli/projects.html index 8061e518..a3c22379 100644 --- a/lorax-composer/_modules/composer/cli/projects.html +++ b/lorax-composer/_modules/composer/cli/projects.html @@ -8,7 +8,7 @@ - composer.cli.projects — Lorax 19.7.15 documentation + composer.cli.projects — Lorax 19.7.18 documentation @@ -16,7 +16,7 @@ - + @@ -38,7 +38,7 @@
  • modules |
  • -
  • Lorax 19.7.15 documentation »
  • +
  • Lorax 19.7.18 documentation »
  • Module code »
  • composer.cli »
  • @@ -73,6 +73,7 @@ import textwrap from composer import http_client as client +from composer.cli.help import projects_help
    [docs]def projects_cmd(opts): """Process projects commands @@ -86,7 +87,10 @@ "list": projects_list, "info": projects_info, } - if opts.args[1] not in cmd_map: + if opts.args[1] == "help" or opts.args[1] == "--help": + print(projects_help) + return 0 + elif opts.args[1] not in cmd_map: log.error("Unknown projects command: %s", opts.args[1]) return 1 @@ -189,7 +193,7 @@
  • modules |
  • -
  • Lorax 19.7.15 documentation »
  • +
  • Lorax 19.7.18 documentation »
  • Module code »
  • composer.cli »
  • diff --git a/lorax-composer/_modules/composer/cli/recipes.html b/lorax-composer/_modules/composer/cli/recipes.html deleted file mode 100644 index b415ddf7..00000000 --- a/lorax-composer/_modules/composer/cli/recipes.html +++ /dev/null @@ -1,613 +0,0 @@ - - - - - - - - - - composer.cli.recipes — Lorax 19.7.11 documentation - - - - - - - - - - - - - - -
    -
    -
    -
    - -

    Source code for composer.cli.recipes

    -#
    -# Copyright (C) 2018  Red Hat, Inc.
    -#
    -# This program is free software; you can redistribute it and/or modify
    -# it under the terms of the GNU General Public License as published by
    -# the Free Software Foundation; either version 2 of the License, or
    -# (at your option) any later version.
    -#
    -# This program is distributed in the hope that it will be useful,
    -# but WITHOUT ANY WARRANTY; without even the implied warranty of
    -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    -# GNU General Public License for more details.
    -#
    -# You should have received a copy of the GNU General Public License
    -# along with this program.  If not, see <http://www.gnu.org/licenses/>.
    -#
    -import logging
    -log = logging.getLogger("composer-cli")
    -
    -import os
    -import json
    -
    -from composer import http_client as client
    -from composer.cli.utilities import argify, frozen_toml_filename, toml_filename, handle_api_result
    -from composer.cli.utilities import packageNEVRA
    -
    -
    [docs]def recipes_cmd(opts): - """Process recipes commands - - :param opts: Cmdline arguments - :type opts: argparse.Namespace - :returns: Value to return from sys.exit() - :rtype: int - - This dispatches the recipes commands to a function - """ - cmd_map = { - "list": recipes_list, - "show": recipes_show, - "changes": recipes_changes, - "diff": recipes_diff, - "save": recipes_save, - "delete": recipes_delete, - "depsolve": recipes_depsolve, - "push": recipes_push, - "freeze": recipes_freeze, - "tag": recipes_tag, - "undo": recipes_undo, - "workspace": recipes_workspace - } - if opts.args[1] not in cmd_map: - log.error("Unknown recipes command: %s", opts.args[1]) - return 1 - - return cmd_map[opts.args[1]](opts.socket, opts.api_version, opts.args[2:], opts.json) -
    -
    [docs]def recipes_list(socket_path, api_version, args, show_json=False): - """Output the list of available recipes - - :param socket_path: Path to the Unix socket to use for API communication - :type socket_path: str - :param api_version: Version of the API to talk to. eg. "0" - :type api_version: str - :param args: List of remaining arguments from the cmdline - :type args: list of str - :param show_json: Set to True to show the JSON output instead of the human readable output - :type show_json: bool - - recipes list - """ - api_route = client.api_url(api_version, "/recipes/list") - result = client.get_url_json(socket_path, api_route) - if show_json: - print(json.dumps(result, indent=4)) - return 0 - - print("Recipes: " + ", ".join([r for r in result["recipes"]])) - - return 0 -
    -
    [docs]def recipes_show(socket_path, api_version, args, show_json=False): - """Show the recipes, in TOML format - - :param socket_path: Path to the Unix socket to use for API communication - :type socket_path: str - :param api_version: Version of the API to talk to. eg. "0" - :type api_version: str - :param args: List of remaining arguments from the cmdline - :type args: list of str - :param show_json: Set to True to show the JSON output instead of the human readable output - :type show_json: bool - - recipes show <recipe,...> Display the recipe in TOML format. - - Multiple recipes will be separated by \n\n - """ - for recipe in argify(args): - api_route = client.api_url(api_version, "/recipes/info/%s?format=toml" % recipe) - print(client.get_url_raw(socket_path, api_route) + "\n\n") - - return 0 -
    -
    [docs]def recipes_changes(socket_path, api_version, args, show_json=False): - """Display the changes for each of the recipes - - :param socket_path: Path to the Unix socket to use for API communication - :type socket_path: str - :param api_version: Version of the API to talk to. eg. "0" - :type api_version: str - :param args: List of remaining arguments from the cmdline - :type args: list of str - :param show_json: Set to True to show the JSON output instead of the human readable output - :type show_json: bool - - recipes changes <recipe,...> Display the changes for each recipe. - """ - api_route = client.api_url(api_version, "/recipes/changes/%s" % (",".join(argify(args)))) - result = client.get_url_json(socket_path, api_route) - if show_json: - print(json.dumps(result, indent=4)) - return 0 - - for recipe in result["recipes"]: - print(recipe["name"]) - for change in recipe["changes"]: - prettyCommitDetails(change) - - return 0 -
    -
    [docs]def prettyCommitDetails(change, indent=4): - """Print the recipe's change in a nice way - - :param change: The individual recipe change dict - :type change: dict - :param indent: Number of spaces to indent - :type indent: int - """ - def revision(): - if change["revision"]: - return " revision %d" % change["revision"] - else: - return "" - - print " " * indent + change["timestamp"] + " " + change["commit"] + revision() - print " " * indent + change["message"] + "\n" -
    -
    [docs]def recipes_diff(socket_path, api_version, args, show_json=False): - """Display the differences between 2 versions of a recipe - - :param socket_path: Path to the Unix socket to use for API communication - :type socket_path: str - :param api_version: Version of the API to talk to. eg. "0" - :type api_version: str - :param args: List of remaining arguments from the cmdline - :type args: list of str - :param show_json: Set to True to show the JSON output instead of the human readable output - :type show_json: bool - - recipes diff <recipe-name> Display the differences between 2 versions of a recipe. - <from-commit> Commit hash or NEWEST - <to-commit> Commit hash, NEWEST, or WORKSPACE - """ - if len(args) == 0: - log.error("recipes diff is missing the recipe name, from commit, and to commit") - return 1 - elif len(args) == 1: - log.error("recipes diff is missing the from commit, and the to commit") - return 1 - elif len(args) == 2: - log.error("recipes diff is missing the to commit") - return 1 - - api_route = client.api_url(api_version, "/recipes/diff/%s/%s/%s" % (args[0], args[1], args[2])) - result = client.get_url_json(socket_path, api_route) - - if show_json: - print(json.dumps(result, indent=4)) - return 0 - - if result.get("error", False): - log.error(result["error"]["msg"]) - return 1 - - for diff in result["diff"]: - print(prettyDiffEntry(diff)) - - return 0 -
    -
    [docs]def prettyDiffEntry(diff): - """Generate nice diff entry string. - - :param diff: Difference entry dict - :type diff: dict - :returns: Nice string - """ - def change(diff): - if diff["old"] and diff["new"]: - return "Changed" - elif diff["new"] and not diff["old"]: - return "Added" - elif diff["old"] and not diff["new"]: - return "Removed" - else: - return "Unknown" - - def name(diff): - if diff["old"]: - return diff["old"].keys()[0] - elif diff["new"]: - return diff["new"].keys()[0] - else: - return "Unknown" - - def details(diff): - if change(diff) == "Changed": - if name(diff) == "Description": - return '"%s" -> "%s"' % (diff["old"][name(diff)], diff["old"][name(diff)]) - elif name(diff) == "Version": - return "%s -> %s" % (diff["old"][name(diff)], diff["old"][name(diff)]) - elif name(diff) in ["Module", "Package"]: - return "%s %s -> %s" % (diff["old"][name(diff)]["name"], diff["old"][name(diff)]["version"], - diff["new"][name(diff)]["version"]) - else: - return "Unknown" - elif change(diff) == "Added": - if name(diff) in ["Module", "Package"]: - return "%s %s" % (diff["new"][name(diff)]["name"], diff["new"][name(diff)]["version"]) - else: - return " ".join([diff["new"][k] for k in diff["new"]]) - elif change(diff) == "Removed": - if name(diff) in ["Module", "Package"]: - return "%s %s" % (diff["old"][name(diff)]["name"], diff["old"][name(diff)]["version"]) - else: - return " ".join([diff["old"][k] for k in diff["old"]]) - - return change(diff) + " " + name(diff) + " " + details(diff) -
    -
    [docs]def recipes_save(socket_path, api_version, args, show_json=False): - """Save the recipe to a TOML file - - :param socket_path: Path to the Unix socket to use for API communication - :type socket_path: str - :param api_version: Version of the API to talk to. eg. "0" - :type api_version: str - :param args: List of remaining arguments from the cmdline - :type args: list of str - :param show_json: Set to True to show the JSON output instead of the human readable output - :type show_json: bool - - recipes save <recipe,...> Save the recipe to a file, <recipe-name>.toml - """ - for recipe in argify(args): - api_route = client.api_url(api_version, "/recipes/info/%s?format=toml" % recipe) - recipe_toml = client.get_url_raw(socket_path, api_route) - open(toml_filename(recipe), "w").write(recipe_toml) - - return 0 -
    -
    [docs]def recipes_delete(socket_path, api_version, args, show_json=False): - """Delete a recipe from the server - - :param socket_path: Path to the Unix socket to use for API communication - :type socket_path: str - :param api_version: Version of the API to talk to. eg. "0" - :type api_version: str - :param args: List of remaining arguments from the cmdline - :type args: list of str - :param show_json: Set to True to show the JSON output instead of the human readable output - :type show_json: bool - - delete <recipe> Delete a recipe from the server - """ - api_route = client.api_url(api_version, "/recipes/delete/%s" % args[0]) - result = client.delete_url_json(socket_path, api_route) - - return handle_api_result(result, show_json) -
    -
    [docs]def recipes_depsolve(socket_path, api_version, args, show_json=False): - """Display the packages needed to install the recipe - - :param socket_path: Path to the Unix socket to use for API communication - :type socket_path: str - :param api_version: Version of the API to talk to. eg. "0" - :type api_version: str - :param args: List of remaining arguments from the cmdline - :type args: list of str - :param show_json: Set to True to show the JSON output instead of the human readable output - :type show_json: bool - - recipes depsolve <recipe,...> Display the packages needed to install the recipe. - """ - api_route = client.api_url(api_version, "/recipes/depsolve/%s" % (",".join(argify(args)))) - result = client.get_url_json(socket_path, api_route) - - if show_json: - print(json.dumps(result, indent=4)) - return 0 - - for recipe in result["recipes"]: - if recipe["recipe"].get("version", ""): - print("Recipe: %s v%s" % (recipe["recipe"]["name"], recipe["recipe"]["version"])) - else: - print("Recipe: %s" % (recipe["recipe"]["name"])) - for dep in recipe["dependencies"]: - print(" " + packageNEVRA(dep)) - - return 0 -
    -
    [docs]def recipes_push(socket_path, api_version, args, show_json=False): - """Push a recipe TOML file to the server, updating the recipe - - :param socket_path: Path to the Unix socket to use for API communication - :type socket_path: str - :param api_version: Version of the API to talk to. eg. "0" - :type api_version: str - :param args: List of remaining arguments from the cmdline - :type args: list of str - :param show_json: Set to True to show the JSON output instead of the human readable output - :type show_json: bool - - push <recipe> Push a recipe TOML file to the server. - """ - api_route = client.api_url(api_version, "/recipes/new") - rval = 0 - for recipe in argify(args): - if not os.path.exists(recipe): - log.error("Missing recipe file: %s", recipe) - continue - recipe_toml = open(recipe, "r").read() - - result = client.post_url_toml(socket_path, api_route, recipe_toml) - if handle_api_result(result, show_json): - rval = 1 - - return rval -
    -
    [docs]def recipes_freeze(socket_path, api_version, args, show_json=False): - """Handle the recipes freeze commands - - :param socket_path: Path to the Unix socket to use for API communication - :type socket_path: str - :param api_version: Version of the API to talk to. eg. "0" - :type api_version: str - :param args: List of remaining arguments from the cmdline - :type args: list of str - :param show_json: Set to True to show the JSON output instead of the human readable output - :type show_json: bool - - recipes freeze <recipe,...> Display the frozen recipe's modules and packages. - recipes freeze show <recipe,...> Display the frozen recipe in TOML format. - recipes freeze save <recipe,...> Save the frozen recipe to a file, <recipe-name>.frozen.toml. - """ - if args[0] == "show": - return recipes_freeze_show(socket_path, api_version, args[1:], show_json) - elif args[0] == "save": - return recipes_freeze_save(socket_path, api_version, args[1:], show_json) - - if len(args) == 0: - log.error("freeze is missing the recipe name") - return 1 - - api_route = client.api_url(api_version, "/recipes/freeze/%s" % (",".join(argify(args)))) - result = client.get_url_json(socket_path, api_route) - - if show_json: - print(json.dumps(result, indent=4)) - else: - for entry in result["recipes"]: - recipe = entry["recipe"] - if recipe.get("version", ""): - print("Recipe: %s v%s" % (recipe["name"], recipe["version"])) - else: - print("Recipe: %s" % (recipe["name"])) - - for m in recipe["modules"]: - print(" %s-%s" % (m["name"], m["version"])) - - for p in recipe["packages"]: - print(" %s-%s" % (p["name"], p["version"])) - - # Print any errors - for err in result.get("errors", []): - log.error("%s: %s", err["recipe"], err["msg"]) - - # Return a 1 if there are any errors - if result.get("errors", []): - return 1 - else: - return 0 -
    -
    [docs]def recipes_freeze_show(socket_path, api_version, args, show_json=False): - """Show the frozen recipe in TOML format - - :param socket_path: Path to the Unix socket to use for API communication - :type socket_path: str - :param api_version: Version of the API to talk to. eg. "0" - :type api_version: str - :param args: List of remaining arguments from the cmdline - :type args: list of str - :param show_json: Set to True to show the JSON output instead of the human readable output - :type show_json: bool - - recipes freeze show <recipe,...> Display the frozen recipe in TOML format. - """ - if len(args) == 0: - log.error("freeze show is missing the recipe name") - return 1 - - for recipe in argify(args): - api_route = client.api_url(api_version, "/recipes/freeze/%s?format=toml" % recipe) - print(client.get_url_raw(socket_path, api_route)) - - return 0 -
    -
    [docs]def recipes_freeze_save(socket_path, api_version, args, show_json=False): - """Save the frozen recipe to a TOML file - - :param socket_path: Path to the Unix socket to use for API communication - :type socket_path: str - :param api_version: Version of the API to talk to. eg. "0" - :type api_version: str - :param args: List of remaining arguments from the cmdline - :type args: list of str - :param show_json: Set to True to show the JSON output instead of the human readable output - :type show_json: bool - - recipes freeze save <recipe,...> Save the frozen recipe to a file, <recipe-name>.frozen.toml. - """ - if len(args) == 0: - log.error("freeze save is missing the recipe name") - return 1 - - for recipe in argify(args): - api_route = client.api_url(api_version, "/recipes/freeze/%s?format=toml" % recipe) - recipe_toml = client.get_url_raw(socket_path, api_route) - open(frozen_toml_filename(recipe), "w").write(recipe_toml) - - return 0 -
    -
    [docs]def recipes_tag(socket_path, api_version, args, show_json=False): - """Tag the most recent recipe commit as a release - - :param socket_path: Path to the Unix socket to use for API communication - :type socket_path: str - :param api_version: Version of the API to talk to. eg. "0" - :type api_version: str - :param args: List of remaining arguments from the cmdline - :type args: list of str - :param show_json: Set to True to show the JSON output instead of the human readable output - :type show_json: bool - - recipes tag <recipe> Tag the most recent recipe commit as a release. - """ - api_route = client.api_url(api_version, "/recipes/tag/%s" % args[0]) - result = client.post_url(socket_path, api_route, "") - - return handle_api_result(result, show_json) -
    -
    [docs]def recipes_undo(socket_path, api_version, args, show_json=False): - """Undo changes to a recipe - - :param socket_path: Path to the Unix socket to use for API communication - :type socket_path: str - :param api_version: Version of the API to talk to. eg. "0" - :type api_version: str - :param args: List of remaining arguments from the cmdline - :type args: list of str - :param show_json: Set to True to show the JSON output instead of the human readable output - :type show_json: bool - - recipes undo <recipe> <commit> Undo changes to a recipe by reverting to the selected commit. - """ - if len(args) == 0: - log.error("undo is missing the recipe name and commit hash") - return 1 - elif len(args) == 1: - log.error("undo is missing commit hash") - return 1 - - api_route = client.api_url(api_version, "/recipes/undo/%s/%s" % (args[0], args[1])) - result = client.post_url(socket_path, api_route, "") - - return handle_api_result(result, show_json) -
    -
    [docs]def recipes_workspace(socket_path, api_version, args, show_json=False): - """Push the recipe TOML to the temporary workspace storage - - :param socket_path: Path to the Unix socket to use for API communication - :type socket_path: str - :param api_version: Version of the API to talk to. eg. "0" - :type api_version: str - :param args: List of remaining arguments from the cmdline - :type args: list of str - :param show_json: Set to True to show the JSON output instead of the human readable output - :type show_json: bool - - recipes workspace <recipe> Push the recipe TOML to the temporary workspace storage. - """ - api_route = client.api_url(api_version, "/recipes/workspace") - rval = 0 - for recipe in argify(args): - if not os.path.exists(recipe): - log.error("Missing recipe file: %s", recipe) - continue - recipe_toml = open(recipe, "r").read() - - result = client.post_url_toml(socket_path, api_route, recipe_toml) - if show_json: - print(json.dumps(result, indent=4)) - elif result.get("error", False): - log.error(result["error"]["msg"]) - - # Any errors results in returning a 1, but we continue with the rest first - if not result.get("status", False): - rval = 1 - - return rval
    -
    - -
    -
    -
    -
    -
    - - -
    -
    -
    -
    - - - - \ No newline at end of file diff --git a/lorax-composer/_modules/composer/cli/sources.html b/lorax-composer/_modules/composer/cli/sources.html new file mode 100644 index 00000000..e44035d7 --- /dev/null +++ b/lorax-composer/_modules/composer/cli/sources.html @@ -0,0 +1,243 @@ + + + + + + + + + + composer.cli.sources — Lorax 19.7.18 documentation + + + + + + + + + + + + + + +
    +
    +
    +
    + +

    Source code for composer.cli.sources

    +#
    +# Copyright (C) 2018  Red Hat, Inc.
    +#
    +# This program is free software; you can redistribute it and/or modify
    +# it under the terms of the GNU General Public License as published by
    +# the Free Software Foundation; either version 2 of the License, or
    +# (at your option) any later version.
    +#
    +# This program is distributed in the hope that it will be useful,
    +# but WITHOUT ANY WARRANTY; without even the implied warranty of
    +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    +# GNU General Public License for more details.
    +#
    +# You should have received a copy of the GNU General Public License
    +# along with this program.  If not, see <http://www.gnu.org/licenses/>.
    +#
    +import logging
    +log = logging.getLogger("composer-cli")
    +
    +import os
    +import json
    +
    +from composer import http_client as client
    +from composer.cli.help import sources_help
    +from composer.cli.utilities import argify, handle_api_result
    +
    +
    [docs]def sources_cmd(opts): + """Process sources commands + + :param opts: Cmdline arguments + :type opts: argparse.Namespace + :returns: Value to return from sys.exit() + :rtype: int + """ + cmd_map = { + "list": sources_list, + "info": sources_info, + "add": sources_add, + "change": sources_add, + "delete": sources_delete, + } + if opts.args[1] == "help" or opts.args[1] == "--help": + print(sources_help) + return 0 + elif opts.args[1] not in cmd_map: + log.error("Unknown sources command: %s", opts.args[1]) + return 1 + + return cmd_map[opts.args[1]](opts.socket, opts.api_version, opts.args[2:], opts.json) +
    +
    [docs]def sources_list(socket_path, api_version, args, show_json=False): + """Output the list of available sources + + :param socket_path: Path to the Unix socket to use for API communication + :type socket_path: str + :param api_version: Version of the API to talk to. eg. "0" + :type api_version: str + :param args: List of remaining arguments from the cmdline + :type args: list of str + :param show_json: Set to True to show the JSON output instead of the human readable output + :type show_json: bool + + sources list + """ + api_route = client.api_url(api_version, "/projects/source/list") + result = client.get_url_json(socket_path, api_route) + if show_json: + print(json.dumps(result, indent=4)) + return 0 + + print("Sources: %s" % ", ".join(result["sources"])) + return 0 +
    +
    [docs]def sources_info(socket_path, api_version, args, show_json=False): + """Output info on a list of projects + + :param socket_path: Path to the Unix socket to use for API communication + :type socket_path: str + :param api_version: Version of the API to talk to. eg. "0" + :type api_version: str + :param args: List of remaining arguments from the cmdline + :type args: list of str + :param show_json: Set to True to show the JSON output instead of the human readable output + :type show_json: bool + + sources info <source-name> + """ + if len(args) == 0: + log.error("sources info is missing the name of the source") + return 1 + + if show_json: + api_route = client.api_url(api_version, "/projects/source/info/%s" % ",".join(args)) + result = client.get_url_json(socket_path, api_route) + print(json.dumps(result, indent=4)) + return 0 + else: + api_route = client.api_url(api_version, "/projects/source/info/%s?format=toml" % ",".join(args)) + result = client.get_url_raw(socket_path, api_route) + print(result) + return 0 +
    +
    [docs]def sources_add(socket_path, api_version, args, show_json=False): + """Add or change a source + + :param socket_path: Path to the Unix socket to use for API communication + :type socket_path: str + :param api_version: Version of the API to talk to. eg. "0" + :type api_version: str + :param args: List of remaining arguments from the cmdline + :type args: list of str + :param show_json: Set to True to show the JSON output instead of the human readable output + :type show_json: bool + + sources add <source.toml> + """ + api_route = client.api_url(api_version, "/projects/source/new") + rval = 0 + for source in argify(args): + if not os.path.exists(source): + log.error("Missing source file: %s", source) + continue + source_toml = open(source, "r").read() + + result = client.post_url_toml(socket_path, api_route, source_toml) + if handle_api_result(result, show_json): + rval = 1 + return rval +
    +
    [docs]def sources_delete(socket_path, api_version, args, show_json=False): + """Delete a source + + :param socket_path: Path to the Unix socket to use for API communication + :type socket_path: str + :param api_version: Version of the API to talk to. eg. "0" + :type api_version: str + :param args: List of remaining arguments from the cmdline + :type args: list of str + :param show_json: Set to True to show the JSON output instead of the human readable output + :type show_json: bool + + sources delete <source-name> + """ + api_route = client.api_url(api_version, "/projects/source/delete/%s" % args[0]) + result = client.delete_url_json(socket_path, api_route) + + return handle_api_result(result, show_json)
    +
    + +
    +
    +
    +
    +
    + + +
    +
    +
    +
    + + + + \ No newline at end of file diff --git a/lorax-composer/_modules/composer/cli/utilities.html b/lorax-composer/_modules/composer/cli/utilities.html index 8bc088af..0702e427 100644 --- a/lorax-composer/_modules/composer/cli/utilities.html +++ b/lorax-composer/_modules/composer/cli/utilities.html @@ -8,7 +8,7 @@ - composer.cli.utilities — Lorax 19.7.15 documentation + composer.cli.utilities — Lorax 19.7.18 documentation @@ -16,7 +16,7 @@ - + @@ -38,7 +38,7 @@
  • modules |
  • -
  • Lorax 19.7.15 documentation »
  • +
  • Lorax 19.7.18 documentation »
  • Module code »
  • composer.cli »
  • @@ -167,7 +167,7 @@
  • modules |
  • -
  • Lorax 19.7.15 documentation »
  • +
  • Lorax 19.7.18 documentation »
  • Module code »
  • composer.cli »
  • diff --git a/lorax-composer/_modules/composer/http_client.html b/lorax-composer/_modules/composer/http_client.html index b01fdb2d..4a276851 100644 --- a/lorax-composer/_modules/composer/http_client.html +++ b/lorax-composer/_modules/composer/http_client.html @@ -8,7 +8,7 @@ - composer.http_client — Lorax 19.7.15 documentation + composer.http_client — Lorax 19.7.18 documentation @@ -16,7 +16,7 @@ - + @@ -38,7 +38,7 @@
  • modules |
  • -
  • Lorax 19.7.15 documentation »
  • +
  • Lorax 19.7.18 documentation »
  • Module code »
  • @@ -283,7 +283,7 @@
  • modules |
  • -
  • Lorax 19.7.15 documentation »
  • +
  • Lorax 19.7.18 documentation »
  • Module code »
  • diff --git a/lorax-composer/_modules/composer/unix_socket.html b/lorax-composer/_modules/composer/unix_socket.html index e8c4d877..7453bac6 100644 --- a/lorax-composer/_modules/composer/unix_socket.html +++ b/lorax-composer/_modules/composer/unix_socket.html @@ -8,7 +8,7 @@ - composer.unix_socket — Lorax 19.7.15 documentation + composer.unix_socket — Lorax 19.7.18 documentation @@ -16,7 +16,7 @@ - + @@ -38,7 +38,7 @@
  • modules |
  • -
  • Lorax 19.7.15 documentation »
  • +
  • Lorax 19.7.18 documentation »
  • Module code »
  • @@ -143,7 +143,7 @@
  • modules |
  • -
  • Lorax 19.7.15 documentation »
  • +
  • Lorax 19.7.18 documentation »
  • Module code »
  • diff --git a/lorax-composer/_modules/index.html b/lorax-composer/_modules/index.html index a01b7f7b..624c7136 100644 --- a/lorax-composer/_modules/index.html +++ b/lorax-composer/_modules/index.html @@ -8,7 +8,7 @@ - Overview: module code — Lorax 19.7.15 documentation + Overview: module code — Lorax 19.7.18 documentation @@ -16,7 +16,7 @@ - + @@ -52,6 +52,7 @@
  • composer.cli.compose
  • composer.cli.modules
  • composer.cli.projects
  • +
  • composer.cli.sources
  • composer.cli.utilities
  • composer.http_client
  • composer.unix_socket
  • @@ -114,7 +115,7 @@
  • modules |
  • -
  • Lorax 19.7.15 documentation »
  • +
  • Lorax 19.7.18 documentation »
  • @@ -108,6 +108,8 @@ else: vernum = pylorax.version.num +DRACUT_DEFAULT = ["--xz", "--install", "/.buildstamp", "--no-early-microcode", "--add", "fips"] + # List of drivers to remove on ppc64 arch to keep initrd < 32MiB REMOVE_PPC64_DRIVERS = "floppy scsi_debug nouveau radeon cirrus mgag200" REMOVE_PPC64_MODULES = "drm plymouth" @@ -215,7 +217,8 @@ add_template_vars=None, add_arch_templates=None, add_arch_template_vars=None, - template_tempdir=None): + template_tempdir=None, + user_dracut_args=None): assert self._configured @@ -369,7 +372,13 @@ workdir=self.workdir) logger.info("rebuilding initramfs images") - dracut_args = ["--xz", "--install", "/.buildstamp", "--no-early-microcode", "--add", "fips"] + if not user_dracut_args: + dracut_args = DRACUT_DEFAULT + else: + dracut_args = [] + for arg in user_dracut_args: + dracut_args += arg.split(" ", 1) + anaconda_args = dracut_args + ["--add", "anaconda pollcdrom"] # ppc64 cannot boot an initrd > 32MiB so remove some drivers @@ -380,6 +389,8 @@ # upgrade.img anaconda_args.extend(["--omit", REMOVE_PPC64_MODULES]) + logger.info("dracut args = %s", dracut_args) + logger.info("anaconda args = %s", anaconda_args) treebuilder.rebuild_initrds(add_args=anaconda_args) if doupgrade: @@ -457,7 +468,7 @@
  • modules |
  • -
  • Lorax 19.7.15 documentation »
  • +
  • Lorax 19.7.18 documentation »
  • Module code »
  • diff --git a/lorax-composer/_modules/pylorax/__init__.html b/lorax-composer/_modules/pylorax/__init__.html deleted file mode 100644 index d59df96d..00000000 --- a/lorax-composer/_modules/pylorax/__init__.html +++ /dev/null @@ -1,471 +0,0 @@ - - - - - - - - - - pylorax.__init__ — Lorax 19.7.11 documentation - - - - - - - - - - - - - - -
    -
    -
    -
    - -

    Source code for pylorax.__init__

    -#
    -# __init__.py
    -#
    -# Copyright (C) 2010  Red Hat, Inc.
    -#
    -# This program is free software; you can redistribute it and/or modify
    -# it under the terms of the GNU General Public License as published by
    -# the Free Software Foundation; either version 2 of the License, or
    -# (at your option) any later version.
    -#
    -# This program is distributed in the hope that it will be useful,
    -# but WITHOUT ANY WARRANTY; without even the implied warranty of
    -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    -# GNU General Public License for more details.
    -#
    -# You should have received a copy of the GNU General Public License
    -# along with this program.  If not, see <http://www.gnu.org/licenses/>.
    -#
    -# Red Hat Author(s):  Martin Gracik <mgracik@redhat.com>
    -#                     David Cantrell <dcantrell@redhat.com>
    -#                     Will Woods <wwoods@redhat.com>
    -
    -# set up logging
    -import logging
    -logger = logging.getLogger("pylorax")
    -logger.addHandler(logging.NullHandler())
    -
    -import sys
    -import os
    -import ConfigParser
    -import tempfile
    -import locale
    -from subprocess import CalledProcessError
    -import selinux
    -
    -from pylorax.base import BaseLoraxClass, DataHolder
    -import pylorax.output as output
    -
    -import yum
    -import pylorax.ltmpl as ltmpl
    -
    -import pylorax.imgutils as imgutils
    -from pylorax.sysutils import joinpaths, linktree, remove
    -from rpmUtils.arch import getBaseArch
    -
    -from pylorax.treebuilder import RuntimeBuilder, TreeBuilder
    -from pylorax.buildstamp import BuildStamp
    -from pylorax.treeinfo import TreeInfo
    -from pylorax.discinfo import DiscInfo
    -from pylorax.executils import runcmd, runcmd_output
    -
    -# get lorax version
    -try:
    -    import pylorax.version
    -except ImportError:
    -    vernum = "devel"
    -else:
    -    vernum = pylorax.version.num
    -
    -# List of drivers to remove on ppc64 arch to keep initrd < 32MiB
    -REMOVE_PPC64_DRIVERS = "floppy scsi_debug nouveau radeon cirrus mgag200"
    -REMOVE_PPC64_MODULES = "drm plymouth"
    -
    -
    [docs]class ArchData(DataHolder): - lib64_arches = ("x86_64", "ppc64", "ppc64le", "s390x", "ia64", "aarch64") - bcj_arch = dict(i386="x86", x86_64="x86", - ppc="powerpc", ppc64="powerpc", ppc64le="powerpc", - arm="arm", armhfp="arm") - - def __init__(self, buildarch): - DataHolder.__init__(self, buildarch=buildarch) - self.basearch = getBaseArch(buildarch) - self.libdir = "lib64" if self.basearch in self.lib64_arches else "lib" - self.bcj = self.bcj_arch.get(self.basearch) -
    -
    [docs]class Lorax(BaseLoraxClass): - - def __init__(self): - BaseLoraxClass.__init__(self) - self._configured = False - self.conf = None - self.outputdir = None - self.workdir = None - self.inroot = None - self.arch = None - self.product = None - self.debug = False - - # set locale to C - locale.setlocale(locale.LC_ALL, 'C') - -
    [docs] def configure(self, conf_file="/etc/lorax/lorax.conf"): - self.conf = ConfigParser.SafeConfigParser() - - # set defaults - self.conf.add_section("lorax") - self.conf.set("lorax", "debug", "1") - self.conf.set("lorax", "sharedir", "/usr/share/lorax") - - self.conf.add_section("output") - self.conf.set("output", "colors", "1") - self.conf.set("output", "encoding", "utf-8") - self.conf.set("output", "ignorelist", "/usr/share/lorax/ignorelist") - - self.conf.add_section("templates") - self.conf.set("templates", "ramdisk", "ramdisk.ltmpl") - - self.conf.add_section("yum") - self.conf.set("yum", "skipbroken", "0") - - self.conf.add_section("compression") - self.conf.set("compression", "type", "xz") - self.conf.set("compression", "args", "") - self.conf.set("compression", "bcj", "on") - - # read the config file - if os.path.isfile(conf_file): - self.conf.read(conf_file) - - # set up the output - self.debug = self.conf.getboolean("lorax", "debug") - output_level = output.DEBUG if self.debug else output.INFO - - colors = self.conf.getboolean("output", "colors") - encoding = self.conf.get("output", "encoding") - - self.output.basic_config(output_level=output_level, - colors=colors, encoding=encoding) - - ignorelist = self.conf.get("output", "ignorelist") - if os.path.isfile(ignorelist): - with open(ignorelist, "r") as fobj: - for line in fobj: - line = line.strip() - if line and not line.startswith("#"): - self.output.ignore(line) - - # cron does not have sbin in PATH, - # so we have to add it ourselves - os.environ["PATH"] = "{0}:/sbin:/usr/sbin".format(os.environ["PATH"]) - - # remove some environmental variables that can cause problems with package scripts - env_remove = ('DISPLAY', 'DBUS_SESSION_BUS_ADDRESS') - _ = [os.environ.pop(k) for k in env_remove if k in os.environ] - - self._configured = True -
    -
    [docs] def init_stream_logging(self): - sh = logging.StreamHandler() - sh.setLevel(logging.INFO) - logger.addHandler(sh) -
    -
    [docs] def init_file_logging(self, logdir, logname="pylorax.log"): - fh = logging.FileHandler(filename=joinpaths(logdir, logname), mode="w") - fh.setLevel(logging.DEBUG) - logger.addHandler(fh) -
    -
    [docs] def run(self, ybo, product, version, release, variant="", bugurl="", - isfinal=False, workdir=None, outputdir=None, buildarch=None, volid=None, - domacboot=False, doupgrade=True, remove_temp=False, - installpkgs=None, - size=2, - add_templates=None, - add_template_vars=None, - add_arch_templates=None, - add_arch_template_vars=None, - template_tempdir=None): - - assert self._configured - - installpkgs = installpkgs or [] - - if domacboot: - try: - runcmd(["rpm", "-q", "hfsplus-tools"]) - except CalledProcessError: - logger.critical("you need to install hfsplus-tools to create mac images") - sys.exit(1) - - # set up work directory - self.workdir = workdir or tempfile.mkdtemp(prefix="pylorax.work.") - if not os.path.isdir(self.workdir): - os.makedirs(self.workdir) - - # set up log directory - logdir = '/var/log/lorax' - if not os.path.isdir(logdir): - os.makedirs(logdir) - - self.init_stream_logging() - self.init_file_logging(logdir) - - logger.debug("version is %s", vernum) - logger.debug("using work directory %s", self.workdir) - logger.debug("using log directory %s", logdir) - - # set up output directory - self.outputdir = outputdir or tempfile.mkdtemp(prefix="pylorax.out.") - if not os.path.isdir(self.outputdir): - os.makedirs(self.outputdir) - logger.debug("using output directory %s", self.outputdir) - - # do we have root privileges? - logger.info("checking for root privileges") - if not os.geteuid() == 0: - logger.critical("no root privileges") - sys.exit(1) - - # is selinux disabled? - # With selinux in enforcing mode the rpcbind package required for - # dracut nfs module, which is in turn required by anaconda module, - # will not get installed, because it's preinstall scriptlet fails, - # resulting in an incomplete initial ramdisk image. - # The reason is that the scriptlet runs tools from the shadow-utils - # package in chroot, particularly groupadd and useradd to add the - # required rpc group and rpc user. This operation fails, because - # the selinux context on files in the chroot, that the shadow-utils - # tools need to access (/etc/group, /etc/passwd, /etc/shadow etc.), - # is wrong and selinux therefore disallows access to these files. - logger.info("checking the selinux mode") - if selinux.is_selinux_enabled() and selinux.security_getenforce(): - logger.critical("selinux must be disabled or in Permissive mode") - sys.exit(1) - - # do we have a proper yum base object? - logger.info("checking yum base object") - if not isinstance(ybo, yum.YumBase): - logger.critical("no yum base object") - sys.exit(1) - self.inroot = ybo.conf.installroot - logger.debug("using install root: %s", self.inroot) - - if not buildarch: - buildarch = get_buildarch(ybo) - - logger.info("setting up build architecture") - self.arch = ArchData(buildarch) - for attr in ('buildarch', 'basearch', 'libdir'): - logger.debug("self.arch.%s = %s", attr, getattr(self.arch,attr)) - - logger.info("setting up build parameters") - product = DataHolder(name=product, version=version, release=release, - variant=variant, bugurl=bugurl, isfinal=isfinal) - self.product = product - logger.debug("product data: %s", product) - - # NOTE: if you change isolabel, you need to change pungi to match, or - # the pungi images won't boot. - isolabel = volid or "%s %s %s" % (self.product.name, self.product.version, - self.arch.basearch) - - if len(isolabel) > 32: - logger.fatal("the volume id cannot be longer than 32 characters") - sys.exit(1) - - templatedir = self.conf.get("lorax", "sharedir") - # NOTE: rb.root = ybo.conf.installroot (== self.inroot) - rb = RuntimeBuilder(product=self.product, arch=self.arch, - yum=ybo, templatedir=templatedir, - installpkgs=installpkgs, - add_templates=add_templates, - add_template_vars=add_template_vars) - - logger.info("installing runtime packages") - rb.yum.conf.skip_broken = self.conf.getboolean("yum", "skipbroken") - rb.install() - - # write .buildstamp - buildstamp = BuildStamp(self.product.name, self.product.version, - self.product.bugurl, self.product.isfinal, self.arch.buildarch) - - buildstamp.write(joinpaths(self.inroot, ".buildstamp")) - - if self.debug: - rb.writepkglists(joinpaths(logdir, "pkglists")) - rb.writepkgsizes(joinpaths(logdir, "original-pkgsizes.txt")) - - logger.info("doing post-install configuration") - rb.postinstall() - - # write .discinfo - discinfo = DiscInfo(self.product.release, self.arch.basearch) - discinfo.write(joinpaths(self.outputdir, ".discinfo")) - - logger.info("backing up installroot") - installroot = joinpaths(self.workdir, "installroot") - linktree(self.inroot, installroot) - - logger.info("generating kernel module metadata") - rb.generate_module_data() - - logger.info("cleaning unneeded files") - rb.cleanup() - - if self.debug: - rb.writepkgsizes(joinpaths(logdir, "final-pkgsizes.txt")) - - logger.info("creating the runtime image") - runtime = "images/install.img" - compression = self.conf.get("compression", "type") - compressargs = self.conf.get("compression", "args").split() - if self.conf.getboolean("compression", "bcj"): - if self.arch.bcj: - compressargs += ["-Xbcj", self.arch.bcj] - else: - logger.info("no BCJ filter for arch %s", self.arch.basearch) - rb.create_runtime(joinpaths(installroot,runtime), - compression=compression, compressargs=compressargs) - - logger.info("preparing to build output tree and boot images") - treebuilder = TreeBuilder(product=self.product, arch=self.arch, - inroot=installroot, outroot=self.outputdir, - runtime=runtime, isolabel=isolabel, - domacboot=domacboot, doupgrade=doupgrade, - templatedir=templatedir, - add_templates=add_arch_templates, - add_template_vars=add_arch_template_vars, - workdir=self.workdir) - - logger.info("rebuilding initramfs images") - dracut_args = ["--xz", "--install", "/.buildstamp", "--no-early-microcode", "--add", "fips"] - anaconda_args = dracut_args + ["--add", "anaconda pollcdrom"] - - # ppc64 cannot boot an initrd > 32MiB so remove some drivers - if self.arch.basearch in ("ppc64", "ppc64le"): - dracut_args.extend(["--omit-drivers", REMOVE_PPC64_DRIVERS]) - - # Only omit dracut modules from the initrd so that they're kept for - # upgrade.img - anaconda_args.extend(["--omit", REMOVE_PPC64_MODULES]) - - treebuilder.rebuild_initrds(add_args=anaconda_args) - - if doupgrade: - # Build upgrade.img. It'd be nice if these could coexist in the same - # image, but that would increase the size of the anaconda initramfs, - # which worries some people (esp. PPC tftpboot). So they're separate. - try: - # If possible, use the 'redhat-upgrade-tool' plymouth theme - themes = runcmd_output(['plymouth-set-default-theme', '--list'], - root=installroot) - if 'redhat-upgrade-tool' in themes.splitlines(): - os.environ['PLYMOUTH_THEME_NAME'] = 'redhat-upgrade-tool' - except RuntimeError: - pass - upgrade_args = dracut_args + ["--add", "system-upgrade convertfs"] - treebuilder.rebuild_initrds(add_args=upgrade_args, prefix="upgrade") - - logger.info("populating output tree and building boot images") - treebuilder.build() - - # write .treeinfo file and we're done - treeinfo = TreeInfo(self.product.name, self.product.version, - self.product.variant, self.arch.basearch) - for section, data in treebuilder.treeinfo_data.items(): - treeinfo.add_section(section, data) - treeinfo.write(joinpaths(self.outputdir, ".treeinfo")) - - # cleanup - if remove_temp: - remove(self.workdir) - -
    -
    [docs]def get_buildarch(ybo): - # get architecture of the available anaconda package - buildarch = None - for anaconda in ybo.doPackageLists(patterns=["anaconda"]).available: - if anaconda.arch != "src": - buildarch = anaconda.arch - break - if not buildarch: - logger.critical("no anaconda package in the repository") - sys.exit(1) - - return buildarch
    -
    - -
    -
    -
    -
    -
    - - -
    -
    -
    -
    - - - - \ No newline at end of file diff --git a/lorax-composer/_modules/pylorax/api.html b/lorax-composer/_modules/pylorax/api.html index 44fc3167..e1957441 100644 --- a/lorax-composer/_modules/pylorax/api.html +++ b/lorax-composer/_modules/pylorax/api.html @@ -8,7 +8,7 @@ - pylorax.api — Lorax 19.7.15 documentation + pylorax.api — Lorax 19.7.18 documentation @@ -16,7 +16,7 @@ - + @@ -38,7 +38,7 @@
  • modules |
  • -
  • Lorax 19.7.15 documentation »
  • +
  • Lorax 19.7.18 documentation »
  • Module code »
  • pylorax »
  • @@ -103,7 +103,7 @@
  • modules |
  • -
  • Lorax 19.7.15 documentation »
  • +
  • Lorax 19.7.18 documentation »
  • Module code »
  • pylorax »
  • diff --git a/lorax-composer/_modules/pylorax/api/compose.html b/lorax-composer/_modules/pylorax/api/compose.html index 3ef89a2e..c90f3ca2 100644 --- a/lorax-composer/_modules/pylorax/api/compose.html +++ b/lorax-composer/_modules/pylorax/api/compose.html @@ -8,7 +8,7 @@ - pylorax.api.compose — Lorax 19.7.15 documentation + pylorax.api.compose — Lorax 19.7.18 documentation @@ -16,7 +16,7 @@ - + @@ -38,7 +38,7 @@
  • modules |
  • -
  • Lorax 19.7.15 documentation »
  • +
  • Lorax 19.7.18 documentation »
  • Module code »
  • pylorax »
  • pylorax.api »
  • @@ -283,10 +283,9 @@ (commit_id, recipe) = read_recipe_and_id(gitlock.repo, branch, recipe_name) # Combine modules and packages and depsolve the list - # TODO include the version/glob in the depsolving - module_names = map(lambda m: m["name"], recipe["modules"] or []) - package_names = map(lambda p: p["name"], recipe["packages"] or []) - projects = sorted(set(module_names+package_names), key=lambda n: n.lower()) + module_nver = recipe.module_nver + package_nver = recipe.package_nver + projects = sorted(set(module_nver+package_nver), key=lambda p: p[0].lower()) deps = [] try: with yumlock.lock: @@ -303,9 +302,10 @@ ks_version = makeVersion(RHEL7) ks = KickstartParser(ks_version, errorsAreFatal=False, missingIncludeIsFatal=False) ks.readKickstartFromString(ks_template+"\n%end\n") + pkgs = [(name, "*") for name in ks.handler.packages.packageList] try: with yumlock.lock: - (template_size, _) = projects_depsolve_with_size(yumlock.yb, ks.handler.packages.packageList, + (template_size, _) = projects_depsolve_with_size(yumlock.yb, pkgs, with_core=not ks.handler.packages.nocore) except ProjectsError as e: log.error("start_build depsolve: %s", str(e)) @@ -587,7 +587,7 @@
  • modules |
  • -
  • Lorax 19.7.15 documentation »
  • +
  • Lorax 19.7.18 documentation »
  • Module code »
  • pylorax »
  • pylorax.api »
  • diff --git a/lorax-composer/_modules/pylorax/api/config.html b/lorax-composer/_modules/pylorax/api/config.html index 8e3fd2f6..c0ef46f3 100644 --- a/lorax-composer/_modules/pylorax/api/config.html +++ b/lorax-composer/_modules/pylorax/api/config.html @@ -8,7 +8,7 @@ - pylorax.api.config — Lorax 19.7.15 documentation + pylorax.api.config — Lorax 19.7.18 documentation @@ -16,7 +16,7 @@ - + @@ -38,7 +38,7 @@
  • modules |
  • -
  • Lorax 19.7.15 documentation »
  • +
  • Lorax 19.7.18 documentation »
  • Module code »
  • pylorax »
  • pylorax.api »
  • @@ -97,9 +97,9 @@ conf.add_section("composer") conf.set("composer", "share_dir", os.path.realpath(joinpaths(root_dir, "/usr/share/lorax/"))) conf.set("composer", "lib_dir", os.path.realpath(joinpaths(root_dir, "/var/lib/lorax/composer/"))) + conf.set("composer", "repo_dir", os.path.realpath(joinpaths(root_dir, "/var/lib/lorax/composer/repos.d/"))) conf.set("composer", "yum_conf", os.path.realpath(joinpaths(root_dir, "/var/tmp/composer/yum.conf"))) conf.set("composer", "yum_root", os.path.realpath(joinpaths(root_dir, "/var/tmp/composer/yum/root/"))) - conf.set("composer", "repo_dir", os.path.realpath(joinpaths(root_dir, "/var/tmp/composer/repos.d/"))) conf.set("composer", "cache_dir", os.path.realpath(joinpaths(root_dir, "/var/tmp/composer/cache/"))) conf.set("composer", "tmp", os.path.realpath(joinpaths(root_dir, "/var/tmp/"))) @@ -128,8 +128,11 @@ :returns: None """ for p in ["yum_conf", "repo_dir", "cache_dir", "yum_root"]: - p_dir = os.path.dirname(conf.get("composer", p)) - if not os.path.exists(p_dir): + p_dir = os.path.abspath(conf.get("composer", p)) + if p == "yum_conf": + p_dir = os.path.dirname(p_dir) + + if not os.path.isdir(p_dir): os.makedirs(p_dir)
    [docs]def make_queue_dirs(conf, gid): @@ -194,7 +197,7 @@
  • modules |
  • -
  • Lorax 19.7.15 documentation »
  • +
  • Lorax 19.7.18 documentation »
  • Module code »
  • pylorax »
  • pylorax.api »
  • diff --git a/lorax-composer/_modules/pylorax/api/crossdomain.html b/lorax-composer/_modules/pylorax/api/crossdomain.html index 7317bbda..b933a1ef 100644 --- a/lorax-composer/_modules/pylorax/api/crossdomain.html +++ b/lorax-composer/_modules/pylorax/api/crossdomain.html @@ -8,7 +8,7 @@ - pylorax.api.crossdomain — Lorax 19.7.15 documentation + pylorax.api.crossdomain — Lorax 19.7.18 documentation @@ -16,7 +16,7 @@ - + @@ -38,7 +38,7 @@
  • modules |
  • -
  • Lorax 19.7.15 documentation »
  • +
  • Lorax 19.7.18 documentation »
  • Module code »
  • pylorax »
  • pylorax.api »
  • @@ -148,7 +148,7 @@
  • modules |
  • -
  • Lorax 19.7.15 documentation »
  • +
  • Lorax 19.7.18 documentation »
  • Module code »
  • pylorax »
  • pylorax.api »
  • diff --git a/lorax-composer/_modules/pylorax/api/projects.html b/lorax-composer/_modules/pylorax/api/projects.html index a5464ea1..b4dc8779 100644 --- a/lorax-composer/_modules/pylorax/api/projects.html +++ b/lorax-composer/_modules/pylorax/api/projects.html @@ -8,7 +8,7 @@ - pylorax.api.projects — Lorax 19.7.15 documentation + pylorax.api.projects — Lorax 19.7.18 documentation @@ -16,7 +16,7 @@ - + @@ -38,7 +38,7 @@
  • modules |
  • -
  • Lorax 19.7.15 documentation »
  • +
  • Lorax 19.7.18 documentation »
  • Module code »
  • pylorax »
  • pylorax.api »
  • @@ -70,6 +70,9 @@ import logging log = logging.getLogger("lorax-composer") +import os +from ConfigParser import ConfigParser +from glob import glob import time from yum.Errors import YumBaseError @@ -239,28 +242,31 @@ return sorted(map(yaps_to_project_info, ybl.available), key=lambda p: p["name"].lower())
    -
    [docs]def projects_depsolve(yb, project_names): +
    [docs]def projects_depsolve(yb, projects): """Return the dependencies for a list of projects :param yb: yum base object :type yb: YumBase - :param project_names: The projects to find the dependencies for - :type project_names: List of Strings + :param projects: The projects and version globs to find the dependencies for + :type projects: List of tuples :returns: NEVRA's of the project and its dependencies :rtype: list of dicts + :raises: ProjectsError if there was a problem installing something """ try: # This resets the transaction yb.closeRpmDB() - for p in project_names: - yb.install(pattern=p) + for name, version in projects: + if not version: + version = "*" + yb.install(pattern="%s-%s" % (name, version)) (rc, msg) = yb.buildTransaction() if rc not in [0, 1, 2]: - raise ProjectsError("There was a problem depsolving %s: %s" % (project_names, msg)) + raise ProjectsError("There was a problem depsolving %s: %s" % (projects, msg)) yb.tsInfo.makelists() deps = sorted(map(tm_to_dep, yb.tsInfo.installed + yb.tsInfo.depinstalled), key=lambda p: p["name"].lower()) except YumBaseError as e: - raise ProjectsError("There was a problem depsolving %s: %s" % (project_names, str(e))) + raise ProjectsError("There was a problem depsolving %s: %s" % (projects, str(e))) finally: yb.closeRpmDB() return deps @@ -285,31 +291,34 @@ installed_size += p.po.installedsize return installed_size
    -
    [docs]def projects_depsolve_with_size(yb, project_names, with_core=True): +
    [docs]def projects_depsolve_with_size(yb, projects, with_core=True): """Return the dependencies and installed size for a list of projects :param yb: yum base object :type yb: YumBase - :param project_names: The projects to find the dependencies for - :type project_names: List of Strings + :param projects: The projects and version globs to find the dependencies for + :type projects: List of tuples :returns: installed size and a list of NEVRA's of the project and its dependencies :rtype: tuple of (int, list of dicts) + :raises: ProjectsError if there was a problem installing something """ try: # This resets the transaction yb.closeRpmDB() - for p in project_names: - yb.install(pattern=p) + for name, version in projects: + if not version: + version = "*" + yb.install(pattern="%s-%s" % (name, version)) if with_core: yb.selectGroup("core", group_package_types=['mandatory', 'default', 'optional']) (rc, msg) = yb.buildTransaction() if rc not in [0, 1, 2]: - raise ProjectsError("There was a problem depsolving %s: %s" % (project_names, msg)) + raise ProjectsError("There was a problem depsolving %s: %s" % (projects, msg)) yb.tsInfo.makelists() installed_size = estimate_size(yb.tsInfo.installed + yb.tsInfo.depinstalled) deps = sorted(map(tm_to_dep, yb.tsInfo.installed + yb.tsInfo.depinstalled), key=lambda p: p["name"].lower()) except YumBaseError as e: - raise ProjectsError("There was a problem depsolving %s: %s" % (project_names, str(e))) + raise ProjectsError("There was a problem depsolving %s: %s" % (projects, str(e))) finally: yb.closeRpmDB() return (installed_size, deps) @@ -359,9 +368,223 @@ modules = sorted(map(yaps_to_project, ybl.available), key=lambda p: p["name"].lower()) # Add the dependency info to each one for module in modules: - module["dependencies"] = projects_depsolve(yb, [module["name"]]) + module["dependencies"] = projects_depsolve(yb, [(module["name"], "*")]) - return modules
    + return modules +
    +
    [docs]def yum_repo_to_file_repo(repo): + """Return a string representation of a repo dict suitable for writing to a .repo file + + :param repo: Yum Repository represented as a dict + :type repo: dict + :returns: A string + :rtype: str + + The YumRepo.dump() function does not produce a string that can be used as a + yum .repo file. So do this manually with only the attributes we care about. + """ + repo_str = "[%s]\n" % repo["id"] + if repo["metalink"]: + repo_str += "metalink = %s\n" % repo["metalink"] + elif repo["mirrorlist"]: + repo_str += "mirrorlist = %s\n" % repo["mirrorlist"] + elif repo["baseurl"]: + repo_str += "baseurl = %s\n" % repo["baseurl"][0] + else: + raise RuntimeError("Repo has no baseurl, metalink, or mirrorlist") + + # proxy is optional + if "proxy" in repo: + repo_str += "proxy = %s\n" % repo["proxy"] + + repo_str += "sslverify = %s\n" % repo["sslverify"] + repo_str += "gpgcheck = %s\n" % repo["gpgcheck"] + if "gpgkey" in repo: + repo_str += "gpgkey = %s\n" % ",".join(repo["gpgkey"]) + + return repo_str +
    +
    [docs]def repo_to_source(repo, system_source): + """Return a Weldr Source dict created from the YumRepository + + :param repo: Yum Repository + :type repo: yum.yumRepo.YumRepository + :param system_source: True if this source is an immutable system source + :type system_source: bool + :returns: A dict with Weldr Source fields filled in + :rtype: dict + + Example:: + + { + "check_gpg": true, + "check_ssl": true, + "gpgkey_url": [ + "file:///etc/pki/rpm-gpg/RPM-GPG-KEY-fedora-28-x86_64" + ], + "name": "fedora", + "proxy": "http://proxy.brianlane.com:8123", + "system": true + "type": "yum-metalink", + "url": "https://mirrors.fedoraproject.org/metalink?repo=fedora-28&arch=x86_64" + } + + """ + source = {"name": repo.id, "system": system_source} + if repo.baseurl: + source["url"] = repo.baseurl[0] + source["type"] = "yum-baseurl" + elif repo.metalink: + source["url"] = repo.metalink + source["type"] = "yum-metalink" + elif repo.mirrorlist: + source["url"] = repo.mirrorlist + source["type"] = "yum-mirrorlist" + else: + raise RuntimeError("Repo has no baseurl, metalink, or mirrorlist") + + # proxy is optional + if repo.proxy: + source["proxy"] = repo.proxy + + if not repo.sslverify: + source["check_ssl"] = False + else: + source["check_ssl"] = True + + if not repo.gpgcheck: + source["check_gpg"] = False + else: + source["check_gpg"] = True + + if repo.gpgkey: + source["gpgkey_urls"] = repo.gpgkey + + return source +
    +
    [docs]def source_to_repo(source): + """Return an add_enable_repo kwargs dict created from a source dict + + :param source: A Weldr source dict + :type source: dict + :returns: A yum YumRepository object + :rtype: yum.yumRepo.YumRepository + + The dict it suitable for passing to yum's add_enable_repo function + after popping off baseurl and mirrorlist. + + Example:: + + { + "gpgcheck": True, + "sslverify": True, + "gpgkey": ["file:///etc/pki/rpm-gpg/RPM-GPG-KEY-fedora-28-x86_64"], + "id": "fedora", + "proxy": "http://proxy.brianlane.com:8123", + "baseurl": "https://mirrors.fedoraproject.org/metalink?repo=fedora-28&arch=x86_64", + "metalink": None, + "mirrorlist": None + } + + """ + repo = {"id": source["name"]} + + # This will allow errors to be raised so we can catch them + # without this they are logged, but the repo is silently disabled + repo["skip_if_unavailable"] = False + + if source["type"] == "yum-baseurl": + repo["baseurl"] = [source["url"]] + repo["metalink"] = None + repo["mirrorlist"] = None + elif source["type"] == "yum-metalink": + repo["metalink"] = source["url"] + repo["baseurl"] = [] + repo["mirrorlist"] = None + elif source["type"] == "yum-mirrorlist": + repo["mirrorlist"] = source["url"] + repo["baseurl"] = [] + repo["metalink"] = None + + if "proxy" in source: + repo["proxy"] = source["proxy"] + + if source["check_ssl"]: + repo["sslverify"] = True + else: + repo["sslverify"] = False + + if source["check_gpg"]: + repo["gpgcheck"] = True + else: + repo["gpgcheck"] = False + + if "gpgkey_urls" in source: + repo["gpgkey"] = source["gpgkey_urls"] + + return repo +
    +
    [docs]def get_source_ids(source_path): + """Return a list of the source ids in a file + + :param source_path: Full path and filename of the source (yum repo) file + :type source_path: str + :returns: A list of source id strings + :rtype: list of str + """ + if not os.path.exists(source_path): + return [] + + cfg = ConfigParser() + cfg.read(source_path) + return cfg.sections() +
    +
    [docs]def get_repo_sources(source_glob): + """Return a list of sources from a directory of yum repositories + + :param source_glob: A glob to use to match the source files, including full path + :type source_glob: str + :returns: A list of the source ids in all of the matching files + :rtype: list of str + """ + sources = [] + for f in glob(source_glob): + sources.extend(get_source_ids(f)) + return sources +
    +
    [docs]def delete_repo_source(source_glob, source_name): + """Delete a source from a repo file + + :param source_glob: A glob of the repo sources to search + :type source_glob: str + :returns: None + :raises: ProjectsError if there was a problem + + A repo file may have multiple sources in it, delete only the selected source. + If it is the last one in the file, delete the file. + + WARNING: This will delete ANY source, the caller needs to ensure that a system + source_name isn't passed to it. + """ + found = False + for f in glob(source_glob): + try: + cfg = ConfigParser() + cfg.read(f) + if source_name in cfg.sections(): + found = True + cfg.remove_section(source_name) + # If there are other sections, rewrite the file without the deleted one + if len(cfg.sections()) > 0: + with open(f, "w") as cfg_file: + cfg.write(cfg_file) + else: + # No sections left, just delete the file + os.unlink(f) + except Exception as e: + raise ProjectsError("Problem deleting repo source %s: %s" % (source_name, str(e))) + if not found: + raise ProjectsError("source %s not found" % source_name)
    @@ -395,7 +618,7 @@
  • modules |
  • -
  • Lorax 19.7.15 documentation »
  • +
  • Lorax 19.7.18 documentation »
  • Module code »
  • pylorax »
  • pylorax.api »
  • diff --git a/lorax-composer/_modules/pylorax/api/queue.html b/lorax-composer/_modules/pylorax/api/queue.html index 1c134bd1..ea11e9a8 100644 --- a/lorax-composer/_modules/pylorax/api/queue.html +++ b/lorax-composer/_modules/pylorax/api/queue.html @@ -8,7 +8,7 @@ - pylorax.api.queue — Lorax 19.7.15 documentation + pylorax.api.queue — Lorax 19.7.18 documentation @@ -16,7 +16,7 @@ - + @@ -38,7 +38,7 @@
  • modules |
  • -
  • Lorax 19.7.15 documentation »
  • +
  • Lorax 19.7.18 documentation »
  • Module code »
  • pylorax »
  • pylorax.api »
  • @@ -142,7 +142,7 @@ # Pick the oldest and move it into ./run/ if not uuids: # No composes left to process, sleep for a bit - time.sleep(30) + time.sleep(5) else: src = joinpaths(cfg.composer_dir, "queue/new", uuids[0]) dst = joinpaths(cfg.composer_dir, "queue/run", uuids[0]) @@ -245,7 +245,7 @@ test_path = joinpaths(results_dir, "TEST") if os.path.exists(test_path): # Pretend to run the compose - time.sleep(10) + time.sleep(5) try: test_mode = int(open(test_path, "r").read()) except Exception: @@ -696,7 +696,7 @@
  • modules |
  • -
  • Lorax 19.7.15 documentation »
  • +
  • Lorax 19.7.18 documentation »
  • Module code »
  • pylorax »
  • pylorax.api »
  • diff --git a/lorax-composer/_modules/pylorax/api/recipes.html b/lorax-composer/_modules/pylorax/api/recipes.html index 1acb8186..4b2827f3 100644 --- a/lorax-composer/_modules/pylorax/api/recipes.html +++ b/lorax-composer/_modules/pylorax/api/recipes.html @@ -8,7 +8,7 @@ - pylorax.api.recipes — Lorax 19.7.15 documentation + pylorax.api.recipes — Lorax 19.7.18 documentation @@ -16,7 +16,7 @@ - + @@ -38,7 +38,7 @@
  • modules |
  • -
  • Lorax 19.7.15 documentation »
  • +
  • Lorax 19.7.18 documentation »
  • Module code »
  • pylorax »
  • pylorax.api »
  • @@ -127,11 +127,21 @@ return map(lambda p: p["name"], self["packages"] or []) @property +
    [docs] def package_nver(self): + """Return the names and versions of the packages""" + return [(p["name"], p["version"]) for p in self["packages"] or []] +
    + @property
    [docs] def module_names(self): """Return the names of the modules""" return map(lambda m: m["name"], self["modules"] or [])
    @property +
    [docs] def module_nver(self): + """Return the names and versions of the modules""" + return [(m["name"], m["version"]) for m in self["modules"] or []] +
    + @property
    [docs] def filename(self): """Return the Recipe's filename @@ -777,7 +787,6 @@ :raises: Can raise errors from Ggit """ revwalk = Git.RevisionWalker.new(repo) - revwalk.set_sort_mode(Git.SortMode.TIME) branch_ref = "refs/heads/%s" % branch revwalk.push_ref(branch_ref) @@ -976,7 +985,7 @@
  • modules |
  • -
  • Lorax 19.7.15 documentation »
  • +
  • Lorax 19.7.18 documentation »
  • Module code »
  • pylorax »
  • pylorax.api »
  • diff --git a/lorax-composer/_modules/pylorax/api/server.html b/lorax-composer/_modules/pylorax/api/server.html index 0aba3c10..638b687f 100644 --- a/lorax-composer/_modules/pylorax/api/server.html +++ b/lorax-composer/_modules/pylorax/api/server.html @@ -8,7 +8,7 @@ - pylorax.api.server — Lorax 19.7.15 documentation + pylorax.api.server — Lorax 19.7.18 documentation @@ -16,7 +16,7 @@ - + @@ -38,7 +38,7 @@
  • modules |
  • -
  • Lorax 19.7.15 documentation »
  • +
  • Lorax 19.7.18 documentation »
  • Module code »
  • pylorax »
  • pylorax.api »
  • @@ -161,7 +161,7 @@
  • modules |
  • -
  • Lorax 19.7.15 documentation »
  • +
  • Lorax 19.7.18 documentation »
  • Module code »
  • pylorax »
  • pylorax.api »
  • diff --git a/lorax-composer/_modules/pylorax/api/v0.html b/lorax-composer/_modules/pylorax/api/v0.html index 0a7a6f41..8366550e 100644 --- a/lorax-composer/_modules/pylorax/api/v0.html +++ b/lorax-composer/_modules/pylorax/api/v0.html @@ -8,7 +8,7 @@ - pylorax.api.v0 — Lorax 19.7.15 documentation + pylorax.api.v0 — Lorax 19.7.18 documentation @@ -16,7 +16,7 @@ - + @@ -38,7 +38,7 @@
  • modules |
  • -
  • Lorax 19.7.15 documentation »
  • +
  • Lorax 19.7.18 documentation »
  • Module code »
  • pylorax »
  • pylorax.api »
  • @@ -554,6 +554,94 @@ ] } +`/api/v0/projects/source/list` +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + + Return the list of repositories used for depsolving and installing packages. + + Example:: + + { + "sources": [ + "fedora", + "fedora-cisco-openh264", + "fedora-updates-testing", + "fedora-updates" + ] + } + +`/api/v0/projects/source/info/<source-names>` +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + + Return information about the comma-separated list of source names. Or all of the + sources if '*' is passed. Note that general globbing is not supported, only '*'. + + immutable system sources will have the "system" field set to true. User added sources + will have it set to false. System sources cannot be changed or deleted. + + Example:: + + { + "errors": [], + "sources": { + "fedora": { + "check_gpg": true, + "check_ssl": true, + "gpgkey_urls": [ + "file:///etc/pki/rpm-gpg/RPM-GPG-KEY-fedora-28-x86_64" + ], + "name": "fedora", + "proxy": "http://proxy.brianlane.com:8123", + "system": true, + "type": "yum-metalink", + "url": "https://mirrors.fedoraproject.org/metalink?repo=fedora-28&arch=x86_64" + } + } + } + +POST `/api/v0/projects/source/new` +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + + Add (or change) a source for use when depsolving blueprints and composing images. + + The ``proxy`` and ``gpgkey_urls`` entries are optional. All of the others are required. The supported + types for the urls are: + + * ``yum-baseurl`` is a URL to a yum repository. + * ``yum-mirrorlist`` is a URL for a mirrorlist. + * ``yum-metalink`` is a URL for a metalink. + + If ``check_ssl`` is true the https certificates must be valid. If they are self-signed you can either set + this to false, or add your Certificate Authority to the host system. + + If ``check_gpg`` is true the GPG key must either be installed on the host system, or ``gpgkey_urls`` + should point to it. + + You can edit an existing source (other than system sources), by doing a POST + of the new version of the source. It will overwrite the previous one. + + Example:: + + { + "name": "custom-source-1", + "url": "https://url/path/to/repository/", + "type": "yum-baseurl", + "check_ssl": true, + "check_gpg": true, + "gpgkey_urls": [ + "https://url/path/to/gpg-key" + ] + } + +DELETE `/api/v0/projects/source/delete/<source-name>` +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + + Delete a user added source. This will fail if a system source is passed to + it. + + The response will be a status response with `status` set to true, or an + error response with it set to false and an error message included. + `/api/v0/modules/list[?offset=0&limit=20]` ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -931,17 +1019,21 @@ import os from flask import jsonify, request, Response, send_file +import pytoml as toml +from pylorax.sysutils import joinpaths from pylorax.api.compose import start_build, compose_types from pylorax.api.crossdomain import crossdomain from pylorax.api.projects import projects_list, projects_info, projects_depsolve -from pylorax.api.projects import modules_list, modules_info, ProjectsError +from pylorax.api.projects import modules_list, modules_info, ProjectsError, repo_to_source +from pylorax.api.projects import get_repo_sources, delete_repo_source, source_to_repo, yum_repo_to_file_repo from pylorax.api.queue import queue_status, build_status, uuid_delete, uuid_status, uuid_info from pylorax.api.queue import uuid_tar, uuid_image, uuid_cancel, uuid_log from pylorax.api.recipes import list_branch_files, read_recipe_commit, recipe_filename, list_commits from pylorax.api.recipes import recipe_from_dict, recipe_from_toml, commit_recipe, delete_recipe, revert_recipe from pylorax.api.recipes import tag_recipe_commit, recipe_diff from pylorax.api.workspace import workspace_read, workspace_write, workspace_delete +from pylorax.api.yumbase import update_metadata # The API functions don't actually get called by any code here # pylint: disable=unused-variable @@ -1234,9 +1326,9 @@ # Combine modules and packages and depsolve the list # TODO include the version/glob in the depsolving - module_names = blueprint.module_names - package_names = blueprint.package_names - projects = sorted(set(module_names+package_names), key=lambda n: n.lower()) + module_nver = blueprint.module_nver + package_nver = blueprint.package_nver + projects = sorted(set(module_nver+package_nver), key=lambda p: p[0].lower()) deps = [] try: with api.config["YUMLOCK"].lock: @@ -1285,10 +1377,9 @@ continue # Combine modules and packages and depsolve the list - # TODO include the version/glob in the depsolving - module_names = map(lambda m: m["name"], blueprint["modules"] or []) - package_names = map(lambda p: p["name"], blueprint["packages"] or []) - projects = sorted(set(module_names+package_names), key=lambda n: n.lower()) + module_nver = blueprint.module_nver + package_nver = blueprint.package_nver + projects = sorted(set(module_nver+package_nver), key=lambda p: p[0].lower()) deps = [] try: with api.config["YUMLOCK"].lock: @@ -1347,13 +1438,146 @@ """Return detailed information about the listed projects""" try: with api.config["YUMLOCK"].lock: - deps = projects_depsolve(api.config["YUMLOCK"].yb, project_names.split(",")) + deps = projects_depsolve(api.config["YUMLOCK"].yb, [(n, "*") for n in project_names.split(",")]) except ProjectsError as e: log.error("(v0_projects_depsolve) %s", str(e)) return jsonify(status=False, errors=[str(e)]), 400 return jsonify(projects=deps) + @api.route("/api/v0/projects/source/list") + @crossdomain(origin="*") + def v0_projects_source_list(): + """Return the list of source names""" + with api.config["YUMLOCK"].lock: + repos = list(api.config["YUMLOCK"].yb.repos.listEnabled()) + sources = sorted([r.id for r in repos]) + return jsonify(sources=sources) + + @api.route("/api/v0/projects/source/info/<source_names>") + @crossdomain(origin="*") + def v0_projects_source_info(source_names): + """Return detailed info about the list of sources""" + out_fmt = request.args.get("format", "json") + + # Return info on all of the sources + if source_names == "*": + with api.config["YUMLOCK"].lock: + source_names = ",".join(r.id for r in api.config["YUMLOCK"].yb.repos.listEnabled()) + + sources = {} + errors = [] + system_sources = get_repo_sources("/etc/yum.repos.d/*.repo") + for source in source_names.split(","): + with api.config["YUMLOCK"].lock: + repo = api.config["YUMLOCK"].yb.repos.repos.get(source, None) + if not repo: + errors.append("%s is not a valid source" % source) + continue + sources[repo.id] = repo_to_source(repo, repo.id in system_sources) + + if out_fmt == "toml" and not errors: + # With TOML output we just want to dump the raw sources, skipping the errors + return toml.dumps(sources) + elif out_fmt == "toml" and errors: + # TOML requested, but there was an error + return jsonify(status=False, errors=errors), 400 + else: + return jsonify(sources=sources, errors=errors) + + @api.route("/api/v0/projects/source/new", methods=["POST"]) + @crossdomain(origin="*") + def v0_projects_source_new(): + """Add a new package source. Or change an existing one""" + if request.headers['Content-Type'] == "text/x-toml": + source = toml.loads(request.data) + else: + source = request.get_json(cache=False) + + system_sources = get_repo_sources("/etc/yum.repos.d/*.repo") + if source["name"] in system_sources: + return jsonify(status=False, errors=["%s is a system source, it cannot be deleted." % source["name"]]), 400 + + try: + # Delete it from yum (if it exists) and replace it with the new one + with api.config["YUMLOCK"].lock: + yb = api.config["YUMLOCK"].yb + # If this repo already exists, delete it and replace it with the new one + repos = list(r.id for r in yb.repos.listEnabled()) + if source["name"] in repos: + yb.repos.delete(source["name"]) + + repo_dict = source_to_repo(source) + repoid = repo_dict.pop("id") + baseurl = repo_dict.pop("baseurl") + mirrorlist = repo_dict.pop("mirrorlist") + yb.add_enable_repo(repoid, baseurl, mirrorlist, **repo_dict) + + log.info("Updating repository metadata after adding %s", source["name"]) + update_metadata(yb) + + # Write the new repo to disk, replacing any existing ones + repo_dir = api.config["COMPOSER_CFG"].get("composer", "repo_dir") + + # Remove any previous sources with this name, ignore it if it isn't found + try: + delete_repo_source(joinpaths(repo_dir, "*.repo"), source["name"]) + except ProjectsError: + pass + + # Make sure the source name can't contain a path traversal by taking the basename + source_path = joinpaths(repo_dir, os.path.basename("%s.repo" % source["name"])) + with open(source_path, "w") as f: + f.write(yum_repo_to_file_repo(source_to_repo(source))) + except Exception as e: + log.error("(v0_projects_source_add) adding %s failed: %s", source["name"], str(e)) + + # Cleanup the mess, if loading it failed we don't want to leave it in memory + with api.config["YUMLOCK"].lock: + yb = api.config["YUMLOCK"].yb + repos = list(r.id for r in yb.repos.listEnabled()) + if source["name"] in repos: + yb.repos.delete(source["name"]) + # delete doesn't remove it from the cache used by listEnabled so we have to force it + yb.repos._cache_enabled_repos = None + + log.info("Updating repository metadata after adding %s failed", source["name"]) + update_metadata(yb) + + return jsonify(status=False, errors=[str(e)]), 400 + + return jsonify(status=True) + + @api.route("/api/v0/projects/source/delete/<source_name>", methods=["DELETE"]) + @crossdomain(origin="*") + def v0_projects_source_delete(source_name): + """Delete the named source and return a status response""" + system_sources = get_repo_sources("/etc/yum.repos.d/*.repo") + if source_name in system_sources: + return jsonify(status=False, errors=["%s is a system source, it cannot be deleted." % source_name]), 400 + share_dir = api.config["COMPOSER_CFG"].get("composer", "repo_dir") + try: + # Remove the file entry for the source + delete_repo_source(joinpaths(share_dir, "*.repo"), source_name) + + # Delete the repo + with api.config["YUMLOCK"].lock: + yb = api.config["YUMLOCK"].yb + repos = list(r.id for r in yb.repos.listEnabled()) + if source_name in repos: + yb.repos.delete(source_name) + # delete doesn't remove it from the cache used by listEnabled so we have to force it + yb.repos._cache_enabled_repos = None + + log.info("Updating repository metadata after removing %s", source_name) + update_metadata(yb) + + except ProjectsError as e: + log.error("(v0_projects_source_delete) %s", str(e)) + return jsonify(status=False, errors=[str(e)]), 400 + + return jsonify(status=True) + @api.route("/api/v0/modules/list") @api.route("/api/v0/modules/list/<module_names>") @crossdomain(origin="*") @@ -1647,7 +1871,7 @@
  • modules |
  • -
  • Lorax 19.7.15 documentation »
  • +
  • Lorax 19.7.18 documentation »
  • Module code »
  • pylorax »
  • pylorax.api »
  • diff --git a/lorax-composer/_modules/pylorax/api/workspace.html b/lorax-composer/_modules/pylorax/api/workspace.html index 6d6fcfba..3db01955 100644 --- a/lorax-composer/_modules/pylorax/api/workspace.html +++ b/lorax-composer/_modules/pylorax/api/workspace.html @@ -8,7 +8,7 @@ - pylorax.api.workspace — Lorax 19.7.15 documentation + pylorax.api.workspace — Lorax 19.7.18 documentation @@ -16,7 +16,7 @@ - + @@ -38,7 +38,7 @@
  • modules |
  • -
  • Lorax 19.7.15 documentation »
  • +
  • Lorax 19.7.18 documentation »
  • Module code »
  • pylorax »
  • pylorax.api »
  • @@ -183,7 +183,7 @@
  • modules |
  • -
  • Lorax 19.7.15 documentation »
  • +
  • Lorax 19.7.18 documentation »
  • Module code »
  • pylorax »
  • pylorax.api »
  • diff --git a/lorax-composer/_modules/pylorax/api/yumbase.html b/lorax-composer/_modules/pylorax/api/yumbase.html index 0c422d17..67890928 100644 --- a/lorax-composer/_modules/pylorax/api/yumbase.html +++ b/lorax-composer/_modules/pylorax/api/yumbase.html @@ -8,7 +8,7 @@ - pylorax.api.yumbase — Lorax 19.7.15 documentation + pylorax.api.yumbase — Lorax 19.7.18 documentation @@ -16,7 +16,7 @@ - + @@ -38,7 +38,7 @@
  • modules |
  • -
  • Lorax 19.7.15 documentation »
  • +
  • Lorax 19.7.18 documentation »
  • Module code »
  • pylorax »
  • pylorax.api »
  • @@ -156,14 +156,24 @@ # Update the metadata from the enabled repos to speed up later operations log.info("Updating yum repository metadata") + update_metadata(yb) + + return yb +
    +
    [docs]def update_metadata(yb): + """Update the metadata for all the enabled repos + + :param yb: The Yum base object + :type yb: yum.YumBase + :returns: None + :rtype: None + """ for r in yb.repos.sort(): r.metadata_expire = 0 r.mdpolicy = "group:all" yb.doRepoSetup() yb.repos.doSetup() - yb.repos.populateSack(mdtype='all', cacheonly=1) - - return yb
    + yb.repos.populateSack(mdtype='all', cacheonly=1) @@ -197,7 +207,7 @@
  • modules |
  • -
  • Lorax 19.7.15 documentation »
  • +
  • Lorax 19.7.18 documentation »
  • Module code »
  • pylorax »
  • pylorax.api »
  • diff --git a/lorax-composer/_modules/pylorax/base.html b/lorax-composer/_modules/pylorax/base.html index d88f34e3..4bce37ac 100644 --- a/lorax-composer/_modules/pylorax/base.html +++ b/lorax-composer/_modules/pylorax/base.html @@ -8,7 +8,7 @@ - pylorax.base — Lorax 19.7.15 documentation + pylorax.base — Lorax 19.7.18 documentation @@ -16,7 +16,7 @@ - + @@ -38,7 +38,7 @@
  • modules |
  • -
  • Lorax 19.7.15 documentation »
  • +
  • Lorax 19.7.18 documentation »
  • Module code »
  • pylorax »
  • @@ -150,7 +150,7 @@
  • modules |
  • -
  • Lorax 19.7.15 documentation »
  • +
  • Lorax 19.7.18 documentation »
  • Module code »
  • pylorax »
  • diff --git a/lorax-composer/_modules/pylorax/buildstamp.html b/lorax-composer/_modules/pylorax/buildstamp.html index 4fde40c2..ba0a69e3 100644 --- a/lorax-composer/_modules/pylorax/buildstamp.html +++ b/lorax-composer/_modules/pylorax/buildstamp.html @@ -8,7 +8,7 @@ - pylorax.buildstamp — Lorax 19.7.15 documentation + pylorax.buildstamp — Lorax 19.7.18 documentation @@ -16,7 +16,7 @@ - + @@ -38,7 +38,7 @@
  • modules |
  • -
  • Lorax 19.7.15 documentation »
  • +
  • Lorax 19.7.18 documentation »
  • Module code »
  • pylorax »
  • @@ -141,7 +141,7 @@
  • modules |
  • -
  • Lorax 19.7.15 documentation »
  • +
  • Lorax 19.7.18 documentation »
  • Module code »
  • pylorax »
  • diff --git a/lorax-composer/_modules/pylorax/creator.html b/lorax-composer/_modules/pylorax/creator.html index d6855121..9383b2b8 100644 --- a/lorax-composer/_modules/pylorax/creator.html +++ b/lorax-composer/_modules/pylorax/creator.html @@ -8,7 +8,7 @@ - pylorax.creator — Lorax 19.7.15 documentation + pylorax.creator — Lorax 19.7.18 documentation @@ -16,7 +16,7 @@ - + @@ -38,7 +38,7 @@
  • modules |
  • -
  • Lorax 19.7.15 documentation »
  • +
  • Lorax 19.7.18 documentation »
  • Module code »
  • pylorax »
  • @@ -697,7 +697,7 @@
  • modules |
  • -
  • Lorax 19.7.15 documentation »
  • +
  • Lorax 19.7.18 documentation »
  • Module code »
  • pylorax »
  • diff --git a/lorax-composer/_modules/pylorax/decorators.html b/lorax-composer/_modules/pylorax/decorators.html index 40d1e0f1..cd5a3f1f 100644 --- a/lorax-composer/_modules/pylorax/decorators.html +++ b/lorax-composer/_modules/pylorax/decorators.html @@ -8,7 +8,7 @@ - pylorax.decorators — Lorax 19.7.15 documentation + pylorax.decorators — Lorax 19.7.18 documentation @@ -16,7 +16,7 @@ - + @@ -38,7 +38,7 @@
  • modules |
  • -
  • Lorax 19.7.15 documentation »
  • +
  • Lorax 19.7.18 documentation »
  • Module code »
  • pylorax »
  • @@ -113,7 +113,7 @@
  • modules |
  • -
  • Lorax 19.7.15 documentation »
  • +
  • Lorax 19.7.18 documentation »
  • Module code »
  • pylorax »
  • diff --git a/lorax-composer/_modules/pylorax/discinfo.html b/lorax-composer/_modules/pylorax/discinfo.html index f2b136dc..c93a6c99 100644 --- a/lorax-composer/_modules/pylorax/discinfo.html +++ b/lorax-composer/_modules/pylorax/discinfo.html @@ -8,7 +8,7 @@ - pylorax.discinfo — Lorax 19.7.15 documentation + pylorax.discinfo — Lorax 19.7.18 documentation @@ -16,7 +16,7 @@ - + @@ -38,7 +38,7 @@
  • modules |
  • -
  • Lorax 19.7.15 documentation »
  • +
  • Lorax 19.7.18 documentation »
  • Module code »
  • pylorax »
  • @@ -122,7 +122,7 @@
  • modules |
  • -
  • Lorax 19.7.15 documentation »
  • +
  • Lorax 19.7.18 documentation »
  • Module code »
  • pylorax »
  • diff --git a/lorax-composer/_modules/pylorax/executils.html b/lorax-composer/_modules/pylorax/executils.html index 98dacd5c..e4f46e03 100644 --- a/lorax-composer/_modules/pylorax/executils.html +++ b/lorax-composer/_modules/pylorax/executils.html @@ -8,7 +8,7 @@ - pylorax.executils — Lorax 19.7.15 documentation + pylorax.executils — Lorax 19.7.18 documentation @@ -16,7 +16,7 @@ - + @@ -38,7 +38,7 @@
  • modules |
  • -
  • Lorax 19.7.15 documentation »
  • +
  • Lorax 19.7.18 documentation »
  • Module code »
  • pylorax »
  • @@ -508,7 +508,7 @@
  • modules |
  • -
  • Lorax 19.7.15 documentation »
  • +
  • Lorax 19.7.18 documentation »
  • Module code »
  • pylorax »
  • diff --git a/lorax-composer/_modules/pylorax/imgutils.html b/lorax-composer/_modules/pylorax/imgutils.html index cf0ea86b..dc206b50 100644 --- a/lorax-composer/_modules/pylorax/imgutils.html +++ b/lorax-composer/_modules/pylorax/imgutils.html @@ -8,7 +8,7 @@ - pylorax.imgutils — Lorax 19.7.15 documentation + pylorax.imgutils — Lorax 19.7.18 documentation @@ -16,7 +16,7 @@ - + @@ -38,7 +38,7 @@
  • modules |
  • -
  • Lorax 19.7.15 documentation »
  • +
  • Lorax 19.7.18 documentation »
  • Module code »
  • pylorax »
  • @@ -221,13 +221,34 @@ raise RuntimeError("Unable to setup %s on %s" % (loop_dev, outfile))
    [docs]def loop_attach(outfile): - '''Attach a loop device to the given file. Return the loop device name. - Raises CalledProcessError if losetup fails.''' - dev = runcmd_output(["losetup", "--find", "--show", outfile]) + """Attach a loop device to the given file. Return the loop device name. - # Sometimes the loop device isn't ready yet, make extra sure before returning - loop_waitfor(dev.strip(), outfile) - return dev.strip() + On rare occasions it appears that the device never shows up, some experiments + seem to indicate that it may be a race with another process using /dev/loop* devices. + + So we now try 3 times before actually failing. + + Raises CalledProcessError if losetup fails. + """ + retries = 0 + while True: + try: + retries += 1 + dev = runcmd_output(["losetup", "--find", "--show", outfile]).strip() + + # Sometimes the loop device isn't ready yet, make extra sure before returning + loop_waitfor(dev, outfile) + except CalledProcessError: + # Problems running losetup are always errors, raise immediately + raise + except RuntimeError as e: + # Try to setup the loop device 3 times + if retries == 3: + logger.error("loop_attach failed, retries exhausted.") + raise + logger.debug("Try %d failed, %s did not appear.", retries, dev) + break + return dev
    [docs]def loop_detach(loopdev): '''Detach the given loop device. Return False on failure.''' @@ -548,7 +569,7 @@
  • modules |
  • -
  • Lorax 19.7.15 documentation »
  • +
  • Lorax 19.7.18 documentation »
  • Module code »
  • pylorax »
  • diff --git a/lorax-composer/_modules/pylorax/installer.html b/lorax-composer/_modules/pylorax/installer.html index 1d54c67b..e1d8a8ea 100644 --- a/lorax-composer/_modules/pylorax/installer.html +++ b/lorax-composer/_modules/pylorax/installer.html @@ -8,7 +8,7 @@ - pylorax.installer — Lorax 19.7.15 documentation + pylorax.installer — Lorax 19.7.18 documentation @@ -16,7 +16,7 @@ - + @@ -38,7 +38,7 @@
  • modules |
  • -
  • Lorax 19.7.15 documentation »
  • +
  • Lorax 19.7.18 documentation »
  • Module code »
  • pylorax »
  • @@ -122,7 +122,8 @@ self.mount_dir = self.initrd_path kernel_list = [("/isolinux/vmlinuz", "/isolinux/initrd.img"), - ("/ppc/ppc64/vmlinuz", "/ppc/ppc64/initrd.img")] + ("/ppc/ppc64/vmlinuz", "/ppc/ppc64/initrd.img"), + ("/images/pxeboot/vmlinuz", "/images/pxeboot/initrd.img")] if os.path.isdir( self.mount_dir+"/repodata" ): self.repo = self.mount_dir else: @@ -168,20 +169,27 @@ def __init__( self, iso, ks_paths, disk_img, img_size=2, kernel_args=None, memory=1024, vnc=None, arch=None, log_check=None, virtio_host="127.0.0.1", virtio_port=6080, - qcow2=False): + qcow2=False, boot_uefi=False, ovmf_path=None): """ + Start the installation - iso is an instance of IsoMountpoint - ks_paths is a list of paths to a kickstart files. All are injected, the - first one is the one executed. - disk_img is the path to a disk image (doesn't need to exist) - img_size is the size, in GiB, of the image if it doesn't exist - kernel_args are extra arguments to pass on the kernel cmdline - memory is the amount of ram to assign to the virt - vnc is passed to the --graphics command verbatim - arch is the optional architecture to use in the virt - log_check is a method that returns True of the log indicates an error - virtio_host and virtio_port are used to communicate with the log monitor + :param iso: Information about the iso to use for the installation + :type iso: IsoMountpoint + :param list ks_paths: Paths to kickstart files. All are injected, the + first one is the one executed. + :param str disk_img: Path to a disk image, created it it doesn't exist + :param int img_size: The image size, in MiB, to create if it doesn't exist + :param str kernel_args: Extra kernel arguments to pass on the kernel cmdline + :param int memory: Amount of RAM to assign to the virt, in MiB + :param str vnc: Arguments to pass to virt-install --graphics + :param str arch: Optional architecture to use in the virt + :param log_check: Method that returns True if the installation fails + :type log_check: method + :param str virtio_host: Hostname to connect virtio log to + :param int virtio_port: Port to connect virtio log to + :param bool qcow2: Set to True if disk_img is a qcow2 + :param bool boot_uefi: Use OVMF to boot the VM in UEFI mode + :param str ovmf_path: Path to the OVMF firmware """ self.virt_name = "LiveOS-"+str(uuid.uuid4()) # add --graphics none later @@ -239,9 +247,15 @@ args.append("--arch") args.append(arch) - rc = execWithRedirect("virt-install", args) - if rc: - raise Exception("Problem starting virtual install") + if boot_uefi and ovmf_path: + args.append("--boot") + args.append("loader=%s/OVMF_CODE.fd,loader_ro=yes,loader_type=pflash,nvram_template=%s/OVMF_VARS.fd,loader_secure=no" % (ovmf_path, ovmf_path)) + + log.info("Running virt-install.") + try: + execWithRedirect("virt-install", args, raise_err=True) + except subprocess.CalledProcessError as e: + raise InstallError("Problem starting virtual install: %s" % e) conn = libvirt.openReadOnly(None) dom = conn.lookupByName(self.virt_name) @@ -268,8 +282,9 @@ """ log.info( "Shutting down %s", self.virt_name) subprocess.call(["virsh", "destroy", self.virt_name]) - subprocess.call(["virsh", "undefine", self.virt_name]) + # Undefine the virt, UEFI installs need to have --nvram passed + subprocess.call(["virsh", "undefine", self.virt_name, "--nvram"])
    [docs]def novirt_install(opts, disk_img, disk_size, repo_url, callback_func=None): """ @@ -409,16 +424,23 @@ else: diskimg_path = disk_img - virt = VirtualInstall(iso_mount, opts.ks, diskimg_path, disk_size, - kernel_args, opts.ram, opts.vnc, opts.arch, - log_check = log_monitor.server.log_check, - virtio_host = log_monitor.host, - virtio_port = log_monitor.port, - qcow2=opts.qcow2) + try: + virt = VirtualInstall(iso_mount, opts.ks, diskimg_path, disk_size, + kernel_args, opts.ram, opts.vnc, opts.arch, + log_check = log_monitor.server.log_check, + virtio_host = log_monitor.host, + virtio_port = log_monitor.port, + qcow2=opts.qcow2, boot_uefi=opts.virt_uefi, + ovmf_path=opts.ovmf_path) - virt.destroy() - log_monitor.shutdown() - iso_mount.umount() + virt.destroy() + log_monitor.shutdown() + except InstallError as e: + log.error("VirtualInstall failed: %s", e) + raise + finally: + log.info("unmounting the iso") + iso_mount.umount() if log_monitor.server.log_check(): raise InstallError("virt_install failed") @@ -471,7 +493,7 @@
  • modules |
  • -
  • Lorax 19.7.15 documentation »
  • +
  • Lorax 19.7.18 documentation »
  • Module code »
  • pylorax »
  • diff --git a/lorax-composer/_modules/pylorax/logmonitor.html b/lorax-composer/_modules/pylorax/logmonitor.html index d00353cd..bab4c4fb 100644 --- a/lorax-composer/_modules/pylorax/logmonitor.html +++ b/lorax-composer/_modules/pylorax/logmonitor.html @@ -8,7 +8,7 @@ - pylorax.logmonitor — Lorax 19.7.15 documentation + pylorax.logmonitor — Lorax 19.7.18 documentation @@ -16,7 +16,7 @@ - + @@ -38,7 +38,7 @@
  • modules |
  • -
  • Lorax 19.7.15 documentation »
  • +
  • Lorax 19.7.18 documentation »
  • Module code »
  • pylorax »
  • @@ -206,7 +206,7 @@
  • modules |
  • -
  • Lorax 19.7.15 documentation »
  • +
  • Lorax 19.7.18 documentation »
  • Module code »
  • pylorax »
  • diff --git a/lorax-composer/_modules/pylorax/ltmpl.html b/lorax-composer/_modules/pylorax/ltmpl.html index c174a63f..5ee57071 100644 --- a/lorax-composer/_modules/pylorax/ltmpl.html +++ b/lorax-composer/_modules/pylorax/ltmpl.html @@ -8,7 +8,7 @@ - pylorax.ltmpl — Lorax 19.7.15 documentation + pylorax.ltmpl — Lorax 19.7.18 documentation @@ -16,7 +16,7 @@ - + @@ -38,7 +38,7 @@
  • modules |
  • -
  • Lorax 19.7.15 documentation »
  • +
  • Lorax 19.7.18 documentation »
  • Module code »
  • pylorax »
  • @@ -748,7 +748,7 @@
  • modules |
  • -
  • Lorax 19.7.15 documentation »
  • +
  • Lorax 19.7.18 documentation »
  • Module code »
  • pylorax »
  • diff --git a/lorax-composer/_modules/pylorax/sysutils.html b/lorax-composer/_modules/pylorax/sysutils.html index b939a376..8444fe8f 100644 --- a/lorax-composer/_modules/pylorax/sysutils.html +++ b/lorax-composer/_modules/pylorax/sysutils.html @@ -8,7 +8,7 @@ - pylorax.sysutils — Lorax 19.7.15 documentation + pylorax.sysutils — Lorax 19.7.18 documentation @@ -16,7 +16,7 @@ - + @@ -38,7 +38,7 @@
  • modules |
  • -
  • Lorax 19.7.15 documentation »
  • +
  • Lorax 19.7.18 documentation »
  • Module code »
  • pylorax »
  • @@ -191,7 +191,7 @@
  • modules |
  • -
  • Lorax 19.7.15 documentation »
  • +
  • Lorax 19.7.18 documentation »
  • Module code »
  • pylorax »
  • diff --git a/lorax-composer/_modules/pylorax/treebuilder.html b/lorax-composer/_modules/pylorax/treebuilder.html index 67cd9112..3f94cef0 100644 --- a/lorax-composer/_modules/pylorax/treebuilder.html +++ b/lorax-composer/_modules/pylorax/treebuilder.html @@ -8,7 +8,7 @@ - pylorax.treebuilder — Lorax 19.7.15 documentation + pylorax.treebuilder — Lorax 19.7.18 documentation @@ -16,7 +16,7 @@ - + @@ -38,7 +38,7 @@
  • modules |
  • -
  • Lorax 19.7.15 documentation »
  • +
  • Lorax 19.7.18 documentation »
  • Module code »
  • pylorax »
  • @@ -403,7 +403,7 @@
  • modules |
  • -
  • Lorax 19.7.15 documentation »
  • +
  • Lorax 19.7.18 documentation »
  • Module code »
  • pylorax »
  • diff --git a/lorax-composer/_modules/pylorax/treeinfo.html b/lorax-composer/_modules/pylorax/treeinfo.html index b4db3d8a..5890bdfc 100644 --- a/lorax-composer/_modules/pylorax/treeinfo.html +++ b/lorax-composer/_modules/pylorax/treeinfo.html @@ -8,7 +8,7 @@ - pylorax.treeinfo — Lorax 19.7.15 documentation + pylorax.treeinfo — Lorax 19.7.18 documentation @@ -16,7 +16,7 @@ - + @@ -38,7 +38,7 @@
  • modules |
  • -
  • Lorax 19.7.15 documentation »
  • +
  • Lorax 19.7.18 documentation »
  • Module code »
  • pylorax »
  • @@ -140,7 +140,7 @@
  • modules |
  • -
  • Lorax 19.7.15 documentation »
  • +
  • Lorax 19.7.18 documentation »
  • Module code »
  • pylorax »
  • diff --git a/lorax-composer/_modules/pylorax/yumhelper.html b/lorax-composer/_modules/pylorax/yumhelper.html index 03c77468..354c135d 100644 --- a/lorax-composer/_modules/pylorax/yumhelper.html +++ b/lorax-composer/_modules/pylorax/yumhelper.html @@ -8,7 +8,7 @@ - pylorax.yumhelper — Lorax 19.7.15 documentation + pylorax.yumhelper — Lorax 19.7.18 documentation @@ -16,7 +16,7 @@ - + @@ -38,7 +38,7 @@
  • modules |
  • -
  • Lorax 19.7.15 documentation »
  • +
  • Lorax 19.7.18 documentation »
  • Module code »
  • pylorax »
  • @@ -208,7 +208,7 @@
  • modules |
  • -
  • Lorax 19.7.15 documentation »
  • +
  • Lorax 19.7.18 documentation »
  • Module code »
  • pylorax »
  • diff --git a/lorax-composer/_sources/composer-cli.txt b/lorax-composer/_sources/composer-cli.txt new file mode 100644 index 00000000..366e01ad --- /dev/null +++ b/lorax-composer/_sources/composer-cli.txt @@ -0,0 +1,62 @@ +composer-cli +============ + +:Authors: + Brian C. Lane + +``composer-cli`` is used to interact with the ``lorax-composer`` API server, managing blueprints, exploring available packages, and building new images. + +It requires `lorax-composer `_ to be installed on the +local system, and the user running it needs to be a member of the ``weldr`` +group. They do not need to be root, but all of the `security precautions +`_ apply. + +composer-cli cmdline arguments +------------------------------ + +.. argparse:: + :ref: composer.cli.cmdline.composer_cli_parser + :prog: composer-cli + +Edit a Blueprint +---------------- + +Start out by listing the available blueprints using ``composer-cli blueprints +list``, pick one and save it to the local directory by running ``composer-cli +blueprints save http-server``. If there are no blueprints available you can +copy one of the examples `from the test suite +`_. + +Edit the file (it will be saved with a .toml extension) and change the +description, add a package or module to it. Send it back to the server by +running ``composer-cli blueprints push http-server.toml``. You can verify that it was +saved by viewing the changelog - ``composer-cli blueprints changes http-server``. + +Build an image +---------------- + +Build a ``qcow2`` disk image from this blueprint by running ``composer-cli +compose start http-server qcow2``. It will print a UUID that you can use to +keep track of the build. You can also cancel the build if needed. + +The available types of images is displayed by ``composer-cli compose types``. +Currently this consists of: ext4-filesystem, live-iso, partitioned-disk, qcow2, +tar + +Monitor the build status +------------------------ + +Monitor it using ``composer-cli compose status``, which will show the status of +all the builds on the system. You can view the end of the anaconda build logs +once it is in the ``RUNNING`` state using ``composer-cli compose log UUID`` +where UUID is the UUID returned by the start command. + +Once the build is in the ``FINISHED`` state you can download the image. + +Download the image +------------------ + +Downloading the final image is done with ``composer-cli compose image UUID`` and it will +save the qcow2 image as ``UUID-disk.qcow2`` which you can then use to boot a VM like this:: + + qemu-kvm --name test-image -m 1024 -hda ./UUID-disk.qcow2 diff --git a/lorax-composer/_sources/composer.cli.rst.txt b/lorax-composer/_sources/composer.cli.rst.txt deleted file mode 100644 index 80dd2b54..00000000 --- a/lorax-composer/_sources/composer.cli.rst.txt +++ /dev/null @@ -1,54 +0,0 @@ -composer\.cli package -===================== - -Submodules ----------- - -composer\.cli\.blueprints module --------------------------------- - -.. automodule:: composer.cli.blueprints - :members: - :undoc-members: - :show-inheritance: - -composer\.cli\.compose module ------------------------------ - -.. automodule:: composer.cli.compose - :members: - :undoc-members: - :show-inheritance: - -composer\.cli\.modules module ------------------------------ - -.. automodule:: composer.cli.modules - :members: - :undoc-members: - :show-inheritance: - -composer\.cli\.projects module ------------------------------- - -.. automodule:: composer.cli.projects - :members: - :undoc-members: - :show-inheritance: - -composer\.cli\.utilities module -------------------------------- - -.. automodule:: composer.cli.utilities - :members: - :undoc-members: - :show-inheritance: - - -Module contents ---------------- - -.. automodule:: composer.cli - :members: - :undoc-members: - :show-inheritance: diff --git a/lorax-composer/_sources/composer.cli.txt b/lorax-composer/_sources/composer.cli.txt index f24a0be8..ed753029 100644 --- a/lorax-composer/_sources/composer.cli.txt +++ b/lorax-composer/_sources/composer.cli.txt @@ -25,6 +25,14 @@ cli Package :undoc-members: :show-inheritance: +:mod:`help` Module +------------------ + +.. automodule:: composer.cli.help + :members: + :undoc-members: + :show-inheritance: + :mod:`modules` Module --------------------- @@ -41,6 +49,14 @@ cli Package :undoc-members: :show-inheritance: +:mod:`sources` Module +--------------------- + +.. automodule:: composer.cli.sources + :members: + :undoc-members: + :show-inheritance: + :mod:`utilities` Module ----------------------- diff --git a/lorax-composer/_sources/composer.rst.txt b/lorax-composer/_sources/composer.rst.txt deleted file mode 100644 index f6658ce6..00000000 --- a/lorax-composer/_sources/composer.rst.txt +++ /dev/null @@ -1,37 +0,0 @@ -composer package -================ - -Subpackages ------------ - -.. toctree:: - - composer.cli - -Submodules ----------- - -composer\.http\_client module ------------------------------ - -.. automodule:: composer.http_client - :members: - :undoc-members: - :show-inheritance: - -composer\.unix\_socket module ------------------------------ - -.. automodule:: composer.unix_socket - :members: - :undoc-members: - :show-inheritance: - - -Module contents ---------------- - -.. automodule:: composer - :members: - :undoc-members: - :show-inheritance: diff --git a/lorax-composer/_sources/index.rst.txt b/lorax-composer/_sources/index.rst.txt deleted file mode 100644 index acd79678..00000000 --- a/lorax-composer/_sources/index.rst.txt +++ /dev/null @@ -1,28 +0,0 @@ -.. Lorax documentation master file, created by - sphinx-quickstart on Wed Apr 8 13:46:00 2015. - You can adapt this file completely to your liking, but it should at least - contain the root `toctree` directive. - -Welcome to Lorax's documentation! -================================= - -Contents: - -.. toctree:: - :maxdepth: 1 - - intro - lorax - livemedia-creator - lorax-composer - product-images - modules - - -Indices and tables -================== - -* :ref:`genindex` -* :ref:`modindex` -* :ref:`search` - diff --git a/lorax-composer/_sources/index.txt b/lorax-composer/_sources/index.txt index acd79678..67f2b9ed 100644 --- a/lorax-composer/_sources/index.txt +++ b/lorax-composer/_sources/index.txt @@ -15,6 +15,7 @@ Contents: lorax livemedia-creator lorax-composer + composer-cli product-images modules diff --git a/lorax-composer/_sources/intro.rst.txt b/lorax-composer/_sources/intro.rst.txt deleted file mode 100644 index 01857ee9..00000000 --- a/lorax-composer/_sources/intro.rst.txt +++ /dev/null @@ -1,67 +0,0 @@ -Introduction to Lorax -===================== - -I am the Lorax. I speak for the trees [and images]. - -Lorax is used to build the Anaconda Installer boot.iso, it consists of a -library, pylorax, a set of templates, and the lorax script. Its operation -is driven by a customized set of Mako templates that lists the packages -to be installed, steps to execute to remove unneeded files, and creation -of the iso for all of the supported architectures. - - - - - - -Before Lorax -============ - -Tree building tools such as pungi and revisor rely on 'buildinstall' in -anaconda/scripts/ to produce the boot images and other such control files -in the final tree. The existing buildinstall scripts written in a mix of -bash and Python are unmaintainable. Lorax is an attempt to replace them -with something more flexible. - - -EXISTING WORKFLOW: - -pungi and other tools call scripts/buildinstall, which in turn call other -scripts to do the image building and data generation. Here's how it -currently looks: - - -> buildinstall - * process command line options - * write temporary yum.conf to point to correct repo - * find anaconda release RPM - * unpack RPM, pull in those versions of upd-instroot, mk-images, - maketreeinfo.py, makestamp.py, and buildinstall - - -> call upd-instroot - - -> call maketreeinfo.py - - -> call mk-images (which figures out which mk-images.ARCH to call) - - -> call makestamp.py - - * clean up - - -PROBLEMS: - -The existing workflow presents some problems with maintaining the scripts. -First, almost all knowledge of what goes in to the stage 1 and stage 2 -images lives in upd-instroot. The mk-images* scripts copy things from the -root created by upd-instroot in order to build the stage 1 image, though -it's not completely clear from reading the scripts. - - -NEW IDEAS: - -Create a new central driver with all information living in Python modules. -Configuration files will provide the knowledge previously contained in the -upd-instroot and mk-images* scripts. - - - diff --git a/lorax-composer/_sources/livemedia-creator.rst.txt b/lorax-composer/_sources/livemedia-creator.rst.txt deleted file mode 100644 index 94d6a331..00000000 --- a/lorax-composer/_sources/livemedia-creator.rst.txt +++ /dev/null @@ -1,391 +0,0 @@ -livemedia-creator -================= - -:Authors: - Brian C. Lane - -livemedia-creator uses `Anaconda `_, -`kickstart `_ and `Lorax -`_ to create bootable media that use the -same install path as a normal system installation. It can be used to make live -isos, bootable (partitioned) disk images, tarfiles, and filesystem images for -use with virtualization and container solutions like libvirt, docker, and -OpenStack. - -The general idea is to use virt-install with kickstart and an Anaconda boot.iso to -install into a disk image and then use the disk image to create the bootable -media. - -livemedia-creator --help will describe all of the options available. At the -minimum you need: - -``--make-iso`` to create a final bootable .iso or one of the other ``--make-*`` options. - -``--iso`` to specify the Anaconda install media to use with virt-install. - -``--ks`` to select the kickstart file describing what to install. - -To use livemedia-creator with virtualization you will need to have virt-install installed. - -If you are going to be using Anaconda directly, with ``--no-virt`` mode, make sure -you have the anaconda-tui package installed. - -Conventions used in this document: - -``lmc`` is an abbreviation for livemedia-creator. - -``builder`` is the system where livemedia-creator is being run - -``image`` is the disk image being created by running livemedia-creator - - -livemedia-creator cmdline arguments ------------------------------------ - -See the output from ``livemedia-creator --help`` for the commandline arguments. - -Quickstart ----------- - -Run this to create a bootable live iso:: - - sudo livemedia-creator --make-iso \ - --iso=/extra/iso/boot.iso --ks=./docs/rhel7-livemedia.ks - -You can run it directly from the lorax git repo like this:: - - sudo PATH=./src/sbin/:$PATH PYTHONPATH=./src/ ./src/sbin/livemedia-creator \ - --make-iso --iso=/extra/iso/boot.iso \ - --ks=./docs/rhel7-livemedia.ks --lorax-templates=./share/ - -You can observe the installation using vnc. The logs will show what port was -chosen, or you can use a specific port by passing it. eg. ``--vnc vnc:127.0.0.1:5`` - -This is usually a good idea when testing changes to the kickstart. lmc tries -to monitor the logs for fatal errors, but may not catch everything. - - -How ISO creation works ----------------------- - -There are 2 stages, the install stage which produces a disk or filesystem image -as its output, and the boot media creation which uses the image as its input. -Normally you would run both stages, but it is possible to stop after the -install stage, by using ``--image-only``, or to skip the install stage and use -a previously created disk image by passing ``--disk-image`` or ``--fs-image`` - -When creating an iso virt-install boots using the passed Anaconda installer iso -and installs the system based on the kickstart. The ``%post`` section of the -kickstart is used to customize the installed system in the same way that -current spin-kickstarts do. - -livemedia-creator monitors the install process for problems by watching the -install logs. They are written to the current directory or to the base -directory specified by the --logfile command. You can also monitor the install -by using a vnc client. This is recommended when first modifying a kickstart, -since there are still places where Anaconda may get stuck without the log -monitor catching it. - -The output from this process is a partitioned disk image. kpartx can be used -to mount and examine it when there is a problem with the install. It can also -be booted using kvm. - -When creating an iso the disk image's / partition is copied into a formatted -filesystem image which is then used as the input to lorax for creation of the -final media. - -The final image is created by lorax, using the templates in /usr/share/lorax/live/ -or the live directory below the directory specified by ``--lorax-templates``. The -templates are written using the Mako template system with some extra commands -added by lorax. - - -Kickstarts ----------- - -The docs/ directory includes several example kickstarts, one to create a live -desktop iso using GNOME, and another to create a minimal disk image. When -creating your own kickstarts you should start with the minimal example, it -includes several needed packages that are not always included by dependencies. - -Or you can use existing spin kickstarts to create live media with a few -changes. Here are the steps I used to convert the Fedora XFCE spin. - -1. Flatten the xfce kickstart using ksflatten -2. Add zerombr so you don't get the disk init dialog -3. Add clearpart --all -4. Add swap partition -5. bootloader target -6. Add shutdown to the kickstart -7. Add network --bootproto=dhcp --activate to activate the network - This works for F16 builds but for F15 and before you need to pass - something on the cmdline that activate the network, like sshd: - - ``livemedia-creator --kernel-args="sshd"`` - -8. Add a root password:: - - rootpw rootme - network --bootproto=dhcp --activate - zerombr - clearpart --all - bootloader --location=mbr - part swap --size=512 - shutdown - -9. In the livesys script section of the %post remove the root password. This - really depends on how the spin wants to work. You could add the live user - that you create to the %wheel group so that sudo works if you wanted to. - - ``passwd -d root > /dev/null`` - -10. Remove /etc/fstab in %post, dracut handles mounting the rootfs - - ``cat /dev/null > /dev/fstab`` - - Do this only for live iso's, the filesystem will be mounted read only if - there is no /etc/fstab - -11. Don't delete initramfs files from /boot in %post -12. When creating live iso's you need to have, at least, these packages in the %package section:: - dracut-config-generic - dracut-live - -dracut-config-rescue - grub-efi - memtest86+ - syslinux - -One drawback to using virt-install is that it pulls the packages from the repo -each time you run it. To speed things up you either need a local mirror of the -packages, or you can use a caching proxy. When using a proxy you pass it to -livemedia-creator like this: - - ``--proxy=http://proxy.yourdomain.com:3128`` - -You also need to use a specific mirror instead of mirrormanager so that the -packages will get cached, so your kickstart url would look like: - - ``url --url="http://dl.fedoraproject.org/pub/fedora/linux/development/rawhide/x86_64/os/"`` - -You can also add an update repo, but don't name it updates. Add --proxy to it -as well. - - -Anaconda image install (no-virt) --------------------------------- - -You can create images without using virt-install by passing ``--no-virt`` on -the cmdline. This will use Anaconda's directory install feature to handle the -install. There are a couple of things to keep in mind when doing this: - -1. It will be most reliable when building images for the same release that the - host is running. Because Anaconda has expectations about the system it is - running under you may encounter strange bugs if you try to build newer or - older releases. - -2. Make sure selinux is set to permissive or disabled. It won't install - correctly with selinux set to enforcing yet. - -3. It may totally trash your host. So far I haven't had this happen, but the - possibility exists that a bug in Anaconda could result in it operating on - real devices. I recommend running it in a virt or on a system that you can - afford to lose all data from. - -The logs from anaconda will be placed in an ./anaconda/ directory in either -the current directory or in the directory used for --logfile - -Example cmdline: - -``sudo livemedia-creator --make-iso --no-virt --ks=./rhel7-livemedia.ks`` - -.. note:: - Using no-virt to create a partitioned disk image (eg. --make-disk or - --make-vagrant) will only create disks usable on the host platform (BIOS - or UEFI). You can create BIOS partitioned disk images on UEFI by using - virt. - - -AMI Images ----------- - -Amazon EC2 images can be created by using the --make-ami switch and an appropriate -kickstart file. All of the work to customize the image is handled by the kickstart. -The example currently included was modified from the cloud-kickstarts version so -that it would work with livemedia-creator. - -Example cmdline: - -``sudo livemedia-creator --make-ami --iso=/path/to/boot.iso --ks=./docs/rhel7-livemedia-ec2.ks`` - -This will produce an ami-root.img file in the working directory. - -At this time I have not tested the image with EC2. Feedback would be welcome. - - -Appliance Creation ------------------- - -livemedia-creator can now replace appliance-tools by using the --make-appliance -switch. This will create the partitioned disk image and an XML file that can be -used with virt-image to setup a virtual system. - -The XML is generated using the Mako template from -/usr/share/lorax/appliance/libvirt.xml You can use a different template by -passing ``--app-template