diff --git a/lorax-composer/.buildinfo b/lorax-composer/.buildinfo index ee6d8d3b..2447dcf4 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: 972a8f4b79671d03d004b6b084c1be20 +config: d0036b060ae50548427b7f3aa21363e9 tags: fbb0d17656682115ca4d033fb2f83ba1 diff --git a/lorax-composer/.doctrees/composer.cli.doctree b/lorax-composer/.doctrees/composer.cli.doctree new file mode 100644 index 00000000..77e24c51 Binary files /dev/null and b/lorax-composer/.doctrees/composer.cli.doctree differ diff --git a/lorax-composer/.doctrees/composer.doctree b/lorax-composer/.doctrees/composer.doctree new file mode 100644 index 00000000..670a404d Binary files /dev/null and b/lorax-composer/.doctrees/composer.doctree differ diff --git a/lorax-composer/.doctrees/environment.pickle b/lorax-composer/.doctrees/environment.pickle index 1d5ac8e1..a3d916ee 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 e632e201..abf17588 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 004bc8f6..74dca9e5 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 new file mode 100644 index 00000000..96d09883 Binary files /dev/null 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 996b9f0e..8f64c815 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 new file mode 100644 index 00000000..c4730d86 Binary files /dev/null and b/lorax-composer/.doctrees/lorax.doctree differ diff --git a/lorax-composer/.doctrees/modules.doctree b/lorax-composer/.doctrees/modules.doctree index dede9510..5560675b 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 new file mode 100644 index 00000000..494ffddb Binary files /dev/null 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 a212f8b7..7e4e3ac0 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 4a6f5f7f..7b33f334 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 new file mode 100644 index 00000000..2540b689 --- /dev/null +++ b/lorax-composer/_modules/composer/cli.html @@ -0,0 +1,150 @@ + + + + + + + + + + composer.cli — Lorax 19.7.11 documentation + + + + + + + + + + + + + + +
+
+
+
+ +

Source code for composer.cli

