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 @@
-
-#!/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 @@
-#
-# 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
-
+#
+# 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)
+
-#
-# __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
-