+#!/usr/bin/python
+#
+# composer-cli
+#
+# 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")
+
+from composer.cli.recipes import recipes_cmd
+from composer.cli.modules import modules_cmd
+from composer.cli.projects import projects_cmd
+from composer.cli.compose import compose_cmd
+
+command_map = {
+    "recipes": recipes_cmd,
+    "modules": modules_cmd,
+    "projects": projects_cmd,
+    "compose": compose_cmd
+    }
+
+
+
[docs]def main(opts): + """ Main program execution + + :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: + 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) + except Exception as e: + log.error(str(e)) + return 1
+
+ +
+
+
+
+
+ + +
+
+
+
+ + + + \ No newline at end of file diff --git a/lorax-composer/_modules/composer/cli/compose.html b/lorax-composer/_modules/composer/cli/compose.html new file mode 100644 index 00000000..ec3c5388 --- /dev/null +++ b/lorax-composer/_modules/composer/cli/compose.html @@ -0,0 +1,506 @@ + + + + + + + + + + composer.cli.compose — Lorax 19.7.11 documentation + + + + + + + + + + + + + + +
+
+
+
+ +

Source code for composer.cli.compose

+#
+# 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 sys
+import json
+
+from composer import http_client as client
+from composer.cli.utilities import argify, handle_api_result, packageNEVRA
+
+
[docs]def compose_cmd(opts): + """Process compose commands + + :param opts: Cmdline arguments + :type opts: argparse.Namespace + :returns: Value to return from sys.exit() + :rtype: int + + This dispatches the compose commands to a function + """ + cmd_map = { + "status": compose_status, + "types": compose_types, + "start": compose_start, + "log": compose_log, + "cancel": compose_cancel, + "delete": compose_delete, + "details": compose_details, + "metadata": compose_metadata, + "results": compose_results, + "logs": compose_logs, + "image": compose_image, + } + if opts.args[1] not in cmd_map: + log.error("Unknown compose command: %s", opts.args[1]) + return 1 + + return cmd_map[opts.args[1]](opts.socket, opts.api_version, opts.args[2:], opts.json, opts.testmode) +
+
[docs]def compose_status(socket_path, api_version, args, show_json=False, testmode=0): + """Return the status of all known composes + + :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 + + This doesn't map directly to an API command, it combines the results from queue, finished, + and failed so raw JSON output is not available. + """ + def get_status(compose): + return {"id": compose["id"], + "recipe": compose["recipe"], + "version": compose["version"], + "status": compose["queue_status"]} + + # Sort the status in a specific order + def sort_status(a): + order = ["RUNNING", "WAITING", "FINISHED", "FAILED"] + return (order.index(a["status"]), a["recipe"], a["version"]) + + status = [] + + # Get the composes currently in the queue + api_route = client.api_url(api_version, "/compose/queue") + result = client.get_url_json(socket_path, api_route) + status.extend(map(get_status, result["run"] + result["new"])) + + # Get the list of finished composes + api_route = client.api_url(api_version, "/compose/finished") + result = client.get_url_json(socket_path, api_route) + status.extend(map(get_status, result["finished"])) + + # Get the list of failed composes + api_route = client.api_url(api_version, "/compose/failed") + result = client.get_url_json(socket_path, api_route) + status.extend(map(get_status, result["failed"])) + + # Sort them by status (running, waiting, finished, failed) and then by name and version. + status.sort(key=sort_status) + + if show_json: + print(json.dumps(status, indent=4)) + return 0 + + # Print them as UUID RECIPE STATUS + for c in status: + print("%s %-8s %-15s %s" % (c["id"], c["status"], c["recipe"], c["version"])) + +
+
[docs]def compose_types(socket_path, api_version, args, show_json=False): + """Return information about the supported compose types + + :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 + + Add additional details to types that are known to composer-cli. Raw JSON output does not + include this extra information. + """ + api_route = client.api_url(api_version, "/compose/types") + result = client.get_url_json(socket_path, api_route) + if show_json: + print(json.dumps(result, indent=4)) + return 0 + + print("Compose Types: " + ", ".join([t["name"] for t in result["types"]])) +
+
[docs]def compose_start(socket_path, api_version, args, show_json=False, testmode=0): + """Start a new compose using the selected recipe and type + + :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 + + compose start <recipe-name> <compose-type> + """ + if len(args) == 0: + log.error("start is missing the recipe name and output type") + return 1 + if len(args) == 1: + log.error("start is missing the output type") + return 1 + + config = { + "recipe_name": args[0], + "compose_type": args[1], + "branch": "master" + } + if testmode: + test_url = "?test=%d" % testmode + else: + test_url = "" + api_route = client.api_url(api_version, "/compose" + test_url) + result = client.post_url_json(socket_path, api_route, json.dumps(config)) + + if show_json: + print(json.dumps(result, indent=4)) + return 0 + + if result.get("error", False): + log.error(result["error"]["msg"]) + return 1 + + if result["status"] == False: + return 1 + + print("Compose %s added to the queue" % result["build_id"]) + return 0 +
+
[docs]def compose_log(socket_path, api_version, args, show_json=False, testmode=0): + """Show the last part of the compose log + + :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 + + compose log <uuid> [<size>kB] + + This will display the last 1kB of the compose's log file. Can be used to follow progress + during the build. + """ + if len(args) == 0: + log.error("log is missing the compose build id") + return 1 + if len(args) == 2: + try: + log_size = int(args[1]) + except ValueError: + log.error("Log size must be an integer.") + return 1 + else: + log_size = 1024 + + api_route = client.api_url(api_version, "/compose/log/%s?size=%d" % (args[0], log_size)) + result = client.get_url_raw(socket_path, api_route) + + print(result) +
+
[docs]def compose_cancel(socket_path, api_version, args, show_json=False, testmode=0): + """Cancel a running compose + + :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 + + compose cancel <uuid> + + This will cancel a running compose. It does nothing if the compose has finished. + """ + if len(args) == 0: + log.error("cancel is missing the compose build id") + return 1 + + api_route = client.api_url(api_version, "/compose/cancel/%s" % args[0]) + result = client.delete_url_json(socket_path, api_route) + return handle_api_result(result, show_json) +
+
[docs]def compose_delete(socket_path, api_version, args, show_json=False, testmode=0): + """Delete a finished compose's results + + :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 + + compose delete <uuid,...> + + Delete the listed compose results. It will only delete results for composes that have finished + or failed, not a running compose. + """ + if len(args) == 0: + log.error("delete is missing the compose build id") + return 1 + + api_route = client.api_url(api_version, "/compose/delete/%s" % (",".join(argify(args)))) + result = client.delete_url_json(socket_path, api_route) + + if show_json: + print(json.dumps(result, indent=4)) + return 0 + + # Print any errors + for err in result.get("errors", []): + log.error("%s: %s", err["uuid"], err["msg"]) + + if result.get("errors", []): + return 1 + else: + return 0 +
+
[docs]def compose_details(socket_path, api_version, args, show_json=False, testmode=0): + """Return detailed information about the compose + + :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 + + compose details <uuid> + + This returns information about the compose, including the recipe and the dependencies. + """ + if len(args) == 0: + log.error("details is missing the compose build id") + return 1 + + api_route = client.api_url(api_version, "/compose/info/%s" % args[0]) + result = client.get_url_json(socket_path, api_route) + if show_json: + print(json.dumps(result, indent=4)) + return 0 + + print("%s %-8s %-15s %s %s" % (result["id"], + result["queue_status"], + result["recipe"]["name"], + result["recipe"]["version"], + result["compose_type"])) + print("Recipe Packages:") + for p in result["recipe"]["packages"]: + print(" %s-%s" % (p["name"], p["version"])) + + print("Recipe Modules:") + for m in result["recipe"]["modules"]: + print(" %s-%s" % (m["name"], m["version"])) + + print("Dependencies:") + for d in result["deps"]["packages"]: + print(" " + packageNEVRA(d)) +
+
[docs]def compose_metadata(socket_path, api_version, args, show_json=False, testmode=0): + """Download a tar file of the compose's metadata + + :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 + + compose metadata <uuid> + + Saves the metadata as uuid-metadata.tar + """ + if len(args) == 0: + log.error("metadata is missing the compose build id") + return 1 + + api_route = client.api_url(api_version, "/compose/metadata/%s" % args[0]) + return client.download_file(socket_path, api_route) +
+
[docs]def compose_results(socket_path, api_version, args, show_json=False, testmode=0): + """Download a tar file of the compose's results + + :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 + + compose results <uuid> + + The results includes the metadata, output image, and logs. + It is saved as uuid.tar + """ + if len(args) == 0: + log.error("results is missing the compose build id") + return 1 + + api_route = client.api_url(api_version, "/compose/results/%s" % args[0]) + return client.download_file(socket_path, api_route, sys.stdout.isatty()) +
+
[docs]def compose_logs(socket_path, api_version, args, show_json=False, testmode=0): + """Download a tar of the compose's logs + + :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 + + compose logs <uuid> + + Saves the logs as uuid-logs.tar + """ + if len(args) == 0: + log.error("logs is missing the compose build id") + return 1 + + api_route = client.api_url(api_version, "/compose/logs/%s" % args[0]) + return client.download_file(socket_path, api_route, sys.stdout.isatty()) +
+
[docs]def compose_image(socket_path, api_version, args, show_json=False, testmode=0): + """Download the compose's output image + + :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 + + compose image <uuid> + + This downloads only the result image, saving it as the image name, which depends on the type + of compose that was selected. + """ + if len(args) == 0: + log.error("logs is missing the compose build id") + return 1 + + api_route = client.api_url(api_version, "/compose/image/%s" % args[0]) + return client.download_file(socket_path, api_route, sys.stdout.isatty())
+
+ +
+
+
+
+
+ + +
+
+
+
+ + + + \ No newline at end of file diff --git a/lorax-composer/_modules/composer/cli/modules.html b/lorax-composer/_modules/composer/cli/modules.html new file mode 100644 index 00000000..17f2e574 --- /dev/null +++ b/lorax-composer/_modules/composer/cli/modules.html @@ -0,0 +1,140 @@ + + + + + + + + + + composer.cli.modules — Lorax 19.7.11 documentation + + + + + + + + + + + + + + +
+
+
+
+ +

Source code for composer.cli.modules

+#
+# 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 json
+
+from composer import http_client as client
+
+
[docs]def modules_cmd(opts): + """Process modules commands + + :param opts: Cmdline arguments + :type opts: argparse.Namespace + :returns: Value to return from sys.exit() + :rtype: int + """ + if opts.args[1] != "list": + log.error("Unknown modules command: %s", opts.args[1]) + return 1 + + api_route = client.api_url(opts.api_version, "/modules/list") + result = client.get_url_json(opts.socket, api_route) + if opts.json: + print(json.dumps(result, indent=4)) + return 0 + + print("Modules:\n" + "\n".join([" "+r["name"] for r in result["modules"]])) + + return 0
+
+ +
+
+
+
+
+ + +
+
+
+
+ + + + \ No newline at end of file diff --git a/lorax-composer/_modules/composer/cli/projects.html b/lorax-composer/_modules/composer/cli/projects.html new file mode 100644 index 00000000..6a4d16ce --- /dev/null +++ b/lorax-composer/_modules/composer/cli/projects.html @@ -0,0 +1,202 @@ + + + + + + + + + + composer.cli.projects — Lorax 19.7.11 documentation + + + + + + + + + + + + + + +
+
+
+
+ +

Source code for composer.cli.projects

+#
+# 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 json
+import textwrap
+
+from composer import http_client as client
+
+
[docs]def projects_cmd(opts): + """Process projects commands + + :param opts: Cmdline arguments + :type opts: argparse.Namespace + :returns: Value to return from sys.exit() + :rtype: int + """ + cmd_map = { + "list": projects_list, + "info": projects_info, + } + if opts.args[1] not in cmd_map: + log.error("Unknown projects 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 projects_list(socket_path, api_version, args, show_json=False): + """Output the list of available 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 + + projects list + """ + api_route = client.api_url(api_version, "/projects/list") + result = client.get_url_json(socket_path, api_route) + if show_json: + print(json.dumps(result, indent=4)) + return 0 + + for proj in result["projects"]: + for k in ["name", "summary", "homepage", "description"]: + print("%s: %s" % (k.title(), textwrap.fill(proj[k], subsequent_indent=" " * (len(k)+2)))) + print("\n\n") + + return 0 +
+
[docs]def projects_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 + + projects info <project,...> + """ + if len(args) == 0: + log.error("projects info is missing the packages") + return 1 + + api_route = client.api_url(api_version, "/projects/info/%s" % ",".join(args)) + result = client.get_url_json(socket_path, api_route) + if show_json: + print(json.dumps(result, indent=4)) + return 0 + + for proj in result["projects"]: + for k in ["name", "summary", "homepage", "description"]: + print("%s: %s" % (k.title(), textwrap.fill(proj[k], subsequent_indent=" " * (len(k)+2)))) + print("Builds: ") + for build in proj["builds"]: + print(" %s%s-%s.%s at %s for %s" % ("" if not build["epoch"] else build["epoch"] + ":", + build["source"]["version"], + build["release"], + build["arch"], + build["build_time"], + build["changelog"])) + print("") + return 0
+
+ +
+
+
+
+
+ + +
+
+
+
+ + + + \ No newline at end of file diff --git a/lorax-composer/_modules/composer/cli/recipes.html b/lorax-composer/_modules/composer/cli/recipes.html new file mode 100644 index 00000000..b415ddf7 --- /dev/null +++ b/lorax-composer/_modules/composer/cli/recipes.html @@ -0,0 +1,613 @@ + + + + + + + + + + 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/utilities.html b/lorax-composer/_modules/composer/cli/utilities.html new file mode 100644 index 00000000..2b1ad1d1 --- /dev/null +++ b/lorax-composer/_modules/composer/cli/utilities.html @@ -0,0 +1,179 @@ + + + + + + + + + + composer.cli.utilities — Lorax 19.7.11 documentation + + + + + + + + + + + + + + +
+
+
+
+ +

Source code for composer.cli.utilities

+#
+# 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 json
+
+
[docs]def argify(args): + """Take a list of human args and return a list with each item + + :param args: list of strings with possible commas and spaces + :type args: list of str + :returns: List of all the items + :rtype: list of str + + Examples: + + ["one,two", "three", ",four", ",five,"] returns ["one", "two", "three", "four", "five"] + """ + return filter(lambda i: i, [arg for entry in args for arg in entry.split(",")]) +
+
[docs]def toml_filename(recipe_name): + """Convert a recipe name into a filename.toml + + :param recipe_name: The recipe's name + :type recipe_name: str + :returns: The recipe name with ' ' converted to - and .toml appended + :rtype: str + """ + return recipe_name.replace(" ", "-") + ".toml" +
+
[docs]def frozen_toml_filename(recipe_name): + """Convert a recipe name into a filename.toml + + :param recipe_name: The recipe's name + :type recipe_name: str + :returns: The recipe name with ' ' converted to - and .toml appended + :rtype: str + """ + return recipe_name.replace(" ", "-") + ".frozen.toml" +
+
[docs]def handle_api_result(result, show_json=False): + """Log any errors, return the correct value + + :param result: JSON result from the http query + :type result: dict + """ + if show_json: + print(json.dumps(result, indent=4)) + elif result.get("error", False): + log.error(result["error"]["msg"]) + + if result["status"] == True: + return 0 + else: + return 1 +
+
[docs]def packageNEVRA(pkg): + """Return the package info as a NEVRA + + :param pkg: The package details + :type pkg: dict + :returns: name-[epoch:]version-release-arch + :rtype: str + """ + if pkg["epoch"]: + return "%s-%s:%s-%s.%s" % (pkg["name"], pkg["epoch"], pkg["version"], pkg["release"], pkg["arch"]) + else: + return "%s-%s-%s.%s" % (pkg["name"], pkg["version"], pkg["release"], pkg["arch"])
+
+ +
+
+
+
+
+ + +
+
+
+
+ + + + \ No newline at end of file diff --git a/lorax-composer/_modules/composer/http_client.html b/lorax-composer/_modules/composer/http_client.html new file mode 100644 index 00000000..f99caf7d --- /dev/null +++ b/lorax-composer/_modules/composer/http_client.html @@ -0,0 +1,290 @@ + + + + + + + + + + composer.http_client — Lorax 19.7.11 documentation + + + + + + + + + + + + + + +
+
+
+
+ +

Source code for composer.http_client

+#
+# 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 sys
+import json
+
+from composer.unix_socket import UnixHTTPConnectionPool
+
+
[docs]def api_url(api_version, url): + """Return the versioned path to the API route + + :param api_version: The version of the API to talk to. eg. "0" + :type api_version: str + :param url: The API route to talk to + :type url: str + :returns: The full url to use for the route and API version + :rtype: str + """ + return os.path.normpath("/api/v%s/%s" % (api_version, url)) +
+
[docs]def get_url_raw(socket_path, url): + """Return the raw results of a GET request + + :param socket_path: Path to the Unix socket to use for API communication + :type socket_path: str + :param url: URL to request + :type url: str + :returns: The raw response from the server + :rtype: str + """ + http = UnixHTTPConnectionPool(socket_path) + r = http.request("GET", url) + return r.data.decode('utf-8') +
+
[docs]def get_url_json(socket_path, url): + """Return the JSON results of a GET request + + :param socket_path: Path to the Unix socket to use for API communication + :type socket_path: str + :param url: URL to request + :type url: str + :returns: The json response from the server + :rtype: dict + """ + http = UnixHTTPConnectionPool(socket_path) + r = http.request("GET", url) + return json.loads(r.data.decode('utf-8')) +
+
[docs]def delete_url_json(socket_path, url): + """Send a DELETE request to the url and return JSON response + + :param socket_path: Path to the Unix socket to use for API communication + :type socket_path: str + :param url: URL to send DELETE to + :type url: str + :returns: The json response from the server + :rtype: dict + """ + http = UnixHTTPConnectionPool(socket_path) + r = http.request("DELETE", url) + return json.loads(r.data.decode("utf-8")) +
+
[docs]def post_url(socket_path, url, body): + """POST raw data to the URL + + :param socket_path: Path to the Unix socket to use for API communication + :type socket_path: str + :param url: URL to send POST to + :type url: str + :param body: The data for the body of the POST + :type body: str + :returns: The json response from the server + :rtype: dict + """ + http = UnixHTTPConnectionPool(socket_path) + r = http.request("POST", url, + body=body.encode("utf-8")) + return json.loads(r.data.decode("utf-8")) +
+
[docs]def post_url_toml(socket_path, url, body): + """POST a TOML recipe to the URL + + :param socket_path: Path to the Unix socket to use for API communication + :type socket_path: str + :param url: URL to send POST to + :type url: str + :param body: The data for the body of the POST + :type body: str + :returns: The json response from the server + :rtype: dict + """ + http = UnixHTTPConnectionPool(socket_path) + r = http.request("POST", url, + body=body.encode("utf-8"), + headers={"Content-Type": "text/x-toml"}) + return json.loads(r.data.decode("utf-8")) +
+
[docs]def post_url_json(socket_path, url, body): + """POST some JSON data to the URL + + :param socket_path: Path to the Unix socket to use for API communication + :type socket_path: str + :param url: URL to send POST to + :type url: str + :param body: The data for the body of the POST + :type body: str + :returns: The json response from the server + :rtype: dict + """ + http = UnixHTTPConnectionPool(socket_path) + r = http.request("POST", url, + body=body.encode("utf-8"), + headers={"Content-Type": "application/json"}) + return json.loads(r.data.decode("utf-8")) +
+
[docs]def get_filename(headers): + """Get the filename from the response header + + :param response: The urllib3 response object + :type response: Response + :raises: RuntimeError if it cannot find a filename in the header + :returns: Filename from content-disposition header + :rtype: str + """ + log.debug("Headers = %s", headers) + if "content-disposition" not in headers: + raise RuntimeError("No Content-Disposition header; cannot get filename") + + try: + k, _, v = headers["content-disposition"].split(";")[1].strip().partition("=") + if k != "filename": + raise RuntimeError("No filename= found in content-disposition header") + except RuntimeError: + raise + except Exception as e: + raise RuntimeError("Error parsing filename from content-disposition header: %s" % str(e)) + + return os.path.basename(v) +
+
[docs]def download_file(socket_path, url, progress=True): + """Download a file, saving it to the CWD with the included filename + + :param socket_path: Path to the Unix socket to use for API communication + :type socket_path: str + :param url: URL to send POST to + :type url: str + """ + http = UnixHTTPConnectionPool(socket_path) + r = http.request("GET", url, preload_content=False) + if r.status == 400: + err = json.loads(r.data.decode("utf-8")) + if not err["status"]: + raise RuntimeError(err["error"]["msg"]) + + filename = get_filename(r.headers) + if os.path.exists(filename): + msg = "%s exists, skipping download" % filename + log.error(msg) + raise RuntimeError(msg) + + with open(filename, "wb") as f: + while True: + data = r.read(10 * 1024**2) + if not data: + break + f.write(data) + + if progress: + data_written = f.tell() + if data_written > 5 * 1024**2: + sys.stdout.write("%s: %0.2f MB \r" % (filename, data_written / 1024**2)) + else: + sys.stdout.write("%s: %0.2f kB\r" % (filename, data_written / 1024)) + sys.stdout.flush() + + print("") + r.release_conn() + + return 0
+
+ +
+
+
+
+
+ + +
+
+
+
+ + + + \ No newline at end of file diff --git a/lorax-composer/_modules/composer/unix_socket.html b/lorax-composer/_modules/composer/unix_socket.html new file mode 100644 index 00000000..e050e756 --- /dev/null +++ b/lorax-composer/_modules/composer/unix_socket.html @@ -0,0 +1,160 @@ + + + + + + + + + + composer.unix_socket — Lorax 19.7.11 documentation + + + + + + + + + + + + + + +
+
+
+
+ +

Source code for composer.unix_socket

+#
+# 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 httplib
+import socket
+import urllib3
+
+
+# These 2 classes were adapted and simplified for use with just urllib3.
+# Originally from https://github.com/msabramo/requests-unixsocket/blob/master/requests_unixsocket/adapters.py
+
+# The following was adapted from some code from docker-py
+# https://github.com/docker/docker-py/blob/master/docker/transport/unixconn.py
+
[docs]class UnixHTTPConnection(httplib.HTTPConnection, object): + + def __init__(self, socket_path, timeout=60): + """Create an HTTP connection to a unix domain socket + + :param socket_path: The path to the Unix domain socket + :param timeout: Number of seconds to timeout the connection + """ + super(UnixHTTPConnection, self).__init__('localhost', timeout=timeout) + self.socket_path = socket_path + self.sock = None + + def __del__(self): # base class does not have d'tor + if self.sock: + self.sock.close() + +
[docs] def connect(self): + sock = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM) + sock.settimeout(self.timeout) + sock.connect(self.socket_path) + self.sock = sock +
+
[docs]class UnixHTTPConnectionPool(urllib3.connectionpool.HTTPConnectionPool): + + def __init__(self, socket_path, timeout=60): + """Create a connection pool using a Unix domain socket + + :param socket_path: The path to the Unix domain socket + :param timeout: Number of seconds to timeout the connection + """ + super(UnixHTTPConnectionPool, self).__init__('localhost', timeout=timeout) + self.socket_path = socket_path + + def _new_conn(self): + return UnixHTTPConnection(self.socket_path, self.timeout) +
+if __name__ == '__main__': + http = UnixHTTPConnectionPool("/var/run/weldr/api.socket") + r = http.request("GET", "/api/v0/recipes/list") + print(r.data) +
+ +
+
+
+
+
+ + +
+
+
+
+ + + + \ No newline at end of file diff --git a/lorax-composer/_modules/index.html b/lorax-composer/_modules/index.html index 81e79b79..29109e22 100644 --- a/lorax-composer/_modules/index.html +++ b/lorax-composer/_modules/index.html @@ -8,7 +8,7 @@ - Overview: module code — Lorax 19.7.10 documentation + Overview: module code — Lorax 19.7.11 documentation @@ -16,7 +16,7 @@ - + @@ -47,6 +47,15 @@

All modules for which code is available

+
@@ -106,11 +115,11 @@
  • modules |
  • -
  • Lorax 19.7.10 documentation »
  • +
  • Lorax 19.7.11 documentation »
  • diff --git a/lorax-composer/_modules/pylorax.html b/lorax-composer/_modules/pylorax.html new file mode 100644 index 00000000..33c00f0d --- /dev/null +++ b/lorax-composer/_modules/pylorax.html @@ -0,0 +1,469 @@ + + + + + + + + + + pylorax — Lorax 19.7.11 documentation + + + + + + + + + + + + + + +
    +
    +
    +
    + +

    Source code for pylorax

    +#
    +# __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/__init__.html b/lorax-composer/_modules/pylorax/__init__.html index 26a0aca7..d59df96d 100644 --- a/lorax-composer/_modules/pylorax/__init__.html +++ b/lorax-composer/_modules/pylorax/__init__.html @@ -8,7 +8,7 @@ - pylorax.__init__ — Lorax 19.7.10 documentation + pylorax.__init__ — Lorax 19.7.11 documentation @@ -16,7 +16,7 @@ - - + + @@ -457,12 +458,13 @@
  • modules |
  • -
  • Lorax 19.7.10 documentation »
  • -
  • Module code »
  • +
  • Lorax 19.7.11 documentation »
  • +
  • Module code »
  • +
  • pylorax »
  • diff --git a/lorax-composer/_modules/pylorax/api.html b/lorax-composer/_modules/pylorax/api.html index f9c805c7..da884676 100644 --- a/lorax-composer/_modules/pylorax/api.html +++ b/lorax-composer/_modules/pylorax/api.html @@ -8,7 +8,7 @@ - pylorax.api — Lorax 19.7.10 documentation + pylorax.api — Lorax 19.7.11 documentation @@ -16,7 +16,7 @@ - - + + @@ -102,12 +103,13 @@
  • modules |
  • -
  • Lorax 19.7.10 documentation »
  • -
  • Module code »
  • +
  • Lorax 19.7.11 documentation »
  • +
  • Module code »
  • +
  • pylorax »
  • diff --git a/lorax-composer/_modules/pylorax/api/compose.html b/lorax-composer/_modules/pylorax/api/compose.html index 4bf6b085..8adb8505 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.10 documentation + pylorax.api.compose — Lorax 19.7.11 documentation @@ -16,7 +16,7 @@ - + @@ -38,8 +38,9 @@
  • modules |
  • -
  • Lorax 19.7.10 documentation »
  • +
  • Lorax 19.7.11 documentation »
  • Module code »
  • +
  • pylorax »
  • pylorax.api »
  • @@ -404,13 +405,14 @@
  • modules |
  • -
  • Lorax 19.7.10 documentation »
  • +
  • Lorax 19.7.11 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 d523f22a..a03c0b10 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.10 documentation + pylorax.api.config — Lorax 19.7.11 documentation @@ -16,7 +16,7 @@ - + @@ -38,8 +38,9 @@
  • modules |
  • -
  • Lorax 19.7.10 documentation »
  • +
  • Lorax 19.7.11 documentation »
  • Module code »
  • +
  • pylorax »
  • pylorax.api »
  • @@ -189,13 +190,14 @@
  • modules |
  • -
  • Lorax 19.7.10 documentation »
  • +
  • Lorax 19.7.11 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 2c937fab..d30b6836 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.10 documentation + pylorax.api.crossdomain — Lorax 19.7.11 documentation @@ -16,7 +16,7 @@ - + @@ -38,8 +38,9 @@
  • modules |
  • -
  • Lorax 19.7.10 documentation »
  • +
  • Lorax 19.7.11 documentation »
  • Module code »
  • +
  • pylorax »
  • pylorax.api »
  • @@ -147,13 +148,14 @@
  • modules |
  • -
  • Lorax 19.7.10 documentation »
  • +
  • Lorax 19.7.11 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 eafcd805..a62b4581 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.10 documentation + pylorax.api.projects — Lorax 19.7.11 documentation @@ -16,7 +16,7 @@ - + @@ -38,8 +38,9 @@
  • modules |
  • -
  • Lorax 19.7.10 documentation »
  • +
  • Lorax 19.7.11 documentation »
  • Module code »
  • +
  • pylorax »
  • pylorax.api »
  • @@ -346,13 +347,14 @@
  • modules |
  • -
  • Lorax 19.7.10 documentation »
  • +
  • Lorax 19.7.11 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 4d376ff7..eed563f9 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.10 documentation + pylorax.api.queue — Lorax 19.7.11 documentation @@ -16,7 +16,7 @@ - + @@ -38,8 +38,9 @@
  • modules |
  • -
  • Lorax 19.7.10 documentation »
  • +
  • Lorax 19.7.11 documentation »
  • Module code »
  • +
  • pylorax »
  • pylorax.api »
  • @@ -646,13 +647,14 @@
  • modules |
  • -
  • Lorax 19.7.10 documentation »
  • +
  • Lorax 19.7.11 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 dcbaa657..4bdaa9f1 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.10 documentation + pylorax.api.recipes — Lorax 19.7.11 documentation @@ -16,7 +16,7 @@ - + @@ -38,8 +38,9 @@
  • modules |
  • -
  • Lorax 19.7.10 documentation »
  • +
  • Lorax 19.7.11 documentation »
  • Module code »
  • +
  • pylorax »
  • pylorax.api »
  • @@ -965,13 +966,14 @@
  • modules |
  • -
  • Lorax 19.7.10 documentation »
  • +
  • Lorax 19.7.11 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 1d8ef406..66ca111f 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.10 documentation + pylorax.api.server — Lorax 19.7.11 documentation @@ -16,7 +16,7 @@ - + @@ -38,8 +38,9 @@
  • modules |
  • -
  • Lorax 19.7.10 documentation »
  • +
  • Lorax 19.7.11 documentation »
  • Module code »
  • +
  • pylorax »
  • pylorax.api »
  • @@ -138,13 +139,14 @@
  • modules |
  • -
  • Lorax 19.7.10 documentation »
  • +
  • Lorax 19.7.11 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 89a10432..9a4abb01 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.10 documentation + pylorax.api.v0 — Lorax 19.7.11 documentation @@ -16,7 +16,7 @@ - + @@ -38,8 +38,9 @@
  • modules |
  • -
  • Lorax 19.7.10 documentation »
  • +
  • Lorax 19.7.11 documentation »
  • Module code »
  • +
  • pylorax »
  • pylorax.api »
  • @@ -1659,13 +1660,14 @@
  • modules |
  • -
  • Lorax 19.7.10 documentation »
  • +
  • Lorax 19.7.11 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 98569aa8..74a0e857 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.10 documentation + pylorax.api.workspace — Lorax 19.7.11 documentation @@ -16,7 +16,7 @@ - + @@ -38,8 +38,9 @@
  • modules |
  • -
  • Lorax 19.7.10 documentation »
  • +
  • Lorax 19.7.11 documentation »
  • Module code »
  • +
  • pylorax »
  • pylorax.api »
  • @@ -182,13 +183,14 @@
  • modules |
  • -
  • Lorax 19.7.10 documentation »
  • +
  • Lorax 19.7.11 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 b7738e85..d3da1561 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.10 documentation + pylorax.api.yumbase — Lorax 19.7.11 documentation @@ -16,7 +16,7 @@ - + @@ -38,8 +38,9 @@
  • modules |
  • -
  • Lorax 19.7.10 documentation »
  • +
  • Lorax 19.7.11 documentation »
  • Module code »
  • +
  • pylorax »
  • pylorax.api »
  • @@ -188,13 +189,14 @@
  • modules |
  • -
  • Lorax 19.7.10 documentation »
  • +
  • Lorax 19.7.11 documentation »
  • Module code »
  • +
  • pylorax »
  • pylorax.api »
  • diff --git a/lorax-composer/_modules/pylorax/base.html b/lorax-composer/_modules/pylorax/base.html index 264f2b61..e8d3d31b 100644 --- a/lorax-composer/_modules/pylorax/base.html +++ b/lorax-composer/_modules/pylorax/base.html @@ -8,7 +8,7 @@ - pylorax.base — Lorax 19.7.10 documentation + pylorax.base — Lorax 19.7.11 documentation @@ -16,7 +16,7 @@ - - + + @@ -149,12 +150,13 @@
  • modules |
  • -
  • Lorax 19.7.10 documentation »
  • -
  • Module code »
  • +
  • Lorax 19.7.11 documentation »
  • +
  • Module code »
  • +
  • pylorax »
  • diff --git a/lorax-composer/_modules/pylorax/buildstamp.html b/lorax-composer/_modules/pylorax/buildstamp.html index b44fc7a9..3bd478eb 100644 --- a/lorax-composer/_modules/pylorax/buildstamp.html +++ b/lorax-composer/_modules/pylorax/buildstamp.html @@ -8,7 +8,7 @@ - pylorax.buildstamp — Lorax 19.7.10 documentation + pylorax.buildstamp — Lorax 19.7.11 documentation @@ -16,7 +16,7 @@ - - + + @@ -140,12 +141,13 @@
  • modules |
  • -
  • Lorax 19.7.10 documentation »
  • -
  • Module code »
  • +
  • Lorax 19.7.11 documentation »
  • +
  • Module code »
  • +
  • pylorax »
  • diff --git a/lorax-composer/_modules/pylorax/creator.html b/lorax-composer/_modules/pylorax/creator.html index 7ac71cc5..118e801f 100644 --- a/lorax-composer/_modules/pylorax/creator.html +++ b/lorax-composer/_modules/pylorax/creator.html @@ -8,7 +8,7 @@ - pylorax.creator — Lorax 19.7.10 documentation + pylorax.creator — Lorax 19.7.11 documentation @@ -16,7 +16,7 @@ - - + + @@ -695,12 +696,13 @@
  • modules |
  • -
  • Lorax 19.7.10 documentation »
  • -
  • Module code »
  • +
  • Lorax 19.7.11 documentation »
  • +
  • Module code »
  • +
  • pylorax »
  • diff --git a/lorax-composer/_modules/pylorax/decorators.html b/lorax-composer/_modules/pylorax/decorators.html index 2e6a9420..01f8846a 100644 --- a/lorax-composer/_modules/pylorax/decorators.html +++ b/lorax-composer/_modules/pylorax/decorators.html @@ -8,7 +8,7 @@ - pylorax.decorators — Lorax 19.7.10 documentation + pylorax.decorators — Lorax 19.7.11 documentation @@ -16,7 +16,7 @@ - - + + @@ -112,12 +113,13 @@
  • modules |
  • -
  • Lorax 19.7.10 documentation »
  • -
  • Module code »
  • +
  • Lorax 19.7.11 documentation »
  • +
  • Module code »
  • +
  • pylorax »
  • diff --git a/lorax-composer/_modules/pylorax/discinfo.html b/lorax-composer/_modules/pylorax/discinfo.html index 0cb566ec..4619dbae 100644 --- a/lorax-composer/_modules/pylorax/discinfo.html +++ b/lorax-composer/_modules/pylorax/discinfo.html @@ -8,7 +8,7 @@ - pylorax.discinfo — Lorax 19.7.10 documentation + pylorax.discinfo — Lorax 19.7.11 documentation @@ -16,7 +16,7 @@ - - + + @@ -121,12 +122,13 @@
  • modules |
  • -
  • Lorax 19.7.10 documentation »
  • -
  • Module code »
  • +
  • Lorax 19.7.11 documentation »
  • +
  • Module code »
  • +
  • pylorax »
  • diff --git a/lorax-composer/_modules/pylorax/executils.html b/lorax-composer/_modules/pylorax/executils.html index cb1d5366..af95bcf7 100644 --- a/lorax-composer/_modules/pylorax/executils.html +++ b/lorax-composer/_modules/pylorax/executils.html @@ -8,7 +8,7 @@ - pylorax.executils — Lorax 19.7.10 documentation + pylorax.executils — Lorax 19.7.11 documentation @@ -16,7 +16,7 @@ - - + + @@ -507,12 +508,13 @@
  • modules |
  • -
  • Lorax 19.7.10 documentation »
  • -
  • Module code »
  • +
  • Lorax 19.7.11 documentation »
  • +
  • Module code »
  • +
  • pylorax »
  • diff --git a/lorax-composer/_modules/pylorax/imgutils.html b/lorax-composer/_modules/pylorax/imgutils.html index bd9369bb..216ad1ea 100644 --- a/lorax-composer/_modules/pylorax/imgutils.html +++ b/lorax-composer/_modules/pylorax/imgutils.html @@ -8,7 +8,7 @@ - pylorax.imgutils — Lorax 19.7.10 documentation + pylorax.imgutils — Lorax 19.7.11 documentation @@ -16,7 +16,7 @@ - - + + @@ -547,12 +548,13 @@
  • modules |
  • -
  • Lorax 19.7.10 documentation »
  • -
  • Module code »
  • +
  • Lorax 19.7.11 documentation »
  • +
  • Module code »
  • +
  • pylorax »
  • diff --git a/lorax-composer/_modules/pylorax/installer.html b/lorax-composer/_modules/pylorax/installer.html index 9cf57f2c..d39f2a3f 100644 --- a/lorax-composer/_modules/pylorax/installer.html +++ b/lorax-composer/_modules/pylorax/installer.html @@ -8,7 +8,7 @@ - pylorax.installer — Lorax 19.7.10 documentation + pylorax.installer — Lorax 19.7.11 documentation @@ -16,7 +16,7 @@ - - + + @@ -470,12 +471,13 @@
  • modules |
  • -
  • Lorax 19.7.10 documentation »
  • -
  • Module code »
  • +
  • Lorax 19.7.11 documentation »
  • +
  • Module code »
  • +
  • pylorax »
  • diff --git a/lorax-composer/_modules/pylorax/logmonitor.html b/lorax-composer/_modules/pylorax/logmonitor.html index 9d0fed1c..e101c133 100644 --- a/lorax-composer/_modules/pylorax/logmonitor.html +++ b/lorax-composer/_modules/pylorax/logmonitor.html @@ -8,7 +8,7 @@ - pylorax.logmonitor — Lorax 19.7.10 documentation + pylorax.logmonitor — Lorax 19.7.11 documentation @@ -16,7 +16,7 @@ - - + + @@ -205,12 +206,13 @@
  • modules |
  • -
  • Lorax 19.7.10 documentation »
  • -
  • Module code »
  • +
  • Lorax 19.7.11 documentation »
  • +
  • Module code »
  • +
  • pylorax »
  • diff --git a/lorax-composer/_modules/pylorax/ltmpl.html b/lorax-composer/_modules/pylorax/ltmpl.html index f5eabb61..ae35dbfd 100644 --- a/lorax-composer/_modules/pylorax/ltmpl.html +++ b/lorax-composer/_modules/pylorax/ltmpl.html @@ -8,7 +8,7 @@ - pylorax.ltmpl — Lorax 19.7.10 documentation + pylorax.ltmpl — Lorax 19.7.11 documentation @@ -16,7 +16,7 @@ - - + + @@ -747,12 +748,13 @@
  • modules |
  • -
  • Lorax 19.7.10 documentation »
  • -
  • Module code »
  • +
  • Lorax 19.7.11 documentation »
  • +
  • Module code »
  • +
  • pylorax »
  • diff --git a/lorax-composer/_modules/pylorax/sysutils.html b/lorax-composer/_modules/pylorax/sysutils.html index c0b86bb6..576aa7a6 100644 --- a/lorax-composer/_modules/pylorax/sysutils.html +++ b/lorax-composer/_modules/pylorax/sysutils.html @@ -8,7 +8,7 @@ - pylorax.sysutils — Lorax 19.7.10 documentation + pylorax.sysutils — Lorax 19.7.11 documentation @@ -16,7 +16,7 @@ - - + + @@ -190,12 +191,13 @@
  • modules |
  • -
  • Lorax 19.7.10 documentation »
  • -
  • Module code »
  • +
  • Lorax 19.7.11 documentation »
  • +
  • Module code »
  • +
  • pylorax »
  • diff --git a/lorax-composer/_modules/pylorax/treebuilder.html b/lorax-composer/_modules/pylorax/treebuilder.html index 6fae699e..3770a870 100644 --- a/lorax-composer/_modules/pylorax/treebuilder.html +++ b/lorax-composer/_modules/pylorax/treebuilder.html @@ -8,7 +8,7 @@ - pylorax.treebuilder — Lorax 19.7.10 documentation + pylorax.treebuilder — Lorax 19.7.11 documentation @@ -16,7 +16,7 @@ - - + + @@ -402,12 +403,13 @@
  • modules |
  • -
  • Lorax 19.7.10 documentation »
  • -
  • Module code »
  • +
  • Lorax 19.7.11 documentation »
  • +
  • Module code »
  • +
  • pylorax »
  • diff --git a/lorax-composer/_modules/pylorax/treeinfo.html b/lorax-composer/_modules/pylorax/treeinfo.html index 7b124a85..a56176ce 100644 --- a/lorax-composer/_modules/pylorax/treeinfo.html +++ b/lorax-composer/_modules/pylorax/treeinfo.html @@ -8,7 +8,7 @@ - pylorax.treeinfo — Lorax 19.7.10 documentation + pylorax.treeinfo — Lorax 19.7.11 documentation @@ -16,7 +16,7 @@ - - + + @@ -139,12 +140,13 @@
  • modules |
  • -
  • Lorax 19.7.10 documentation »
  • -
  • Module code »
  • +
  • Lorax 19.7.11 documentation »
  • +
  • Module code »
  • +
  • pylorax »
  • diff --git a/lorax-composer/_modules/pylorax/yumhelper.html b/lorax-composer/_modules/pylorax/yumhelper.html index 38b28734..6e43a1f3 100644 --- a/lorax-composer/_modules/pylorax/yumhelper.html +++ b/lorax-composer/_modules/pylorax/yumhelper.html @@ -8,7 +8,7 @@ - pylorax.yumhelper — Lorax 19.7.10 documentation + pylorax.yumhelper — Lorax 19.7.11 documentation @@ -16,7 +16,7 @@ - - + + @@ -207,12 +208,13 @@
  • modules |
  • -
  • Lorax 19.7.10 documentation »
  • -
  • Module code »
  • +
  • Lorax 19.7.11 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..ef03b94a --- /dev/null +++ b/lorax-composer/_sources/composer.cli.txt @@ -0,0 +1,51 @@ +cli Package +=========== + +:mod:`cli` Package +------------------ + +.. automodule:: composer.cli + :members: + :undoc-members: + :show-inheritance: + +:mod:`compose` Module +--------------------- + +.. automodule:: composer.cli.compose + :members: + :undoc-members: + :show-inheritance: + +:mod:`modules` Module +--------------------- + +.. automodule:: composer.cli.modules + :members: + :undoc-members: + :show-inheritance: + +:mod:`projects` Module +---------------------- + +.. automodule:: composer.cli.projects + :members: + :undoc-members: + :show-inheritance: + +:mod:`recipes` Module +--------------------- + +.. automodule:: composer.cli.recipes + :members: + :undoc-members: + :show-inheritance: + +:mod:`utilities` Module +----------------------- + +.. automodule:: composer.cli.utilities + :members: + :undoc-members: + :show-inheritance: + diff --git a/lorax-composer/_sources/composer.txt b/lorax-composer/_sources/composer.txt new file mode 100644 index 00000000..20a6703d --- /dev/null +++ b/lorax-composer/_sources/composer.txt @@ -0,0 +1,34 @@ +composer Package +================ + +:mod:`composer` Package +----------------------- + +.. automodule:: composer + :members: + :undoc-members: + :show-inheritance: + +:mod:`http_client` Module +------------------------- + +.. automodule:: composer.http_client + :members: + :undoc-members: + :show-inheritance: + +:mod:`unix_socket` Module +------------------------- + +.. automodule:: composer.unix_socket + :members: + :undoc-members: + :show-inheritance: + +Subpackages +----------- + +.. toctree:: + + composer.cli + diff --git a/lorax-composer/_sources/livemedia-creator.txt b/lorax-composer/_sources/livemedia-creator.txt new file mode 100644 index 00000000..94d6a331 --- /dev/null +++ b/lorax-composer/_sources/livemedia-creator.txt @@ -0,0 +1,391 @@ +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