From a525eb4fbe83833e4c8db83dc3ea74033dff8c2c Mon Sep 17 00:00:00 2001 From: "Brian C. Lane" Date: Mon, 6 May 2019 16:54:17 -0700 Subject: [PATCH] Add lorax 30.19 documentation --- f30-branch/.buildinfo | 4 + f30-branch/.doctrees/composer-cli.doctree | Bin 0 -> 42649 bytes f30-branch/.doctrees/composer.cli.doctree | Bin 0 -> 253727 bytes f30-branch/.doctrees/composer.doctree | Bin 0 -> 63408 bytes f30-branch/.doctrees/environment.pickle | Bin 0 -> 2081254 bytes f30-branch/.doctrees/index.doctree | Bin 0 -> 6413 bytes f30-branch/.doctrees/intro.doctree | Bin 0 -> 9092 bytes .../.doctrees/livemedia-creator.doctree | Bin 0 -> 168624 bytes f30-branch/.doctrees/lorax-composer.doctree | Bin 0 -> 112455 bytes f30-branch/.doctrees/lorax.doctree | Bin 0 -> 70322 bytes f30-branch/.doctrees/modules.doctree | Bin 0 -> 2507 bytes f30-branch/.doctrees/product-images.doctree | Bin 0 -> 7036 bytes f30-branch/.doctrees/pylorax.api.doctree | Bin 0 -> 539731 bytes f30-branch/.doctrees/pylorax.doctree | Bin 0 -> 403576 bytes f30-branch/README | 5 + f30-branch/_modules/composer/cli.html | 254 + .../_modules/composer/cli/blueprints.html | 711 ++ f30-branch/_modules/composer/cli/cmdline.html | 250 + f30-branch/_modules/composer/cli/compose.html | 714 ++ f30-branch/_modules/composer/cli/modules.html | 248 + .../_modules/composer/cli/projects.html | 310 + f30-branch/_modules/composer/cli/sources.html | 352 + f30-branch/_modules/composer/cli/status.html | 256 + .../_modules/composer/cli/utilities.html | 295 + f30-branch/_modules/composer/http_client.html | 458 + f30-branch/_modules/composer/unix_socket.html | 259 + f30-branch/_modules/index.html | 234 + f30-branch/_modules/pylorax.html | 650 + f30-branch/_modules/pylorax/api/bisect.html | 249 + .../_modules/pylorax/api/checkparams.html | 244 + f30-branch/_modules/pylorax/api/cmdline.html | 263 + f30-branch/_modules/pylorax/api/compose.html | 1405 +++ f30-branch/_modules/pylorax/api/config.html | 338 + .../_modules/pylorax/api/crossdomain.html | 264 + f30-branch/_modules/pylorax/api/dnfbase.html | 381 + f30-branch/_modules/pylorax/api/gitrpm.html | 421 + f30-branch/_modules/pylorax/api/projects.html | 783 ++ f30-branch/_modules/pylorax/api/queue.html | 847 ++ f30-branch/_modules/pylorax/api/recipes.html | 1219 ++ f30-branch/_modules/pylorax/api/server.html | 299 + .../_modules/pylorax/api/timestamp.html | 251 + f30-branch/_modules/pylorax/api/v0.html | 2268 ++++ .../_modules/pylorax/api/workspace.html | 299 + f30-branch/_modules/pylorax/base.html | 267 + f30-branch/_modules/pylorax/buildstamp.html | 266 + f30-branch/_modules/pylorax/cmdline.html | 515 + f30-branch/_modules/pylorax/creator.html | 931 ++ f30-branch/_modules/pylorax/decorators.html | 230 + f30-branch/_modules/pylorax/discinfo.html | 245 + f30-branch/_modules/pylorax/dnfbase.html | 385 + f30-branch/_modules/pylorax/dnfhelper.html | 310 + f30-branch/_modules/pylorax/executils.html | 549 + f30-branch/_modules/pylorax/imgutils.html | 754 ++ f30-branch/_modules/pylorax/installer.html | 830 ++ f30-branch/_modules/pylorax/ltmpl.html | 1087 ++ f30-branch/_modules/pylorax/monitor.html | 403 + f30-branch/_modules/pylorax/mount.html | 319 + f30-branch/_modules/pylorax/sysutils.html | 332 + f30-branch/_modules/pylorax/treebuilder.html | 603 + f30-branch/_modules/pylorax/treeinfo.html | 263 + f30-branch/_sources/composer-cli.rst.txt | 62 + f30-branch/_sources/composer.cli.rst.txt | 86 + f30-branch/_sources/composer.rst.txt | 37 + f30-branch/_sources/index.rst.txt | 34 + f30-branch/_sources/intro.rst.txt | 67 + f30-branch/_sources/livemedia-creator.rst.txt | 657 + f30-branch/_sources/lorax-composer.rst.txt | 504 + f30-branch/_sources/lorax.rst.txt | 152 + f30-branch/_sources/modules.rst.txt | 8 + f30-branch/_sources/product-images.rst.txt | 27 + f30-branch/_sources/pylorax.api.rst.txt | 150 + f30-branch/_sources/pylorax.rst.txt | 165 + f30-branch/_static/ajax-loader.gif | Bin 0 -> 673 bytes f30-branch/_static/basic.css | 676 + f30-branch/_static/comment-bright.png | Bin 0 -> 756 bytes f30-branch/_static/comment-close.png | Bin 0 -> 829 bytes f30-branch/_static/comment.png | Bin 0 -> 641 bytes f30-branch/_static/css/badge_only.css | 1 + f30-branch/_static/css/theme.css | 6 + f30-branch/_static/doctools.js | 315 + f30-branch/_static/documentation_options.js | 10 + f30-branch/_static/down-pressed.png | Bin 0 -> 222 bytes f30-branch/_static/down.png | Bin 0 -> 202 bytes f30-branch/_static/file.png | Bin 0 -> 286 bytes f30-branch/_static/fonts/Inconsolata-Bold.ttf | Bin 0 -> 108360 bytes .../_static/fonts/Inconsolata-Regular.ttf | Bin 0 -> 95960 bytes f30-branch/_static/fonts/Lato-Bold.ttf | Bin 0 -> 657188 bytes f30-branch/_static/fonts/Lato-BoldItalic.ttf | Bin 0 -> 699008 bytes f30-branch/_static/fonts/Lato-Italic.ttf | Bin 0 -> 723544 bytes f30-branch/_static/fonts/Lato-Regular.ttf | Bin 0 -> 657212 bytes f30-branch/_static/fonts/Lato/lato-bold.eot | Bin 0 -> 256056 bytes f30-branch/_static/fonts/Lato/lato-bold.ttf | Bin 0 -> 657188 bytes f30-branch/_static/fonts/Lato/lato-bold.woff | Bin 0 -> 309728 bytes f30-branch/_static/fonts/Lato/lato-bold.woff2 | Bin 0 -> 184912 bytes .../_static/fonts/Lato/lato-bolditalic.eot | Bin 0 -> 266158 bytes .../_static/fonts/Lato/lato-bolditalic.ttf | Bin 0 -> 699008 bytes .../_static/fonts/Lato/lato-bolditalic.woff | Bin 0 -> 323344 bytes .../_static/fonts/Lato/lato-bolditalic.woff2 | Bin 0 -> 193308 bytes f30-branch/_static/fonts/Lato/lato-italic.eot | Bin 0 -> 268604 bytes f30-branch/_static/fonts/Lato/lato-italic.ttf | Bin 0 -> 723544 bytes .../_static/fonts/Lato/lato-italic.woff | Bin 0 -> 328412 bytes .../_static/fonts/Lato/lato-italic.woff2 | Bin 0 -> 195704 bytes .../_static/fonts/Lato/lato-regular.eot | Bin 0 -> 253461 bytes .../_static/fonts/Lato/lato-regular.ttf | Bin 0 -> 657212 bytes .../_static/fonts/Lato/lato-regular.woff | Bin 0 -> 309192 bytes .../_static/fonts/Lato/lato-regular.woff2 | Bin 0 -> 182708 bytes f30-branch/_static/fonts/RobotoSlab-Bold.ttf | Bin 0 -> 170616 bytes .../_static/fonts/RobotoSlab-Regular.ttf | Bin 0 -> 169064 bytes .../fonts/RobotoSlab/roboto-slab-v7-bold.eot | Bin 0 -> 79520 bytes .../fonts/RobotoSlab/roboto-slab-v7-bold.ttf | Bin 0 -> 170616 bytes .../fonts/RobotoSlab/roboto-slab-v7-bold.woff | Bin 0 -> 87624 bytes .../RobotoSlab/roboto-slab-v7-bold.woff2 | Bin 0 -> 67312 bytes .../RobotoSlab/roboto-slab-v7-regular.eot | Bin 0 -> 78331 bytes .../RobotoSlab/roboto-slab-v7-regular.ttf | Bin 0 -> 169064 bytes .../RobotoSlab/roboto-slab-v7-regular.woff | Bin 0 -> 86288 bytes .../RobotoSlab/roboto-slab-v7-regular.woff2 | Bin 0 -> 66444 bytes .../_static/fonts/fontawesome-webfont.eot | Bin 0 -> 165742 bytes .../_static/fonts/fontawesome-webfont.svg | 2671 ++++ .../_static/fonts/fontawesome-webfont.ttf | Bin 0 -> 165548 bytes .../_static/fonts/fontawesome-webfont.woff | Bin 0 -> 98024 bytes .../_static/fonts/fontawesome-webfont.woff2 | Bin 0 -> 77160 bytes f30-branch/_static/jquery-3.2.1.js | 10253 ++++++++++++++++ f30-branch/_static/jquery.js | 4 + f30-branch/_static/js/modernizr.min.js | 4 + f30-branch/_static/js/theme.js | 3 + f30-branch/_static/language_data.js | 297 + f30-branch/_static/minus.png | Bin 0 -> 90 bytes f30-branch/_static/plus.png | Bin 0 -> 90 bytes f30-branch/_static/pygments.css | 69 + f30-branch/_static/searchtools.js | 481 + f30-branch/_static/underscore-1.3.1.js | 999 ++ f30-branch/_static/underscore.js | 31 + f30-branch/_static/up-pressed.png | Bin 0 -> 214 bytes f30-branch/_static/up.png | Bin 0 -> 203 bytes f30-branch/_static/websupport.js | 808 ++ f30-branch/composer-cli.html | 404 + f30-branch/composer.cli.html | 1211 ++ f30-branch/composer.html | 542 + f30-branch/genindex.html | 1212 ++ f30-branch/index.html | 238 + f30-branch/intro.html | 261 + f30-branch/livemedia-creator.html | 1209 ++ f30-branch/lorax-composer.html | 781 ++ f30-branch/lorax.html | 591 + f30-branch/modules.html | 293 + f30-branch/objects.inv | Bin 0 -> 3357 bytes f30-branch/product-images.html | 233 + f30-branch/py-modindex.html | 436 + f30-branch/pylorax.api.html | 2815 +++++ f30-branch/pylorax.html | 1802 +++ f30-branch/search.html | 217 + f30-branch/searchindex.js | 1 + 152 files changed, 53643 insertions(+) create mode 100644 f30-branch/.buildinfo create mode 100644 f30-branch/.doctrees/composer-cli.doctree create mode 100644 f30-branch/.doctrees/composer.cli.doctree create mode 100644 f30-branch/.doctrees/composer.doctree create mode 100644 f30-branch/.doctrees/environment.pickle create mode 100644 f30-branch/.doctrees/index.doctree create mode 100644 f30-branch/.doctrees/intro.doctree create mode 100644 f30-branch/.doctrees/livemedia-creator.doctree create mode 100644 f30-branch/.doctrees/lorax-composer.doctree create mode 100644 f30-branch/.doctrees/lorax.doctree create mode 100644 f30-branch/.doctrees/modules.doctree create mode 100644 f30-branch/.doctrees/product-images.doctree create mode 100644 f30-branch/.doctrees/pylorax.api.doctree create mode 100644 f30-branch/.doctrees/pylorax.doctree create mode 100644 f30-branch/README create mode 100644 f30-branch/_modules/composer/cli.html create mode 100644 f30-branch/_modules/composer/cli/blueprints.html create mode 100644 f30-branch/_modules/composer/cli/cmdline.html create mode 100644 f30-branch/_modules/composer/cli/compose.html create mode 100644 f30-branch/_modules/composer/cli/modules.html create mode 100644 f30-branch/_modules/composer/cli/projects.html create mode 100644 f30-branch/_modules/composer/cli/sources.html create mode 100644 f30-branch/_modules/composer/cli/status.html create mode 100644 f30-branch/_modules/composer/cli/utilities.html create mode 100644 f30-branch/_modules/composer/http_client.html create mode 100644 f30-branch/_modules/composer/unix_socket.html create mode 100644 f30-branch/_modules/index.html create mode 100644 f30-branch/_modules/pylorax.html create mode 100644 f30-branch/_modules/pylorax/api/bisect.html create mode 100644 f30-branch/_modules/pylorax/api/checkparams.html create mode 100644 f30-branch/_modules/pylorax/api/cmdline.html create mode 100644 f30-branch/_modules/pylorax/api/compose.html create mode 100644 f30-branch/_modules/pylorax/api/config.html create mode 100644 f30-branch/_modules/pylorax/api/crossdomain.html create mode 100644 f30-branch/_modules/pylorax/api/dnfbase.html create mode 100644 f30-branch/_modules/pylorax/api/gitrpm.html create mode 100644 f30-branch/_modules/pylorax/api/projects.html create mode 100644 f30-branch/_modules/pylorax/api/queue.html create mode 100644 f30-branch/_modules/pylorax/api/recipes.html create mode 100644 f30-branch/_modules/pylorax/api/server.html create mode 100644 f30-branch/_modules/pylorax/api/timestamp.html create mode 100644 f30-branch/_modules/pylorax/api/v0.html create mode 100644 f30-branch/_modules/pylorax/api/workspace.html create mode 100644 f30-branch/_modules/pylorax/base.html create mode 100644 f30-branch/_modules/pylorax/buildstamp.html create mode 100644 f30-branch/_modules/pylorax/cmdline.html create mode 100644 f30-branch/_modules/pylorax/creator.html create mode 100644 f30-branch/_modules/pylorax/decorators.html create mode 100644 f30-branch/_modules/pylorax/discinfo.html create mode 100644 f30-branch/_modules/pylorax/dnfbase.html create mode 100644 f30-branch/_modules/pylorax/dnfhelper.html create mode 100644 f30-branch/_modules/pylorax/executils.html create mode 100644 f30-branch/_modules/pylorax/imgutils.html create mode 100644 f30-branch/_modules/pylorax/installer.html create mode 100644 f30-branch/_modules/pylorax/ltmpl.html create mode 100644 f30-branch/_modules/pylorax/monitor.html create mode 100644 f30-branch/_modules/pylorax/mount.html create mode 100644 f30-branch/_modules/pylorax/sysutils.html create mode 100644 f30-branch/_modules/pylorax/treebuilder.html create mode 100644 f30-branch/_modules/pylorax/treeinfo.html create mode 100644 f30-branch/_sources/composer-cli.rst.txt create mode 100644 f30-branch/_sources/composer.cli.rst.txt create mode 100644 f30-branch/_sources/composer.rst.txt create mode 100644 f30-branch/_sources/index.rst.txt create mode 100644 f30-branch/_sources/intro.rst.txt create mode 100644 f30-branch/_sources/livemedia-creator.rst.txt create mode 100644 f30-branch/_sources/lorax-composer.rst.txt create mode 100644 f30-branch/_sources/lorax.rst.txt create mode 100644 f30-branch/_sources/modules.rst.txt create mode 100644 f30-branch/_sources/product-images.rst.txt create mode 100644 f30-branch/_sources/pylorax.api.rst.txt create mode 100644 f30-branch/_sources/pylorax.rst.txt create mode 100644 f30-branch/_static/ajax-loader.gif create mode 100644 f30-branch/_static/basic.css create mode 100644 f30-branch/_static/comment-bright.png create mode 100644 f30-branch/_static/comment-close.png create mode 100644 f30-branch/_static/comment.png create mode 100644 f30-branch/_static/css/badge_only.css create mode 100644 f30-branch/_static/css/theme.css create mode 100644 f30-branch/_static/doctools.js create mode 100644 f30-branch/_static/documentation_options.js create mode 100644 f30-branch/_static/down-pressed.png create mode 100644 f30-branch/_static/down.png create mode 100644 f30-branch/_static/file.png create mode 100644 f30-branch/_static/fonts/Inconsolata-Bold.ttf create mode 100644 f30-branch/_static/fonts/Inconsolata-Regular.ttf create mode 100644 f30-branch/_static/fonts/Lato-Bold.ttf create mode 100644 f30-branch/_static/fonts/Lato-BoldItalic.ttf create mode 100644 f30-branch/_static/fonts/Lato-Italic.ttf create mode 100644 f30-branch/_static/fonts/Lato-Regular.ttf create mode 100644 f30-branch/_static/fonts/Lato/lato-bold.eot create mode 100644 f30-branch/_static/fonts/Lato/lato-bold.ttf create mode 100644 f30-branch/_static/fonts/Lato/lato-bold.woff create mode 100644 f30-branch/_static/fonts/Lato/lato-bold.woff2 create mode 100644 f30-branch/_static/fonts/Lato/lato-bolditalic.eot create mode 100644 f30-branch/_static/fonts/Lato/lato-bolditalic.ttf create mode 100644 f30-branch/_static/fonts/Lato/lato-bolditalic.woff create mode 100644 f30-branch/_static/fonts/Lato/lato-bolditalic.woff2 create mode 100644 f30-branch/_static/fonts/Lato/lato-italic.eot create mode 100644 f30-branch/_static/fonts/Lato/lato-italic.ttf create mode 100644 f30-branch/_static/fonts/Lato/lato-italic.woff create mode 100644 f30-branch/_static/fonts/Lato/lato-italic.woff2 create mode 100644 f30-branch/_static/fonts/Lato/lato-regular.eot create mode 100644 f30-branch/_static/fonts/Lato/lato-regular.ttf create mode 100644 f30-branch/_static/fonts/Lato/lato-regular.woff create mode 100644 f30-branch/_static/fonts/Lato/lato-regular.woff2 create mode 100644 f30-branch/_static/fonts/RobotoSlab-Bold.ttf create mode 100644 f30-branch/_static/fonts/RobotoSlab-Regular.ttf create mode 100644 f30-branch/_static/fonts/RobotoSlab/roboto-slab-v7-bold.eot create mode 100644 f30-branch/_static/fonts/RobotoSlab/roboto-slab-v7-bold.ttf create mode 100644 f30-branch/_static/fonts/RobotoSlab/roboto-slab-v7-bold.woff create mode 100644 f30-branch/_static/fonts/RobotoSlab/roboto-slab-v7-bold.woff2 create mode 100644 f30-branch/_static/fonts/RobotoSlab/roboto-slab-v7-regular.eot create mode 100644 f30-branch/_static/fonts/RobotoSlab/roboto-slab-v7-regular.ttf create mode 100644 f30-branch/_static/fonts/RobotoSlab/roboto-slab-v7-regular.woff create mode 100644 f30-branch/_static/fonts/RobotoSlab/roboto-slab-v7-regular.woff2 create mode 100644 f30-branch/_static/fonts/fontawesome-webfont.eot create mode 100644 f30-branch/_static/fonts/fontawesome-webfont.svg create mode 100644 f30-branch/_static/fonts/fontawesome-webfont.ttf create mode 100644 f30-branch/_static/fonts/fontawesome-webfont.woff create mode 100644 f30-branch/_static/fonts/fontawesome-webfont.woff2 create mode 100644 f30-branch/_static/jquery-3.2.1.js create mode 100644 f30-branch/_static/jquery.js create mode 100644 f30-branch/_static/js/modernizr.min.js create mode 100644 f30-branch/_static/js/theme.js create mode 100644 f30-branch/_static/language_data.js create mode 100644 f30-branch/_static/minus.png create mode 100644 f30-branch/_static/plus.png create mode 100644 f30-branch/_static/pygments.css create mode 100644 f30-branch/_static/searchtools.js create mode 100644 f30-branch/_static/underscore-1.3.1.js create mode 100644 f30-branch/_static/underscore.js create mode 100644 f30-branch/_static/up-pressed.png create mode 100644 f30-branch/_static/up.png create mode 100644 f30-branch/_static/websupport.js create mode 100644 f30-branch/composer-cli.html create mode 100644 f30-branch/composer.cli.html create mode 100644 f30-branch/composer.html create mode 100644 f30-branch/genindex.html create mode 100644 f30-branch/index.html create mode 100644 f30-branch/intro.html create mode 100644 f30-branch/livemedia-creator.html create mode 100644 f30-branch/lorax-composer.html create mode 100644 f30-branch/lorax.html create mode 100644 f30-branch/modules.html create mode 100644 f30-branch/objects.inv create mode 100644 f30-branch/product-images.html create mode 100644 f30-branch/py-modindex.html create mode 100644 f30-branch/pylorax.api.html create mode 100644 f30-branch/pylorax.html create mode 100644 f30-branch/search.html create mode 100644 f30-branch/searchindex.js diff --git a/f30-branch/.buildinfo b/f30-branch/.buildinfo new file mode 100644 index 00000000..8f0cd80b --- /dev/null +++ b/f30-branch/.buildinfo @@ -0,0 +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: 150abd10d5534e2058453340c8ae7e16 +tags: 645f666f9bcd5a90fca523b33c5a78b7 diff --git a/f30-branch/.doctrees/composer-cli.doctree b/f30-branch/.doctrees/composer-cli.doctree new file mode 100644 index 0000000000000000000000000000000000000000..0f18d8a510abde2d19ad1b639182d6bd78bbad92 GIT binary patch literal 42649 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 HpE&UU1OfH6 literal 0 HcmV?d00001 diff --git a/f30-branch/.doctrees/composer.doctree b/f30-branch/.doctrees/composer.doctree new file mode 100644 index 0000000000000000000000000000000000000000..46edb66073ac00865f90acbb38b19586c203a33d GIT binary patch literal 63408 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 literal 0 HcmV?d00001 diff --git a/f30-branch/.doctrees/environment.pickle b/f30-branch/.doctrees/environment.pickle new file mode 100644 index 0000000000000000000000000000000000000000..8b71630b25a83841cf3a1edf3773134f9991026e GIT binary patch literal 2081254 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

i|t0UVlAQ>)i`1tfV43Gn#GE-CT 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⁢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

&RNMSqtxq7SpsrNZV~>f@9d>iYy<^ffJ`Emi%E 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 H&ldgxl6?8r literal 0 HcmV?d00001 diff --git a/f30-branch/.doctrees/index.doctree b/f30-branch/.doctrees/index.doctree new file mode 100644 index 0000000000000000000000000000000000000000..ff73c1699f3a676405534deb2d91a49965767ed7 GIT binary patch literal 6413 zcmds5-ESOM6}MyW+Ut+CC7E_{eVQILKAeKU_hl(K?sTRAMo{r)CU9-Z#*Exe*jNKedc%WeC+IGtuy!B zbI-ZwoZmV3-t*nrAD_A~rT?k68#sv+UcBiCE{jt&PlN&UW%{G^+Jp2v>4s`W_F)_( zk;76o1rCP`&yAR$-cL99l$vvb!Po-cP=9_si0q+J$g-sfd}Cn8l0`;Wc<@McyZ_EP2={>Tc6up_;!#Oj_n(^ zYlqU1T*O+Wfo+5ylgv9ZWMCX&!-gmJh2OhjbP{O@X~aB8JlC+L;W6xG!3lh63m-C= z5e0!X_GIA5h_OAx6%li!IAEJ8pSz#J%-J{Ckeu>=W!v{s9b(?#P1Ou-1XZSdG3nT9 z!Im--a2!BEAea$ujDREBo*l;^;=AG7gY0YGO~TQjV>|m1>uR3XaNzC4p-teK4 zLZf73m#8ud((kAE)FIZNr#&RvfVsjpby)}^O>So(&|3v;G;OIEnT{tit}qJR#F3^L z*gfbW*>eMsJUw7)N%)SJxQyx!L2`rJ+~JJ(xZwMIz|{P}9=a@)JiR*&e50xmq;}P8 z&kH)~Bh}=>by4mfspVW$NtpMOL5D@mP47;r1r4}3@V%q-5yku)x$hU@`>)pnrB-sc znumyg2>a|hX1-R$ER(aFbXe(fWgKQ;Hf3ODj*88(dJB0u4%OwHmw>N%9|yi&1?vVB z>y)g3^;&isj52JqAgC)}%+HLh&Ub^z2vAd_EXj;lBio0=;{wblWgj73VLv~pJFxC8 z%CL7U8TKiS^uf$pSZL+{&{u+0b#{{ST6F(;oP9-fCkx`gQ7bh=$|(OGZuot% zA+`3n#kKIBjX3k}be9e!Og8nZZbp9tDY3$-;k$wh2{M&J!m>lUR3s=o7 zuTwRBT|4Eyyi^gUbQP#>OUfpdRgrx=)XuiT<n(Kze;M#@QxSAJear-O%A3@YZ1);ZG04%=$F(xlc+e%<_m$f zuzBFGBXXnvDySz7nwTKr#6deCMdLy1-y_;+zx_8b=5O`CiT~};bpBAKcmT@t&BZu!jc2X^mP3v?V57&ES5(RNATm=>3YG$Hq!TMmr*8jI)y+1)h z)mg`8*oRm=F<8ZJj|; z0==u|VA=)|p>CufqbRt|Z`odgBdV2JC=&>D^Omf!kg= zY^c+0DBvq;%OozC8mi&4PSS(AHkElg`XHCt15jErLYIS#N*}7W@WVt}cnnbG?cr=m zWY^p_V}b9B6u4-s%1ABgjNYWI93zTCt#6pdn9m0Q{Y`crtptQ zVayUYu+U)w*D4O`AZAu|rACP(tpgE@45BNRD`L87!H|5Hb45b%D{2YG#7F`CkcH6Y zoXskl2dW*i(4uRBe(2F&a0v(tZ`Wu)d}p+*J)y0?PKu2#e4z=MhwstZ!U zrKZNbx&%NCtTMOC?OR!6?vXmDcO!hXS*ZB|+>(!60C^G3-*Sk=TaAy3I(%Jkkv5_`} literal 0 HcmV?d00001 diff --git a/f30-branch/.doctrees/intro.doctree b/f30-branch/.doctrees/intro.doctree new file mode 100644 index 0000000000000000000000000000000000000000..41392cbcbe4859fbbc02163261e95421781c5699 GIT binary patch literal 9092 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 literal 0 HcmV?d00001 diff --git a/f30-branch/.doctrees/livemedia-creator.doctree b/f30-branch/.doctrees/livemedia-creator.doctree new file mode 100644 index 0000000000000000000000000000000000000000..f52af60085ed6ed02d7bcbc01320d7676e1cc81c GIT binary patch literal 168624 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 literal 0 HcmV?d00001 diff --git a/f30-branch/.doctrees/lorax-composer.doctree b/f30-branch/.doctrees/lorax-composer.doctree new file mode 100644 index 0000000000000000000000000000000000000000..162d19acc7137399b07a9f25147b7c518a7bd250 GIT binary patch literal 112455 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 B+%y0H literal 0 HcmV?d00001 diff --git a/f30-branch/.doctrees/lorax.doctree b/f30-branch/.doctrees/lorax.doctree new file mode 100644 index 0000000000000000000000000000000000000000..22dd765c5ef1eca36b94a88d83a8c15b852148ba GIT binary patch literal 70322 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 literal 0 HcmV?d00001 diff --git a/f30-branch/.doctrees/modules.doctree b/f30-branch/.doctrees/modules.doctree new file mode 100644 index 0000000000000000000000000000000000000000..c96b257ed1a579b2945b3de6c3332a623930df93 GIT binary patch literal 2507 zcmZ8jOK%%D5O!kgWm~f2G(99S+M)<*x3JX$JrunZJsZ%eDRK!2YDtNNcgZaumUKwb z9$LfzZ)N{ae_B88%aR2wv@@Ld%s1RW4*vabv{(JYgz3yXncHin8MiSE6QkhD#ed@E zpYcPy2qQyRR(q5281^8M37Ip)ReT@CUKm)OIjL1AFM6Uc2Jhnq4tq|zoNpb4eQUCq zj&XYM3tuNcHS@1nxr6#y=$8}@Tok8%PQxK}&d9lU2rvb-FPVk&FvxOhEtq)kDO%KT zL*~l|VotML!xu@$hk>KZbQHxzyc9>`iP#VQHno@2TpPN+GJ1*{*lD4e&$+!e*2Qp; zOU2{OQ$I)C!%P>Y_KIyKR_7c}Y$>E#UsJ}Kf)1R{oZ;I=sAa_~diO=wSf#*v+ zZ}7YfgT^^>L>Y{%GP{^Ho-H@=?>GyCa#Mv2f`?&pZqZx@Np| zBHkRJV6?1yH46vJT+icu=nKghdU+qlje(9aRKA#V!&$u93x`!FtycLa-h1(t_)c7i zOY!y10V;WgF72cfPMWN*R)&@(;-%XeCjD>P4%BvLe$G1+^+3?JFfG~CCuxtMy`TEm zM1jWaMzHO!>#?{@x=+F+^Cry~X?i~k$1SJoGYW0Fb5bpVh-V!{7Dg+_(e}{)mF6C0 zZZRSzvoKn6#SL}Zpfw{#F|CdkC5WTi4T0@jkcSQFO%7^|N|7|B=@jmIso|R#&K6ob zr5)coym=DF32tZ%SueCHA~+L^0VxS)GQrluGcMl$EhSQXQMU@2VlOv%R#yM3>PnR6!W}u$}IpMkCnKq07W*82*NpPKo zex}s|IiehrkrGQtuA}IO3H45s2Ud70F;QdYi;{FxhpJW#iGeeU=E>km#X)J6O}&3$ zm6m&^$+}4MbjsJIww!EhRwI%*awn~<=ktUxY3V#qi8N6TS~Q5uaExRu=ASm(V%&(4 zcDsa0$xDKqupd-b<$Ihsa|+jKu*2uVxzhePovv1^>vH3SR@d4rr;OiCA!(;Eyhs|! zV$vXv7pSsBRfphTz4`Wglx1QT*&{cJ^Wf%n>&9t~TTF7dsN4uw%m@n$Ih`CP}U{TJPs@o(T$=puaVy(6O|92()i~iD1&*Ty5P3>sZm?NVPz# zTkxNybzuE`SMHQ!aWmj?17Cu32Wu~~T+>^^3o3Jh>&fuy>iih?xw?y3zhPLf0pJc( zBo&jGYeyV4OOCaIJN6kIVJaLx7*HErHR(DIXFJ^b<^>zp79G(~(~nCV{6S*Y@8X?! zYr`lp+@?gaK1A_uX2m)F--;s}#@nr`w8xpm8cObAvF0Q=wQ_Zs+nibs!J}ctT<2Z) zE74CLF+1tej>S>ywNg6Uv5&QAT-w7g9@}b8@Vx-AcVZKK+5jW?Sh3h6wZRV4JM5#5 SUA5$pr8UzaGF^Rse*HfOFy#XP literal 0 HcmV?d00001 diff --git a/f30-branch/.doctrees/product-images.doctree b/f30-branch/.doctrees/product-images.doctree new file mode 100644 index 0000000000000000000000000000000000000000..29957cfd9e8c5ea5c5c3b74b947f2bbd57cde430 GIT binary patch literal 7036 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

0ZF8nZNi1=*!7P5$I)Y0fMS#!37UqSDO# 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 literal 0 HcmV?d00001 diff --git a/f30-branch/README b/f30-branch/README new file mode 100644 index 00000000..615e8738 --- /dev/null +++ b/f30-branch/README @@ -0,0 +1,5 @@ +To build the docs for this branch run: +make test-in-docker +make docs-in-docker + +If you already have a welder/lorax-composer:latest docker image you can skip running 'test-in-docker'. diff --git a/f30-branch/_modules/composer/cli.html b/f30-branch/_modules/composer/cli.html new file mode 100644 index 00000000..bf312b71 --- /dev/null +++ b/f30-branch/_modules/composer/cli.html @@ -0,0 +1,254 @@ + + + + + + + + + + + composer.cli — Lorax 30.19 documentation + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/f30-branch/_modules/composer/cli/blueprints.html b/f30-branch/_modules/composer/cli/blueprints.html new file mode 100644 index 00000000..b40a065f --- /dev/null +++ b/f30-branch/_modules/composer/cli/blueprints.html @@ -0,0 +1,711 @@ + + + + + + + + + + + composer.cli.blueprints — Lorax 30.19 documentation + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + +
+ + + + + +
+ +
+ + + + + + + + + + + + + + + + + +
+ + + + +
+
+
+
+ +

Source code for composer.cli.blueprints

+#
+# 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
+
+from composer import http_client as client
+from composer.cli.help import blueprints_help
+from composer.cli.utilities import argify, frozen_toml_filename, toml_filename, handle_api_result
+from composer.cli.utilities import packageNEVRA
+
+
[docs]def blueprints_cmd(opts): + """Process blueprints commands + + :param opts: Cmdline arguments + :type opts: argparse.Namespace + :returns: Value to return from sys.exit() + :rtype: int + + This dispatches the blueprints commands to a function + """ + cmd_map = { + "list": blueprints_list, + "show": blueprints_show, + "changes": blueprints_changes, + "diff": blueprints_diff, + "save": blueprints_save, + "delete": blueprints_delete, + "depsolve": blueprints_depsolve, + "push": blueprints_push, + "freeze": blueprints_freeze, + "tag": blueprints_tag, + "undo": blueprints_undo, + "workspace": blueprints_workspace + } + if opts.args[1] == "help" or opts.args[1] == "--help": + print(blueprints_help) + return 0 + elif opts.args[1] not in cmd_map: + log.error("Unknown blueprints command: %s", opts.args[1]) + return 1 + + return cmd_map[opts.args[1]](opts.socket, opts.api_version, opts.args[2:], opts.json)
+ +
[docs]def blueprints_list(socket_path, api_version, args, show_json=False): + """Output the list of available blueprints + + :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 + + blueprints list + """ + api_route = client.api_url(api_version, "/blueprints/list") + result = client.get_url_json_unlimited(socket_path, api_route) + (rc, exit_now) = handle_api_result(result, show_json) + if exit_now: + return rc + + # "list" should output a plain list of identifiers, one per line. + print("\n".join(result["blueprints"])) + + return rc
+ +
[docs]def blueprints_show(socket_path, api_version, args, show_json=False): + """Show the blueprints, 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 + + blueprints show <blueprint,...> Display the blueprint in TOML format. + + Multiple blueprints will be separated by \n\n + """ + for blueprint in argify(args): + api_route = client.api_url(api_version, "/blueprints/info/%s?format=toml" % blueprint) + print(client.get_url_raw(socket_path, api_route) + "\n\n") + + return 0
+ +
[docs]def blueprints_changes(socket_path, api_version, args, show_json=False): + """Display the changes for each of the blueprints + + :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 + + blueprints changes <blueprint,...> Display the changes for each blueprint. + """ + def changes_total_fn(data): + """Return the maximum number of possible changes""" + + # Each blueprint can have a different total, return the largest one + return max([c["total"] for c in data["blueprints"]]) + + api_route = client.api_url(api_version, "/blueprints/changes/%s" % (",".join(argify(args)))) + result = client.get_url_json_unlimited(socket_path, api_route, total_fn=changes_total_fn) + (rc, exit_now) = handle_api_result(result, show_json) + if exit_now: + return rc + + for blueprint in result["blueprints"]: + print(blueprint["name"]) + for change in blueprint["changes"]: + prettyCommitDetails(change) + + return rc
+ +
[docs]def prettyCommitDetails(change, indent=4): + """Print the blueprint's change in a nice way + + :param change: The individual blueprint 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 blueprints_diff(socket_path, api_version, args, show_json=False): + """Display the differences between 2 versions of a blueprint + + :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 + + blueprints diff <blueprint-name> Display the differences between 2 versions of a blueprint. + <from-commit> Commit hash or NEWEST + <to-commit> Commit hash, NEWEST, or WORKSPACE + """ + if len(args) == 0: + log.error("blueprints diff is missing the blueprint name, from commit, and to commit") + return 1 + elif len(args) == 1: + log.error("blueprints diff is missing the from commit, and the to commit") + return 1 + elif len(args) == 2: + log.error("blueprints diff is missing the to commit") + return 1 + + api_route = client.api_url(api_version, "/blueprints/diff/%s/%s/%s" % (args[0], args[1], args[2])) + result = client.get_url_json(socket_path, api_route) + (rc, exit_now) = handle_api_result(result, show_json) + if exit_now: + return rc + + for diff in result["diff"]: + print(prettyDiffEntry(diff)) + + return rc
+ +
[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 list(diff["old"].keys())[0] + elif diff["new"]: + return list(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["new"][name(diff)]) + elif name(diff) == "Version": + return "%s -> %s" % (diff["old"][name(diff)], diff["new"][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"]) + elif name(diff) in ["Group"]: + return diff["new"][name(diff)]["name"] + 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"]) + elif name(diff) in ["Group"]: + return diff["old"][name(diff)]["name"] + else: + return " ".join([diff["old"][k] for k in diff["old"]]) + + return change(diff) + " " + name(diff) + " " + details(diff)
+ +
[docs]def blueprints_save(socket_path, api_version, args, show_json=False): + """Save the blueprint 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 + + blueprints save <blueprint,...> Save the blueprint to a file, <blueprint-name>.toml + """ + for blueprint in argify(args): + api_route = client.api_url(api_version, "/blueprints/info/%s?format=toml" % blueprint) + blueprint_toml = client.get_url_raw(socket_path, api_route) + open(toml_filename(blueprint), "w").write(blueprint_toml) + + return 0
+ +
[docs]def blueprints_delete(socket_path, api_version, args, show_json=False): + """Delete a blueprint 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 <blueprint> Delete a blueprint from the server + """ + api_route = client.api_url(api_version, "/blueprints/delete/%s" % args[0]) + result = client.delete_url_json(socket_path, api_route) + + return handle_api_result(result, show_json)[0]
+ +
[docs]def blueprints_depsolve(socket_path, api_version, args, show_json=False): + """Display the packages needed to install the blueprint + + :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 + + blueprints depsolve <blueprint,...> Display the packages needed to install the blueprint. + """ + api_route = client.api_url(api_version, "/blueprints/depsolve/%s" % (",".join(argify(args)))) + result = client.get_url_json(socket_path, api_route) + (rc, exit_now) = handle_api_result(result, show_json) + if exit_now: + return rc + + for blueprint in result["blueprints"]: + if blueprint["blueprint"].get("version", ""): + print("blueprint: %s v%s" % (blueprint["blueprint"]["name"], blueprint["blueprint"]["version"])) + else: + print("blueprint: %s" % (blueprint["blueprint"]["name"])) + for dep in blueprint["dependencies"]: + print(" " + packageNEVRA(dep)) + + return rc
+ +
[docs]def blueprints_push(socket_path, api_version, args, show_json=False): + """Push a blueprint TOML file to the server, updating the blueprint + + :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 <blueprint> Push a blueprint TOML file to the server. + """ + api_route = client.api_url(api_version, "/blueprints/new") + rval = 0 + for blueprint in argify(args): + if not os.path.exists(blueprint): + log.error("Missing blueprint file: %s", blueprint) + continue + blueprint_toml = open(blueprint, "r").read() + + result = client.post_url_toml(socket_path, api_route, blueprint_toml) + if handle_api_result(result, show_json)[0]: + rval = 1 + + return rval
+ +
[docs]def blueprints_freeze(socket_path, api_version, args, show_json=False): + """Handle the blueprints 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 + + blueprints freeze <blueprint,...> Display the frozen blueprint's modules and packages. + blueprints freeze show <blueprint,...> Display the frozen blueprint in TOML format. + blueprints freeze save <blueprint,...> Save the frozen blueprint to a file, <blueprint-name>.frozen.toml. + """ + if args[0] == "show": + return blueprints_freeze_show(socket_path, api_version, args[1:], show_json) + elif args[0] == "save": + return blueprints_freeze_save(socket_path, api_version, args[1:], show_json) + + if len(args) == 0: + log.error("freeze is missing the blueprint name") + return 1 + + api_route = client.api_url(api_version, "/blueprints/freeze/%s" % (",".join(argify(args)))) + result = client.get_url_json(socket_path, api_route) + (rc, exit_now) = handle_api_result(result, show_json) + if exit_now: + return rc + + for entry in result["blueprints"]: + blueprint = entry["blueprint"] + if blueprint.get("version", ""): + print("blueprint: %s v%s" % (blueprint["name"], blueprint["version"])) + else: + print("blueprint: %s" % (blueprint["name"])) + + for m in blueprint["modules"]: + print(" %s-%s" % (m["name"], m["version"])) + + for p in blueprint["packages"]: + print(" %s-%s" % (p["name"], p["version"])) + + return rc
+ +
[docs]def blueprints_freeze_show(socket_path, api_version, args, show_json=False): + """Show the frozen blueprint 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 + + blueprints freeze show <blueprint,...> Display the frozen blueprint in TOML format. + """ + if len(args) == 0: + log.error("freeze show is missing the blueprint name") + return 1 + + for blueprint in argify(args): + api_route = client.api_url(api_version, "/blueprints/freeze/%s?format=toml" % blueprint) + print(client.get_url_raw(socket_path, api_route)) + + return 0
+ +
[docs]def blueprints_freeze_save(socket_path, api_version, args, show_json=False): + """Save the frozen blueprint 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 + + blueprints freeze save <blueprint,...> Save the frozen blueprint to a file, <blueprint-name>.frozen.toml. + """ + if len(args) == 0: + log.error("freeze save is missing the blueprint name") + return 1 + + for blueprint in argify(args): + api_route = client.api_url(api_version, "/blueprints/freeze/%s?format=toml" % blueprint) + blueprint_toml = client.get_url_raw(socket_path, api_route) + open(frozen_toml_filename(blueprint), "w").write(blueprint_toml) + + return 0
+ +
[docs]def blueprints_tag(socket_path, api_version, args, show_json=False): + """Tag the most recent blueprint 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 + + blueprints tag <blueprint> Tag the most recent blueprint commit as a release. + """ + api_route = client.api_url(api_version, "/blueprints/tag/%s" % args[0]) + result = client.post_url(socket_path, api_route, "") + + return handle_api_result(result, show_json)[0]
+ +
[docs]def blueprints_undo(socket_path, api_version, args, show_json=False): + """Undo changes to a blueprint + + :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 + + blueprints undo <blueprint> <commit> Undo changes to a blueprint by reverting to the selected commit. + """ + if len(args) == 0: + log.error("undo is missing the blueprint 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, "/blueprints/undo/%s/%s" % (args[0], args[1])) + result = client.post_url(socket_path, api_route, "") + + return handle_api_result(result, show_json)[0]
+ +
[docs]def blueprints_workspace(socket_path, api_version, args, show_json=False): + """Push the blueprint 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 + + blueprints workspace <blueprint> Push the blueprint TOML to the temporary workspace storage. + """ + api_route = client.api_url(api_version, "/blueprints/workspace") + rval = 0 + for blueprint in argify(args): + if not os.path.exists(blueprint): + log.error("Missing blueprint file: %s", blueprint) + continue + blueprint_toml = open(blueprint, "r").read() + + result = client.post_url_toml(socket_path, api_route, blueprint_toml) + if handle_api_result(result, show_json)[0]: + rval = 1 + + return rval
+
+ +
+ +
+ + +
+
+ +
+ +
+ + + + + + + + + + + + \ No newline at end of file diff --git a/f30-branch/_modules/composer/cli/cmdline.html b/f30-branch/_modules/composer/cli/cmdline.html new file mode 100644 index 00000000..926b6959 --- /dev/null +++ b/f30-branch/_modules/composer/cli/cmdline.html @@ -0,0 +1,250 @@ + + + + + + + + + + + composer.cli.cmdline — Lorax 30.19 documentation + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + +
+ + + + + +
+ +
+ + + + + + + + + + + + + + + + + +
+ + + + +
+
+
+
+ +

Source code for composer.cli.cmdline

+#
+# 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 os
+import sys
+import argparse
+
+from composer import vernum
+from composer.cli.help import epilog
+
+VERSION = "{0}-{1}".format(os.path.basename(sys.argv[0]), vernum)
+
+
[docs]def composer_cli_parser(): + """ Return the ArgumentParser for composer-cli""" + + parser = argparse.ArgumentParser(description="Lorax Composer commandline tool", + epilog=epilog, + formatter_class=argparse.RawDescriptionHelpFormatter, + fromfile_prefix_chars="@") + + parser.add_argument("-j", "--json", action="store_true", default=False, + help="Output the raw JSON response instead of the normal output.") + parser.add_argument("-s", "--socket", default="/run/weldr/api.socket", metavar="SOCKET", + help="Path to the socket file to listen on") + parser.add_argument("--log", dest="logfile", default=None, metavar="LOG", + help="Path to logfile (./composer-cli.log)") + parser.add_argument("-a", "--api", dest="api_version", default="0", metavar="APIVER", + help="API Version to use") + parser.add_argument("--test", dest="testmode", default=0, type=int, metavar="TESTMODE", + help="Pass test mode to compose. 1=Mock compose with fail. 2=Mock compose with finished.") + parser.add_argument("-V", action="store_true", dest="showver", + help="show program's version number and exit") + + # Commands are implemented by parsing the remaining arguments outside of argparse + parser.add_argument('args', nargs=argparse.REMAINDER) + + return parser
+
+ +
+ +
+ + +
+
+ +
+ +
+ + + + + + + + + + + + \ No newline at end of file diff --git a/f30-branch/_modules/composer/cli/compose.html b/f30-branch/_modules/composer/cli/compose.html new file mode 100644 index 00000000..76fd3aaa --- /dev/null +++ b/f30-branch/_modules/composer/cli/compose.html @@ -0,0 +1,714 @@ + + + + + + + + + + + composer.cli.compose — Lorax 30.19 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")
+
+from datetime import datetime
+import sys
+import json
+
+from composer import http_client as client
+from composer.cli.help import compose_help
+from composer.cli.utilities import argify, handle_api_result, packageNEVRA
+
+
[docs]def compose_cmd(opts): + """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 = { + "list": compose_list, + "status": compose_status, + "types": compose_types, + "start": compose_start, + "log": compose_log, + "cancel": compose_cancel, + "delete": compose_delete, + "info": compose_info, + "metadata": compose_metadata, + "results": compose_results, + "logs": compose_logs, + "image": compose_image, + } + if opts.args[1] == "help" or opts.args[1] == "--help": + print(compose_help) + return 0 + elif opts.args[1] not in cmd_map: + log.error("Unknown compose command: %s", opts.args[1]) + return 1 + + return cmd_map[opts.args[1]](opts.socket, opts.api_version, opts.args[2:], opts.json, opts.testmode)
+ +
[docs]def compose_list(socket_path, api_version, args, show_json=False, testmode=0): + """Return a simple list of compose identifiers""" + + states = ("running", "waiting", "finished", "failed") + + which = set() + + if any(a not in states for a in args): + # TODO: error about unknown state + return 1 + elif not args: + which.update(states) + else: + which.update(args) + + results = [] + + if "running" in which or "waiting" in which: + api_route = client.api_url(api_version, "/compose/queue") + r = client.get_url_json(socket_path, api_route) + if "running" in which: + results += r["run"] + if "waiting" in which: + results += r["new"] + + if "finished" in which: + api_route = client.api_url(api_version, "/compose/finished") + r = client.get_url_json(socket_path, api_route) + results += r["finished"] + + if "failed" in which: + api_route = client.api_url(api_version, "/compose/failed") + r = client.get_url_json(socket_path, api_route) + results += r["failed"] + + if results: + if show_json: + print(json.dumps(results, indent=4)) + else: + list_fmt = "{id} {queue_status} {blueprint} {version} {compose_type}" + print("\n".join(list_fmt.format(**c) for c in results)) + + return 0
+ +
[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 + :param testmode: unused in this function + :type testmode: int + + 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"], + "blueprint": compose["blueprint"], + "version": compose["version"], + "compose_type": compose["compose_type"], + "image_size": compose["image_size"], + "status": compose["queue_status"], + "created": compose.get("job_created"), + "started": compose.get("job_started"), + "finished": compose.get("job_finished")} + + # Sort the status in a specific order + def sort_status(a): + order = ["RUNNING", "WAITING", "FINISHED", "FAILED"] + return (order.index(a["status"]), a["blueprint"], a["version"], a["compose_type"]) + + 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(list(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(list(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(list(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 blueprint STATUS + for c in status: + if c["image_size"] > 0: + image_size = str(c["image_size"]) + else: + image_size = "" + + dt = datetime.fromtimestamp(c.get("finished") or c.get("started") or c.get("created")) + + print("%s %-8s %s %-15s %s %-16s %s" % (c["id"], c["status"], dt.strftime("%c"), c["blueprint"], + c["version"], c["compose_type"], image_size))
+ + +
[docs]def compose_types(socket_path, api_version, args, show_json=False, testmode=0): + """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 + :param testmode: unused in this function + :type testmode: int + + 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 + + # output a plain list of identifiers, one per line + print("\n".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 blueprint 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 + :param testmode: Set to 1 to simulate a failed compose, set to 2 to simulate a finished one. + :type testmode: int + + compose start <blueprint-name> <compose-type> + """ + if len(args) == 0: + log.error("start is missing the blueprint name and output type") + return 1 + if len(args) == 1: + log.error("start is missing the output type") + return 1 + + config = { + "blueprint_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)) + (rc, exit_now) = handle_api_result(result, show_json) + if exit_now: + return rc + + print("Compose %s added to the queue" % result["build_id"]) + return rc
+ +
[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 + :param testmode: unused in this function + :type testmode: int + + 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)) + try: + result = client.get_url_raw(socket_path, api_route) + except RuntimeError as e: + print(str(e)) + return 1 + + print(result) + return 0
+ +
[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 + :param testmode: unused in this function + :type testmode: int + + 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)[0]
+ +
[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 + :param testmode: unused in this function + :type testmode: int + + 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) + return handle_api_result(result, show_json)[0]
+ +
[docs]def compose_info(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 + :param testmode: unused in this function + :type testmode: int + + compose info <uuid> + + This returns information about the compose, including the blueprint and the dependencies. + """ + if len(args) == 0: + log.error("info 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) + (rc, exit_now) = handle_api_result(result, show_json) + if exit_now: + return rc + + if result["image_size"] > 0: + image_size = str(result["image_size"]) + else: + image_size = "" + + + print("%s %-8s %-15s %s %-16s %s" % (result["id"], + result["queue_status"], + result["blueprint"]["name"], + result["blueprint"]["version"], + result["compose_type"], + image_size)) + print("Packages:") + for p in result["blueprint"]["packages"]: + print(" %s-%s" % (p["name"], p["version"])) + + print("Modules:") + for m in result["blueprint"]["modules"]: + print(" %s-%s" % (m["name"], m["version"])) + + print("Dependencies:") + for d in result["deps"]["packages"]: + print(" " + packageNEVRA(d)) + + return rc
+ +
[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 + :param testmode: unused in this function + :type testmode: int + + 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]) + try: + rc = client.download_file(socket_path, api_route) + except RuntimeError as e: + print(str(e)) + rc = 1 + + return rc
+ +
[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 + :param testmode: unused in this function + :type testmode: int + + 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]) + try: + rc = client.download_file(socket_path, api_route, sys.stdout.isatty()) + except RuntimeError as e: + print(str(e)) + rc = 1 + + return rc
+ +
[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 + :param testmode: unused in this function + :type testmode: int + + 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]) + try: + rc = client.download_file(socket_path, api_route, sys.stdout.isatty()) + except RuntimeError as e: + print(str(e)) + rc = 1 + + return rc
+ +
[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 + :param testmode: unused in this function + :type testmode: int + + 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]) + try: + rc = client.download_file(socket_path, api_route, sys.stdout.isatty()) + except RuntimeError as e: + print(str(e)) + rc = 1 + + return rc
+
+ +
+ +
+ + +
+
+ +
+ +
+ + + + + + + + + + + + \ No newline at end of file diff --git a/f30-branch/_modules/composer/cli/modules.html b/f30-branch/_modules/composer/cli/modules.html new file mode 100644 index 00000000..7eb574c8 --- /dev/null +++ b/f30-branch/_modules/composer/cli/modules.html @@ -0,0 +1,248 @@ + + + + + + + + + + + composer.cli.modules — Lorax 30.19 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")
+
+from composer import http_client as client
+from composer.cli.help import modules_help
+from composer.cli.utilities import handle_api_result
+
+
[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] == "help" or opts.args[1] == "--help": + print(modules_help) + return 0 + elif opts.args[1] != "list": + log.error("Unknown modules command: %s", opts.args[1]) + return 1 + + api_route = client.api_url(opts.api_version, "/modules/list") + result = client.get_url_json_unlimited(opts.socket, api_route) + (rc, exit_now) = handle_api_result(result, opts.json) + if exit_now: + return rc + + # "list" should output a plain list of identifiers, one per line. + print("\n".join(r["name"] for r in result["modules"])) + + return rc
+
+ +
+ +
+ + +
+
+ +
+ +
+ + + + + + + + + + + + \ No newline at end of file diff --git a/f30-branch/_modules/composer/cli/projects.html b/f30-branch/_modules/composer/cli/projects.html new file mode 100644 index 00000000..a1a78a5f --- /dev/null +++ b/f30-branch/_modules/composer/cli/projects.html @@ -0,0 +1,310 @@ + + + + + + + + + + + composer.cli.projects — Lorax 30.19 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 textwrap
+
+from composer import http_client as client
+from composer.cli.help import projects_help
+from composer.cli.utilities import handle_api_result
+
+
[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] == "help" or opts.args[1] == "--help": + print(projects_help) + return 0 + elif opts.args[1] not in cmd_map: + log.error("Unknown projects command: %s", opts.args[1]) + return 1 + + 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_unlimited(socket_path, api_route) + (rc, exit_now) = handle_api_result(result, show_json) + if exit_now: + return rc + + for proj in result["projects"]: + for k in [field for field in ("name", "summary", "homepage", "description") if proj[field]]: + print("%s: %s" % (k.title(), textwrap.fill(proj[k], subsequent_indent=" " * (len(k)+2)))) + print("\n\n") + + return rc
+ +
[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) + (rc, exit_now) = handle_api_result(result, show_json) + if exit_now: + return rc + + for proj in result["projects"]: + for k in [field for field in ("name", "summary", "homepage", "description") if proj[field]]: + 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 str(build["epoch"]) + ":", + build["source"]["version"], + build["release"], + build["arch"], + build["build_time"], + build["changelog"])) + print("") + return rc
+
+ +
+ +
+ + +
+
+ +
+ +
+ + + + + + + + + + + + \ No newline at end of file diff --git a/f30-branch/_modules/composer/cli/sources.html b/f30-branch/_modules/composer/cli/sources.html new file mode 100644 index 00000000..521760a0 --- /dev/null +++ b/f30-branch/_modules/composer/cli/sources.html @@ -0,0 +1,352 @@ + + + + + + + + + + + composer.cli.sources — Lorax 30.19 documentation + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + +
+ + + + + +
+ +
+ + + + + + + + + + + + + + + + + +
+ + + + +
+
+
+
+ +

Source code for composer.cli.sources

+#
+# Copyright (C) 2018  Red Hat, Inc.
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program.  If not, see <http://www.gnu.org/licenses/>.
+#
+import logging
+log = logging.getLogger("composer-cli")
+
+import os
+
+from composer import http_client as client
+from composer.cli.help import sources_help
+from composer.cli.utilities import argify, handle_api_result
+
+
[docs]def sources_cmd(opts): + """Process sources commands + + :param opts: Cmdline arguments + :type opts: argparse.Namespace + :returns: Value to return from sys.exit() + :rtype: int + """ + cmd_map = { + "list": sources_list, + "info": sources_info, + "add": sources_add, + "change": sources_add, + "delete": sources_delete, + } + if opts.args[1] == "help" or opts.args[1] == "--help": + print(sources_help) + return 0 + elif opts.args[1] not in cmd_map: + log.error("Unknown sources command: %s", opts.args[1]) + return 1 + + return cmd_map[opts.args[1]](opts.socket, opts.api_version, opts.args[2:], opts.json)
+ +
[docs]def sources_list(socket_path, api_version, args, show_json=False): + """Output the list of available sources + + :param socket_path: Path to the Unix socket to use for API communication + :type socket_path: str + :param api_version: Version of the API to talk to. eg. "0" + :type api_version: str + :param args: List of remaining arguments from the cmdline + :type args: list of str + :param show_json: Set to True to show the JSON output instead of the human readable output + :type show_json: bool + + sources list + """ + api_route = client.api_url(api_version, "/projects/source/list") + result = client.get_url_json(socket_path, api_route) + (rc, exit_now) = handle_api_result(result, show_json) + if exit_now: + return rc + + # "list" should output a plain list of identifiers, one per line. + print("\n".join(result["sources"])) + return rc
+ +
[docs]def sources_info(socket_path, api_version, args, show_json=False): + """Output info on a list of projects + + :param socket_path: Path to the Unix socket to use for API communication + :type socket_path: str + :param api_version: Version of the API to talk to. eg. "0" + :type api_version: str + :param args: List of remaining arguments from the cmdline + :type args: list of str + :param show_json: Set to True to show the JSON output instead of the human readable output + :type show_json: bool + + sources info <source-name> + """ + if len(args) == 0: + log.error("sources info is missing the name of the source") + return 1 + + if show_json: + api_route = client.api_url(api_version, "/projects/source/info/%s" % ",".join(args)) + result = client.get_url_json(socket_path, api_route) + rc = handle_api_result(result, show_json)[0] + else: + api_route = client.api_url(api_version, "/projects/source/info/%s?format=toml" % ",".join(args)) + try: + result = client.get_url_raw(socket_path, api_route) + print(result) + rc = 0 + except RuntimeError as e: + print(str(e)) + rc = 1 + + return rc
+ +
[docs]def sources_add(socket_path, api_version, args, show_json=False): + """Add or change a source + + :param socket_path: Path to the Unix socket to use for API communication + :type socket_path: str + :param api_version: Version of the API to talk to. eg. "0" + :type api_version: str + :param args: List of remaining arguments from the cmdline + :type args: list of str + :param show_json: Set to True to show the JSON output instead of the human readable output + :type show_json: bool + + sources add <source.toml> + """ + api_route = client.api_url(api_version, "/projects/source/new") + rval = 0 + for source in argify(args): + if not os.path.exists(source): + log.error("Missing source file: %s", source) + continue + source_toml = open(source, "r").read() + + result = client.post_url_toml(socket_path, api_route, source_toml) + if handle_api_result(result, show_json)[0]: + rval = 1 + return rval
+ +
[docs]def sources_delete(socket_path, api_version, args, show_json=False): + """Delete a source + + :param socket_path: Path to the Unix socket to use for API communication + :type socket_path: str + :param api_version: Version of the API to talk to. eg. "0" + :type api_version: str + :param args: List of remaining arguments from the cmdline + :type args: list of str + :param show_json: Set to True to show the JSON output instead of the human readable output + :type show_json: bool + + sources delete <source-name> + """ + api_route = client.api_url(api_version, "/projects/source/delete/%s" % args[0]) + result = client.delete_url_json(socket_path, api_route) + + return handle_api_result(result, show_json)[0]
+
+ +
+ +
+ + +
+
+ +
+ +
+ + + + + + + + + + + + \ No newline at end of file diff --git a/f30-branch/_modules/composer/cli/status.html b/f30-branch/_modules/composer/cli/status.html new file mode 100644 index 00000000..60207c2a --- /dev/null +++ b/f30-branch/_modules/composer/cli/status.html @@ -0,0 +1,256 @@ + + + + + + + + + + + composer.cli.status — Lorax 30.19 documentation + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + +
+ + + + + +
+ +
+ + + + + + + + + + + + + + + + + +
+ + + + +
+
+
+
+ +

Source code for composer.cli.status

+#
+# 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 import http_client as client
+from composer.cli.help import status_help
+from composer.cli.utilities import handle_api_result
+
+
[docs]def status_cmd(opts): + """Process status commands + + :param opts: Cmdline arguments + :type opts: argparse.Namespace + :returns: Value to return from sys.exit() + :rtype: int + """ + if opts.args[1] == "help" or opts.args[1] == "--help": + print(status_help) + return 0 + elif opts.args[1] != "show": + log.error("Unknown status command: %s", opts.args[1]) + return 1 + + result = client.get_url_json(opts.socket, "/api/status") + (rc, exit_now) = handle_api_result(result, opts.json) + if exit_now: + return rc + + print("API server status:") + print(" Database version: " + result["db_version"]) + print(" Database supported: %s" % result["db_supported"]) + print(" Schema version: " + result["schema_version"]) + print(" API version: " + result["api"]) + print(" Backend: " + result["backend"]) + print(" Build: " + result["build"]) + + if result["msgs"]: + print("Error messages:") + print("\n".join([" " + r for r in result["msgs"]])) + + return rc
+
+ +
+ +
+ + +
+
+ +
+ +
+ + + + + + + + + + + + \ No newline at end of file diff --git a/f30-branch/_modules/composer/cli/utilities.html b/f30-branch/_modules/composer/cli/utilities.html new file mode 100644 index 00000000..659fc36e --- /dev/null +++ b/f30-branch/_modules/composer/cli/utilities.html @@ -0,0 +1,295 @@ + + + + + + + + + + + composer.cli.utilities — Lorax 30.19 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 [i for i in [arg for entry in args for arg in entry.split(",")] if i]
+ +
[docs]def toml_filename(blueprint_name): + """Convert a blueprint name into a filename.toml + + :param blueprint_name: The blueprint's name + :type blueprint_name: str + :returns: The blueprint name with ' ' converted to - and .toml appended + :rtype: str + """ + return blueprint_name.replace(" ", "-") + ".toml"
+ +
[docs]def frozen_toml_filename(blueprint_name): + """Convert a blueprint name into a filename.toml + + :param blueprint_name: The blueprint's name + :type blueprint_name: str + :returns: The blueprint name with ' ' converted to - and .toml appended + :rtype: str + """ + return blueprint_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 + :rtype: tuple + :returns: (rc, should_exit_now) + + Return the correct rc for the program (0 or 1), and whether or + not to continue processing the results. + """ + if show_json: + print(json.dumps(result, indent=4)) + else: + for err in result.get("errors", []): + log.error(err["msg"]) + + # What's the rc? If status is present, use that + # If not, use length of errors + if "status" in result: + rc = bool(not result["status"]) + else: + rc = bool(len(result.get("errors", [])) > 0) + + # Caller should return if showing json, or status was present and False + exit_now = show_json or ("status" in result and rc) + return (rc, exit_now)
+ +
[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/f30-branch/_modules/composer/http_client.html b/f30-branch/_modules/composer/http_client.html new file mode 100644 index 00000000..5c13bd32 --- /dev/null +++ b/f30-branch/_modules/composer/http_client.html @@ -0,0 +1,458 @@ + + + + + + + + + + + composer.http_client — Lorax 30.19 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 urllib.parse import urlparse, urlunparse
+
+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 append_query(url, query): + """Add a query argument to a URL + + The query should be of the form "param1=what&param2=ever", i.e., no + leading '?'. The new query data will be appended to any existing + query string. + + :param url: The original URL + :type url: str + :param query: The query to append + :type query: str + :returns: The new URL with the query argument included + :rtype: str + """ + + url_parts = urlparse(url) + if url_parts.query: + new_query = url_parts.query + "&" + query + else: + new_query = query + return urlunparse([url_parts[0], url_parts[1], url_parts[2], + url_parts[3], new_query, url_parts[5]])
+ +
[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) + if r.status == 400: + err = json.loads(r.data.decode("utf-8")) + if "status" in err and err["status"] == False: + msgs = [e["msg"] for e in err["errors"]] + raise RuntimeError(", ".join(msgs)) + + 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 get_url_json_unlimited(socket_path, url, total_fn=None): + """Return the JSON results of a GET request + + For URLs that use offset/limit arguments, this command will + fetch all results for the given 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 + """ + def default_total_fn(data): + """Return the total number of available results""" + return data["total"] + + http = UnixHTTPConnectionPool(socket_path) + + # Start with limit=0 to just get the number of objects + total_url = append_query(url, "limit=0") + r_total = http.request("GET", total_url) + json_total = json.loads(r_total.data.decode('utf-8')) + + # Where to get the total from + if not total_fn: + total_fn = default_total_fn + + # Add the "total" returned by limit=0 as the new limit + unlimited_url = append_query(url, "limit=%d" % total_fn(json_total)) + r_unlimited = http.request("GET", unlimited_url) + return json.loads(r_unlimited.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 string 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"]: + msgs = [e["msg"] for e in err["errors"]] + raise RuntimeError(", ".join(msgs)) + + 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/f30-branch/_modules/composer/unix_socket.html b/f30-branch/_modules/composer/unix_socket.html new file mode 100644 index 00000000..96be294e --- /dev/null +++ b/f30-branch/_modules/composer/unix_socket.html @@ -0,0 +1,259 @@ + + + + + + + + + + + composer.unix_socket — Lorax 30.19 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 http.client
+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(http.client.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)
+
+ +
+ +
+ + +
+
+ +
+ +
+ + + + + + + + + + + + \ No newline at end of file diff --git a/f30-branch/_modules/index.html b/f30-branch/_modules/index.html new file mode 100644 index 00000000..b88a6f8b --- /dev/null +++ b/f30-branch/_modules/index.html @@ -0,0 +1,234 @@ + + + + + + + + + + + Overview: module code — Lorax 30.19 documentation + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/f30-branch/_modules/pylorax.html b/f30-branch/_modules/pylorax.html new file mode 100644 index 00000000..a7d0202e --- /dev/null +++ b/f30-branch/_modules/pylorax.html @@ -0,0 +1,650 @@ + + + + + + + + + + + pylorax — Lorax 30.19 documentation + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + +
+ + + + + +
+ +
+ + + + + + + + + + + + + + + + + +
+ + + + +
+
+
+
+ +

Source code for pylorax

+#
+# __init__.py
+#
+# Copyright (C) 2010-2015  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())
+
+program_log = logging.getLogger("program")
+
+import sys
+import os
+import configparser
+import tempfile
+import locale
+from subprocess import CalledProcessError
+import selinux
+from glob import glob
+
+from pylorax.base import BaseLoraxClass, DataHolder
+import pylorax.output as output
+
+import dnf
+
+from pylorax.sysutils import joinpaths, remove, linktree
+
+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
+
+DRACUT_DEFAULT = ["--xz", "--install", "/.buildstamp", "--no-early-microcode", "--add", "fips"]
+
+# Used for DNF conf.module_platform_id
+DEFAULT_PLATFORM_ID = "platform:f30"
+
+
[docs]class ArchData(DataHolder): + lib64_arches = ("x86_64", "ppc64le", "s390x", "ia64", "aarch64") + bcj_arch = dict(i386="x86", x86_64="x86", + ppc64le="powerpc", + arm="arm", armhfp="arm") + + def __init__(self, buildarch): + super(ArchData, self).__init__() + self.buildarch = buildarch + self.basearch = dnf.rpm.basearch(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.product = None + self.workdir = None + self.arch = None + self.conf = None + self.inroot = None + self.debug = False + self.outputdir = None + self._templatedir = None + + # 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.set("lorax", "logdir", "/var/log/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("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 + + if sys.stdout.isatty(): + colors = self.conf.getboolean("output", "colors") + else: + colors = False + 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') + list(os.environ.pop(k) for k in env_remove if k in os.environ) + + self._configured = True
+ + @property + def templatedir(self): + """Find the template directory. + + Pick the first directory under sharedir/templates.d/ if it exists. + Otherwise use the sharedir + """ + if not self._templatedir: + self._templatedir = find_templates(self.conf.get("lorax", "sharedir")) + logger.info("Using templatedir %s", self._templatedir) + return self._templatedir + +
[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, dbo, product, version, release, variant="", bugurl="", + isfinal=False, workdir=None, outputdir=None, buildarch=None, volid=None, + domacboot=True, doupgrade=True, remove_temp=False, + installpkgs=None, excludepkgs=None, + size=2, + add_templates=None, + add_template_vars=None, + add_arch_templates=None, + add_arch_template_vars=None, + verify=True, + user_dracut_args=None, + squashfs_only=False): + + assert self._configured + + installpkgs = installpkgs or [] + excludepkgs = excludepkgs 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 = self.conf.get("lorax", "logdir") + if not os.path.isdir(logdir): + os.makedirs(logdir) + + self.init_stream_logging() + self.init_file_logging(logdir) + + logger.debug("version is %s", vernum) + log_selinux_state() + + 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) + + # do we have a proper dnf base object? + logger.info("checking dnf base object") + if not isinstance(dbo, dnf.Base): + logger.critical("no dnf base object") + sys.exit(1) + self.inroot = dbo.conf.installroot + logger.debug("using install root: %s", self.inroot) + + if not buildarch: + buildarch = get_buildarch(dbo) + + 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") + self.product = DataHolder(name=product, version=version, release=release, + variant=variant, bugurl=bugurl, isfinal=isfinal) + logger.debug("product data: %s", self.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) + + # NOTE: rb.root = dbo.conf.installroot (== self.inroot) + rb = RuntimeBuilder(product=self.product, arch=self.arch, + dbo=dbo, templatedir=self.templatedir, + installpkgs=installpkgs, + excludepkgs=excludepkgs, + add_templates=add_templates, + add_template_vars=add_template_vars) + + logger.info("installing runtime packages") + rb.install() + + # write .buildstamp + buildstamp = BuildStamp(self.product.name, self.product.version, + self.product.bugurl, self.product.isfinal, + self.arch.buildarch, self.product.variant) + + 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 verify: + logger.info("verifying the installroot") + if not rb.verify(): + sys.exit(1) + else: + logger.info("Skipping verify") + + 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() # pylint: disable=no-member + 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) + if squashfs_only: + # Create an ext4 rootfs.img and compress it with squashfs + rb.create_squashfs_runtime(joinpaths(installroot,runtime), + compression=compression, compressargs=compressargs, + size=size) + else: + # Create an ext4 rootfs.img and compress it with squashfs + rb.create_ext4_runtime(joinpaths(installroot,runtime), + compression=compression, compressargs=compressargs, + size=size) + rb.finished() + + 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=self.templatedir, + add_templates=add_arch_templates, + add_template_vars=add_arch_template_vars, + workdir=self.workdir) + + logger.info("rebuilding initramfs images") + if not user_dracut_args: + dracut_args = DRACUT_DEFAULT + else: + dracut_args = [] + for arg in user_dracut_args: + dracut_args += arg.split(" ", 1) + + anaconda_args = dracut_args + ["--add", "anaconda pollcdrom qemu qemu-net"] + + logger.info("dracut args = %s", dracut_args) + logger.info("anaconda args = %s", anaconda_args) + treebuilder.rebuild_initrds(add_args=anaconda_args) + + 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(dbo): + # get architecture of the available anaconda package + buildarch = None + q = dbo.sack.query() + a = q.available() + for anaconda in a.filter(name="anaconda-core"): + if anaconda.arch != "src": + buildarch = anaconda.arch + break + if not buildarch: + logger.critical("no anaconda-core package in the repository") + sys.exit(1) + + return buildarch
+ + +
[docs]def setup_logging(logfile, theLogger): + """ + Setup the various logs + + :param logfile: filename to write the log to + :type logfile: string + :param theLogger: top-level logger + :type theLogger: logging.Logger + """ + if not os.path.isdir(os.path.abspath(os.path.dirname(logfile))): + os.makedirs(os.path.abspath(os.path.dirname(logfile))) + + # Setup logging to console and to logfile + logger.setLevel(logging.DEBUG) + theLogger.setLevel(logging.DEBUG) + + sh = logging.StreamHandler() + sh.setLevel(logging.INFO) + fmt = logging.Formatter("%(asctime)s: %(message)s") + sh.setFormatter(fmt) + logger.addHandler(sh) + theLogger.addHandler(sh) + + fh = logging.FileHandler(filename=logfile, mode="w") + fh.setLevel(logging.DEBUG) + fmt = logging.Formatter("%(asctime)s %(levelname)s %(name)s: %(message)s") + fh.setFormatter(fmt) + logger.addHandler(fh) + theLogger.addHandler(fh) + + # External program output log + program_log.setLevel(logging.DEBUG) + f = os.path.abspath(os.path.dirname(logfile))+"/program.log" + fh = logging.FileHandler(filename=f, mode="w") + fh.setLevel(logging.DEBUG) + fmt = logging.Formatter("%(asctime)s %(levelname)s: %(message)s") + fh.setFormatter(fmt) + program_log.addHandler(fh)
+ + +
[docs]def find_templates(templatedir="/usr/share/lorax"): + """ Find the templates to use. + + :param str templatedir: Top directory to search for templates + :returns: Path to templates + :rtype: str + + If there is a templates.d directory under templatedir the + lowest numbered directory entry is returned. + + eg. /usr/share/lorax/templates.d/99-generic/ + """ + if os.path.isdir(joinpaths(templatedir, "templates.d")): + try: + templatedir = sorted(glob(joinpaths(templatedir, "templates.d", "*")))[0] + except IndexError: + pass + return templatedir
+ +
[docs]def log_selinux_state(): + """Log the current state of selinux""" + if selinux.is_selinux_enabled(): + if selinux.security_getenforce(): + logger.info("selinux is enabled and in Enforcing mode") + else: + logger.info("selinux is enabled and in Permissive mode") + else: + logger.info("selinux is Disabled")
+
+ +
+ +
+ + +
+
+ +
+ +
+ + + + + + + + + + + + \ No newline at end of file diff --git a/f30-branch/_modules/pylorax/api/bisect.html b/f30-branch/_modules/pylorax/api/bisect.html new file mode 100644 index 00000000..9e4bdcba --- /dev/null +++ b/f30-branch/_modules/pylorax/api/bisect.html @@ -0,0 +1,249 @@ + + + + + + + + + + + pylorax.api.bisect — Lorax 30.19 documentation + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + +
+ + + + + +
+ +
+ + + + + + + + + + + + + + + + + +
+ + + + +
+
+
+
+ +

Source code for pylorax.api.bisect

+#
+# 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/>.
+#
+
[docs]def insort_left(a, x, key=None, lo=0, hi=None): + """Insert item x in list a, and keep it sorted assuming a is sorted. + + :param a: sorted list + :type a: list + :param x: item to insert into the list + :type x: object + :param key: Function to use to compare items in the list + :type key: function + :returns: index where the item was inserted + :rtype: int + + If x is already in a, insert it to the left of the leftmost x. + Optional args lo (default 0) and hi (default len(a)) bound the + slice of a to be searched. + + This is a modified version of bisect.insort_left that can use a + function for the compare, and returns the index position where it + was inserted. + """ + if key is None: + key = lambda i: i + + if lo < 0: + raise ValueError('lo must be non-negative') + if hi is None: + hi = len(a) + while lo < hi: + mid = (lo+hi)//2 + if key(a[mid]) < key(x): lo = mid+1 + else: hi = mid + a.insert(lo, x) + return lo
+
+ +
+ +
+ + +
+
+ +
+ +
+ + + + + + + + + + + + \ No newline at end of file diff --git a/f30-branch/_modules/pylorax/api/checkparams.html b/f30-branch/_modules/pylorax/api/checkparams.html new file mode 100644 index 00000000..3ad8010a --- /dev/null +++ b/f30-branch/_modules/pylorax/api/checkparams.html @@ -0,0 +1,244 @@ + + + + + + + + + + + pylorax.api.checkparams — Lorax 30.19 documentation + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + +
+ + + + + +
+ +
+ + + + + + + + + + + + + + + + + +
+ + + + +
+
+
+
+ +

Source code for pylorax.api.checkparams

+#
+# 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("lorax-composer")
+
+from flask import jsonify
+from functools import update_wrapper
+
+# A decorator for checking the parameters provided to the API route implementing
+# functions.  The tuples parameter is a list of tuples.  Each tuple is the string
+# name of a parameter ("blueprint_name", not blueprint_name), the value it's set
+# to by flask if the caller did not provide it, and a message to be returned to
+# the user.
+#
+# If the parameter is set to its default, the error message is returned.  Otherwise,
+# the decorated function is called and its return value is returned.
+
[docs]def checkparams(tuples): + def decorator(f): + def wrapped_function(*args, **kwargs): + for tup in tuples: + if kwargs[tup[0]] == tup[1]: + log.error("(%s) %s", f.__name__, tup[2]) + return jsonify(status=False, errors=[tup[2]]), 400 + + return f(*args, **kwargs) + + return update_wrapper(wrapped_function, f) + + return decorator
+
+ +
+ +
+ + +
+
+ +
+ +
+ + + + + + + + + + + + \ No newline at end of file diff --git a/f30-branch/_modules/pylorax/api/cmdline.html b/f30-branch/_modules/pylorax/api/cmdline.html new file mode 100644 index 00000000..89ca340f --- /dev/null +++ b/f30-branch/_modules/pylorax/api/cmdline.html @@ -0,0 +1,263 @@ + + + + + + + + + + + pylorax.api.cmdline — Lorax 30.19 documentation + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + +
+ + + + + +
+ +
+ + + + + + + + + + + + + + + + + +
+ + + + +
+
+
+
+ +

Source code for pylorax.api.cmdline

+#
+# cmdline.py
+#
+# 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 os
+import sys
+import argparse
+
+from pylorax import vernum
+
+DEFAULT_USER  = "root"
+DEFAULT_GROUP = "weldr"
+
+version = "{0}-{1}".format(os.path.basename(sys.argv[0]), vernum)
+
+
[docs]def lorax_composer_parser(): + """ Return the ArgumentParser for lorax-composer""" + + parser = argparse.ArgumentParser(description="Lorax Composer API Server", + fromfile_prefix_chars="@") + + parser.add_argument("--socket", default="/run/weldr/api.socket", metavar="SOCKET", + help="Path to the socket file to listen on") + parser.add_argument("--user", default=DEFAULT_USER, metavar="USER", + help="User to use for reduced permissions") + parser.add_argument("--group", default=DEFAULT_GROUP, metavar="GROUP", + help="Group to set ownership of the socket to") + parser.add_argument("--log", dest="logfile", default="/var/log/lorax-composer/composer.log", metavar="LOG", + help="Path to logfile (/var/log/lorax-composer/composer.log)") + parser.add_argument("--mockfiles", default="/var/tmp/bdcs-mockfiles/", metavar="MOCKFILES", + help="Path to JSON files used for /api/mock/ paths (/var/tmp/bdcs-mockfiles/)") + parser.add_argument("--sharedir", type=os.path.abspath, metavar="SHAREDIR", + help="Directory containing all the templates. Overrides config file sharedir") + parser.add_argument("-V", action="store_true", dest="showver", + help="show program's version number and exit") + parser.add_argument("-c", "--config", default="/etc/lorax/composer.conf", metavar="CONFIG", + help="Path to lorax-composer configuration file.") + parser.add_argument("--releasever", default=None, metavar="STRING", + help="Release version to use for $releasever in dnf repository urls") + parser.add_argument("--tmp", default="/var/tmp", + help="Top level temporary directory") + parser.add_argument("--proxy", default=None, metavar="PROXY", + help="Set proxy for DNF, overrides configuration file setting.") + parser.add_argument("--no-system-repos", action="store_true", default=False, + help="Do not copy over system repos from /etc/yum.repos.d/ at startup") + parser.add_argument("BLUEPRINTS", metavar="BLUEPRINTS", + help="Path to the blueprints") + + return parser
+
+ +
+ +
+ + +
+
+ +
+ +
+ + + + + + + + + + + + \ No newline at end of file diff --git a/f30-branch/_modules/pylorax/api/compose.html b/f30-branch/_modules/pylorax/api/compose.html new file mode 100644 index 00000000..c7787dbb --- /dev/null +++ b/f30-branch/_modules/pylorax/api/compose.html @@ -0,0 +1,1405 @@ + + + + + + + + + + + pylorax.api.compose — Lorax 30.19 documentation + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + +
+ + + + + +
+ +
+ + + + + + + + + + + + + + + + + +
+ + + + +
+
+
+
+ +

Source code for pylorax.api.compose

+# Copyright (C) 2018-2019 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/>.
+#
+""" Setup for composing an image
+
+Adding New Output Types
+-----------------------
+
+The new output type must add a kickstart template to ./share/composer/ where the
+name of the kickstart (without the trailing .ks) matches the entry in compose_args.
+
+The kickstart should not have any url or repo entries, these will be added at build
+time. The %packages section should be the last thing, and while it can contain mandatory
+packages required by the output type, it should not have the trailing %end because the
+package NEVRAs will be appended to it at build time.
+
+compose_args should have a name matching the kickstart, and it should set the novirt_install
+parameters needed to generate the desired output. Other types should be set to False.
+
+"""
+import logging
+log = logging.getLogger("lorax-composer")
+
+import os
+from glob import glob
+from io import StringIO
+from math import ceil
+import pytoml as toml
+import shutil
+from uuid import uuid4
+
+# Use pykickstart to calculate disk image size
+from pykickstart.parser import KickstartParser
+from pykickstart.version import makeVersion
+
+from pylorax import ArchData, find_templates, get_buildarch
+from pylorax.api.gitrpm import create_gitrpm_repo
+from pylorax.api.projects import projects_depsolve, projects_depsolve_with_size, dep_nevra
+from pylorax.api.projects import ProjectsError
+from pylorax.api.recipes import read_recipe_and_id
+from pylorax.api.timestamp import TS_CREATED, write_timestamp
+from pylorax.base import DataHolder
+from pylorax.imgutils import default_image_name
+from pylorax.ltmpl import LiveTemplateRunner
+from pylorax.sysutils import joinpaths, flatconfig
+
+
+
[docs]def test_templates(dbo, share_dir): + """ Try depsolving each of the the templates and report any errors + + :param dbo: dnf base object + :type dbo: dnf.Base + :returns: List of template types and errors + :rtype: List of errors + + Return a list of templates and errors encountered or an empty list + """ + template_errors = [] + for compose_type in compose_types(share_dir): + # Read the kickstart template for this type + ks_template_path = joinpaths(share_dir, "composer", compose_type) + ".ks" + ks_template = open(ks_template_path, "r").read() + + # How much space will the packages in the default template take? + ks_version = makeVersion() + ks = KickstartParser(ks_version, errorsAreFatal=False, missingIncludeIsFatal=False) + ks.readKickstartFromString(ks_template+"\n%end\n") + pkgs = [(name, "*") for name in ks.handler.packages.packageList] + grps = [grp.name for grp in ks.handler.packages.groupList] + try: + projects_depsolve(dbo, pkgs, grps) + except ProjectsError as e: + template_errors.append("Error depsolving %s: %s" % (compose_type, str(e))) + + return template_errors
+ + +
[docs]def repo_to_ks(r, url="url"): + """ Return a kickstart line with the correct args. + :param r: DNF repository information + :type r: dnf.Repo + :param url: "url" or "baseurl" to use for the baseurl parameter + :type url: str + :returns: kickstart command arguments for url/repo command + :rtype: str + + Set url to "baseurl" if it is a repo, leave it as "url" for the installation url. + """ + cmd = "" + # url uses --url not --baseurl + if r.baseurl: + cmd += '--%s="%s" ' % (url, r.baseurl[0]) + elif r.metalink: + cmd += '--metalink="%s" ' % r.metalink + elif r.mirrorlist: + cmd += '--mirrorlist="%s" ' % r.mirrorlist + else: + raise RuntimeError("Repo has no baseurl, metalink, or mirrorlist") + + if r.proxy: + cmd += '--proxy="%s" ' % r.proxy + + if not r.sslverify: + cmd += '--noverifyssl' + + if r.sslcacert: + cmd += ' --sslcacert="%s"' % r.sslcacert + if r.sslclientcert: + cmd += ' --sslclientcert="%s"' % r.sslclientcert + if r.sslclientkey: + cmd += ' --sslclientkey="%s"' % r.sslclientkey + + return cmd
+ + +
[docs]def bootloader_append(line, kernel_append): + """ Insert the kernel_append string into the --append argument + + :param line: The bootloader ... line + :type line: str + :param kernel_append: The arguments to append to the --append section + :type kernel_append: str + + Using pykickstart to process the line is the best way to make sure it + is parsed correctly, and re-assembled for inclusion into the final kickstart + """ + ks_version = makeVersion() + ks = KickstartParser(ks_version, errorsAreFatal=False, missingIncludeIsFatal=False) + ks.readKickstartFromString(line) + + if ks.handler.bootloader.appendLine: + ks.handler.bootloader.appendLine += " %s" % kernel_append + else: + ks.handler.bootloader.appendLine = kernel_append + + # Converting back to a string includes a comment, return just the bootloader line + return str(ks.handler.bootloader).splitlines()[-1]
+ + +
[docs]def get_kernel_append(recipe): + """Return the customizations.kernel append value + + :param recipe: + :type recipe: Recipe object + :returns: append value or empty string + :rtype: str + """ + if "customizations" not in recipe or \ + "kernel" not in recipe["customizations"] or \ + "append" not in recipe["customizations"]["kernel"]: + return "" + return recipe["customizations"]["kernel"]["append"]
+ + +
[docs]def timezone_cmd(line, settings): + """ Update the timezone line with the settings + + :param line: The timezone ... line + :type line: str + :param settings: A dict with timezone and/or ntpservers list + :type settings: dict + + Using pykickstart to process the line is the best way to make sure it + is parsed correctly, and re-assembled for inclusion into the final kickstart + """ + ks_version = makeVersion() + ks = KickstartParser(ks_version, errorsAreFatal=False, missingIncludeIsFatal=False) + ks.readKickstartFromString(line) + + if "timezone" in settings: + ks.handler.timezone.timezone = settings["timezone"] + if "ntpservers" in settings: + ks.handler.timezone.ntpservers = settings["ntpservers"] + + # Converting back to a string includes a comment, return just the timezone line + return str(ks.handler.timezone).splitlines()[-1]
+ + +
[docs]def get_timezone_settings(recipe): + """Return the customizations.timezone dict + + :param recipe: + :type recipe: Recipe object + :returns: append value or empty string + :rtype: dict + """ + if "customizations" not in recipe or \ + "timezone" not in recipe["customizations"]: + return {} + return recipe["customizations"]["timezone"]
+ + +
[docs]def lang_cmd(line, languages): + """ Update the lang line with the languages + + :param line: The lang ... line + :type line: str + :param settings: The list of languages + :type settings: list + + Using pykickstart to process the line is the best way to make sure it + is parsed correctly, and re-assembled for inclusion into the final kickstart + """ + ks_version = makeVersion() + ks = KickstartParser(ks_version, errorsAreFatal=False, missingIncludeIsFatal=False) + ks.readKickstartFromString(line) + + if languages: + ks.handler.lang.lang = languages[0] + + if len(languages) > 1: + ks.handler.lang.addsupport = languages[1:] + + # Converting back to a string includes a comment, return just the lang line + return str(ks.handler.lang).splitlines()[-1]
+ + +
[docs]def get_languages(recipe): + """Return the customizations.locale.languages list + + :param recipe: The recipe + :type recipe: Recipe object + :returns: list of language strings + :rtype: list + """ + if "customizations" not in recipe or \ + "locale" not in recipe["customizations"] or \ + "languages" not in recipe["customizations"]["locale"]: + return [] + return recipe["customizations"]["locale"]["languages"]
+ + +
[docs]def keyboard_cmd(line, layout): + """ Update the keyboard line with the layout + + :param line: The keyboard ... line + :type line: str + :param settings: The keyboard layout + :type settings: str + + Using pykickstart to process the line is the best way to make sure it + is parsed correctly, and re-assembled for inclusion into the final kickstart + """ + ks_version = makeVersion() + ks = KickstartParser(ks_version, errorsAreFatal=False, missingIncludeIsFatal=False) + ks.readKickstartFromString(line) + + if layout: + ks.handler.keyboard.keyboard = layout + ks.handler.keyboard.vc_keymap = "" + ks.handler.keyboard.x_layouts = [] + + # Converting back to a string includes a comment, return just the keyboard line + return str(ks.handler.keyboard).splitlines()[-1]
+ + +
[docs]def get_keyboard_layout(recipe): + """Return the customizations.locale.keyboard list + + :param recipe: The recipe + :type recipe: Recipe object + :returns: The keyboard layout string + :rtype: str + """ + if "customizations" not in recipe or \ + "locale" not in recipe["customizations"] or \ + "keyboard" not in recipe["customizations"]["locale"]: + return [] + return recipe["customizations"]["locale"]["keyboard"]
+ + +
[docs]def firewall_cmd(line, settings): + """ Update the firewall line with the new ports and services + + :param line: The firewall ... line + :type line: str + :param settings: A dict with the list of services and ports to enable and disable + :type settings: dict + + Using pykickstart to process the line is the best way to make sure it + is parsed correctly, and re-assembled for inclusion into the final kickstart + """ + ks_version = makeVersion() + ks = KickstartParser(ks_version, errorsAreFatal=False, missingIncludeIsFatal=False) + ks.readKickstartFromString(line) + + # Do not override firewall --disabled + if ks.handler.firewall.enabled != False and settings: + ks.handler.firewall.ports = sorted(set(settings["ports"] + ks.handler.firewall.ports)) + ks.handler.firewall.services = sorted(set(settings["enabled"] + ks.handler.firewall.services)) + ks.handler.firewall.remove_services = sorted(set(settings["disabled"] + ks.handler.firewall.remove_services)) + + # Converting back to a string includes a comment, return just the keyboard line + return str(ks.handler.firewall).splitlines()[-1]
+ + +
[docs]def get_firewall_settings(recipe): + """Return the customizations.firewall settings + + :param recipe: The recipe + :type recipe: Recipe object + :returns: A dict of settings + :rtype: dict + """ + settings = {"ports": [], "enabled": [], "disabled": []} + + if "customizations" not in recipe or \ + "firewall" not in recipe["customizations"]: + return settings + + settings["ports"] = recipe["customizations"]["firewall"].get("ports", []) + + if "services" in recipe["customizations"]["firewall"]: + settings["enabled"] = recipe["customizations"]["firewall"]["services"].get("enabled", []) + settings["disabled"] = recipe["customizations"]["firewall"]["services"].get("disabled", []) + return settings
+ + +
[docs]def services_cmd(line, settings): + """ Update the services line with additional services to enable/disable + + :param line: The services ... line + :type line: str + :param settings: A dict with the list of services to enable and disable + :type settings: dict + + Using pykickstart to process the line is the best way to make sure it + is parsed correctly, and re-assembled for inclusion into the final kickstart + """ + # Empty services and no additional settings, return an empty string + if not line and not settings["enabled"] and not settings["disabled"]: + return "" + + ks_version = makeVersion() + ks = KickstartParser(ks_version, errorsAreFatal=False, missingIncludeIsFatal=False) + + # Allow passing in a 'default' so that the enable/disable may be applied to it, without + # parsing it and emitting a kickstart error message + if line != "services": + ks.readKickstartFromString(line) + + # Add to any existing services, removing any duplicates + ks.handler.services.enabled = sorted(set(settings["enabled"] + ks.handler.services.enabled)) + ks.handler.services.disabled = sorted(set(settings["disabled"] + ks.handler.services.disabled)) + + # Converting back to a string includes a comment, return just the keyboard line + return str(ks.handler.services).splitlines()[-1]
+ + +
[docs]def get_services(recipe): + """Return the customizations.services settings + + :param recipe: The recipe + :type recipe: Recipe object + :returns: A dict of settings + :rtype: dict + """ + settings = {"enabled": [], "disabled": []} + + if "customizations" not in recipe or \ + "services" not in recipe["customizations"]: + return settings + + settings["enabled"] = sorted(recipe["customizations"]["services"].get("enabled", [])) + settings["disabled"] = sorted(recipe["customizations"]["services"].get("disabled", [])) + return settings
+ + +
[docs]def get_default_services(recipe): + """Get the default string for services, based on recipe + :param recipe: The recipe + + :type recipe: Recipe object + :returns: string with "services" or "" + :rtype: str + + When no services have been selected we don't need to add anything to the kickstart + so return an empty string. Otherwise return "services" which will be updated with + the settings. + """ + services = get_services(recipe) + + if services["enabled"] or services["disabled"]: + return "services" + else: + return ""
+ + +
[docs]def customize_ks_template(ks_template, recipe): + """ Customize the kickstart template and return it + + :param ks_template: The kickstart template + :type ks_template: str + :param recipe: + :type recipe: Recipe object + + Apply customizations to existing template commands, or add defaults for ones that are + missing and required. + + Apply customizations.kernel.append to the bootloader argument in the template. + Add bootloader line if it is missing. + + Add default timezone if needed. It does NOT replace an existing timezone entry + """ + # Commands to be modified [NEW-COMMAND-FUNC, NEW-VALUE, DEFAULT, REPLACE] + # The function is called with a kickstart command string and the value to replace + # The value is specific to the command, and is understood by the function + # The default is a complete kickstart command string, suitable for writing to the template + # If REPLACE is False it will not change an existing entry only add a missing one + commands = {"bootloader": [bootloader_append, + get_kernel_append(recipe), + 'bootloader --location=none', True], + "timezone": [timezone_cmd, + get_timezone_settings(recipe), + 'timezone UTC', False], + "lang": [lang_cmd, + get_languages(recipe), + 'lang en_US.UTF-8', True], + "keyboard": [keyboard_cmd, + get_keyboard_layout(recipe), + 'keyboard --xlayouts us --vckeymap us', True], + "firewall": [firewall_cmd, + get_firewall_settings(recipe), + 'firewall --enabled', True], + "services": [services_cmd, + get_services(recipe), + get_default_services(recipe), True] + } + found = {} + + output = StringIO() + for line in ks_template.splitlines(): + for cmd in commands: + (new_command, value, default, replace) = commands[cmd] + if line.startswith(cmd): + found[cmd] = True + if value and replace: + log.debug("Replacing %s with %s", cmd, value) + print(new_command(line, value), file=output) + else: + log.debug("Skipping %s", cmd) + print(line, file=output) + break + else: + # No matches, write the line as-is + print(line, file=output) + + # Write out defaults for the ones not found + # These must go FIRST because the template still needs to have the packages added + defaults = StringIO() + for cmd in commands: + if cmd in found: + continue + (new_command, value, default, _) = commands[cmd] + if value and default: + log.debug("Setting %s to use %s", cmd, value) + print(new_command(default, value), file=defaults) + elif default: + log.debug("Setting %s to %s", cmd, default) + print(default, file=defaults) + + return defaults.getvalue() + output.getvalue()
+ + +
[docs]def write_ks_root(f, user): + """ Write kickstart root password and sshkey entry + + :param f: kickstart file object + :type f: open file object + :param user: A blueprint user dictionary + :type user: dict + :returns: True if it wrote a rootpw command to the kickstart + :rtype: bool + + If the entry contains a ssh key, use sshkey to write it + If it contains password, use rootpw to set it + + root cannot be used with the user command. So only key and password are supported + for root. + """ + wrote_rootpw = False + + # ssh key uses the sshkey kickstart command + if "key" in user: + f.write('sshkey --user %s "%s"\n' % (user["name"], user["key"])) + + if "password" in user: + if any(user["password"].startswith(prefix) for prefix in ["$2b$", "$6$", "$5$"]): + log.debug("Detected pre-crypted password") + f.write('rootpw --iscrypted "%s"\n' % user["password"]) + wrote_rootpw = True + else: + log.debug("Detected plaintext password") + f.write('rootpw --plaintext "%s"\n' % user["password"]) + wrote_rootpw = True + + return wrote_rootpw
+ +
[docs]def write_ks_user(f, user): + """ Write kickstart user and sshkey entry + + :param f: kickstart file object + :type f: open file object + :param user: A blueprint user dictionary + :type user: dict + + If the entry contains a ssh key, use sshkey to write it + All of the user fields are optional, except name, write out a kickstart user entry + with whatever options are relevant. + """ + # ssh key uses the sshkey kickstart command + if "key" in user: + f.write('sshkey --user %s "%s"\n' % (user["name"], user["key"])) + + # Write out the user kickstart command, much of it is optional + f.write("user --name %s" % user["name"]) + if "home" in user: + f.write(" --homedir %s" % user["home"]) + + if "password" in user: + if any(user["password"].startswith(prefix) for prefix in ["$2b$", "$6$", "$5$"]): + log.debug("Detected pre-crypted password") + f.write(" --iscrypted") + else: + log.debug("Detected plaintext password") + f.write(" --plaintext") + + f.write(" --password \"%s\"" % user["password"]) + + if "shell" in user: + f.write(" --shell %s" % user["shell"]) + + if "uid" in user: + f.write(" --uid %d" % int(user["uid"])) + + if "gid" in user: + f.write(" --gid %d" % int(user["gid"])) + + if "description" in user: + f.write(" --gecos \"%s\"" % user["description"]) + + if "groups" in user: + f.write(" --groups %s" % ",".join(user["groups"])) + + f.write("\n")
+ + +
[docs]def write_ks_group(f, group): + """ Write kickstart group entry + + :param f: kickstart file object + :type f: open file object + :param group: A blueprint group dictionary + :type user: dict + + gid is optional + """ + if "name" not in group: + raise RuntimeError("group entry requires a name") + + f.write("group --name %s" % group["name"]) + if "gid" in group: + f.write(" --gid %d" % int(group["gid"])) + + f.write("\n")
+ + +
[docs]def add_customizations(f, recipe): + """ Add customizations to the kickstart file + + :param f: kickstart file object + :type f: open file object + :param recipe: + :type recipe: Recipe object + :returns: None + :raises: RuntimeError if there was a problem writing to the kickstart + """ + if "customizations" not in recipe: + f.write('rootpw --lock\n') + return + customizations = recipe["customizations"] + + # allow customizations to be incorrectly specified as [[customizations]] instead of [customizations] + if isinstance(customizations, list): + customizations = customizations[0] + + if "hostname" in customizations: + f.write("network --hostname=%s\n" % customizations["hostname"]) + + # TODO - remove this, should use user section to define this + if "sshkey" in customizations: + # This is a list of entries + for sshkey in customizations["sshkey"]: + if "user" not in sshkey or "key" not in sshkey: + log.error("%s is incorrect, skipping", sshkey) + continue + f.write('sshkey --user %s "%s"\n' % (sshkey["user"], sshkey["key"])) + + # Creating a user also creates a group. Make a list of the names for later + user_groups = [] + # kickstart requires a rootpw line + wrote_rootpw = False + if "user" in customizations: + # only name is required, everything else is optional + for user in customizations["user"]: + if "name" not in user: + raise RuntimeError("user entry requires a name") + + # root is special, cannot use normal user command for it + if user["name"] == "root": + wrote_rootpw = write_ks_root(f, user) + continue + + write_ks_user(f, user) + user_groups.append(user["name"]) + + if "group" in customizations: + for group in customizations["group"]: + if group["name"] not in user_groups: + write_ks_group(f, group) + else: + log.warning("Skipping group %s, already created by user", group["name"]) + + # Lock the root account if no root user password has been specified + if not wrote_rootpw: + f.write('rootpw --lock\n')
+ + +
[docs]def get_extra_pkgs(dbo, share_dir, compose_type): + """Return extra packages needed for the output type + + :param dbo: dnf base object + :type dbo: dnf.Base + :param share_dir: Path to the top level share directory + :type share_dir: str + :param compose_type: The type of output to create from the recipe + :type compose_type: str + :returns: List of package names (name only, not NEVRA) + :rtype: list + + Currently this is only needed by live-iso, it reads ./live/live-install.tmpl and + processes only the installpkg lines. It lists the packages needed to complete creation of the + iso using the templates such as x86.tmpl + + Keep in mind that the live-install.tmpl is shared between livemedia-creator and lorax-composer, + even though the results are applied differently. + """ + if compose_type != "live-iso": + return [] + + # get the arch information to pass to the runner + arch = ArchData(get_buildarch(dbo)) + defaults = DataHolder(basearch=arch.basearch) + templatedir = joinpaths(find_templates(share_dir), "live") + runner = LiveTemplateRunner(dbo, templatedir=templatedir, defaults=defaults) + runner.run("live-install.tmpl") + log.debug("extra pkgs = %s", runner.pkgs) + + return runner.pkgnames
+ + +
[docs]def start_build(cfg, dnflock, gitlock, branch, recipe_name, compose_type, test_mode=0): + """ Start the build + + :param cfg: Configuration object + :type cfg: ComposerConfig + :param dnflock: Lock and YumBase for depsolving + :type dnflock: YumLock + :param recipe: The recipe to build + :type recipe: str + :param compose_type: The type of output to create from the recipe + :type compose_type: str + :returns: Unique ID for the build that can be used to track its status + :rtype: str + """ + share_dir = cfg.get("composer", "share_dir") + lib_dir = cfg.get("composer", "lib_dir") + + # Make sure compose_type is valid + if compose_type not in compose_types(share_dir): + raise RuntimeError("Invalid compose type (%s), must be one of %s" % (compose_type, compose_types(share_dir))) + + # Some image types (live-iso) need extra packages for composer to execute the output template + with dnflock.lock: + extra_pkgs = get_extra_pkgs(dnflock.dbo, share_dir, compose_type) + log.debug("Extra packages needed for %s: %s", compose_type, extra_pkgs) + + with gitlock.lock: + (commit_id, recipe) = read_recipe_and_id(gitlock.repo, branch, recipe_name) + + # Combine modules and packages and depsolve the list + module_nver = recipe.module_nver + package_nver = recipe.package_nver + package_nver.extend([(name, '*') for name in extra_pkgs]) + + projects = sorted(set(module_nver+package_nver), key=lambda p: p[0].lower()) + deps = [] + log.info("depsolving %s", recipe["name"]) + try: + # This can possibly update repodata and reset the YumBase object. + with dnflock.lock_check: + (installed_size, deps) = projects_depsolve_with_size(dnflock.dbo, projects, recipe.group_names, with_core=False) + except ProjectsError as e: + log.error("start_build depsolve: %s", str(e)) + raise RuntimeError("Problem depsolving %s: %s" % (recipe["name"], str(e))) + + # Read the kickstart template for this type + ks_template_path = joinpaths(share_dir, "composer", compose_type) + ".ks" + ks_template = open(ks_template_path, "r").read() + + # How much space will the packages in the default template take? + ks_version = makeVersion() + ks = KickstartParser(ks_version, errorsAreFatal=False, missingIncludeIsFatal=False) + ks.readKickstartFromString(ks_template+"\n%end\n") + pkgs = [(name, "*") for name in ks.handler.packages.packageList] + grps = [grp.name for grp in ks.handler.packages.groupList] + try: + with dnflock.lock: + (template_size, _) = projects_depsolve_with_size(dnflock.dbo, pkgs, grps, with_core=not ks.handler.packages.nocore) + except ProjectsError as e: + log.error("start_build depsolve: %s", str(e)) + raise RuntimeError("Problem depsolving %s: %s" % (recipe["name"], str(e))) + log.debug("installed_size = %d, template_size=%d", installed_size, template_size) + + # Minimum LMC disk size is 1GiB, and anaconda bumps the estimated size up by 10% (which doesn't always work). + installed_size = int((installed_size+template_size)) * 1.2 + log.debug("/ partition size = %d", installed_size) + + # Create the results directory + build_id = str(uuid4()) + results_dir = joinpaths(lib_dir, "results", build_id) + os.makedirs(results_dir) + + # Write the recipe commit hash + commit_path = joinpaths(results_dir, "COMMIT") + with open(commit_path, "w") as f: + f.write(commit_id) + + # Write the original recipe + recipe_path = joinpaths(results_dir, "blueprint.toml") + with open(recipe_path, "w") as f: + f.write(recipe.toml()) + + # Write the frozen recipe + frozen_recipe = recipe.freeze(deps) + recipe_path = joinpaths(results_dir, "frozen.toml") + with open(recipe_path, "w") as f: + f.write(frozen_recipe.toml()) + + # Write out the dependencies to the results dir + deps_path = joinpaths(results_dir, "deps.toml") + with open(deps_path, "w") as f: + f.write(toml.dumps({"packages":deps})) + + # Save a copy of the original kickstart + shutil.copy(ks_template_path, results_dir) + + with dnflock.lock: + repos = list(dnflock.dbo.repos.iter_enabled()) + if not repos: + raise RuntimeError("No enabled repos, canceling build.") + + # Create the git rpms, if any, and return the path to the repo under results_dir + gitrpm_repo = create_gitrpm_repo(results_dir, recipe) + + # Create the final kickstart with repos and package list + ks_path = joinpaths(results_dir, "final-kickstart.ks") + with open(ks_path, "w") as f: + ks_url = repo_to_ks(repos[0], "url") + log.debug("url = %s", ks_url) + f.write('url %s\n' % ks_url) + for idx, r in enumerate(repos[1:]): + ks_repo = repo_to_ks(r, "baseurl") + log.debug("repo composer-%s = %s", idx, ks_repo) + f.write('repo --name="composer-%s" %s\n' % (idx, ks_repo)) + + if gitrpm_repo: + log.debug("repo gitrpms = %s", gitrpm_repo) + f.write('repo --name="gitrpms" --baseurl="file://%s"\n' % gitrpm_repo) + + # Setup the disk for booting + # TODO Add GPT and UEFI boot support + f.write('clearpart --all --initlabel\n') + + # Write the root partition and it's size in MB (rounded up) + f.write('part / --size=%d\n' % ceil(installed_size / 1024**2)) + + # Some customizations modify the template before writing it + f.write(customize_ks_template(ks_template, recipe)) + + for d in deps: + f.write(dep_nevra(d)+"\n") + + # Include the rpms from the gitrpm repo directory + if gitrpm_repo: + for rpm in glob(os.path.join(gitrpm_repo, "*.rpm")): + f.write(os.path.basename(rpm)[:-4]+"\n") + + f.write("%end\n") + + # Other customizations can be appended to the kickstart + add_customizations(f, recipe) + + # Setup the config to pass to novirt_install + log_dir = joinpaths(results_dir, "logs/") + cfg_args = compose_args(compose_type) + + # Get the title, project, and release version from the host + if not os.path.exists("/etc/os-release"): + log.error("/etc/os-release is missing, cannot determine product or release version") + os_release = flatconfig("/etc/os-release") + + log.debug("os_release = %s", dict(os_release.items())) + + cfg_args["title"] = os_release.get("PRETTY_NAME", "") + cfg_args["project"] = os_release.get("NAME", "") + cfg_args["releasever"] = os_release.get("VERSION_ID", "") + cfg_args["volid"] = "" + cfg_args["extra_boot_args"] = get_kernel_append(recipe) + + if "compression" not in cfg_args: + cfg_args["compression"] = "xz" + + if "compress_args" not in cfg_args: + cfg_args["compress_args"] = [] + + cfg_args.update({ + "ks": [ks_path], + "logfile": log_dir, + "timeout": 60, # 60 minute timeout + }) + with open(joinpaths(results_dir, "config.toml"), "w") as f: + f.write(toml.dumps(cfg_args)) + + # Set the initial status + open(joinpaths(results_dir, "STATUS"), "w").write("WAITING") + + # Set the test mode, if requested + if test_mode > 0: + open(joinpaths(results_dir, "TEST"), "w").write("%s" % test_mode) + + write_timestamp(results_dir, TS_CREATED) + log.info("Adding %s (%s %s) to compose queue", build_id, recipe["name"], compose_type) + os.symlink(results_dir, joinpaths(lib_dir, "queue/new/", build_id)) + + return build_id
+ +# Supported output types +
[docs]def compose_types(share_dir): + r""" Returns a list of the supported output types + + The output types come from the kickstart names in /usr/share/lorax/composer/\*ks + """ + return sorted([os.path.basename(ks)[:-3] for ks in glob(joinpaths(share_dir, "composer/*.ks"))])
+ +
[docs]def compose_args(compose_type): + """ Returns the settings to pass to novirt_install for the compose type + + :param compose_type: The type of compose to create, from `compose_types()` + :type compose_type: str + + This will return a dict of options that match the ArgumentParser options for livemedia-creator. + These are the ones the define the type of output, it's filename, etc. + Other options will be filled in by `make_compose()` + """ + _MAP = {"tar": {"make_iso": False, + "make_disk": False, + "make_fsimage": False, + "make_appliance": False, + "make_ami": False, + "make_tar": True, + "make_tar_disk": False, + "make_pxe_live": False, + "make_ostree_live": False, + "make_oci": False, + "make_vagrant": False, + "ostree": False, + "live_rootfs_keep_size": False, + "live_rootfs_size": 0, + "image_size_align": 0, + "image_type": False, # False instead of None because of TOML + "qemu_args": [], + "image_name": default_image_name("xz", "root.tar"), + "tar_disk_name": None, + "image_only": True, + "app_name": None, + "app_template": None, + "app_file": None, + }, + "live-iso": {"make_iso": True, + "make_disk": False, + "make_fsimage": False, + "make_appliance": False, + "make_ami": False, + "make_tar": False, + "make_tar_disk": False, + "make_pxe_live": False, + "make_ostree_live": False, + "make_oci": False, + "make_vagrant": False, + "ostree": False, + "live_rootfs_keep_size": False, + "live_rootfs_size": 0, + "image_size_align": 0, + "image_type": False, # False instead of None because of TOML + "qemu_args": [], + "image_name": "live.iso", + "tar_disk_name": None, + "fs_label": "Anaconda", # Live booting may expect this to be 'Anaconda' + "image_only": False, + "app_name": None, + "app_template": None, + "app_file": None, + "iso_only": True, + "iso_name": "live.iso", + }, + "partitioned-disk": {"make_iso": False, + "make_disk": True, + "make_fsimage": False, + "make_appliance": False, + "make_ami": False, + "make_tar": False, + "make_tar_disk": False, + "make_pxe_live": False, + "make_ostree_live": False, + "make_oci": False, + "make_vagrant": False, + "ostree": False, + "live_rootfs_keep_size": False, + "live_rootfs_size": 0, + "image_size_align": 0, + "image_type": False, # False instead of None because of TOML + "qemu_args": [], + "image_name": "disk.img", + "tar_disk_name": None, + "fs_label": "", + "image_only": True, + "app_name": None, + "app_template": None, + "app_file": None, + }, + "qcow2": {"make_iso": False, + "make_disk": True, + "make_fsimage": False, + "make_appliance": False, + "make_ami": False, + "make_tar": False, + "make_tar_disk": False, + "make_pxe_live": False, + "make_ostree_live": False, + "make_oci": False, + "make_vagrant": False, + "ostree": False, + "live_rootfs_keep_size": False, + "live_rootfs_size": 0, + "image_size_align": 0, + "image_type": "qcow2", + "qemu_args": [], + "image_name": "disk.qcow2", + "tar_disk_name": None, + "fs_label": "", + "image_only": True, + "app_name": None, + "app_template": None, + "app_file": None, + }, + "ext4-filesystem": {"make_iso": False, + "make_disk": False, + "make_fsimage": True, + "make_appliance": False, + "make_ami": False, + "make_tar": False, + "make_tar_disk": False, + "make_pxe_live": False, + "make_ostree_live": False, + "make_oci": False, + "make_vagrant": False, + "ostree": False, + "live_rootfs_keep_size": False, + "live_rootfs_size": 0, + "image_size_align": 0, + "image_type": False, # False instead of None because of TOML + "qemu_args": [], + "image_name": "filesystem.img", + "tar_disk_name": None, + "fs_label": "", + "image_only": True, + "app_name": None, + "app_template": None, + "app_file": None, + }, + "ami": {"make_iso": False, + "make_disk": True, + "make_fsimage": False, + "make_appliance": False, + "make_ami": False, + "make_tar": False, + "make_tar_disk": False, + "make_pxe_live": False, + "make_ostree_live": False, + "make_oci": False, + "make_vagrant": False, + "ostree": False, + "live_rootfs_keep_size": False, + "live_rootfs_size": 0, + "image_size_align": 0, + "image_type": False, + "qemu_args": [], + "image_name": "disk.ami", + "tar_disk_name": None, + "fs_label": "", + "image_only": True, + "app_name": None, + "app_template": None, + "app_file": None, + }, + "vhd": {"make_iso": False, + "make_disk": True, + "make_fsimage": False, + "make_appliance": False, + "make_ami": False, + "make_tar": False, + "make_tar_disk": False, + "make_pxe_live": False, + "make_ostree_live": False, + "make_oci": False, + "make_vagrant": False, + "ostree": False, + "live_rootfs_keep_size": False, + "live_rootfs_size": 0, + "image_size_align": 0, + "image_type": "vpc", + "qemu_args": ["-o", "subformat=fixed,force_size"], + "image_name": "disk.vhd", + "tar_disk_name": None, + "fs_label": "", + "image_only": True, + "app_name": None, + "app_template": None, + "app_file": None, + }, + "vmdk": {"make_iso": False, + "make_disk": True, + "make_fsimage": False, + "make_appliance": False, + "make_ami": False, + "make_tar": False, + "make_tar_disk": False, + "make_pxe_live": False, + "make_ostree_live": False, + "make_oci": False, + "make_vagrant": False, + "ostree": False, + "live_rootfs_keep_size": False, + "live_rootfs_size": 0, + "image_size_align": 0, + "image_type": "vmdk", + "qemu_args": [], + "image_name": "disk.vmdk", + "tar_disk_name": None, + "fs_label": "", + "image_only": True, + "app_name": None, + "app_template": None, + "app_file": None, + }, + "openstack": {"make_iso": False, + "make_disk": True, + "make_fsimage": False, + "make_appliance": False, + "make_ami": False, + "make_tar": False, + "make_tar_disk": False, + "make_pxe_live": False, + "make_ostree_live": False, + "make_oci": False, + "make_vagrant": False, + "ostree": False, + "live_rootfs_keep_size": False, + "live_rootfs_size": 0, + "image_size_align": 0, + "image_type": "qcow2", + "qemu_args": [], + "image_name": "disk.qcow2", + "tar_disk_name": None, + "fs_label": "", + "image_only": True, + "app_name": None, + "app_template": None, + "app_file": None, + }, + "google": {"make_iso": False, + "make_disk": True, + "make_fsimage": False, + "make_appliance": False, + "make_ami": False, + "make_tar": False, + "make_tar_disk": True, + "make_pxe_live": False, + "make_ostree_live": False, + "make_oci": False, + "make_vagrant": False, + "ostree": False, + "live_rootfs_keep_size": False, + "live_rootfs_size": 0, + "image_size_align": 1024, + "image_type": False, # False instead of None because of TOML + "qemu_args": [], + "image_name": "disk.tar.gz", + "tar_disk_name": "disk.raw", + "compression": "gzip", + "compress_args": ["-9"], + "fs_label": "", + "image_only": True, + "app_name": None, + "app_template": None, + "app_file": None, + }, + "hyper-v": {"make_iso": False, + "make_disk": True, + "make_fsimage": False, + "make_appliance": False, + "make_ami": False, + "make_tar": False, + "make_tar_disk": False, + "make_pxe_live": False, + "make_ostree_live": False, + "make_oci": False, + "make_vagrant": False, + "ostree": False, + "live_rootfs_keep_size": False, + "live_rootfs_size": 0, + "image_size_align": 0, + "image_type": "vhdx", + "qemu_args": [], + "image_name": "disk.vhdx", + "tar_disk_name": None, + "fs_label": "", + "image_only": True, + "app_name": None, + "app_template": None, + "app_file": None, + }, + "alibaba": {"make_iso": False, + "make_disk": True, + "make_fsimage": False, + "make_appliance": False, + "make_ami": False, + "make_tar": False, + "make_tar_disk": False, + "make_pxe_live": False, + "make_ostree_live": False, + "make_oci": False, + "make_vagrant": False, + "ostree": False, + "live_rootfs_keep_size": False, + "live_rootfs_size": 0, + "image_size_align": 0, + "image_type": "qcow2", + "qemu_args": [], + "image_name": "disk.qcow2", + "tar_disk_name": None, + "fs_label": "", + "image_only": True, + "app_name": None, + "app_template": None, + "app_file": None, + }, + } + return _MAP[compose_type]
+ +
[docs]def move_compose_results(cfg, results_dir): + """Move the final image to the results_dir and cleanup the unneeded compose files + + :param cfg: Build configuration + :type cfg: DataHolder + :param results_dir: Directory to put the results into + :type results_dir: str + """ + if cfg["make_tar"]: + shutil.move(joinpaths(cfg["result_dir"], cfg["image_name"]), results_dir) + elif cfg["make_iso"]: + # Output from live iso is always a boot.iso under images/, move and rename it + shutil.move(joinpaths(cfg["result_dir"], cfg["iso_name"]), joinpaths(results_dir, cfg["image_name"])) + elif cfg["make_disk"] or cfg["make_fsimage"]: + shutil.move(joinpaths(cfg["result_dir"], cfg["image_name"]), joinpaths(results_dir, cfg["image_name"])) + + + # Cleanup the compose directory, but only if it looks like a compose directory + if os.path.basename(cfg["result_dir"]) == "compose": + shutil.rmtree(cfg["result_dir"]) + else: + log.error("Incorrect compose directory, not cleaning up")
+
+ +
+ +
+ + +
+
+ +
+ +
+ + + + + + + + + + + + \ No newline at end of file diff --git a/f30-branch/_modules/pylorax/api/config.html b/f30-branch/_modules/pylorax/api/config.html new file mode 100644 index 00000000..d6dfdefe --- /dev/null +++ b/f30-branch/_modules/pylorax/api/config.html @@ -0,0 +1,338 @@ + + + + + + + + + + + pylorax.api.config — Lorax 30.19 documentation + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + +
+ + + + + +
+ +
+ + + + + + + + + + + + + + + + + +
+ + + + +
+
+
+
+ +

Source code for pylorax.api.config

+#
+# Copyright (C) 2017  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 configparser
+import grp
+import os
+import pwd
+
+from pylorax.sysutils import joinpaths
+
+
[docs]class ComposerConfig(configparser.ConfigParser): +
[docs] def get_default(self, section, option, default): + try: + return self.get(section, option) + except configparser.Error: + return default
+ + +
[docs]def configure(conf_file="/etc/lorax/composer.conf", root_dir="/", test_config=False): + """lorax-composer configuration + + :param conf_file: Path to the config file overriding the default settings + :type conf_file: str + :param root_dir: Directory to prepend to paths, defaults to / + :type root_dir: str + :param test_config: Set to True to skip reading conf_file + :type test_config: bool + """ + conf = ComposerConfig() + + # set defaults + conf.add_section("composer") + conf.set("composer", "share_dir", os.path.realpath(joinpaths(root_dir, "/usr/share/lorax/"))) + conf.set("composer", "lib_dir", os.path.realpath(joinpaths(root_dir, "/var/lib/lorax/composer/"))) + conf.set("composer", "repo_dir", os.path.realpath(joinpaths(root_dir, "/var/lib/lorax/composer/repos.d/"))) + conf.set("composer", "dnf_conf", os.path.realpath(joinpaths(root_dir, "/var/tmp/composer/dnf.conf"))) + conf.set("composer", "dnf_root", os.path.realpath(joinpaths(root_dir, "/var/tmp/composer/dnf/root/"))) + conf.set("composer", "cache_dir", os.path.realpath(joinpaths(root_dir, "/var/tmp/composer/cache/"))) + conf.set("composer", "tmp", os.path.realpath(joinpaths(root_dir, "/var/tmp/"))) + + conf.add_section("users") + conf.set("users", "root", "1") + + # Enable all available repo files by default + conf.add_section("repos") + conf.set("repos", "use_system_repos", "1") + conf.set("repos", "enabled", "*") + + conf.add_section("dnf") + + if not test_config: + # read the config file + if os.path.isfile(conf_file): + conf.read(conf_file) + + return conf
+ +
[docs]def make_owned_dir(p_dir, uid, gid): + """Make a directory and its parents, setting owner and group + + :param p_dir: path to directory to create + :type p_dir: string + :param uid: uid of owner + :type uid: int + :param gid: gid of owner + :type gid: int + :returns: list of errors + :rtype: list of str + + Check to make sure it does not have o+rw permissions and that it is owned by uid:gid + """ + errors = [] + if not os.path.isdir(p_dir): + # Make sure no o+rw permissions are set + orig_umask = os.umask(0o006) + os.makedirs(p_dir, 0o771) + os.chown(p_dir, uid, gid) + os.umask(orig_umask) + else: + p_stat = os.stat(p_dir) + if p_stat.st_mode & 0o006 != 0: + errors.append("Incorrect permissions on %s, no o+rw permissions are allowed." % p_dir) + + if p_stat.st_gid != gid or p_stat.st_uid != 0: + gr_name = grp.getgrgid(gid).gr_name + u_name = pwd.getpwuid(uid) + errors.append("%s should be owned by %s:%s" % (p_dir, u_name, gr_name)) + + return errors
+ +
[docs]def make_dnf_dirs(conf, uid, gid): + """Make any missing dnf directories owned by user:group + + :param conf: The configuration to use + :type conf: ComposerConfig + :param uid: uid of owner + :type uid: int + :param gid: gid of owner + :type gid: int + :returns: list of errors + :rtype: list of str + """ + errors = [] + for p in ["dnf_conf", "repo_dir", "cache_dir", "dnf_root"]: + p_dir = os.path.abspath(conf.get("composer", p)) + if p == "dnf_conf": + p_dir = os.path.dirname(p_dir) + errors.extend(make_owned_dir(p_dir, uid, gid))
+ +
[docs]def make_queue_dirs(conf, gid): + """Make any missing queue directories + + :param conf: The configuration to use + :type conf: ComposerConfig + :param gid: Group ID that has access to the queue directories + :type gid: int + :returns: list of errors + :rtype: list of str + """ + errors = [] + lib_dir = conf.get("composer", "lib_dir") + for p in ["queue/run", "queue/new", "results"]: + p_dir = joinpaths(lib_dir, p) + errors.extend(make_owned_dir(p_dir, 0, gid)) + return errors
+
+ +
+ +
+ + +
+
+ +
+ +
+ + + + + + + + + + + + \ No newline at end of file diff --git a/f30-branch/_modules/pylorax/api/crossdomain.html b/f30-branch/_modules/pylorax/api/crossdomain.html new file mode 100644 index 00000000..3c9b2af8 --- /dev/null +++ b/f30-branch/_modules/pylorax/api/crossdomain.html @@ -0,0 +1,264 @@ + + + + + + + + + + + pylorax.api.crossdomain — Lorax 30.19 documentation + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + +
+ + + + + +
+ +
+ + + + + + + + + + + + + + + + + +
+ + + + +
+
+
+
+ +

Source code for pylorax.api.crossdomain

+#
+# Copyright (C) 2017  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/>.
+#
+
+# crossdomain decorator from - http://flask.pocoo.org/snippets/56/
+from datetime import timedelta
+from flask import make_response, request, current_app
+from functools import update_wrapper
+
+
+
[docs]def crossdomain(origin, methods=None, headers=None, + max_age=21600, attach_to_all=True, + automatic_options=True): + if methods is not None: + methods = ', '.join(sorted(x.upper() for x in methods)) + if headers is not None and not isinstance(headers, str): + headers = ', '.join(x.upper() for x in headers) + if not isinstance(origin, list): + origin = [origin] + if isinstance(max_age, timedelta): + max_age = int(max_age.total_seconds()) + + def get_methods(): + if methods is not None: + return methods + + options_resp = current_app.make_default_options_response() + return options_resp.headers['allow'] + + def decorator(f): + def wrapped_function(*args, **kwargs): + if automatic_options and request.method == 'OPTIONS': + resp = current_app.make_default_options_response() + else: + resp = make_response(f(*args, **kwargs)) + if not attach_to_all and request.method != 'OPTIONS': + return resp + + h = resp.headers + + h.extend([("Access-Control-Allow-Origin", orig) for orig in origin]) + h['Access-Control-Allow-Methods'] = get_methods() + h['Access-Control-Max-Age'] = str(max_age) + if headers is not None: + h['Access-Control-Allow-Headers'] = headers + return resp + + f.provide_automatic_options = False + f.required_methods = ['OPTIONS'] + return update_wrapper(wrapped_function, f) + return decorator
+
+ +
+ +
+ + +
+
+ +
+ +
+ + + + + + + + + + + + \ No newline at end of file diff --git a/f30-branch/_modules/pylorax/api/dnfbase.html b/f30-branch/_modules/pylorax/api/dnfbase.html new file mode 100644 index 00000000..bd419a52 --- /dev/null +++ b/f30-branch/_modules/pylorax/api/dnfbase.html @@ -0,0 +1,381 @@ + + + + + + + + + + + pylorax.api.dnfbase — Lorax 30.19 documentation + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + +
+ + + + + +
+ +
+ + + + + + + + + + + + + + + + + +
+ + + + +
+
+
+
+ +

Source code for pylorax.api.dnfbase

+#
+# Copyright (C) 2017-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/>.
+#
+# pylint: disable=bad-preconf-access
+
+import logging
+log = logging.getLogger("lorax-composer")
+
+import dnf
+import dnf.logging
+from glob import glob
+import os
+import shutil
+from threading import Lock
+import time
+
+from pylorax import DEFAULT_PLATFORM_ID
+from pylorax.sysutils import flatconfig
+
+
[docs]class DNFLock(object): + """Hold the dnf.Base object and a Lock to control access to it. + + self.dbo is a property that returns the dnf.Base object, but it *may* change + from one call to the next if the upstream repositories have changed. + """ + def __init__(self, conf, expire_secs=6*60*60): + self._conf = conf + self._lock = Lock() + self.dbo = get_base_object(self._conf) + self._expire_secs = expire_secs + self._expire_time = time.time() + self._expire_secs + + @property + def lock(self): + """Check for repo updates (using expiration time) and return the lock + + If the repository has been updated, tear down the old dnf.Base and + create a new one. This is the only way to force dnf to use the new + metadata. + """ + if time.time() > self._expire_time: + return self.lock_check + return self._lock + + @property + def lock_check(self): + """Force a check for repo updates and return the lock + + Use this method sparingly, it removes the repodata and downloads a new copy every time. + """ + self._expire_time = time.time() + self._expire_secs + self.dbo.update_cache() + return self._lock
+ +
[docs]def get_base_object(conf): + """Get the DNF object with settings from the config file + + :param conf: configuration object + :type conf: ComposerParser + :returns: A DNF Base object + :rtype: dnf.Base + """ + cachedir = os.path.abspath(conf.get("composer", "cache_dir")) + dnfconf = os.path.abspath(conf.get("composer", "dnf_conf")) + dnfroot = os.path.abspath(conf.get("composer", "dnf_root")) + repodir = os.path.abspath(conf.get("composer", "repo_dir")) + + # Setup the config for the DNF Base object + dbo = dnf.Base() + dbc = dbo.conf +# TODO - Handle this +# dbc.logdir = logdir + dbc.installroot = dnfroot + if not os.path.isdir(dnfroot): + os.makedirs(dnfroot) + if not os.path.isdir(repodir): + os.makedirs(repodir) + + dbc.cachedir = cachedir + dbc.reposdir = [repodir] + dbc.install_weak_deps = False + dbc.prepend_installroot('persistdir') + # this is a weird 'AppendOption' thing that, when you set it, + # actually appends. Doing this adds 'nodocs' to the existing list + # of values, over in libdnf, it does not replace the existing values. + dbc.tsflags = ['nodocs'] + + if conf.get_default("dnf", "proxy", None): + dbc.proxy = conf.get("dnf", "proxy") + + if conf.has_option("dnf", "sslverify") and not conf.getboolean("dnf", "sslverify"): + dbc.sslverify = False + + _releasever = conf.get_default("composer", "releasever", None) + if not _releasever: + # Use the releasever of the host system + _releasever = dnf.rpm.detect_releasever("/") + log.info("releasever = %s", _releasever) + dbc.releasever = _releasever + + # DNF 3.2 needs to have module_platform_id set, otherwise depsolve won't work correctly + if not os.path.exists("/etc/os-release"): + log.warning("/etc/os-release is missing, cannot determine platform id, falling back to %s", DEFAULT_PLATFORM_ID) + platform_id = DEFAULT_PLATFORM_ID + else: + os_release = flatconfig("/etc/os-release") + platform_id = os_release.get("PLATFORM_ID", DEFAULT_PLATFORM_ID) + log.info("Using %s for module_platform_id", platform_id) + dbc.module_platform_id = platform_id + + # Make sure metadata is always current + dbc.metadata_expire = 0 + dbc.metadata_expire_filter = "never" + + # write the dnf configuration file + with open(dnfconf, "w") as f: + f.write(dbc.dump()) + + # dnf needs the repos all in one directory, composer uses repodir for this + # if system repos are supposed to be used, copy them into repodir, overwriting any previous copies + if not conf.has_option("repos", "use_system_repos") or conf.getboolean("repos", "use_system_repos"): + for repo_file in glob("/etc/yum.repos.d/*.repo"): + shutil.copy2(repo_file, repodir) + dbo.read_all_repos() + + # Remove any duplicate repo entries. These can cause problems with Anaconda, which will fail + # with space problems. + repos = sorted(list(r.id for r in dbo.repos.iter_enabled())) + seen = {"baseurl": [], "mirrorlist": [], "metalink": []} + for source_name in repos: + remove = False + repo = dbo.repos.get(source_name, None) + if repo is None: + log.warning("repo %s vanished while removing duplicates", source_name) + continue + if repo.baseurl: + if repo.baseurl[0] in seen["baseurl"]: + log.info("Removing duplicate repo: %s baseurl=%s", source_name, repo.baseurl[0]) + remove = True + else: + seen["baseurl"].append(repo.baseurl[0]) + elif repo.mirrorlist: + if repo.mirrorlist in seen["mirrorlist"]: + log.info("Removing duplicate repo: %s mirrorlist=%s", source_name, repo.mirrorlist) + remove = True + else: + seen["mirrorlist"].append(repo.mirrorlist) + elif repo.metalink: + if repo.metalink in seen["metalink"]: + log.info("Removing duplicate repo: %s metalink=%s", source_name, repo.metalink) + remove = True + else: + seen["metalink"].append(repo.metalink) + + if remove: + del dbo.repos[source_name] + + # Update the metadata from the enabled repos to speed up later operations + log.info("Updating repository metadata") + try: + dbo.fill_sack(load_system_repo=False) + dbo.read_comps() + dbo.update_cache() + except dnf.exceptions.Error as e: + log.error("Failed to update metadata: %s", str(e)) + raise RuntimeError("Fetching metadata failed: %s" % str(e)) + + return dbo
+
+ +
+ +
+ + +
+
+ +
+ +
+ + + + + + + + + + + + \ No newline at end of file diff --git a/f30-branch/_modules/pylorax/api/gitrpm.html b/f30-branch/_modules/pylorax/api/gitrpm.html new file mode 100644 index 00000000..cad4e0bf --- /dev/null +++ b/f30-branch/_modules/pylorax/api/gitrpm.html @@ -0,0 +1,421 @@ + + + + + + + + + + + pylorax.api.gitrpm — Lorax 30.19 documentation + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + +
+ + + + + +
+ +
+ + + + + + + + + + + + + + + + + +
+ + + + +
+
+
+
+ +

Source code for pylorax.api.gitrpm

+# Copyright (C) 2019 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/>.
+#
+""" Clone a git repository and package it as an rpm
+
+This module contains functions for cloning a git repo, creating a tar archive of
+the selected commit, branch, or tag, and packaging the files into an rpm that will
+be installed by anaconda when creating the image.
+"""
+import logging
+log = logging.getLogger("lorax-composer")
+
+import os
+from rpmfluff import SimpleRpmBuild
+import shutil
+import subprocess
+import tempfile
+import time
+
+from pylorax.sysutils import joinpaths
+
+
[docs]def get_repo_description(gitRepo): + """ Return a description including the git repo and reference + + :param gitRepo: A dict with the repository details + :type gitRepo: dict + :returns: A string with the git repo url and reference + :rtype: str + """ + return "Created from %s, reference '%s', on %s" % (gitRepo["repo"], gitRepo["ref"], time.ctime())
+ +
[docs]class GitArchiveTarball: + """Create a git archive of the selected git repo and reference""" + def __init__(self, gitRepo): + self._gitRepo = gitRepo + self.sourceName = self._gitRepo["rpmname"]+".tar.xz" + +
[docs] def write_file(self, sourcesDir): + """ Create the tar archive + + :param sourcesDir: Path to use for creating the archive + :type sourcesDir: str + + This clones the git repository and creates a git archive from the specified reference. + The result is in RPMNAME.tar.xz under the sourcesDir + """ + # Clone the repository into a temporary location + cmd = ["git", "clone", self._gitRepo["repo"], joinpaths(sourcesDir, "gitrepo")] + log.debug(cmd) + try: + subprocess.check_output(cmd) + except subprocess.CalledProcessError as e: + log.error("Failed to clone %s: %s", self._gitRepo["repo"], e.output) + raise RuntimeError("Failed to clone %s" % self._gitRepo["repo"]) + + oldcwd = os.getcwd() + try: + os.chdir(joinpaths(sourcesDir, "gitrepo")) + + # Configure archive to create a .tar.xz + cmd = ["git", "config", "tar.tar.xz.command", "xz -c"] + log.debug(cmd) + subprocess.check_call(cmd) + + cmd = ["git", "archive", "--prefix", self._gitRepo["rpmname"] + "/", "-o", joinpaths(sourcesDir, self.sourceName), self._gitRepo["ref"]] + log.debug(cmd) + try: + subprocess.check_output(cmd) + except subprocess.CalledProcessError as e: + log.error("Failed to archive %s: %s", self._gitRepo["repo"], e.output) + raise RuntimeError("Failed to clone %s" % self._gitRepo["repo"]) + finally: + # Cleanup even if there was an error + os.chdir(oldcwd) + shutil.rmtree(joinpaths(sourcesDir, "gitrepo"))
+ +
[docs]class GitRpmBuild(SimpleRpmBuild): + """Build an rpm containing files from a git repository""" + def __init__(self, *args, **kwargs): + self._base_dir = None + super().__init__(*args, **kwargs) + +
[docs] def check(self): + raise NotImplementedError
+ +
[docs] def get_base_dir(self): + """Place all the files under a temporary directory + rpmbuild/ + """ + if not self._base_dir: + self._base_dir = tempfile.mkdtemp(prefix="lorax-git-rpm.") + return joinpaths(self._base_dir, "rpmbuild")
+ +
[docs] def cleanup_tmpdir(self): + """Remove the temporary directory and all of its contents + """ + if len(self._base_dir) < 5: + raise RuntimeError("Invalid base_dir: %s" % self.get_base_dir()) + + shutil.rmtree(self._base_dir)
+ +
[docs] def clean(self): + """Remove the base directory from inside the tmpdir""" + if len(self.get_base_dir()) < 5: + raise RuntimeError("Invalid base_dir: %s" % self.get_base_dir()) + shutil.rmtree(self.get_base_dir(), ignore_errors=True)
+ +
[docs] def add_git_tarball(self, gitRepo): + """Add a tar archive of a git repository to the rpm + + :param gitRepo: A dict with the repository details + :type gitRepo: dict + + This populates the rpm with the URL of the git repository, the summary + describing the repo, the description of the repository and reference used, + and sets up the rpm to install the archive contents into the destination + path. + """ + self.addUrl(gitRepo["repo"]) + self.add_summary(gitRepo["summary"]) + self.add_description(get_repo_description(gitRepo)) + self.addLicense("Unknown") + sourceIndex = self.add_source(GitArchiveTarball(gitRepo)) + self.section_build += "tar -xvf %s\n" % self.sources[sourceIndex].sourceName + dest = os.path.normpath(gitRepo["destination"]) + # Prevent double slash root + if dest == "/": + dest = "" + self.create_parent_dirs(dest) + self.section_install += "cp -r %s/. $RPM_BUILD_ROOT/%s\n" % (gitRepo["rpmname"], dest) + sub = self.get_subpackage(None) + if not dest: + # / is special, we don't want to include / itself, just what's under it + sub.section_files += "/*\n" + else: + sub.section_files += "%s/\n" % dest
+ +
[docs]def make_git_rpm(gitRepo, dest): + """ Create an rpm from the specified git repo + + :param gitRepo: A dict with the repository details + :type gitRepo: dict + + This will clone the git repository, create an archive of the selected reference, + and build an rpm that will install the files from the repository under the destination + directory. The gitRepo dict should have the following fields:: + + rpmname: "server-config" + rpmversion: "1.0" + rpmrelease: "1" + summary: "Setup files for server deployment" + repo: "PATH OF GIT REPO TO CLONE" + ref: "v1.0" + destination: "/opt/server/" + + * rpmname: Name of the rpm to create, also used as the prefix name in the tar archive + * rpmversion: Version of the rpm, eg. "1.0.0" + * rpmrelease: Release of the rpm, eg. "1" + * summary: Summary string for the rpm + * repo: URL of the get repo to clone and create the archive from + * ref: Git reference to check out. eg. origin/branch-name, git tag, or git commit hash + * destination: Path to install the / of the git repo at when installing the rpm + """ + gitRpm = GitRpmBuild(gitRepo["rpmname"], gitRepo["rpmversion"], gitRepo["rpmrelease"], ["noarch"]) + try: + gitRpm.add_git_tarball(gitRepo) + gitRpm.do_make() + rpmfile = gitRpm.get_built_rpm("noarch") + shutil.move(rpmfile, dest) + except Exception as e: + log.error("Creating git repo rpm: %s", e) + raise RuntimeError("Creating git repo rpm: %s" % e) + finally: + gitRpm.cleanup_tmpdir() + + return os.path.basename(rpmfile)
+ +# Create the git rpms, if any, and return the path to the repo under results_dir +
[docs]def create_gitrpm_repo(results_dir, recipe): + """Create a dnf repository with the rpms from the recipe + + :param results_dir: Path to create the repository under + :type results_dir: str + :param recipe: The recipe to get the repos.git entries from + :type recipe: Recipe + :returns: Path to the dnf repository or "" + :rtype: str + + This function creates a dnf repository directory at results_dir+"repo/", + creates rpms for all of the repos.git entries in the recipe, runs createrepo_c + on the dnf repository so that Anaconda can use it, and returns the path to the + repository to the caller. + """ + if "repos" not in recipe or "git" not in recipe["repos"]: + return "" + + gitrepo = joinpaths(results_dir, "repo/") + if not os.path.exists(gitrepo): + os.makedirs(gitrepo) + for r in recipe["repos"]["git"]: + make_git_rpm(r, gitrepo) + cmd = ["createrepo_c", gitrepo] + log.debug(cmd) + try: + subprocess.check_output(cmd) + except subprocess.CalledProcessError as e: + log.error("Failed to create repo at %s: %s", gitrepo, e.output) + raise RuntimeError("Failed to create repo at %s" % gitrepo) + + return gitrepo
+
+ +
+ +
+ + +
+
+ +
+ +
+ + + + + + + + + + + + \ No newline at end of file diff --git a/f30-branch/_modules/pylorax/api/projects.html b/f30-branch/_modules/pylorax/api/projects.html new file mode 100644 index 00000000..4d5ce33f --- /dev/null +++ b/f30-branch/_modules/pylorax/api/projects.html @@ -0,0 +1,783 @@ + + + + + + + + + + + pylorax.api.projects — Lorax 30.19 documentation + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + +
+ + + + + +
+ +
+ + + + + + + + + + + + + + + + + +
+ + + + +
+
+
+
+ +

Source code for pylorax.api.projects

+#
+# Copyright (C) 2017  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("lorax-composer")
+
+from configparser import ConfigParser
+import dnf
+from glob import glob
+import os
+import time
+
+from pylorax.api.bisect import insort_left
+
+TIME_FORMAT = "%Y-%m-%dT%H:%M:%S"
+
+
+
[docs]class ProjectsError(Exception): + pass
+ + +
[docs]def api_time(t): + """Convert time since epoch to a string + + :param t: Seconds since epoch + :type t: int + :returns: Time string + :rtype: str + """ + return time.strftime(TIME_FORMAT, time.localtime(t))
+ + +
[docs]def api_changelog(changelog): + """Convert the changelog to a string + + :param changelog: A list of time, author, string tuples. + :type changelog: tuple + :returns: The most recent changelog text or "" + :rtype: str + + This returns only the most recent changelog entry. + """ + try: + entry = changelog[0][2] + except IndexError: + entry = "" + return entry
+ + +
[docs]def pkg_to_project(pkg): + """Extract the details from a hawkey.Package object + + :param pkgs: hawkey.Package object with package details + :type pkgs: hawkey.Package + :returns: A dict with the name, summary, description, and url. + :rtype: dict + + upstream_vcs is hard-coded to UPSTREAM_VCS + """ + return {"name": pkg.name, + "summary": pkg.summary, + "description": pkg.description, + "homepage": pkg.url, + "upstream_vcs": "UPSTREAM_VCS"}
+ + +
[docs]def pkg_to_build(pkg): + """Extract the build details from a hawkey.Package object + + :param pkg: hawkey.Package object with package details + :type pkg: hawkey.Package + :returns: A dict with the build details, epoch, release, arch, build_time, changelog, ... + :rtype: dict + + metadata entries are hard-coded to {} + + Note that this only returns the build dict, it does not include the name, description, etc. + """ + return {"epoch": pkg.epoch, + "release": pkg.release, + "arch": pkg.arch, + "build_time": api_time(pkg.buildtime), + "changelog": "CHANGELOG_NEEDED", # XXX Not in hawkey.Package + "build_config_ref": "BUILD_CONFIG_REF", + "build_env_ref": "BUILD_ENV_REF", + "metadata": {}, + "source": {"license": pkg.license, + "version": pkg.version, + "source_ref": "SOURCE_REF", + "metadata": {}}}
+ + +
[docs]def pkg_to_project_info(pkg): + """Extract the details from a hawkey.Package object + + :param pkg: hawkey.Package object with package details + :type pkg: hawkey.Package + :returns: A dict with the project details, as well as epoch, release, arch, build_time, changelog, ... + :rtype: dict + + metadata entries are hard-coded to {} + """ + return {"name": pkg.name, + "summary": pkg.summary, + "description": pkg.description, + "homepage": pkg.url, + "upstream_vcs": "UPSTREAM_VCS", + "builds": [pkg_to_build(pkg)]}
+ + +
[docs]def pkg_to_dep(pkg): + """Extract the info from a hawkey.Package object + + :param pkg: A hawkey.Package object + :type pkg: hawkey.Package + :returns: A dict with name, epoch, version, release, arch + :rtype: dict + """ + return {"name": pkg.name, + "epoch": pkg.epoch, + "version": pkg.version, + "release": pkg.release, + "arch": pkg.arch}
+ + +
[docs]def proj_to_module(proj): + """Extract the name from a project_info dict + + :param pkg: dict with package details + :type pkg: dict + :returns: A dict with name, and group_type + :rtype: dict + + group_type is hard-coded to "rpm" + """ + return {"name": proj["name"], + "group_type": "rpm"}
+ + +
[docs]def dep_evra(dep): + """Return the epoch:version-release.arch for the dep + + :param dep: dependency dict + :type dep: dict + :returns: epoch:version-release.arch + :rtype: str + """ + if dep["epoch"] == 0: + return dep["version"]+"-"+dep["release"]+"."+dep["arch"] + else: + return str(dep["epoch"])+":"+dep["version"]+"-"+dep["release"]+"."+dep["arch"]
+ +
[docs]def dep_nevra(dep): + """Return the name-epoch:version-release.arch""" + return dep["name"]+"-"+dep_evra(dep)
+ + +
[docs]def projects_list(dbo): + """Return a list of projects + + :param dbo: dnf base object + :type dbo: dnf.Base + :returns: List of project info dicts with name, summary, description, homepage, upstream_vcs + :rtype: list of dicts + """ + return projects_info(dbo, None)
+ + +
[docs]def projects_info(dbo, project_names): + """Return details about specific projects + + :param dbo: dnf base object + :type dbo: dnf.Base + :param project_names: List of names of projects to get info about + :type project_names: str + :returns: List of project info dicts with pkg_to_project as well as epoch, version, release, etc. + :rtype: list of dicts + + If project_names is None it will return the full list of available packages + """ + if project_names: + pkgs = dbo.sack.query().available().filter(name__glob=project_names) + else: + pkgs = dbo.sack.query().available() + + # iterate over pkgs + # - if pkg.name isn't in the results yet, add pkg_to_project_info in sorted position + # - if pkg.name is already in results, get its builds. If the build for pkg is different + # in any way (version, arch, etc.) add it to the entry's builds list. If it is the same, + # skip it. + results = [] + results_names = {} + for p in pkgs: + if p.name.lower() not in results_names: + idx = insort_left(results, pkg_to_project_info(p), key=lambda p: p["name"].lower()) + results_names[p.name.lower()] = idx + else: + build = pkg_to_build(p) + if build not in results[results_names[p.name.lower()]]["builds"]: + results[results_names[p.name.lower()]]["builds"].append(build) + + return results
+ +def _depsolve(dbo, projects, groups): + """Add projects to a new transaction + + :param dbo: dnf base object + :type dbo: dnf.Base + :param projects: The projects and version globs to find the dependencies for + :type projects: List of tuples + :param groups: The groups to include in dependency solving + :type groups: List of str + :returns: None + :rtype: None + :raises: ProjectsError if there was a problem installing something + """ + # This resets the transaction and updates the cache. + # It is important that the cache always be synchronized because Anaconda will grab its own copy + # and if that is different the NEVRAs will not match and the build will fail. + dbo.reset(goal=True) + install_errors = [] + for name in groups: + try: + dbo.group_install(name, ["mandatory", "default"]) + except dnf.exceptions.MarkingError as e: + install_errors.append(("Group %s" % (name), str(e))) + + for name, version in projects: + # Find the best package matching the name + version glob + # dnf can return multiple packages if it is in more than 1 repository + query = dbo.sack.query().filterm(provides__glob=name) + if version: + query.filterm(version__glob=version) + + query.filterm(latest=1) + if not query: + install_errors.append(("%s-%s" % (name, version), "No match")) + continue + sltr = dnf.selector.Selector(dbo.sack).set(pkg=query) + + # NOTE: dnf says in near future there will be a "goal" attribute of Base class + # so yes, we're using a 'private' attribute here on purpose and with permission. + dbo._goal.install(select=sltr, optional=False) + + if install_errors: + raise ProjectsError("The following package(s) had problems: %s" % ",".join(["%s (%s)" % (pattern, err) for pattern, err in install_errors])) + +
[docs]def projects_depsolve(dbo, projects, groups): + """Return the dependencies for a list of projects + + :param dbo: dnf base object + :type dbo: dnf.Base + :param projects: The projects to find the dependencies for + :type projects: List of Strings + :param groups: The groups to include in dependency solving + :type groups: List of str + :returns: NEVRA's of the project and its dependencies + :rtype: list of dicts + :raises: ProjectsError if there was a problem installing something + """ + _depsolve(dbo, projects, groups) + + try: + dbo.resolve() + except dnf.exceptions.DepsolveError as e: + raise ProjectsError("There was a problem depsolving %s: %s" % (projects, str(e))) + + if len(dbo.transaction) == 0: + return [] + + return sorted(map(pkg_to_dep, dbo.transaction.install_set), key=lambda p: p["name"].lower())
+ + +
[docs]def estimate_size(packages, block_size=6144): + """Estimate the installed size of a package list + + :param packages: The packages to be installed + :type packages: list of hawkey.Package objects + :param block_size: The block size to use for rounding up file sizes. + :type block_size: int + :returns: The estimated size of installed packages + :rtype: int + + Estimating actual requirements is difficult without the actual file sizes, which + dnf doesn't provide access to. So use the file count and block size to estimate + a minimum size for each package. + """ + installed_size = 0 + for p in packages: + installed_size += len(p.files) * block_size + installed_size += p.installsize + return installed_size
+ + +
[docs]def projects_depsolve_with_size(dbo, projects, groups, with_core=True): + """Return the dependencies and installed size for a list of projects + + :param dbo: dnf base object + :type dbo: dnf.Base + :param project_names: The projects to find the dependencies for + :type project_names: List of Strings + :param groups: The groups to include in dependency solving + :type groups: List of str + :returns: installed size and a list of NEVRA's of the project and its dependencies + :rtype: tuple of (int, list of dicts) + :raises: ProjectsError if there was a problem installing something + """ + _depsolve(dbo, projects, groups) + + if with_core: + dbo.group_install("core", ['mandatory', 'default', 'optional']) + + try: + dbo.resolve() + except dnf.exceptions.DepsolveError as e: + raise ProjectsError("There was a problem depsolving %s: %s" % (projects, str(e))) + + if len(dbo.transaction) == 0: + return (0, []) + + installed_size = estimate_size(dbo.transaction.install_set) + deps = sorted(map(pkg_to_dep, dbo.transaction.install_set), key=lambda p: p["name"].lower()) + return (installed_size, deps)
+ + +
[docs]def modules_list(dbo, module_names): + """Return a list of modules + + :param dbo: dnf base object + :type dbo: dnf.Base + :param offset: Number of modules to skip + :type limit: int + :param limit: Maximum number of modules to return + :type limit: int + :returns: List of module information and total count + :rtype: tuple of a list of dicts and an Int + + Modules don't exist in RHEL7 so this only returns projects + and sets the type to "rpm" + + """ + # TODO - Figure out what to do with this for Fedora 'modules' + return list(map(proj_to_module, projects_info(dbo, module_names)))
+ +
[docs]def modules_info(dbo, module_names): + """Return details about a module, including dependencies + + :param dbo: dnf base object + :type dbo: dnf.Base + :param module_names: Names of the modules to get info about + :type module_names: str + :returns: List of dicts with module details and dependencies. + :rtype: list of dicts + """ + modules = projects_info(dbo, module_names) + + # Add the dependency info to each one + for module in modules: + module["dependencies"] = projects_depsolve(dbo, [(module["name"], "*.*")], []) + + return modules
+ +
[docs]def dnf_repo_to_file_repo(repo): + """Return a string representation of a DNF Repo object suitable for writing to a .repo file + + :param repo: DNF Repository + :type repo: dnf.RepoDict + :returns: A string + :rtype: str + + The DNF Repo.dump() function does not produce a string that can be used as a dnf .repo file, + it ouputs baseurl and gpgkey as python lists which DNF cannot read. So do this manually with + only the attributes we care about. + """ + repo_str = "[%s]\nname = %s\n" % (repo.id, repo.name) + if repo.metalink: + repo_str += "metalink = %s\n" % repo.metalink + elif repo.mirrorlist: + repo_str += "mirrorlist = %s\n" % repo.mirrorlist + elif repo.baseurl: + repo_str += "baseurl = %s\n" % repo.baseurl[0] + else: + raise RuntimeError("Repo has no baseurl, metalink, or mirrorlist") + + # proxy is optional + if repo.proxy: + repo_str += "proxy = %s\n" % repo.proxy + + repo_str += "sslverify = %s\n" % repo.sslverify + repo_str += "gpgcheck = %s\n" % repo.gpgcheck + if repo.gpgkey: + repo_str += "gpgkey = %s\n" % ",".join(repo.gpgkey) + + return repo_str
+ +
[docs]def repo_to_source(repo, system_source): + """Return a Weldr Source dict created from the DNF Repository + + :param repo: DNF Repository + :type repo: dnf.RepoDict + :param system_source: True if this source is an immutable system source + :type system_source: bool + :returns: A dict with Weldr Source fields filled in + :rtype: dict + + Example:: + + { + "check_gpg": true, + "check_ssl": true, + "gpgkey_url": [ + "file:///etc/pki/rpm-gpg/RPM-GPG-KEY-fedora-28-x86_64" + ], + "name": "fedora", + "proxy": "http://proxy.brianlane.com:8123", + "system": true + "type": "yum-metalink", + "url": "https://mirrors.fedoraproject.org/metalink?repo=fedora-28&arch=x86_64" + } + + """ + source = {"name": repo.id, "system": system_source} + if repo.baseurl: + source["url"] = repo.baseurl[0] + source["type"] = "yum-baseurl" + elif repo.metalink: + source["url"] = repo.metalink + source["type"] = "yum-metalink" + elif repo.mirrorlist: + source["url"] = repo.mirrorlist + source["type"] = "yum-mirrorlist" + else: + raise RuntimeError("Repo has no baseurl, metalink, or mirrorlist") + + # proxy is optional + if repo.proxy: + source["proxy"] = repo.proxy + + if not repo.sslverify: + source["check_ssl"] = False + else: + source["check_ssl"] = True + + if not repo.gpgcheck: + source["check_gpg"] = False + else: + source["check_gpg"] = True + + if repo.gpgkey: + source["gpgkey_urls"] = list(repo.gpgkey) + + return source
+ +
[docs]def source_to_repo(source, dnf_conf): + """Return a dnf Repo object created from a source dict + + :param source: A Weldr source dict + :type source: dict + :returns: A dnf Repo object + :rtype: dnf.Repo + + Example:: + + { + "check_gpg": True, + "check_ssl": True, + "gpgkey_urls": [ + "file:///etc/pki/rpm-gpg/RPM-GPG-KEY-fedora-28-x86_64" + ], + "name": "fedora", + "proxy": "http://proxy.brianlane.com:8123", + "system": True + "type": "yum-metalink", + "url": "https://mirrors.fedoraproject.org/metalink?repo=fedora-28&arch=x86_64" + } + + """ + repo = dnf.repo.Repo(source["name"], dnf_conf) + # This will allow errors to be raised so we can catch them + # without this they are logged, but the repo is silently disabled + repo.skip_if_unavailable = False + + if source["type"] == "yum-baseurl": + repo.baseurl = source["url"] + elif source["type"] == "yum-metalink": + repo.metalink = source["url"] + elif source["type"] == "yum-mirrorlist": + repo.mirrorlist = source["url"] + + if "proxy" in source: + repo.proxy = source["proxy"] + + if source["check_ssl"]: + repo.sslverify = True + else: + repo.sslverify = False + + if source["check_gpg"]: + repo.gpgcheck = True + else: + repo.gpgcheck = False + + if "gpgkey_urls" in source: + repo.gpgkey = tuple(source["gpgkey_urls"]) + + repo.enable() + + return repo
+ +
[docs]def get_source_ids(source_path): + """Return a list of the source ids in a file + + :param source_path: Full path and filename of the source (yum repo) file + :type source_path: str + :returns: A list of source id strings + :rtype: list of str + """ + if not os.path.exists(source_path): + return [] + + cfg = ConfigParser() + cfg.read(source_path) + return cfg.sections()
+ +
[docs]def get_repo_sources(source_glob): + """Return a list of sources from a directory of yum repositories + + :param source_glob: A glob to use to match the source files, including full path + :type source_glob: str + :returns: A list of the source ids in all of the matching files + :rtype: list of str + """ + sources = [] + for f in glob(source_glob): + sources.extend(get_source_ids(f)) + return sources
+ +
[docs]def delete_repo_source(source_glob, source_name): + """Delete a source from a repo file + + :param source_glob: A glob of the repo sources to search + :type source_glob: str + :returns: None + :raises: ProjectsError if there was a problem + + A repo file may have multiple sources in it, delete only the selected source. + If it is the last one in the file, delete the file. + + WARNING: This will delete ANY source, the caller needs to ensure that a system + source_name isn't passed to it. + """ + found = False + for f in glob(source_glob): + try: + cfg = ConfigParser() + cfg.read(f) + if source_name in cfg.sections(): + found = True + cfg.remove_section(source_name) + # If there are other sections, rewrite the file without the deleted one + if len(cfg.sections()) > 0: + with open(f, "w") as cfg_file: + cfg.write(cfg_file) + else: + # No sections left, just delete the file + os.unlink(f) + except Exception as e: + raise ProjectsError("Problem deleting repo source %s: %s" % (source_name, str(e))) + if not found: + raise ProjectsError("source %s not found" % source_name)
+
+ +
+ +
+ + +
+
+ +
+ +
+ + + + + + + + + + + + \ No newline at end of file diff --git a/f30-branch/_modules/pylorax/api/queue.html b/f30-branch/_modules/pylorax/api/queue.html new file mode 100644 index 00000000..015ce4bf --- /dev/null +++ b/f30-branch/_modules/pylorax/api/queue.html @@ -0,0 +1,847 @@ + + + + + + + + + + + pylorax.api.queue — Lorax 30.7 documentation + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + +
+ + + + + +
+ +
+ + + + + + + + + + + + + + + + + +
+ + + + +
+
+
+
+ +

Source code for pylorax.api.queue

+# 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/>.
+#
+""" Functions to monitor compose queue and run anaconda"""
+import logging
+log = logging.getLogger("pylorax")
+
+import os
+import grp
+from glob import glob
+import multiprocessing as mp
+import pytoml as toml
+import pwd
+import shutil
+import subprocess
+from subprocess import Popen, PIPE
+import time
+
+from pylorax import find_templates
+from pylorax.api.compose import move_compose_results
+from pylorax.api.recipes import recipe_from_file
+from pylorax.api.timestamp import TS_CREATED, TS_STARTED, TS_FINISHED, write_timestamp, timestamp_dict
+from pylorax.base import DataHolder
+from pylorax.creator import run_creator
+from pylorax.sysutils import joinpaths
+
+
[docs]def start_queue_monitor(cfg, uid, gid): + """Start the queue monitor as a mp process + + :param cfg: Configuration settings + :type cfg: ComposerConfig + :param uid: User ID that owns the queue + :type uid: int + :param gid: Group ID that owns the queue + :type gid: int + :returns: None + """ + lib_dir = cfg.get("composer", "lib_dir") + share_dir = cfg.get("composer", "share_dir") + tmp = cfg.get("composer", "tmp") + monitor_cfg = DataHolder(composer_dir=lib_dir, share_dir=share_dir, uid=uid, gid=gid, tmp=tmp) + p = mp.Process(target=monitor, args=(monitor_cfg,)) + p.daemon = True + p.start()
+ +
[docs]def monitor(cfg): + """Monitor the queue for new compose requests + + :param cfg: Configuration settings + :type cfg: DataHolder + :returns: Does not return + + The queue has 2 subdirectories, new and run. When a compose is ready to be run + a symlink to the uniquely named results directory should be placed in ./queue/new/ + + When the it is ready to be run (it is checked every 30 seconds or after a previous + compose is finished) the symlink will be moved into ./queue/run/ and a STATUS file + will be created in the results directory. + + STATUS can contain one of: RUNNING, FINISHED, FAILED + + If the system is restarted while a compose is running it will move any old symlinks + from ./queue/run/ to ./queue/new/ and rerun them. + """ + def queue_sort(uuid): + """Sort the queue entries by their mtime, not their names""" + return os.stat(joinpaths(cfg.composer_dir, "queue/new", uuid)).st_mtime + + # Move any symlinks in the run queue back to the new queue + for link in os.listdir(joinpaths(cfg.composer_dir, "queue/run")): + src = joinpaths(cfg.composer_dir, "queue/run", link) + dst = joinpaths(cfg.composer_dir, "queue/new", link) + os.rename(src, dst) + log.debug("Moved unfinished compose %s back to new state", src) + + while True: + uuids = sorted(os.listdir(joinpaths(cfg.composer_dir, "queue/new")), key=queue_sort) + + # Pick the oldest and move it into ./run/ + if not uuids: + # No composes left to process, sleep for a bit + time.sleep(5) + else: + src = joinpaths(cfg.composer_dir, "queue/new", uuids[0]) + dst = joinpaths(cfg.composer_dir, "queue/run", uuids[0]) + try: + os.rename(src, dst) + except OSError: + # The symlink may vanish if uuid_cancel() has been called + continue + + log.info("Starting new compose: %s", dst) + open(joinpaths(dst, "STATUS"), "w").write("RUNNING\n") + + try: + make_compose(cfg, os.path.realpath(dst)) + log.info("Finished building %s, results are in %s", dst, os.path.realpath(dst)) + open(joinpaths(dst, "STATUS"), "w").write("FINISHED\n") + write_timestamp(dst, TS_FINISHED) + except Exception: + import traceback + log.error("traceback: %s", traceback.format_exc()) + +# TODO - Write the error message to an ERROR-LOG file to include with the status +# log.error("Error running compose: %s", e) + open(joinpaths(dst, "STATUS"), "w").write("FAILED\n") + write_timestamp(dst, TS_FINISHED) + + os.unlink(dst)
+ +
[docs]def make_compose(cfg, results_dir): + """Run anaconda with the final-kickstart.ks from results_dir + + :param cfg: Configuration settings + :type cfg: DataHolder + :param results_dir: The directory containing the metadata and results for the build + :type results_dir: str + :returns: Nothing + :raises: May raise various exceptions + + This takes the final-kickstart.ks, and the settings in config.toml and runs Anaconda + in no-virt mode (directly on the host operating system). Exceptions should be caught + at the higer level. + + If there is a failure, the build artifacts will be cleaned up, and any logs will be + moved into logs/anaconda/ and their ownership will be set to the user from the cfg + object. + """ + + # Check on the ks's presence + ks_path = joinpaths(results_dir, "final-kickstart.ks") + if not os.path.exists(ks_path): + raise RuntimeError("Missing kickstart file at %s" % ks_path) + + # The anaconda logs are copied into ./anaconda/ in this directory + log_dir = joinpaths(results_dir, "logs/") + if not os.path.exists(log_dir): + os.makedirs(log_dir) + + # Load the compose configuration + cfg_path = joinpaths(results_dir, "config.toml") + if not os.path.exists(cfg_path): + raise RuntimeError("Missing config.toml for %s" % results_dir) + cfg_dict = toml.loads(open(cfg_path, "r").read()) + + # The keys in cfg_dict correspond to the arguments setup in livemedia-creator + # keys that define what to build should be setup in compose_args, and keys with + # defaults should be setup here. + + # Make sure that image_name contains no path components + cfg_dict["image_name"] = os.path.basename(cfg_dict["image_name"]) + + # Only support novirt installation, set some other defaults + cfg_dict["no_virt"] = True + cfg_dict["disk_image"] = None + cfg_dict["fs_image"] = None + cfg_dict["keep_image"] = False + cfg_dict["domacboot"] = False + cfg_dict["anaconda_args"] = "" + cfg_dict["proxy"] = "" + cfg_dict["armplatform"] = "" + cfg_dict["squashfs_args"] = None + + cfg_dict["lorax_templates"] = find_templates(cfg.share_dir) + cfg_dict["tmp"] = cfg.tmp + cfg_dict["dracut_args"] = None # Use default args for dracut + + # TODO How to support other arches? + cfg_dict["arch"] = None + + # Compose things in a temporary directory inside the results directory + cfg_dict["result_dir"] = joinpaths(results_dir, "compose") + os.makedirs(cfg_dict["result_dir"]) + + install_cfg = DataHolder(**cfg_dict) + + # Some kludges for the 99-copy-logs %post, failure in it will crash the build + for f in ["/tmp/NOSAVE_INPUT_KS", "/tmp/NOSAVE_LOGS"]: + open(f, "w") + + # Placing a CANCEL file in the results directory will make execWithRedirect send anaconda a SIGTERM + def cancel_build(): + return os.path.exists(joinpaths(results_dir, "CANCEL")) + + log.debug("cfg = %s", install_cfg) + try: + test_path = joinpaths(results_dir, "TEST") + write_timestamp(results_dir, TS_STARTED) + if os.path.exists(test_path): + # Pretend to run the compose + time.sleep(5) + try: + test_mode = int(open(test_path, "r").read()) + except Exception: + test_mode = 1 + if test_mode == 1: + raise RuntimeError("TESTING FAILED compose") + else: + open(joinpaths(results_dir, install_cfg.image_name), "w").write("TEST IMAGE") + else: + run_creator(install_cfg, callback_func=cancel_build) + + # Extract the results of the compose into results_dir and cleanup the compose directory + move_compose_results(install_cfg, results_dir) + finally: + # Make sure any remaining temporary directories are removed (eg. if there was an exception) + for d in glob(joinpaths(cfg.tmp, "lmc-*")): + if os.path.isdir(d): + shutil.rmtree(d) + elif os.path.isfile(d): + os.unlink(d) + + # Make sure that everything under the results directory is owned by the user + user = pwd.getpwuid(cfg.uid).pw_name + group = grp.getgrgid(cfg.gid).gr_name + log.debug("Install finished, chowning results to %s:%s", user, group) + subprocess.call(["chown", "-R", "%s:%s" % (user, group), results_dir])
+ +
[docs]def get_compose_type(results_dir): + """Return the type of composition. + + :param results_dir: The directory containing the metadata and results for the build + :type results_dir: str + :returns: The type of compose (eg. 'tar') + :rtype: str + :raises: RuntimeError if no kickstart template can be found. + """ + # Should only be 2 kickstarts, the final-kickstart.ks and the template + t = [os.path.basename(ks)[:-3] for ks in glob(joinpaths(results_dir, "*.ks")) + if "final-kickstart" not in ks] + if len(t) != 1: + raise RuntimeError("Cannot find ks template for build %s" % os.path.basename(results_dir)) + return t[0]
+ +
[docs]def compose_detail(results_dir): + """Return details about the build. + + :param results_dir: The directory containing the metadata and results for the build + :type results_dir: str + :returns: A dictionary with details about the compose + :rtype: dict + :raises: IOError if it cannot read the directory, STATUS, or blueprint file. + + The following details are included in the dict: + + * id - The uuid of the comoposition + * queue_status - The final status of the composition (FINISHED or FAILED) + * compose_type - The type of output generated (tar, iso, etc.) + * blueprint - Blueprint name + * version - Blueprint version + * image_size - Size of the image, if finished. 0 otherwise. + + Various timestamps are also included in the dict. These are all Unix UTC timestamps. + It is possible for these timestamps to not always exist, in which case they will be + None in Python (or null in JSON). The following timestamps are included: + + * job_created - When the user submitted the compose + * job_started - Anaconda started running + * job_finished - Job entered FINISHED or FAILED state + """ + build_id = os.path.basename(os.path.abspath(results_dir)) + status = open(joinpaths(results_dir, "STATUS")).read().strip() + blueprint = recipe_from_file(joinpaths(results_dir, "blueprint.toml")) + + compose_type = get_compose_type(results_dir) + + image_path = get_image_name(results_dir)[1] + if status == "FINISHED" and os.path.exists(image_path): + image_size = os.stat(image_path).st_size + else: + image_size = 0 + + times = timestamp_dict(results_dir) + + return {"id": build_id, + "queue_status": status, + "job_created": times.get(TS_CREATED), + "job_started": times.get(TS_STARTED), + "job_finished": times.get(TS_FINISHED), + "compose_type": compose_type, + "blueprint": blueprint["name"], + "version": blueprint["version"], + "image_size": image_size + }
+ +
[docs]def queue_status(cfg): + """Return details about what is in the queue. + + :param cfg: Configuration settings + :type cfg: ComposerConfig + :returns: A list of the new composes, and a list of the running composes + :rtype: dict + + This returns a dict with 2 lists. "new" is the list of uuids that are waiting to be built, + and "run" has the uuids that are being built (currently limited to 1 at a time). + """ + queue_dir = joinpaths(cfg.get("composer", "lib_dir"), "queue") + new_queue = [os.path.realpath(p) for p in glob(joinpaths(queue_dir, "new/*"))] + run_queue = [os.path.realpath(p) for p in glob(joinpaths(queue_dir, "run/*"))] + + new_details = [] + for n in new_queue: + try: + d = compose_detail(n) + except IOError: + continue + new_details.append(d) + + run_details = [] + for r in run_queue: + try: + d = compose_detail(r) + except IOError: + continue + run_details.append(d) + + return { + "new": new_details, + "run": run_details + }
+ +
[docs]def uuid_status(cfg, uuid): + """Return the details of a specific UUID compose + + :param cfg: Configuration settings + :type cfg: ComposerConfig + :param uuid: The UUID of the build + :type uuid: str + :returns: Details about the build + :rtype: dict or None + + Returns the same dict as `compose_details()` + """ + uuid_dir = joinpaths(cfg.get("composer", "lib_dir"), "results", uuid) + try: + return compose_detail(uuid_dir) + except IOError: + return None
+ +
[docs]def build_status(cfg, status_filter=None): + """Return the details of finished or failed builds + + :param cfg: Configuration settings + :type cfg: ComposerConfig + :param status_filter: What builds to return. None == all, "FINISHED", or "FAILED" + :type status_filter: str + :returns: A list of the build details (from compose_details) + :rtype: list of dicts + + This returns a list of build details for each of the matching builds on the + system. It does not return the status of builds that have not been finished. + Use queue_status() for those. + """ + if status_filter: + status_filter = [status_filter] + else: + status_filter = ["FINISHED", "FAILED"] + + results = [] + result_dir = joinpaths(cfg.get("composer", "lib_dir"), "results") + for build in glob(result_dir + "/*"): + log.debug("Checking status of build %s", build) + + try: + status = open(joinpaths(build, "STATUS"), "r").read().strip() + if status in status_filter: + results.append(compose_detail(build)) + except IOError: + pass + return results
+ +
[docs]def uuid_cancel(cfg, uuid): + """Cancel a build and delete its results + + :param cfg: Configuration settings + :type cfg: ComposerConfig + :param uuid: The UUID of the build + :type uuid: str + :returns: True if it was canceled and deleted + :rtype: bool + + Only call this if the build status is WAITING or RUNNING + """ + # This status can change (and probably will) while it is in the middle of doing this: + # It can move from WAITING -> RUNNING or it can move from RUNNING -> FINISHED|FAILED + + # If it is in WAITING remove the symlink and then check to make sure it didn't show up + # in RUNNING + queue_dir = joinpaths(cfg.get("composer", "lib_dir"), "queue") + uuid_new = joinpaths(queue_dir, "new", uuid) + if os.path.exists(uuid_new): + try: + os.unlink(uuid_new) + except OSError: + # The symlink may vanish if the queue monitor started the build + pass + uuid_run = joinpaths(queue_dir, "run", uuid) + if not os.path.exists(uuid_run): + # Successfully removed it before the build started + return uuid_delete(cfg, uuid) + + # Tell the build to stop running + cancel_path = joinpaths(cfg.get("composer", "lib_dir"), "results", uuid, "CANCEL") + open(cancel_path, "w").write("\n") + + # Wait for status to move to FAILED + started = time.time() + while True: + status = uuid_status(cfg, uuid) + if status is None or status["queue_status"] == "FAILED": + break + + # Is this taking too long? Exit anyway and try to cleanup. + if time.time() > started + (10 * 60): + log.error("Failed to cancel the build of %s", uuid) + break + + time.sleep(5) + + # Remove the partial results + uuid_delete(cfg, uuid)
+ +
[docs]def uuid_delete(cfg, uuid): + """Delete all of the results from a compose + + :param cfg: Configuration settings + :type cfg: ComposerConfig + :param uuid: The UUID of the build + :type uuid: str + :returns: True if it was deleted + :rtype: bool + :raises: This will raise an error if the delete failed + """ + uuid_dir = joinpaths(cfg.get("composer", "lib_dir"), "results", uuid) + if not uuid_dir or len(uuid_dir) < 10: + raise RuntimeError("Directory length is too short: %s" % uuid_dir) + shutil.rmtree(uuid_dir) + return True
+ +
[docs]def uuid_info(cfg, uuid): + """Return information about the composition + + :param cfg: Configuration settings + :type cfg: ComposerConfig + :param uuid: The UUID of the build + :type uuid: str + :returns: dictionary of information about the composition or None + :rtype: dict + :raises: RuntimeError if there was a problem + + This will return a dict with the following fields populated: + + * id - The uuid of the comoposition + * config - containing the configuration settings used to run Anaconda + * blueprint - The depsolved blueprint used to generate the kickstart + * commit - The (local) git commit hash for the blueprint used + * deps - The NEVRA of all of the dependencies used in the composition + * compose_type - The type of output generated (tar, iso, etc.) + * queue_status - The final status of the composition (FINISHED or FAILED) + """ + uuid_dir = joinpaths(cfg.get("composer", "lib_dir"), "results", uuid) + if not os.path.exists(uuid_dir): + return None + + # Load the compose configuration + cfg_path = joinpaths(uuid_dir, "config.toml") + if not os.path.exists(cfg_path): + raise RuntimeError("Missing config.toml for %s" % uuid) + cfg_dict = toml.loads(open(cfg_path, "r").read()) + + frozen_path = joinpaths(uuid_dir, "frozen.toml") + if not os.path.exists(frozen_path): + raise RuntimeError("Missing frozen.toml for %s" % uuid) + frozen_dict = toml.loads(open(frozen_path, "r").read()) + + deps_path = joinpaths(uuid_dir, "deps.toml") + if not os.path.exists(deps_path): + raise RuntimeError("Missing deps.toml for %s" % uuid) + deps_dict = toml.loads(open(deps_path, "r").read()) + + details = compose_detail(uuid_dir) + + commit_path = joinpaths(uuid_dir, "COMMIT") + if not os.path.exists(commit_path): + raise RuntimeError("Missing commit hash for %s" % uuid) + commit_id = open(commit_path, "r").read().strip() + + return {"id": uuid, + "config": cfg_dict, + "blueprint": frozen_dict, + "commit": commit_id, + "deps": deps_dict, + "compose_type": details["compose_type"], + "queue_status": details["queue_status"], + "image_size": details["image_size"] + }
+ +
[docs]def uuid_tar(cfg, uuid, metadata=False, image=False, logs=False): + """Return a tar of the build data + + :param cfg: Configuration settings + :type cfg: ComposerConfig + :param uuid: The UUID of the build + :type uuid: str + :param metadata: Set to true to include all the metadata needed to reproduce the build + :type metadata: bool + :param image: Set to true to include the output image + :type image: bool + :param logs: Set to true to include the logs from the build + :type logs: bool + :returns: A stream of bytes from tar + :rtype: A generator + :raises: RuntimeError if there was a problem (eg. missing config file) + + This yields an uncompressed tar's data to the caller. It includes + the selected data to the caller by returning the Popen stdout from the tar process. + """ + uuid_dir = joinpaths(cfg.get("composer", "lib_dir"), "results", uuid) + if not os.path.exists(uuid_dir): + raise RuntimeError("%s is not a valid build_id" % uuid) + + # Load the compose configuration + cfg_path = joinpaths(uuid_dir, "config.toml") + if not os.path.exists(cfg_path): + raise RuntimeError("Missing config.toml for %s" % uuid) + cfg_dict = toml.loads(open(cfg_path, "r").read()) + image_name = cfg_dict["image_name"] + + def include_file(f): + if f.endswith("/logs"): + return logs + if f.endswith(image_name): + return image + return metadata + filenames = [os.path.basename(f) for f in glob(joinpaths(uuid_dir, "*")) if include_file(f)] + + tar = Popen(["tar", "-C", uuid_dir, "-cf-"] + filenames, stdout=PIPE) + return tar.stdout
+ +
[docs]def uuid_image(cfg, uuid): + """Return the filename and full path of the build's image file + + :param cfg: Configuration settings + :type cfg: ComposerConfig + :param uuid: The UUID of the build + :type uuid: str + :returns: The image filename and full path + :rtype: tuple of strings + :raises: RuntimeError if there was a problem (eg. invalid uuid, missing config file) + """ + uuid_dir = joinpaths(cfg.get("composer", "lib_dir"), "results", uuid) + return get_image_name(uuid_dir)
+ +
[docs]def get_image_name(uuid_dir): + """Return the filename and full path of the build's image file + + :param uuid: The UUID of the build + :type uuid: str + :returns: The image filename and full path + :rtype: tuple of strings + :raises: RuntimeError if there was a problem (eg. invalid uuid, missing config file) + """ + uuid = os.path.basename(os.path.abspath(uuid_dir)) + if not os.path.exists(uuid_dir): + raise RuntimeError("%s is not a valid build_id" % uuid) + + # Load the compose configuration + cfg_path = joinpaths(uuid_dir, "config.toml") + if not os.path.exists(cfg_path): + raise RuntimeError("Missing config.toml for %s" % uuid) + cfg_dict = toml.loads(open(cfg_path, "r").read()) + image_name = cfg_dict["image_name"] + + return (image_name, joinpaths(uuid_dir, image_name))
+ +
[docs]def uuid_log(cfg, uuid, size=1024): + """Return `size` kbytes from the end of the anaconda.log + + :param cfg: Configuration settings + :type cfg: ComposerConfig + :param uuid: The UUID of the build + :type uuid: str + :param size: Number of kbytes to read. Default is 1024 + :type size: int + :returns: Up to `size` kbytes from the end of the log + :rtype: str + :raises: RuntimeError if there was a problem (eg. no log file available) + + This function tries to return lines from the end of the log, it will + attempt to start on a line boundry, and may return less than `size` kbytes. + """ + uuid_dir = joinpaths(cfg.get("composer", "lib_dir"), "results", uuid) + if not os.path.exists(uuid_dir): + raise RuntimeError("%s is not a valid build_id" % uuid) + + # While a build is running the logs will be in /tmp/anaconda.log and when it + # has finished they will be in the results directory + status = uuid_status(cfg, uuid) + if status is None: + raise RuntimeError("Status is missing for %s" % uuid) + + if status["queue_status"] == "RUNNING": + log_path = "/tmp/anaconda.log" + else: + log_path = joinpaths(uuid_dir, "logs", "anaconda", "anaconda.log") + if not os.path.exists(log_path): + raise RuntimeError("No anaconda.log available.") + + with open(log_path, "r") as f: + f.seek(0, 2) + end = f.tell() + if end < 1024 * size: + f.seek(0, 0) + else: + f.seek(end - (1024 * size)) + # Find the start of the next line and return the rest + f.readline() + return f.read()
+
+ +
+ +
+ + +
+
+ +
+ +
+ + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/f30-branch/_modules/pylorax/api/recipes.html b/f30-branch/_modules/pylorax/api/recipes.html new file mode 100644 index 00000000..50928cf1 --- /dev/null +++ b/f30-branch/_modules/pylorax/api/recipes.html @@ -0,0 +1,1219 @@ + + + + + + + + + + + pylorax.api.recipes — Lorax 30.19 documentation + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + +
+ + + + + +
+ +
+ + + + + + + + + + + + + + + + + +
+ + + + +
+
+
+
+ +

Source code for pylorax.api.recipes

+#
+# Copyright (C) 2017-2019  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 gi
+gi.require_version("Ggit", "1.0")
+from gi.repository import Ggit as Git
+from gi.repository import Gio
+from gi.repository import GLib
+
+import os
+import pytoml as toml
+import semantic_version as semver
+
+from pylorax.api.projects import dep_evra
+from pylorax.base import DataHolder
+from pylorax.sysutils import joinpaths
+
+
+
[docs]class CommitTimeValError(Exception): + pass
+ +
[docs]class RecipeFileError(Exception): + pass
+ +
[docs]class RecipeError(Exception): + pass
+ + +
[docs]class Recipe(dict): + """A Recipe of package and modules + + This is a subclass of dict that enforces the constructor arguments + and adds a .filename property to return the recipe's filename, + and a .toml() function to return the recipe as a TOML string. + """ + def __init__(self, name, description, version, modules, packages, groups, customizations=None, gitrepos=None): + # Check that version is empty or semver compatible + if version: + semver.Version(version) + + # Make sure modules, packages, and groups are listed by their case-insensitive names + if modules is not None: + modules = sorted(modules, key=lambda m: m["name"].lower()) + if packages is not None: + packages = sorted(packages, key=lambda p: p["name"].lower()) + if groups is not None: + groups = sorted(groups, key=lambda g: g["name"].lower()) + + # Only support [[repos.git]] for now + if gitrepos is not None: + repos = {"git": sorted(gitrepos, key=lambda g: g["repo"].lower())} + else: + repos = None + dict.__init__(self, name=name, + description=description, + version=version, + modules=modules, + packages=packages, + groups=groups, + customizations=customizations, + repos=repos) + + # We don't want customizations=None to show up in the TOML so remove it + if customizations is None: + del self["customizations"] + + # Don't include empty repos or repos.git + if repos is None or not repos["git"]: + del self["repos"] + + @property + def package_names(self): + """Return the names of the packages""" + return [p["name"] for p in self["packages"] or []] + + @property + def package_nver(self): + """Return the names and version globs of the packages""" + return [(p["name"], p["version"]) for p in self["packages"] or []] + + @property + def module_names(self): + """Return the names of the modules""" + return [m["name"] for m in self["modules"] or []] + + @property + def module_nver(self): + """Return the names and version globs of the modules""" + return [(m["name"], m["version"]) for m in self["modules"] or []] + + @property + def group_names(self): + """Return the names of the groups. Groups do not have versions.""" + return map(lambda g: g["name"], self["groups"] or []) + + @property + def filename(self): + """Return the Recipe's filename + + Replaces spaces in the name with '-' and appends .toml + """ + return recipe_filename(self.get("name")) + +
[docs] def toml(self): + """Return the Recipe in TOML format""" + return toml.dumps(self)
+ +
[docs] def bump_version(self, old_version=None): + """semver recipe version number bump + + :param old_version: An optional old version number + :type old_version: str + :returns: The new version number or None + :rtype: str + :raises: ValueError + + If neither have a version, 0.0.1 is returned + If there is no old version the new version is checked and returned + If there is no new version, but there is a old one, bump its patch level + If the old and new versions are the same, bump the patch level + If they are different, check and return the new version + """ + new_version = self.get("version") + if not new_version and not old_version: + self["version"] = "0.0.1" + + elif new_version and not old_version: + semver.Version(new_version) + self["version"] = new_version + + elif not new_version or new_version == old_version: + new_version = str(semver.Version(old_version).next_patch()) + self["version"] = new_version + + else: + semver.Version(new_version) + self["version"] = new_version + + # Return the new version + return str(semver.Version(self["version"]))
+ +
[docs] def freeze(self, deps): + """ Return a new Recipe with full module and package NEVRA + + :param deps: A list of dependency NEVRA to use to fill in the modules and packages + :type deps: list( + :returns: A new Recipe object + :rtype: Recipe + """ + module_names = self.module_names + package_names = self.package_names + group_names = self.group_names + + new_modules = [] + new_packages = [] + new_groups = [] + for dep in deps: + if dep["name"] in package_names: + new_packages.append(RecipePackage(dep["name"], dep_evra(dep))) + elif dep["name"] in module_names: + new_modules.append(RecipeModule(dep["name"], dep_evra(dep))) + elif dep["name"] in group_names: + new_groups.append(RecipeGroup(dep["name"])) + if "customizations" in self: + customizations = self["customizations"] + else: + customizations = None + if "repos" in self and "git" in self["repos"]: + gitrepos = self["repos"]["git"] + else: + gitrepos = None + + return Recipe(self["name"], self["description"], self["version"], + new_modules, new_packages, new_groups, customizations, gitrepos)
+ +
[docs]class RecipeModule(dict): + def __init__(self, name, version): + dict.__init__(self, name=name, version=version)
+ +
[docs]class RecipePackage(RecipeModule): + pass
+ +
[docs]class RecipeGroup(dict): + def __init__(self, name): + dict.__init__(self, name=name)
+ +
[docs]def NewRecipeGit(toml_dict): + """Create a RecipeGit object from fields in a TOML dict + + :param rpmname: Name of the rpm to create, also used as the prefix name in the tar archive + :type rpmname: str + :param rpmversion: Version of the rpm, eg. "1.0.0" + :type rpmversion: str + :param rpmrelease: Release of the rpm, eg. "1" + :type rpmrelease: str + :param summary: Summary string for the rpm + :type summary: str + :param repo: URL of the get repo to clone and create the archive from + :type repo: str + :param ref: Git reference to check out. eg. origin/branch-name, git tag, or git commit hash + :type ref: str + :param destination: Path to install the / of the git repo at when installing the rpm + :type destination: str + :returns: A populated RecipeGit object + :rtype: RecipeGit + + The TOML should look like this:: + + [[repos.git]] + rpmname="server-config" + rpmversion="1.0" + rpmrelease="1" + summary="Setup files for server deployment" + repo="PATH OF GIT REPO TO CLONE" + ref="v1.0" + destination="/opt/server/" + + Note that the repo path supports anything that git supports, file://, https://, http:// + + Currently there is no support for authentication + """ + return RecipeGit(toml_dict.get("rpmname"), + toml_dict.get("rpmversion"), + toml_dict.get("rpmrelease"), + toml_dict.get("summary", ""), + toml_dict.get("repo"), + toml_dict.get("ref"), + toml_dict.get("destination"))
+ +
[docs]class RecipeGit(dict): + def __init__(self, rpmname, rpmversion, rpmrelease, summary, repo, ref, destination): + dict.__init__(self, rpmname=rpmname, rpmversion=rpmversion, rpmrelease=rpmrelease, + summary=summary, repo=repo, ref=ref, destination=destination)
+ +
[docs]def recipe_from_file(recipe_path): + """Return a recipe file as a Recipe object + + :param recipe_path: Path to the recipe fila + :type recipe_path: str + :returns: A Recipe object + :rtype: Recipe + """ + with open(recipe_path, 'rb') as f: + return recipe_from_toml(f.read())
+ +
[docs]def recipe_from_toml(recipe_str): + """Create a Recipe object from a toml string. + + :param recipe_str: The Recipe TOML string + :type recipe_str: str + :returns: A Recipe object + :rtype: Recipe + :raises: TomlError + """ + recipe_dict = toml.loads(recipe_str) + return recipe_from_dict(recipe_dict)
+ +
[docs]def recipe_from_dict(recipe_dict): + """Create a Recipe object from a plain dict. + + :param recipe_dict: A plain dict of the recipe + :type recipe_dict: dict + :returns: A Recipe object + :rtype: Recipe + :raises: RecipeError + """ + # Make RecipeModule objects from the toml + # The TOML may not have modules or packages in it. Set them to None in this case + try: + if recipe_dict.get("modules"): + modules = [RecipeModule(m.get("name"), m.get("version")) for m in recipe_dict["modules"]] + else: + modules = [] + if recipe_dict.get("packages"): + packages = [RecipePackage(p.get("name"), p.get("version")) for p in recipe_dict["packages"]] + else: + packages = [] + if recipe_dict.get("groups"): + groups = [RecipeGroup(g.get("name")) for g in recipe_dict["groups"]] + else: + groups = [] + if recipe_dict.get("repos") and recipe_dict.get("repos").get("git"): + gitrepos = [NewRecipeGit(r) for r in recipe_dict["repos"]["git"]] + else: + gitrepos = [] + name = recipe_dict["name"] + description = recipe_dict["description"] + version = recipe_dict.get("version", None) + customizations = recipe_dict.get("customizations", None) + + # [customizations] was incorrectly documented at first, so we have to support using it + # as [[customizations]] by grabbing the first element. + if isinstance(customizations, list): + customizations = customizations[0] + + except KeyError as e: + raise RecipeError("There was a problem parsing the recipe: %s" % str(e)) + + return Recipe(name, description, version, modules, packages, groups, customizations, gitrepos)
+ +
[docs]def gfile(path): + """Convert a string path to GFile for use with Git""" + return Gio.file_new_for_path(path)
+ +
[docs]def recipe_filename(name): + """Return the toml filename for a recipe + + Replaces spaces with '-' and appends '.toml' + """ + # XXX Raise and error if this is empty? + return name.replace(" ", "-") + ".toml"
+ +
[docs]def head_commit(repo, branch): + """Get the branch's HEAD Commit Object + + :param repo: Open repository + :type repo: Git.Repository + :param branch: Branch name + :type branch: str + :returns: Branch's head commit + :rtype: Git.Commit + :raises: Can raise errors from Ggit + """ + branch_obj = repo.lookup_branch(branch, Git.BranchType.LOCAL) + commit_id = branch_obj.get_target() + return repo.lookup(commit_id, Git.Commit)
+ +
[docs]def prepare_commit(repo, branch, builder): + """Prepare for a commit + + :param repo: Open repository + :type repo: Git.Repository + :param branch: Branch name + :type branch: str + :param builder: instance of TreeBuilder + :type builder: TreeBuilder + :returns: (Tree, Sig, Ref) + :rtype: tuple + :raises: Can raise errors from Ggit + """ + tree_id = builder.write() + tree = repo.lookup(tree_id, Git.Tree) + sig = Git.Signature.new_now("bdcs-api-server", "user-email") + ref = "refs/heads/%s" % branch + return (tree, sig, ref)
+ +
[docs]def open_or_create_repo(path): + """Open an existing repo, or create a new one + + :param path: path to recipe directory + :type path: string + :returns: A repository object + :rtype: Git.Repository + :raises: Can raise errors from Ggit + + A bare git repo will be created in the git directory of the specified path. + If a repo already exists it will be opened and returned instead of + creating a new one. + """ + Git.init() + git_path = joinpaths(path, "git") + if os.path.exists(joinpaths(git_path, "HEAD")): + return Git.Repository.open(gfile(git_path)) + + repo = Git.Repository.init_repository(gfile(git_path), True) + + # Make an initial empty commit + sig = Git.Signature.new_now("bdcs-api-server", "user-email") + tree_id = repo.get_index().write_tree() + tree = repo.lookup(tree_id, Git.Tree) + repo.create_commit("HEAD", sig, sig, "UTF-8", "Initial Recipe repository commit", tree, []) + return repo
+ +
[docs]def write_commit(repo, branch, filename, message, content): + """Make a new commit to a repository's branch + + :param repo: Open repository + :type repo: Git.Repository + :param branch: Branch name + :type branch: str + :param filename: full path of the file to add + :type filename: str + :param message: The commit message + :type message: str + :param content: The data to write + :type content: str + :returns: OId of the new commit + :rtype: Git.OId + :raises: Can raise errors from Ggit + """ + try: + parent_commit = head_commit(repo, branch) + except GLib.GError: + # Branch doesn't exist, make a new one based on master + master_head = head_commit(repo, "master") + repo.create_branch(branch, master_head, 0) + parent_commit = head_commit(repo, branch) + + parent_commit = head_commit(repo, branch) + blob_id = repo.create_blob_from_buffer(content.encode("UTF-8")) + + # Use treebuilder to make a new entry for this filename and blob + parent_tree = parent_commit.get_tree() + builder = repo.create_tree_builder_from_tree(parent_tree) + builder.insert(filename, blob_id, Git.FileMode.BLOB) + (tree, sig, ref) = prepare_commit(repo, branch, builder) + return repo.create_commit(ref, sig, sig, "UTF-8", message, tree, [parent_commit])
+ +
[docs]def read_commit_spec(repo, spec): + """Return the raw content of the blob specified by the spec + + :param repo: Open repository + :type repo: Git.Repository + :param spec: Git revparse spec + :type spec: str + :returns: Contents of the commit + :rtype: str + :raises: Can raise errors from Ggit + + eg. To read the README file from master the spec is "master:README" + """ + commit_id = repo.revparse(spec).get_id() + blob = repo.lookup(commit_id, Git.Blob) + return blob.get_raw_content()
+ +
[docs]def read_commit(repo, branch, filename, commit=None): + """Return the contents of a file on a specific branch or commit. + + :param repo: Open repository + :type repo: Git.Repository + :param branch: Branch name + :type branch: str + :param filename: filename to read + :type filename: str + :param commit: Optional commit hash + :type commit: str + :returns: The commit id, and the contents of the commit + :rtype: tuple(str, str) + :raises: Can raise errors from Ggit + + If no commit is passed the master:filename is returned, otherwise it will be + commit:filename + """ + if not commit: + # Find the most recent commit for filename on the selected branch + commits = list_commits(repo, branch, filename, 1) + if not commits: + raise RecipeError("No commits for %s on the %s branch." % (filename, branch)) + commit = commits[0].commit + return (commit, read_commit_spec(repo, "%s:%s" % (commit, filename)))
+ +
[docs]def read_recipe_commit(repo, branch, recipe_name, commit=None): + """Read a recipe commit from git and return a Recipe object + + :param repo: Open repository + :type repo: Git.Repository + :param branch: Branch name + :type branch: str + :param recipe_name: Recipe name to read + :type recipe_name: str + :param commit: Optional commit hash + :type commit: str + :returns: A Recipe object + :rtype: Recipe + :raises: Can raise errors from Ggit + + If no commit is passed the master:filename is returned, otherwise it will be + commit:filename + """ + if not repo_file_exists(repo, branch, recipe_filename(recipe_name)): + raise RecipeFileError("Unknown blueprint") + + (_, recipe_toml) = read_commit(repo, branch, recipe_filename(recipe_name), commit) + return recipe_from_toml(recipe_toml)
+ +
[docs]def read_recipe_and_id(repo, branch, recipe_name, commit=None): + """Read a recipe commit and its id from git + + :param repo: Open repository + :type repo: Git.Repository + :param branch: Branch name + :type branch: str + :param recipe_name: Recipe name to read + :type recipe_name: str + :param commit: Optional commit hash + :type commit: str + :returns: The commit id, and a Recipe object + :rtype: tuple(str, Recipe) + :raises: Can raise errors from Ggit + + If no commit is passed the master:filename is returned, otherwise it will be + commit:filename + """ + (commit_id, recipe_toml) = read_commit(repo, branch, recipe_filename(recipe_name), commit) + return (commit_id, recipe_from_toml(recipe_toml))
+ +
[docs]def list_branch_files(repo, branch): + """Return a sorted list of the files on the branch HEAD + + :param repo: Open repository + :type repo: Git.Repository + :param branch: Branch name + :type branch: str + :returns: A sorted list of the filenames + :rtype: list(str) + :raises: Can raise errors from Ggit + """ + commit = head_commit(repo, branch).get_id().to_string() + return list_commit_files(repo, commit)
+ +
[docs]def list_commit_files(repo, commit): + """Return a sorted list of the files on a commit + + :param repo: Open repository + :type repo: Git.Repository + :param commit: The commit hash to list + :type commit: str + :returns: A sorted list of the filenames + :rtype: list(str) + :raises: Can raise errors from Ggit + """ + commit_id = Git.OId.new_from_string(commit) + commit_obj = repo.lookup(commit_id, Git.Commit) + tree = commit_obj.get_tree() + return sorted([tree.get(i).get_name() for i in range(0, tree.size())])
+ +
[docs]def delete_recipe(repo, branch, recipe_name): + """Delete a recipe from a branch. + + :param repo: Open repository + :type repo: Git.Repository + :param branch: Branch name + :type branch: str + :param recipe_name: Recipe name to delete + :type recipe_name: str + :returns: OId of the new commit + :rtype: Git.OId + :raises: Can raise errors from Ggit + """ + return delete_file(repo, branch, recipe_filename(recipe_name))
+ +
[docs]def delete_file(repo, branch, filename): + """Delete a file from a branch. + + :param repo: Open repository + :type repo: Git.Repository + :param branch: Branch name + :type branch: str + :param filename: filename to delete + :type filename: str + :returns: OId of the new commit + :rtype: Git.OId + :raises: Can raise errors from Ggit + """ + parent_commit = head_commit(repo, branch) + parent_tree = parent_commit.get_tree() + builder = repo.create_tree_builder_from_tree(parent_tree) + builder.remove(filename) + (tree, sig, ref) = prepare_commit(repo, branch, builder) + message = "Recipe %s deleted" % filename + return repo.create_commit(ref, sig, sig, "UTF-8", message, tree, [parent_commit])
+ +
[docs]def revert_recipe(repo, branch, recipe_name, commit): + """Revert the contents of a recipe to that of a previous commit + + :param repo: Open repository + :type repo: Git.Repository + :param branch: Branch name + :type branch: str + :param recipe_name: Recipe name to revert + :type recipe_name: str + :param commit: Commit hash + :type commit: str + :returns: OId of the new commit + :rtype: Git.OId + :raises: Can raise errors from Ggit + """ + return revert_file(repo, branch, recipe_filename(recipe_name), commit)
+ +
[docs]def revert_file(repo, branch, filename, commit): + """Revert the contents of a file to that of a previous commit + + :param repo: Open repository + :type repo: Git.Repository + :param branch: Branch name + :type branch: str + :param filename: filename to revert + :type filename: str + :param commit: Commit hash + :type commit: str + :returns: OId of the new commit + :rtype: Git.OId + :raises: Can raise errors from Ggit + """ + commit_id = Git.OId.new_from_string(commit) + commit_obj = repo.lookup(commit_id, Git.Commit) + revert_tree = commit_obj.get_tree() + entry = revert_tree.get_by_name(filename) + blob_id = entry.get_id() + parent_commit = head_commit(repo, branch) + + # Use treebuilder to modify the tree + parent_tree = parent_commit.get_tree() + builder = repo.create_tree_builder_from_tree(parent_tree) + builder.insert(filename, blob_id, Git.FileMode.BLOB) + (tree, sig, ref) = prepare_commit(repo, branch, builder) + commit_hash = commit_id.to_string() + message = "%s reverted to commit %s" % (filename, commit_hash) + return repo.create_commit(ref, sig, sig, "UTF-8", message, tree, [parent_commit])
+ +
[docs]def commit_recipe(repo, branch, recipe): + """Commit a recipe to a branch + + :param repo: Open repository + :type repo: Git.Repository + :param branch: Branch name + :type branch: str + :param recipe: Recipe to commit + :type recipe: Recipe + :returns: OId of the new commit + :rtype: Git.OId + :raises: Can raise errors from Ggit + """ + try: + old_recipe = read_recipe_commit(repo, branch, recipe["name"]) + old_version = old_recipe["version"] + except Exception: + old_version = None + + recipe.bump_version(old_version) + recipe_toml = recipe.toml() + message = "Recipe %s, version %s saved." % (recipe["name"], recipe["version"]) + return write_commit(repo, branch, recipe.filename, message, recipe_toml)
+ +
[docs]def commit_recipe_file(repo, branch, filename): + """Commit a recipe file to a branch + + :param repo: Open repository + :type repo: Git.Repository + :param branch: Branch name + :type branch: str + :param filename: Path to the recipe file to commit + :type filename: str + :returns: OId of the new commit + :rtype: Git.OId + :raises: Can raise errors from Ggit or RecipeFileError + """ + try: + recipe = recipe_from_file(filename) + except IOError: + raise RecipeFileError + + return commit_recipe(repo, branch, recipe)
+ +
[docs]def commit_recipe_directory(repo, branch, directory): + r"""Commit all \*.toml files from a directory, if they aren't already in git. + + :param repo: Open repository + :type repo: Git.Repository + :param branch: Branch name + :type branch: str + :param directory: The directory of \*.toml recipes to commit + :type directory: str + :returns: None + :raises: Can raise errors from Ggit or RecipeFileError + + Files with Toml or RecipeFileErrors will be skipped, and the remainder will + be tried. + """ + dir_files = set([e for e in os.listdir(directory) if e.endswith(".toml")]) + branch_files = set(list_branch_files(repo, branch)) + new_files = dir_files.difference(branch_files) + + for f in new_files: + # Skip files with errors, but try the others + try: + commit_recipe_file(repo, branch, joinpaths(directory, f)) + except (RecipeFileError, toml.TomlError): + pass
+ +
[docs]def tag_recipe_commit(repo, branch, recipe_name): + """Tag a file's most recent commit + + :param repo: Open repository + :type repo: Git.Repository + :param branch: Branch name + :type branch: str + :param recipe_name: Recipe name to tag + :type recipe_name: str + :returns: Tag id or None if it failed. + :rtype: Git.OId + :raises: Can raise errors from Ggit + + Uses tag_file_commit() + """ + if not repo_file_exists(repo, branch, recipe_filename(recipe_name)): + raise RecipeFileError("Unknown blueprint") + + return tag_file_commit(repo, branch, recipe_filename(recipe_name))
+ +
[docs]def tag_file_commit(repo, branch, filename): + """Tag a file's most recent commit + + :param repo: Open repository + :type repo: Git.Repository + :param branch: Branch name + :type branch: str + :param filename: Filename to tag + :type filename: str + :returns: Tag id or None if it failed. + :rtype: Git.OId + :raises: Can raise errors from Ggit + + This uses git tags, of the form `refs/tags/<branch>/<filename>/r<revision>` + Only the most recent recipe commit can be tagged to prevent out of order tagging. + Revisions start at 1 and increment for each new commit that is tagged. + If the commit has already been tagged it will return false. + """ + file_commits = list_commits(repo, branch, filename) + if not file_commits: + return None + + # Find the most recently tagged version (may not be one) and add 1 to it. + for details in file_commits: + if details.revision is not None: + new_revision = details.revision + 1 + break + else: + new_revision = 1 + + name = "%s/%s/r%d" % (branch, filename, new_revision) + sig = Git.Signature.new_now("bdcs-api-server", "user-email") + commit_id = Git.OId.new_from_string(file_commits[0].commit) + commit = repo.lookup(commit_id, Git.Commit) + return repo.create_tag(name, commit, sig, name, Git.CreateFlags.NONE)
+ +
[docs]def find_commit_tag(repo, branch, filename, commit_id): + """Find the tag that matches the commit_id + + :param repo: Open repository + :type repo: Git.Repository + :param branch: Branch name + :type branch: str + :param filename: filename to revert + :type filename: str + :param commit_id: The commit id to check + :type commit_id: Git.OId + :returns: The tag or None if there isn't one + :rtype: str or None + + There should be only 1 tag pointing to a commit, but there may not + be a tag at all. + + The tag will look like: 'refs/tags/<branch>/<filename>/r<revision>' + """ + pattern = "%s/%s/r*" % (branch, filename) + tags = [t for t in repo.list_tags_match(pattern) if is_commit_tag(repo, commit_id, t)] + if len(tags) != 1: + return None + else: + return tags[0]
+ +
[docs]def is_commit_tag(repo, commit_id, tag): + """Check to see if a tag points to a specific commit. + + :param repo: Open repository + :type repo: Git.Repository + :param commit_id: The commit id to check + :type commit_id: Git.OId + :param tag: The tag to check + :type tag: str + :returns: True if the tag points to the commit, False otherwise + :rtype: bool + """ + ref = repo.lookup_reference("refs/tags/" + tag) + tag_id = ref.get_target() + tag = repo.lookup(tag_id, Git.Tag) + target_id = tag.get_target_id() + return commit_id.compare(target_id) == 0
+ +
[docs]def get_revision_from_tag(tag): + """Return the revision number from a tag + + :param tag: The tag to exract the revision from + :type tag: str + :returns: The integer revision or None + :rtype: int or None + + The revision is the part after the r in 'branch/filename/rXXX' + """ + if tag is None: + return None + try: + return int(tag.rsplit('r', 2)[-1]) + except (ValueError, IndexError): + return None
+ +
[docs]class CommitDetails(DataHolder): + def __init__(self, commit, timestamp, message, revision=None): + DataHolder.__init__(self, + commit = commit, + timestamp = timestamp, + message = message, + revision = revision)
+ +
[docs]def list_commits(repo, branch, filename, limit=0): + """List the commit history of a file on a branch. + + :param repo: Open repository + :type repo: Git.Repository + :param branch: Branch name + :type branch: str + :param filename: filename to revert + :type filename: str + :param limit: Number of commits to return (0=all) + :type limit: int + :returns: A list of commit details + :rtype: list(CommitDetails) + :raises: Can raise errors from Ggit + """ + revwalk = Git.RevisionWalker.new(repo) + branch_ref = "refs/heads/%s" % branch + revwalk.push_ref(branch_ref) + + commits = [] + while True: + commit_id = revwalk.next() + if not commit_id: + break + commit = repo.lookup(commit_id, Git.Commit) + + parents = commit.get_parents() + # No parents? Must be the first commit. + if parents.get_size() == 0: + continue + + tree = commit.get_tree() + # Is the filename in this tree? If not, move on. + if not tree.get_by_name(filename): + continue + + # Is filename different in all of the parent commits? + parent_commits = list(map(parents.get, range(0, parents.get_size()))) + is_diff = all([is_parent_diff(repo, filename, tree, pc) for pc in parent_commits]) + # No changes from parents, skip it. + if not is_diff: + continue + + tag = find_commit_tag(repo, branch, filename, commit.get_id()) + try: + commits.append(get_commit_details(commit, get_revision_from_tag(tag))) + if limit and len(commits) > limit: + break + except CommitTimeValError: + # Skip any commits that have trouble converting the time + # TODO - log details about this failure + pass + + # These will be in reverse time sort order thanks to revwalk + return commits
+ +
[docs]def get_commit_details(commit, revision=None): + """Return the details about a specific commit. + + :param commit: The commit to get details from + :type commit: Git.Commit + :param revision: Optional commit revision + :type revision: int + :returns: Details about the commit + :rtype: CommitDetails + :raises: CommitTimeValError or Ggit exceptions + + """ + message = commit.get_message() + commit_str = commit.get_id().to_string() + sig = commit.get_committer() + + datetime = sig.get_time() + # XXX What do we do with timezone? + _timezone = sig.get_time_zone() + timeval = GLib.TimeVal() + ok = datetime.to_timeval(timeval) + if not ok: + raise CommitTimeValError + time_str = timeval.to_iso8601() + + return CommitDetails(commit_str, time_str, message, revision)
+ +
[docs]def is_parent_diff(repo, filename, tree, parent): + """Check to see if the commit is different from its parents + + :param repo: Open repository + :type repo: Git.Repository + :param filename: filename to revert + :type filename: str + :param tree: The commit's tree + :type tree: Git.Tree + :param parent: The commit's parent commit + :type parent: Git.Commit + :retuns: True if filename in the commit is different from its parents + :rtype: bool + """ + diff_opts = Git.DiffOptions.new() + diff_opts.set_pathspec([filename]) + diff = Git.Diff.new_tree_to_tree(repo, parent.get_tree(), tree, diff_opts) + return diff.get_num_deltas() > 0
+ +
[docs]def find_name(name, lst): + """Find the dict matching the name in a list and return it. + + :param name: Name to search for + :type name: str + :param lst: List of dict's with "name" field + :returns: First dict with matching name, or None + :rtype: dict or None + """ + for e in lst: + if e["name"] == name: + return e + return None
+ +
[docs]def diff_items(title, old_items, new_items): + """Return the differences between two lists of dicts. + + :param title: Title of the entry + :type title: str + :param old_items: List of item dicts with "name" field + :type old_items: list(dict) + :param new_items: List of item dicts with "name" field + :type new_items: list(dict) + :returns: List of diff dicts with old/new entries + :rtype: list(dict) + """ + diffs = [] + old_names = set(m["name"] for m in old_items) + new_names = set(m["name"] for m in new_items) + + added_items = new_names.difference(old_names) + added_items = sorted(added_items, key=lambda n: n.lower()) + + removed_items = old_names.difference(new_names) + removed_items = sorted(removed_items, key=lambda n: n.lower()) + + same_items = old_names.intersection(new_names) + same_items = sorted(same_items, key=lambda n: n.lower()) + + for name in added_items: + diffs.append({"old":None, + "new":{title:find_name(name, new_items)}}) + + for name in removed_items: + diffs.append({"old":{title:find_name(name, old_items)}, + "new":None}) + + for name in same_items: + old_item = find_name(name, old_items) + new_item = find_name(name, new_items) + if old_item != new_item: + diffs.append({"old":{title:old_item}, + "new":{title:new_item}}) + + return diffs
+ + +
[docs]def recipe_diff(old_recipe, new_recipe): + """Diff two versions of a recipe + + :param old_recipe: The old version of the recipe + :type old_recipe: Recipe + :param new_recipe: The new version of the recipe + :type new_recipe: Recipe + :returns: A list of diff dict entries with old/new + :rtype: list(dict) + """ + + diffs = [] + # These cannot be added or removed, just different + for element in ["name", "description", "version"]: + if old_recipe[element] != new_recipe[element]: + diffs.append({"old":{element.title():old_recipe[element]}, + "new":{element.title():new_recipe[element]}}) + + diffs.extend(diff_items("Module", old_recipe["modules"], new_recipe["modules"])) + diffs.extend(diff_items("Package", old_recipe["packages"], new_recipe["packages"])) + diffs.extend(diff_items("Group", old_recipe["groups"], new_recipe["groups"])) + + return diffs
+ +
[docs]def repo_file_exists(repo, branch, filename): + """Return True if the filename exists on the branch + + :param repo: Open repository + :type repo: Git.Repository + :param branch: Branch name + :type branch: str + :param filename: Filename to check + :type filename: str + :returns: True if the filename exists on the HEAD of the branch, False otherwise. + :rtype: bool + """ + commit = head_commit(repo, branch).get_id().to_string() + commit_id = Git.OId.new_from_string(commit) + commit_obj = repo.lookup(commit_id, Git.Commit) + tree = commit_obj.get_tree() + return tree.get_by_name(filename) is not None
+
+ +
+ +
+ + +
+
+ +
+ +
+ + + + + + + + + + + + \ No newline at end of file diff --git a/f30-branch/_modules/pylorax/api/server.html b/f30-branch/_modules/pylorax/api/server.html new file mode 100644 index 00000000..264b3d4e --- /dev/null +++ b/f30-branch/_modules/pylorax/api/server.html @@ -0,0 +1,299 @@ + + + + + + + + + + + pylorax.api.server — Lorax 30.7 documentation + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + +
+ + + + + +
+ +
+ + + + + + + + + + + + + + + + + +
+ + + + +
+
+
+
+ +

Source code for pylorax.api.server

+#
+# Copyright (C) 2017  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("lorax-composer")
+
+from collections import namedtuple
+from flask import Flask, jsonify, redirect, send_from_directory
+from glob import glob
+import os
+
+from pylorax import vernum
+from pylorax.api.crossdomain import crossdomain
+from pylorax.api.v0 import v0_api
+from pylorax.sysutils import joinpaths
+
+GitLock = namedtuple("GitLock", ["repo", "lock", "dir"])
+
+server = Flask(__name__)
+
+__all__ = ["server", "GitLock"]
+
+@server.route('/')
+def server_root():
+    redirect("/api/docs/")
+
+@server.route("/api/docs/")
+@server.route("/api/docs/<path:path>")
+def api_docs(path=None):
+    # Find the html docs
+    try:
+        # This assumes it is running from the source tree
+        docs_path = os.path.abspath(joinpaths(os.path.dirname(__file__), "../../../docs/html"))
+    except IndexError:
+        docs_path = glob("/usr/share/doc/lorax-*/html/")[0]
+
+    if not path:
+        path="index.html"
+    return send_from_directory(docs_path, path)
+
+@server.route("/api/status")
+@crossdomain(origin="*")
+def v0_status():
+    """
+    `/api/v0/status`
+    ^^^^^^^^^^^^^^^^
+    Return the status of the API Server::
+
+          { "api": "0",
+            "build": "devel",
+            "db_supported": true,
+            "db_version": "0",
+            "schema_version": "0",
+            "backend": "lorax-composer",
+            "msgs": []}
+
+    The 'msgs' field can be a list of strings describing startup problems or status that
+    should be displayed to the user. eg. if the compose templates are not depsolving properly
+    the errors will be in 'msgs'.
+    """
+    return jsonify(backend="lorax-composer",
+                   build=vernum,
+                   api="0",
+                   db_version="0",
+                   schema_version="0",
+                   db_supported=True,
+                   msgs=server.config["TEMPLATE_ERRORS"])
+
+v0_api(server)
+
+ +
+ +
+ + +
+
+ +
+ +
+ + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/f30-branch/_modules/pylorax/api/timestamp.html b/f30-branch/_modules/pylorax/api/timestamp.html new file mode 100644 index 00000000..6f48bc27 --- /dev/null +++ b/f30-branch/_modules/pylorax/api/timestamp.html @@ -0,0 +1,251 @@ + + + + + + + + + + + pylorax.api.timestamp — Lorax 30.19 documentation + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + +
+ + + + + +
+ +
+ + + + + + + + + + + + + + + + + +
+ + + + +
+
+
+
+ +

Source code for pylorax.api.timestamp

+#
+# 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 pytoml as toml
+import time
+
+from pylorax.sysutils import joinpaths
+
+TS_CREATED  = "created"
+TS_STARTED  = "started"
+TS_FINISHED = "finished"
+
+
[docs]def write_timestamp(destdir, ty): + path = joinpaths(destdir, "times.toml") + + try: + contents = toml.loads(open(path, "r").read()) + except IOError: + contents = toml.loads("") + + if ty == TS_CREATED: + contents[TS_CREATED] = time.time() + elif ty == TS_STARTED: + contents[TS_STARTED] = time.time() + elif ty == TS_FINISHED: + contents[TS_FINISHED] = time.time() + + with open(path, "w") as f: + f.write(toml.dumps(contents))
+ +
[docs]def timestamp_dict(destdir): + path = joinpaths(destdir, "times.toml") + + try: + return toml.loads(open(path, "r").read()) + except IOError: + return toml.loads("")
+
+ +
+ +
+ + +
+
+ +
+ +
+ + + + + + + + + + + + \ No newline at end of file diff --git a/f30-branch/_modules/pylorax/api/v0.html b/f30-branch/_modules/pylorax/api/v0.html new file mode 100644 index 00000000..a59906ea --- /dev/null +++ b/f30-branch/_modules/pylorax/api/v0.html @@ -0,0 +1,2268 @@ + + + + + + + + + + + pylorax.api.v0 — Lorax 30.7 documentation + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + +
+ + + + + +
+ +
+ + + + + + + + + + + + + + + + + +
+ + + + +
+
+
+
+ +

Source code for pylorax.api.v0

+#
+# Copyright (C) 2017-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/>.
+#
+""" Setup v0 of the API server
+
+v0_api() must be called to setup the API routes for Flask
+
+Status Responses
+----------------
+
+Some requests only return a status/error response.
+
+  The response will be a status response with `status` set to true, or an
+  error response with it set to false and an error message included.
+
+  Example response::
+
+      {
+        "status": true
+      }
+
+  Error response::
+
+      {
+        "errors": ["ggit-error: Failed to remove entry. File isn't in the tree - jboss.toml (-1)"]
+        "status": false
+      }
+
+API Routes
+----------
+
+All of the blueprints routes support the optional `branch` argument. If it is not
+used then the API will use the `master` branch for blueprints. If you want to create
+a new branch use the `new` or `workspace` routes with ?branch=<branch-name> to
+store the new blueprint on the new branch.
+
+`/api/v0/blueprints/list`
+^^^^^^^^^^^^^^^^^^^^^^^^^
+
+  List the available blueprints::
+
+      { "limit": 20,
+        "offset": 0,
+        "blueprints": [
+          "atlas",
+          "development",
+          "glusterfs",
+          "http-server",
+          "jboss",
+          "kubernetes" ],
+        "total": 6 }
+
+`/api/v0/blueprints/info/<blueprint_names>[?format=<json|toml>]`
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+  Return the JSON representation of the blueprint. This includes 3 top level
+  objects.  `changes` which lists whether or not the workspace is different from
+  the most recent commit. `blueprints` which lists the JSON representation of the
+  blueprint, and `errors` which will list any errors, like non-existant blueprints.
+
+  By default the response is JSON, but if `?format=toml` is included in the URL's
+  arguments it will return the response as the blueprint's raw TOML content.
+  *Unless* there is an error which will only return a 400 and a standard error
+  `Status Response`_.
+
+  If there is an error when JSON is requested the successful blueprints and the
+  errors will both be returned.
+
+  Example of json response::
+
+      {
+        "changes": [
+          {
+            "changed": false,
+            "name": "glusterfs"
+          }
+        ],
+        "errors": [],
+        "blueprints": [
+          {
+            "description": "An example GlusterFS server with samba",
+            "modules": [
+              {
+                "name": "glusterfs",
+                "version": "3.7.*"
+              },
+              {
+                "name": "glusterfs-cli",
+                "version": "3.7.*"
+              }
+            ],
+            "name": "glusterfs",
+            "packages": [
+              {
+                "name": "2ping",
+                "version": "3.2.1"
+              },
+              {
+                "name": "samba",
+                "version": "4.2.*"
+              }
+            ],
+            "version": "0.0.6"
+          }
+        ]
+      }
+
+  Error example::
+
+      {
+        "changes": [],
+        "errors": ["ggit-error: the path 'missing.toml' does not exist in the given tree (-3)"]
+        "blueprints": []
+      }
+
+`/api/v0/blueprints/changes/<blueprint_names>[?offset=0&limit=20]`
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+  Return the commits to a blueprint. By default it returns the first 20 commits, this
+  can be changed by passing `offset` and/or `limit`. The response will include the
+  commit hash, summary, timestamp, and optionally the revision number. The commit
+  hash can be passed to `/api/v0/blueprints/diff/` to retrieve the exact changes.
+
+  Example::
+
+      {
+        "errors": [],
+        "limit": 20,
+        "offset": 0,
+        "blueprints": [
+          {
+            "changes": [
+              {
+                "commit": "e083921a7ed1cf2eec91ad12b9ad1e70ef3470be",
+                "message": "blueprint glusterfs, version 0.0.6 saved.",
+                "revision": null,
+                "timestamp": "2017-11-23T00:18:13Z"
+              },
+              {
+                "commit": "cee5f4c20fc33ea4d54bfecf56f4ad41ad15f4f3",
+                "message": "blueprint glusterfs, version 0.0.5 saved.",
+                "revision": null,
+                "timestamp": "2017-11-11T01:00:28Z"
+              },
+              {
+                "commit": "29b492f26ed35d80800b536623bafc51e2f0eff2",
+                "message": "blueprint glusterfs, version 0.0.4 saved.",
+                "revision": null,
+                "timestamp": "2017-11-11T00:28:30Z"
+              },
+              {
+                "commit": "03374adbf080fe34f5c6c29f2e49cc2b86958bf2",
+                "message": "blueprint glusterfs, version 0.0.3 saved.",
+                "revision": null,
+                "timestamp": "2017-11-10T23:15:52Z"
+              },
+              {
+                "commit": "0e08ecbb708675bfabc82952599a1712a843779d",
+                "message": "blueprint glusterfs, version 0.0.2 saved.",
+                "revision": null,
+                "timestamp": "2017-11-10T23:14:56Z"
+              },
+              {
+                "commit": "3e11eb87a63d289662cba4b1804a0947a6843379",
+                "message": "blueprint glusterfs, version 0.0.1 saved.",
+                "revision": null,
+                "timestamp": "2017-11-08T00:02:47Z"
+              }
+            ],
+            "name": "glusterfs",
+            "total": 6
+          }
+        ]
+      }
+
+POST `/api/v0/blueprints/new`
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+  Create a new blueprint, or update an existing blueprint. This supports both JSON and TOML
+  for the blueprint format. The blueprint should be in the body of the request with the
+  `Content-Type` header set to either `application/json` or `text/x-toml`.
+
+  The response will be a status response with `status` set to true, or an
+  error response with it set to false and an error message included.
+
+DELETE `/api/v0/blueprints/delete/<blueprint_name>`
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+  Delete a blueprint. The blueprint is deleted from the branch, and will no longer
+  be listed by the `list` route. A blueprint can be undeleted using the `undo` route
+  to revert to a previous commit.
+
+  The response will be a status response with `status` set to true, or an
+  error response with it set to false and an error message included.
+
+POST `/api/v0/blueprints/workspace`
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+  Write a blueprint to the temporary workspace. This works exactly the same as `new` except
+  that it does not create a commit. JSON and TOML bodies are supported.
+
+  The workspace is meant to be used as a temporary blueprint storage for clients.
+  It will be read by the `info` and `diff` routes if it is different from the
+  most recent commit.
+
+  The response will be a status response with `status` set to true, or an
+  error response with it set to false and an error message included.
+
+DELETE `/api/v0/blueprints/workspace/<blueprint_name>`
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+  Remove the temporary workspace copy of a blueprint. The `info` route will now
+  return the most recent commit of the blueprint. Any changes that were in the
+  workspace will be lost.
+
+  The response will be a status response with `status` set to true, or an
+  error response with it set to false and an error message included.
+
+POST `/api/v0/blueprints/undo/<blueprint_name>/<commit>`
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+  This will revert the blueprint to a previous commit. The commit hash from the `changes`
+  route can be used in this request.
+
+  The response will be a status response with `status` set to true, or an
+  error response with it set to false and an error message included.
+
+POST `/api/v0/blueprints/tag/<blueprint_name>`
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+  Tag a blueprint as a new release. This uses git tags with a special format.
+  `refs/tags/<branch>/<filename>/r<revision>`. Only the most recent blueprint commit
+  can be tagged. Revisions start at 1 and increment for each new tag
+  (per-blueprint). If the commit has already been tagged it will return false.
+
+  The response will be a status response with `status` set to true, or an
+  error response with it set to false and an error message included.
+
+`/api/v0/blueprints/diff/<blueprint_name>/<from_commit>/<to_commit>`
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+  Return the differences between two commits, or the workspace. The commit hash
+  from the `changes` response can be used here, or several special strings:
+
+  - NEWEST will select the newest git commit. This works for `from_commit` or `to_commit`
+  - WORKSPACE will select the workspace copy. This can only be used in `to_commit`
+
+  eg. `/api/v0/blueprints/diff/glusterfs/NEWEST/WORKSPACE` will return the differences
+  between the most recent git commit and the contents of the workspace.
+
+  Each entry in the response's diff object contains the old blueprint value and the new one.
+  If old is null and new is set, then it was added.
+  If new is null and old is set, then it was removed.
+  If both are set, then it was changed.
+
+  The old/new entries will have the name of the blueprint field that was changed. This
+  can be one of: Name, Description, Version, Module, or Package.
+  The contents for these will be the old/new values for them.
+
+  In the example below the version was changed and the ping package was added.
+
+  Example::
+
+      {
+        "diff": [
+          {
+            "new": {
+              "Version": "0.0.6"
+            },
+            "old": {
+              "Version": "0.0.5"
+            }
+          },
+          {
+            "new": {
+              "Package": {
+                "name": "ping",
+                "version": "3.2.1"
+              }
+            },
+            "old": null
+          }
+        ]
+      }
+
+`/api/v0/blueprints/freeze/<blueprint_names>`
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+  Return a JSON representation of the blueprint with the package and module versions set
+  to the exact versions chosen by depsolving the blueprint.
+
+  Example::
+
+      {
+        "errors": [],
+        "blueprints": [
+          {
+            "blueprint": {
+              "description": "An example GlusterFS server with samba",
+              "modules": [
+                {
+                  "name": "glusterfs",
+                  "version": "3.8.4-18.4.el7.x86_64"
+                },
+                {
+                  "name": "glusterfs-cli",
+                  "version": "3.8.4-18.4.el7.x86_64"
+                }
+              ],
+              "name": "glusterfs",
+              "packages": [
+                {
+                  "name": "ping",
+                  "version": "2:3.2.1-2.el7.noarch"
+                },
+                {
+                  "name": "samba",
+                  "version": "4.6.2-8.el7.x86_64"
+                }
+              ],
+              "version": "0.0.6"
+            }
+          }
+        ]
+      }
+
+`/api/v0/blueprints/depsolve/<blueprint_names>`
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+  Depsolve the blueprint using yum, return the blueprint used, and the NEVRAs of the packages
+  chosen to satisfy the blueprint's requirements. The response will include a list of results,
+  with the full dependency list in `dependencies`, the NEVRAs for the blueprint's direct modules
+  and packages in `modules`, and any error will be in `errors`.
+
+  Example::
+
+      {
+        "errors": [],
+        "blueprints": [
+          {
+            "dependencies": [
+              {
+                "arch": "noarch",
+                "epoch": "0",
+                "name": "2ping",
+                "release": "2.el7",
+                "version": "3.2.1"
+              },
+              {
+                "arch": "x86_64",
+                "epoch": "0",
+                "name": "acl",
+                "release": "12.el7",
+                "version": "2.2.51"
+              },
+              {
+                "arch": "x86_64",
+                "epoch": "0",
+                "name": "audit-libs",
+                "release": "3.el7",
+                "version": "2.7.6"
+              },
+              {
+                "arch": "x86_64",
+                "epoch": "0",
+                "name": "avahi-libs",
+                "release": "17.el7",
+                "version": "0.6.31"
+              },
+              ...
+            ],
+            "modules": [
+              {
+                "arch": "noarch",
+                "epoch": "0",
+                "name": "2ping",
+                "release": "2.el7",
+                "version": "3.2.1"
+              },
+              {
+                "arch": "x86_64",
+                "epoch": "0",
+                "name": "glusterfs",
+                "release": "18.4.el7",
+                "version": "3.8.4"
+              },
+              ...
+            ],
+            "blueprint": {
+              "description": "An example GlusterFS server with samba",
+              "modules": [
+                {
+                  "name": "glusterfs",
+                  "version": "3.7.*"
+                },
+             ...
+            }
+          }
+        ]
+      }
+
+`/api/v0/projects/list[?offset=0&limit=20]`
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+  List all of the available projects. By default this returns the first 20 items,
+  but this can be changed by setting the `offset` and `limit` arguments.
+
+  Example::
+
+      {
+        "limit": 20,
+        "offset": 0,
+        "projects": [
+          {
+            "description": "0 A.D. (pronounced \"zero ey-dee\") is a ...",
+            "homepage": "http://play0ad.com",
+            "name": "0ad",
+            "summary": "Cross-Platform RTS Game of Ancient Warfare",
+            "upstream_vcs": "UPSTREAM_VCS"
+          },
+          ...
+        ],
+        "total": 21770
+      }
+
+`/api/v0/projects/info/<project_names>`
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+  Return information about the comma-separated list of projects. It includes the description
+  of the package along with the list of available builds.
+
+  Example::
+
+      {
+        "projects": [
+          {
+            "builds": [
+              {
+                "arch": "x86_64",
+                "build_config_ref": "BUILD_CONFIG_REF",
+                "build_env_ref": "BUILD_ENV_REF",
+                "build_time": "2017-03-01T08:39:23",
+                "changelog": "- restore incremental backups correctly, files ...",
+                "epoch": "2",
+                "metadata": {},
+                "release": "32.el7",
+                "source": {
+                  "license": "GPLv3+",
+                  "metadata": {},
+                  "source_ref": "SOURCE_REF",
+                  "version": "1.26"
+                }
+              }
+            ],
+            "description": "The GNU tar program saves many ...",
+            "homepage": "http://www.gnu.org/software/tar/",
+            "name": "tar",
+            "summary": "A GNU file archiving program",
+            "upstream_vcs": "UPSTREAM_VCS"
+          }
+        ]
+      }
+
+`/api/v0/projects/depsolve/<project_names>`
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+  Depsolve the comma-separated list of projects and return the list of NEVRAs needed
+  to satisfy the request.
+
+  Example::
+
+      {
+        "projects": [
+          {
+            "arch": "noarch",
+            "epoch": "0",
+            "name": "basesystem",
+            "release": "7.el7",
+            "version": "10.0"
+          },
+          {
+            "arch": "x86_64",
+            "epoch": "0",
+            "name": "bash",
+            "release": "28.el7",
+            "version": "4.2.46"
+          },
+          {
+            "arch": "x86_64",
+            "epoch": "0",
+            "name": "filesystem",
+            "release": "21.el7",
+            "version": "3.2"
+          },
+          ...
+        ]
+      }
+
+`/api/v0/projects/source/list`
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+  Return the list of repositories used for depsolving and installing packages.
+
+  Example::
+
+      {
+        "sources": [
+          "fedora",
+          "fedora-cisco-openh264",
+          "fedora-updates-testing",
+          "fedora-updates"
+        ]
+      }
+
+`/api/v0/projects/source/info/<source-names>`
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+  Return information about the comma-separated list of source names. Or all of the
+  sources if '*' is passed. Note that general globbing is not supported, only '*'.
+
+  immutable system sources will have the "system" field set to true. User added sources
+  will have it set to false. System sources cannot be changed or deleted.
+
+  Example::
+
+      {
+        "errors": [],
+        "sources": {
+          "fedora": {
+            "check_gpg": true,
+            "check_ssl": true,
+            "gpgkey_urls": [
+              "file:///etc/pki/rpm-gpg/RPM-GPG-KEY-fedora-28-x86_64"
+            ],
+            "name": "fedora",
+            "proxy": "http://proxy.brianlane.com:8123",
+            "system": true,
+            "type": "yum-metalink",
+            "url": "https://mirrors.fedoraproject.org/metalink?repo=fedora-28&arch=x86_64"
+          }
+        }
+      }
+
+POST `/api/v0/projects/source/new`
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+  Add (or change) a source for use when depsolving blueprints and composing images.
+
+  The ``proxy`` and ``gpgkey_urls`` entries are optional. All of the others are required. The supported
+  types for the urls are:
+
+  * ``yum-baseurl`` is a URL to a yum repository.
+  * ``yum-mirrorlist`` is a URL for a mirrorlist.
+  * ``yum-metalink`` is a URL for a metalink.
+
+  If ``check_ssl`` is true the https certificates must be valid. If they are self-signed you can either set
+  this to false, or add your Certificate Authority to the host system.
+
+  If ``check_gpg`` is true the GPG key must either be installed on the host system, or ``gpgkey_urls``
+  should point to it.
+
+  You can edit an existing source (other than system sources), by doing a POST
+  of the new version of the source. It will overwrite the previous one.
+
+  Example::
+
+      {
+          "name": "custom-source-1",
+          "url": "https://url/path/to/repository/",
+          "type": "yum-baseurl",
+          "check_ssl": true,
+          "check_gpg": true,
+          "gpgkey_urls": [
+              "https://url/path/to/gpg-key"
+          ]
+      }
+
+DELETE `/api/v0/projects/source/delete/<source-name>`
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+  Delete a user added source. This will fail if a system source is passed to
+  it.
+
+  The response will be a status response with `status` set to true, or an
+  error response with it set to false and an error message included.
+
+`/api/v0/modules/list[?offset=0&limit=20]`
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+  Return a list of all of the available modules. This includes the name and the
+  group_type, which is always "rpm" for lorax-composer. By default this returns
+  the first 20 items. This can be changed by setting the `offset` and `limit`
+  arguments.
+
+  Example::
+
+      {
+        "limit": 20,
+        "modules": [
+          {
+            "group_type": "rpm",
+            "name": "0ad"
+          },
+          {
+            "group_type": "rpm",
+            "name": "0ad-data"
+          },
+          {
+            "group_type": "rpm",
+            "name": "0install"
+          },
+          {
+            "group_type": "rpm",
+            "name": "2048-cli"
+          },
+          ...
+        ]
+        "total": 21770
+      }
+
+`/api/v0/modules/list/<module_names>[?offset=0&limit=20]`
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+  Return the list of comma-separated modules. Output is the same as `/modules/list`
+
+  Example::
+
+      {
+        "limit": 20,
+        "modules": [
+          {
+            "group_type": "rpm",
+            "name": "tar"
+          }
+        ],
+        "offset": 0,
+        "total": 1
+      }
+
+`/api/v0/modules/info/<module_names>`
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+  Return the module's dependencies, and the information about the module.
+
+  Example::
+
+      {
+        "modules": [
+          {
+            "dependencies": [
+              {
+                "arch": "noarch",
+                "epoch": "0",
+                "name": "basesystem",
+                "release": "7.el7",
+                "version": "10.0"
+              },
+              {
+                "arch": "x86_64",
+                "epoch": "0",
+                "name": "bash",
+                "release": "28.el7",
+                "version": "4.2.46"
+              },
+              ...
+            ],
+            "description": "The GNU tar program saves ...",
+            "homepage": "http://www.gnu.org/software/tar/",
+            "name": "tar",
+            "summary": "A GNU file archiving program",
+            "upstream_vcs": "UPSTREAM_VCS"
+          }
+        ]
+      }
+
+POST `/api/v0/compose`
+^^^^^^^^^^^^^^^^^^^^^^
+
+  Start a compose. The content type should be 'application/json' and the body of the POST
+  should look like this::
+
+      {
+        "blueprint_name": "http-server",
+        "compose_type": "tar",
+        "branch": "master"
+      }
+
+  Pass it the name of the blueprint, the type of output (from '/api/v0/compose/types'), and the
+  blueprint branch to use. 'branch' is optional and will default to master. It will create a new
+  build and add it to the queue. It returns the build uuid and a status if it succeeds::
+
+      {
+        "build_id": "e6fa6db4-9c81-4b70-870f-a697ca405cdf",
+        "status": true
+      }
+
+`/api/v0/compose/types`
+^^^^^^^^^^^^^^^^^^^^^^^
+
+  Returns the list of supported output types that are valid for use with 'POST /api/v0/compose'
+
+      {
+        "types": [
+          {
+            "enabled": true,
+            "name": "tar"
+          }
+        ]
+      }
+
+`/api/v0/compose/queue`
+^^^^^^^^^^^^^^^^^^^^^^^
+
+  Return the status of the build queue. It includes information about the builds waiting,
+  and the build that is running.
+
+  Example::
+
+      {
+        "new": [
+          {
+            "id": "45502a6d-06e8-48a5-a215-2b4174b3614b",
+            "blueprint": "glusterfs",
+            "queue_status": "WAITING",
+            "job_created": 1517362647.4570868,
+            "version": "0.0.6"
+          },
+          {
+            "id": "6d292bd0-bec7-4825-8d7d-41ef9c3e4b73",
+            "blueprint": "kubernetes",
+            "queue_status": "WAITING",
+            "job_created": 1517362659.0034983,
+            "version": "0.0.1"
+          }
+        ],
+        "run": [
+          {
+            "id": "745712b2-96db-44c0-8014-fe925c35e795",
+            "blueprint": "glusterfs",
+            "queue_status": "RUNNING",
+            "job_created": 1517362633.7965999,
+            "job_started": 1517362633.8001345,
+            "version": "0.0.6"
+          }
+        ]
+      }
+
+`/api/v0/compose/finished`
+^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+  Return the details on all of the finished composes on the system.
+
+  Example::
+
+      {
+        "finished": [
+          {
+            "id": "70b84195-9817-4b8a-af92-45e380f39894",
+            "blueprint": "glusterfs",
+            "queue_status": "FINISHED",
+            "job_created": 1517351003.8210032,
+            "job_started": 1517351003.8230415,
+            "job_finished": 1517359234.1003145,
+            "version": "0.0.6"
+          },
+          {
+            "id": "e695affd-397f-4af9-9022-add2636e7459",
+            "blueprint": "glusterfs",
+            "queue_status": "FINISHED",
+            "job_created": 1517362289.7193348,
+            "job_started": 1517362289.9751132,
+            "job_finished": 1517363500.1234567,
+            "version": "0.0.6"
+          }
+        ]
+      }
+
+`/api/v0/compose/failed`
+^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+  Return the details on all of the failed composes on the system.
+
+  Example::
+
+      {
+        "failed": [
+           {
+            "id": "8c8435ef-d6bd-4c68-9bf1-a2ef832e6b1a",
+            "blueprint": "http-server",
+            "queue_status": "FAILED",
+            "job_created": 1517523249.9301329,
+            "job_started": 1517523249.9314211,
+            "job_finished": 1517523255.5623411,
+            "version": "0.0.2"
+          }
+        ]
+      }
+
+`/api/v0/compose/status/<uuids>[?blueprint=<blueprint_name>&status=<compose_status>&type=<compose_type>]`
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+  Return the details for each of the comma-separated list of uuids. A uuid of '*' will return
+  details for all composes.
+
+  Example::
+
+      {
+        "uuids": [
+          {
+            "id": "8c8435ef-d6bd-4c68-9bf1-a2ef832e6b1a",
+            "blueprint": "http-server",
+            "queue_status": "FINISHED",
+            "job_created": 1517523644.2384307,
+            "job_started": 1517523644.2551234,
+            "job_finished": 1517523689.9864314,
+            "version": "0.0.2"
+          },
+          {
+            "id": "45502a6d-06e8-48a5-a215-2b4174b3614b",
+            "blueprint": "glusterfs",
+            "queue_status": "FINISHED",
+            "job_created": 1517363442.188399,
+            "job_started": 1517363442.325324,
+            "job_finished": 1517363451.653621,
+            "version": "0.0.6"
+          }
+        ]
+      }
+
+DELETE `/api/v0/compose/cancel/<uuid>`
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+  Cancel the build, if it is not finished, and delete the results. It will return a
+  status of True if it is successful.
+
+  Example::
+
+      {
+        "status": true,
+        "uuid": "03397f8d-acff-4cdb-bd31-f629b7a948f5"
+      }
+
+DELETE `/api/v0/compose/delete/<uuids>`
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+  Delete the list of comma-separated uuids from the compose results.
+
+  Example::
+
+      {
+        "errors": [],
+        "uuids": [
+          {
+            "status": true,
+            "uuid": "ae1bf7e3-7f16-4c9f-b36e-3726a1093fd0"
+          }
+        ]
+      }
+
+`/api/v0/compose/info/<uuid>`
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+  Get detailed information about the compose. The returned JSON string will
+  contain the following information:
+
+    * id - The uuid of the comoposition
+    * config - containing the configuration settings used to run Anaconda
+    * blueprint - The depsolved blueprint used to generate the kickstart
+    * commit - The (local) git commit hash for the blueprint used
+    * deps - The NEVRA of all of the dependencies used in the composition
+    * compose_type - The type of output generated (tar, iso, etc.)
+    * queue_status - The final status of the composition (FINISHED or FAILED)
+
+  Example::
+
+      {
+        "commit": "7078e521a54b12eae31c3fd028680da7a0815a4d",
+        "compose_type": "tar",
+        "config": {
+          "anaconda_args": "",
+          "armplatform": "",
+          "compress_args": [],
+          "compression": "xz",
+          "image_name": "root.tar.xz",
+          ...
+        },
+        "deps": {
+          "packages": [
+            {
+              "arch": "x86_64",
+              "epoch": "0",
+              "name": "acl",
+              "release": "14.el7",
+              "version": "2.2.51"
+            }
+          ]
+        },
+        "id": "c30b7d80-523b-4a23-ad52-61b799739ce8",
+        "queue_status": "FINISHED",
+        "blueprint": {
+          "description": "An example kubernetes master",
+          ...
+        }
+      }
+
+`/api/v0/compose/metadata/<uuid>`
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+  Returns a .tar of the metadata used for the build. This includes all the
+  information needed to reproduce the build, including the final kickstart
+  populated with repository and package NEVRA.
+
+  The mime type is set to 'application/x-tar' and the filename is set to
+  UUID-metadata.tar
+
+  The .tar is uncompressed, but is not large.
+
+`/api/v0/compose/results/<uuid>`
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+  Returns a .tar of the metadata, logs, and output image of the build. This
+  includes all the information needed to reproduce the build, including the
+  final kickstart populated with repository and package NEVRA. The output image
+  is already in compressed form so the returned tar is not compressed.
+
+  The mime type is set to 'application/x-tar' and the filename is set to
+  UUID.tar
+
+`/api/v0/compose/logs/<uuid>`
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+  Returns a .tar of the anaconda build logs. The tar is not compressed, but is
+  not large.
+
+  The mime type is set to 'application/x-tar' and the filename is set to
+  UUID-logs.tar
+
+`/api/v0/compose/image/<uuid>`
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+  Returns the output image from the build. The filename is set to the filename
+  from the build with the UUID as a prefix. eg. UUID-root.tar.xz or UUID-boot.iso.
+
+`/api/v0/compose/log/<uuid>[?size=kbytes]`
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+  Returns the end of the anaconda.log. The size parameter is optional and defaults to 1Mbytes
+  if it is not included. The returned data is raw text from the end of the logfile, starting on
+  a line boundry.
+
+  Example::
+
+      12:59:24,222 INFO anaconda: Running Thread: AnaConfigurationThread (140629395244800)
+      12:59:24,223 INFO anaconda: Configuring installed system
+      12:59:24,912 INFO anaconda: Configuring installed system
+      12:59:24,912 INFO anaconda: Creating users
+      12:59:24,913 INFO anaconda: Clearing libuser.conf at /tmp/libuser.Dyy8Gj
+      12:59:25,154 INFO anaconda: Creating users
+      12:59:25,155 INFO anaconda: Configuring addons
+      12:59:25,155 INFO anaconda: Configuring addons
+      12:59:25,155 INFO anaconda: Generating initramfs
+      12:59:49,467 INFO anaconda: Generating initramfs
+      12:59:49,467 INFO anaconda: Running post-installation scripts
+      12:59:49,467 INFO anaconda: Running kickstart %%post script(s)
+      12:59:50,782 INFO anaconda: All kickstart %%post script(s) have been run
+      12:59:50,782 INFO anaconda: Running post-installation scripts
+      12:59:50,784 INFO anaconda: Thread Done: AnaConfigurationThread (140629395244800)
+
+"""
+
+import logging
+log = logging.getLogger("lorax-composer")
+
+import os
+from flask import jsonify, request, Response, send_file
+import pytoml as toml
+
+from pylorax.sysutils import joinpaths
+from pylorax.api.checkparams import checkparams
+from pylorax.api.compose import start_build, compose_types
+from pylorax.api.crossdomain import crossdomain
+from pylorax.api.errors import *                               # pylint: disable=wildcard-import
+from pylorax.api.projects import projects_list, projects_info, projects_depsolve
+from pylorax.api.projects import modules_list, modules_info, ProjectsError, repo_to_source
+from pylorax.api.projects import get_repo_sources, delete_repo_source, source_to_repo, dnf_repo_to_file_repo
+from pylorax.api.queue import queue_status, build_status, uuid_delete, uuid_status, uuid_info
+from pylorax.api.queue import uuid_tar, uuid_image, uuid_cancel, uuid_log
+from pylorax.api.recipes import RecipeError, list_branch_files, read_recipe_commit, recipe_filename, list_commits
+from pylorax.api.recipes import recipe_from_dict, recipe_from_toml, commit_recipe, delete_recipe, revert_recipe
+from pylorax.api.recipes import tag_recipe_commit, recipe_diff
+from pylorax.api.regexes import VALID_API_STRING
+from pylorax.api.workspace import workspace_read, workspace_write, workspace_delete
+
+# The API functions don't actually get called by any code here
+# pylint: disable=unused-variable
+
+
[docs]def take_limits(iterable, offset, limit): + """ Apply offset and limit to an iterable object + + :param iterable: The object to limit + :type iterable: iter + :param offset: The number of items to skip + :type offset: int + :param limit: The total number of items to return + :type limit: int + :returns: A subset of the iterable + """ + return iterable[offset:][:limit]
+ +
[docs]def blueprint_exists(api, branch, blueprint_name): + try: + with api.config["GITLOCK"].lock: + read_recipe_commit(api.config["GITLOCK"].repo, branch, blueprint_name) + + return True + except RecipeError: + return False
+ +
[docs]def v0_api(api): + # Note that Sphinx will not generate documentations for any of these. + @api.route("/api/v0/blueprints/list") + @crossdomain(origin="*") + def v0_blueprints_list(): + """List the available blueprints on a branch.""" + branch = request.args.get("branch", "master") + if VALID_API_STRING.match(branch) is None: + return jsonify(status=False, errors=[{"id": INVALID_CHARS, "msg": "Invalid characters in branch argument"}]), 400 + + try: + limit = int(request.args.get("limit", "20")) + offset = int(request.args.get("offset", "0")) + except ValueError as e: + return jsonify(status=False, errors=[{"id": BAD_LIMIT_OR_OFFSET, "msg": str(e)}]), 400 + + with api.config["GITLOCK"].lock: + blueprints = [f[:-5] for f in list_branch_files(api.config["GITLOCK"].repo, branch)] + limited_blueprints = take_limits(blueprints, offset, limit) + return jsonify(blueprints=limited_blueprints, limit=limit, offset=offset, total=len(blueprints)) + + @api.route("/api/v0/blueprints/info", defaults={'blueprint_names': ""}) + @api.route("/api/v0/blueprints/info/<blueprint_names>") + @crossdomain(origin="*") + @checkparams([("blueprint_names", "", "no blueprint names given")]) + def v0_blueprints_info(blueprint_names): + """Return the contents of the blueprint, or a list of blueprints""" + if VALID_API_STRING.match(blueprint_names) is None: + return jsonify(status=False, errors=[{"id": INVALID_CHARS, "msg": "Invalid characters in API path"}]), 400 + + branch = request.args.get("branch", "master") + if VALID_API_STRING.match(branch) is None: + return jsonify(status=False, errors=[{"id": INVALID_CHARS, "msg": "Invalid characters in branch argument"}]), 400 + + out_fmt = request.args.get("format", "json") + if VALID_API_STRING.match(out_fmt) is None: + return jsonify(status=False, errors=[{"id": INVALID_CHARS, "msg": "Invalid characters in format argument"}]), 400 + + blueprints = [] + changes = [] + errors = [] + for blueprint_name in [n.strip() for n in blueprint_names.split(",")]: + exceptions = [] + # Get the workspace version (if it exists) + try: + with api.config["GITLOCK"].lock: + ws_blueprint = workspace_read(api.config["GITLOCK"].repo, branch, blueprint_name) + except Exception as e: + ws_blueprint = None + exceptions.append(str(e)) + log.error("(v0_blueprints_info) %s", str(e)) + + # Get the git version (if it exists) + try: + with api.config["GITLOCK"].lock: + git_blueprint = read_recipe_commit(api.config["GITLOCK"].repo, branch, blueprint_name) + except Exception as e: + git_blueprint = None + exceptions.append(str(e)) + log.error("(v0_blueprints_info) %s", str(e)) + + if not ws_blueprint and not git_blueprint: + # Neither blueprint, return an error + errors.append({"id": UNKNOWN_BLUEPRINT, "msg": "%s: %s" % (blueprint_name, ", ".join(exceptions))}) + elif ws_blueprint and not git_blueprint: + # No git blueprint, return the workspace blueprint + changes.append({"name":blueprint_name, "changed":True}) + blueprints.append(ws_blueprint) + elif not ws_blueprint and git_blueprint: + # No workspace blueprint, no change, return the git blueprint + changes.append({"name":blueprint_name, "changed":False}) + blueprints.append(git_blueprint) + else: + # Both exist, maybe changed, return the workspace blueprint + changes.append({"name":blueprint_name, "changed":ws_blueprint != git_blueprint}) + blueprints.append(ws_blueprint) + + # Sort all the results by case-insensitive blueprint name + changes = sorted(changes, key=lambda c: c["name"].lower()) + blueprints = sorted(blueprints, key=lambda r: r["name"].lower()) + + if out_fmt == "toml": + if errors: + # If there are errors they need to be reported, use JSON and 400 for this + return jsonify(status=False, errors=errors), 400 + else: + # With TOML output we just want to dump the raw blueprint, skipping the rest. + return "\n\n".join([r.toml() for r in blueprints]) + else: + return jsonify(changes=changes, blueprints=blueprints, errors=errors) + + @api.route("/api/v0/blueprints/changes", defaults={'blueprint_names': ""}) + @api.route("/api/v0/blueprints/changes/<blueprint_names>") + @crossdomain(origin="*") + @checkparams([("blueprint_names", "", "no blueprint names given")]) + def v0_blueprints_changes(blueprint_names): + """Return the changes to a blueprint or list of blueprints""" + if VALID_API_STRING.match(blueprint_names) is None: + return jsonify(status=False, errors=[{"id": INVALID_CHARS, "msg": "Invalid characters in API path"}]), 400 + + branch = request.args.get("branch", "master") + if VALID_API_STRING.match(branch) is None: + return jsonify(status=False, errors=[{"id": INVALID_CHARS, "msg": "Invalid characters in branch argument"}]), 400 + + try: + limit = int(request.args.get("limit", "20")) + offset = int(request.args.get("offset", "0")) + except ValueError as e: + return jsonify(status=False, errors=[{"id": BAD_LIMIT_OR_OFFSET, "msg": str(e)}]), 400 + + blueprints = [] + errors = [] + for blueprint_name in [n.strip() for n in blueprint_names.split(",")]: + filename = recipe_filename(blueprint_name) + + if not blueprint_exists(api, branch, blueprint_name): + errors.append({"id": UNKNOWN_BLUEPRINT, "msg": "Unknown blueprint name: %s" % blueprint_name}) + continue + + try: + with api.config["GITLOCK"].lock: + commits = list_commits(api.config["GITLOCK"].repo, branch, filename) + limited_commits = take_limits(list_commits(api.config["GITLOCK"].repo, branch, filename), offset, limit) + except Exception as e: + errors.append({"id": BLUEPRINTS_ERROR, "msg": "%s: %s" % (blueprint_name, str(e))}) + log.error("(v0_blueprints_changes) %s", str(e)) + else: + blueprints.append({"name":blueprint_name, "changes":limited_commits, "total":len(commits)}) + + blueprints = sorted(blueprints, key=lambda r: r["name"].lower()) + + return jsonify(blueprints=blueprints, errors=errors, offset=offset, limit=limit) + + @api.route("/api/v0/blueprints/new", methods=["POST"]) + @crossdomain(origin="*") + def v0_blueprints_new(): + """Commit a new blueprint""" + branch = request.args.get("branch", "master") + if VALID_API_STRING.match(branch) is None: + return jsonify(status=False, errors=[{"id": INVALID_CHARS, "msg": "Invalid characters in branch argument"}]), 400 + + try: + if request.headers['Content-Type'] == "text/x-toml": + blueprint = recipe_from_toml(request.data) + else: + blueprint = recipe_from_dict(request.get_json(cache=False)) + + if VALID_API_STRING.match(blueprint["name"]) is None: + return jsonify(status=False, errors=[{"id": INVALID_CHARS, "msg": "Invalid characters in API path"}]), 400 + + with api.config["GITLOCK"].lock: + commit_recipe(api.config["GITLOCK"].repo, branch, blueprint) + + # Read the blueprint with new version and write it to the workspace + blueprint = read_recipe_commit(api.config["GITLOCK"].repo, branch, blueprint["name"]) + workspace_write(api.config["GITLOCK"].repo, branch, blueprint) + except Exception as e: + log.error("(v0_blueprints_new) %s", str(e)) + return jsonify(status=False, errors=[{"id": BLUEPRINTS_ERROR, "msg": str(e)}]), 400 + else: + return jsonify(status=True) + + @api.route("/api/v0/blueprints/delete", defaults={'blueprint_name': ""}, methods=["DELETE"]) + @api.route("/api/v0/blueprints/delete/<blueprint_name>", methods=["DELETE"]) + @crossdomain(origin="*") + @checkparams([("blueprint_name", "", "no blueprint name given")]) + def v0_blueprints_delete(blueprint_name): + """Delete a blueprint from git""" + if VALID_API_STRING.match(blueprint_name) is None: + return jsonify(status=False, errors=[{"id": INVALID_CHARS, "msg": "Invalid characters in API path"}]), 400 + + branch = request.args.get("branch", "master") + if VALID_API_STRING.match(branch) is None: + return jsonify(status=False, errors=[{"id": INVALID_CHARS, "msg": "Invalid characters in branch argument"}]), 400 + + try: + with api.config["GITLOCK"].lock: + delete_recipe(api.config["GITLOCK"].repo, branch, blueprint_name) + except Exception as e: + log.error("(v0_blueprints_delete) %s", str(e)) + return jsonify(status=False, errors=[{"id": BLUEPRINTS_ERROR, "msg": str(e)}]), 400 + else: + return jsonify(status=True) + + @api.route("/api/v0/blueprints/workspace", methods=["POST"]) + @crossdomain(origin="*") + def v0_blueprints_workspace(): + """Write a blueprint to the workspace""" + branch = request.args.get("branch", "master") + if VALID_API_STRING.match(branch) is None: + return jsonify(status=False, errors=[{"id": INVALID_CHARS, "msg": "Invalid characters in branch argument"}]), 400 + + try: + if request.headers['Content-Type'] == "text/x-toml": + blueprint = recipe_from_toml(request.data) + else: + blueprint = recipe_from_dict(request.get_json(cache=False)) + + if VALID_API_STRING.match(blueprint["name"]) is None: + return jsonify(status=False, errors=[{"id": INVALID_CHARS, "msg": "Invalid characters in API path"}]), 400 + + with api.config["GITLOCK"].lock: + workspace_write(api.config["GITLOCK"].repo, branch, blueprint) + except Exception as e: + log.error("(v0_blueprints_workspace) %s", str(e)) + return jsonify(status=False, errors=[{"id": BLUEPRINTS_ERROR, "msg": str(e)}]), 400 + else: + return jsonify(status=True) + + @api.route("/api/v0/blueprints/workspace", defaults={'blueprint_name': ""}, methods=["DELETE"]) + @api.route("/api/v0/blueprints/workspace/<blueprint_name>", methods=["DELETE"]) + @crossdomain(origin="*") + @checkparams([("blueprint_name", "", "no blueprint name given")]) + def v0_blueprints_delete_workspace(blueprint_name): + """Delete a blueprint from the workspace""" + if VALID_API_STRING.match(blueprint_name) is None: + return jsonify(status=False, errors=[{"id": INVALID_CHARS, "msg": "Invalid characters in API path"}]), 400 + + branch = request.args.get("branch", "master") + if VALID_API_STRING.match(branch) is None: + return jsonify(status=False, errors=[{"id": INVALID_CHARS, "msg": "Invalid characters in branch argument"}]), 400 + + try: + with api.config["GITLOCK"].lock: + workspace_delete(api.config["GITLOCK"].repo, branch, blueprint_name) + except Exception as e: + log.error("(v0_blueprints_delete_workspace) %s", str(e)) + return jsonify(status=False, errors=[{"id": BLUEPRINTS_ERROR, "msg": str(e)}]), 400 + else: + return jsonify(status=True) + + @api.route("/api/v0/blueprints/undo", defaults={'blueprint_name': "", 'commit': ""}, methods=["POST"]) + @api.route("/api/v0/blueprints/undo/<blueprint_name>", defaults={'commit': ""}, methods=["POST"]) + @api.route("/api/v0/blueprints/undo/<blueprint_name>/<commit>", methods=["POST"]) + @crossdomain(origin="*") + @checkparams([("blueprint_name", "", "no blueprint name given"), + ("commit", "", "no commit ID given")]) + def v0_blueprints_undo(blueprint_name, commit): + """Undo changes to a blueprint by reverting to a previous commit.""" + if VALID_API_STRING.match(blueprint_name) is None: + return jsonify(status=False, errors=[{"id": INVALID_CHARS, "msg": "Invalid characters in API path"}]), 400 + + branch = request.args.get("branch", "master") + if VALID_API_STRING.match(branch) is None: + return jsonify(status=False, errors=[{"id": INVALID_CHARS, "msg": "Invalid characters in branch argument"}]), 400 + + try: + with api.config["GITLOCK"].lock: + revert_recipe(api.config["GITLOCK"].repo, branch, blueprint_name, commit) + + # Read the new recipe and write it to the workspace + blueprint = read_recipe_commit(api.config["GITLOCK"].repo, branch, blueprint_name) + workspace_write(api.config["GITLOCK"].repo, branch, blueprint) + except Exception as e: + log.error("(v0_blueprints_undo) %s", str(e)) + return jsonify(status=False, errors=[{"id": UNKNOWN_COMMIT, "msg": str(e)}]), 400 + else: + return jsonify(status=True) + + @api.route("/api/v0/blueprints/tag", defaults={'blueprint_name': ""}, methods=["POST"]) + @api.route("/api/v0/blueprints/tag/<blueprint_name>", methods=["POST"]) + @crossdomain(origin="*") + @checkparams([("blueprint_name", "", "no blueprint name given")]) + def v0_blueprints_tag(blueprint_name): + """Tag a blueprint's latest blueprint commit as a 'revision'""" + if VALID_API_STRING.match(blueprint_name) is None: + return jsonify(status=False, errors=[{"id": INVALID_CHARS, "msg": "Invalid characters in API path"}]), 400 + + branch = request.args.get("branch", "master") + if VALID_API_STRING.match(branch) is None: + return jsonify(status=False, errors=[{"id": INVALID_CHARS, "msg": "Invalid characters in branch argument"}]), 400 + + try: + with api.config["GITLOCK"].lock: + tag_recipe_commit(api.config["GITLOCK"].repo, branch, blueprint_name) + except Exception as e: + log.error("(v0_blueprints_tag) %s", str(e)) + return jsonify(status=False, errors=[{"id": BLUEPRINTS_ERROR, "msg": str(e)}]), 400 + else: + return jsonify(status=True) + + @api.route("/api/v0/blueprints/diff", defaults={'blueprint_name': "", 'from_commit': "", 'to_commit': ""}) + @api.route("/api/v0/blueprints/diff/<blueprint_name>", defaults={'from_commit': "", 'to_commit': ""}) + @api.route("/api/v0/blueprints/diff/<blueprint_name>/<from_commit>", defaults={'to_commit': ""}) + @api.route("/api/v0/blueprints/diff/<blueprint_name>/<from_commit>/<to_commit>") + @crossdomain(origin="*") + @checkparams([("blueprint_name", "", "no blueprint name given"), + ("from_commit", "", "no from commit ID given"), + ("to_commit", "", "no to commit ID given")]) + def v0_blueprints_diff(blueprint_name, from_commit, to_commit): + """Return the differences between two commits of a blueprint""" + for s in [blueprint_name, from_commit, to_commit]: + if VALID_API_STRING.match(s) is None: + return jsonify(status=False, errors=[{"id": INVALID_CHARS, "msg": "Invalid characters in API path"}]), 400 + + branch = request.args.get("branch", "master") + if VALID_API_STRING.match(branch) is None: + return jsonify(status=False, errors=[{"id": INVALID_CHARS, "msg": "Invalid characters in branch argument"}]), 400 + + try: + if from_commit == "NEWEST": + with api.config["GITLOCK"].lock: + old_blueprint = read_recipe_commit(api.config["GITLOCK"].repo, branch, blueprint_name) + else: + with api.config["GITLOCK"].lock: + old_blueprint = read_recipe_commit(api.config["GITLOCK"].repo, branch, blueprint_name, from_commit) + except Exception as e: + log.error("(v0_blueprints_diff) %s", str(e)) + return jsonify(status=False, errors=[{"id": UNKNOWN_COMMIT, "msg": str(e)}]), 400 + + try: + if to_commit == "WORKSPACE": + with api.config["GITLOCK"].lock: + new_blueprint = workspace_read(api.config["GITLOCK"].repo, branch, blueprint_name) + # If there is no workspace, use the newest commit instead + if not new_blueprint: + with api.config["GITLOCK"].lock: + new_blueprint = read_recipe_commit(api.config["GITLOCK"].repo, branch, blueprint_name) + elif to_commit == "NEWEST": + with api.config["GITLOCK"].lock: + new_blueprint = read_recipe_commit(api.config["GITLOCK"].repo, branch, blueprint_name) + else: + with api.config["GITLOCK"].lock: + new_blueprint = read_recipe_commit(api.config["GITLOCK"].repo, branch, blueprint_name, to_commit) + except Exception as e: + log.error("(v0_blueprints_diff) %s", str(e)) + return jsonify(status=False, errors=[{"id": UNKNOWN_COMMIT, "msg": str(e)}]), 400 + + diff = recipe_diff(old_blueprint, new_blueprint) + return jsonify(diff=diff) + + @api.route("/api/v0/blueprints/freeze", defaults={'blueprint_names': ""}) + @api.route("/api/v0/blueprints/freeze/<blueprint_names>") + @crossdomain(origin="*") + @checkparams([("blueprint_names", "", "no blueprint names given")]) + def v0_blueprints_freeze(blueprint_names): + """Return the blueprint with the exact modules and packages selected by depsolve""" + if VALID_API_STRING.match(blueprint_names) is None: + return jsonify(status=False, errors=[{"id": INVALID_CHARS, "msg": "Invalid characters in API path"}]), 400 + + branch = request.args.get("branch", "master") + if VALID_API_STRING.match(branch) is None: + return jsonify(status=False, errors=[{"id": INVALID_CHARS, "msg": "Invalid characters in branch argument"}]), 400 + + out_fmt = request.args.get("format", "json") + if VALID_API_STRING.match(out_fmt) is None: + return jsonify(status=False, errors=[{"id": INVALID_CHARS, "msg": "Invalid characters in format argument"}]), 400 + + blueprints = [] + errors = [] + for blueprint_name in [n.strip() for n in sorted(blueprint_names.split(","), key=lambda n: n.lower())]: + # get the blueprint + # Get the workspace version (if it exists) + blueprint = None + try: + with api.config["GITLOCK"].lock: + blueprint = workspace_read(api.config["GITLOCK"].repo, branch, blueprint_name) + except Exception: + pass + + if not blueprint: + # No workspace version, get the git version (if it exists) + try: + with api.config["GITLOCK"].lock: + blueprint = read_recipe_commit(api.config["GITLOCK"].repo, branch, blueprint_name) + except Exception as e: + errors.append({"id": BLUEPRINTS_ERROR, "msg": "%s: %s" % (blueprint_name, str(e))}) + log.error("(v0_blueprints_freeze) %s", str(e)) + + # No blueprint found, skip it. + if not blueprint: + errors.append({"id": UNKNOWN_BLUEPRINT, "msg": "%s: blueprint_not_found" % blueprint_name}) + continue + + # Combine modules and packages and depsolve the list + # TODO include the version/glob in the depsolving + module_nver = blueprint.module_nver + package_nver = blueprint.package_nver + projects = sorted(set(module_nver+package_nver), key=lambda p: p[0].lower()) + deps = [] + try: + with api.config["DNFLOCK"].lock: + deps = projects_depsolve(api.config["DNFLOCK"].dbo, projects, blueprint.group_names) + except ProjectsError as e: + errors.append({"id": BLUEPRINTS_ERROR, "msg": "%s: %s" % (blueprint_name, str(e))}) + log.error("(v0_blueprints_freeze) %s", str(e)) + + blueprints.append({"blueprint": blueprint.freeze(deps)}) + + if out_fmt == "toml": + # With TOML output we just want to dump the raw blueprint, skipping the rest. + return "\n\n".join([e["blueprint"].toml() for e in blueprints]) + else: + return jsonify(blueprints=blueprints, errors=errors) + + @api.route("/api/v0/blueprints/depsolve", defaults={'blueprint_names': ""}) + @api.route("/api/v0/blueprints/depsolve/<blueprint_names>") + @crossdomain(origin="*") + @checkparams([("blueprint_names", "", "no blueprint names given")]) + def v0_blueprints_depsolve(blueprint_names): + """Return the dependencies for a blueprint""" + if VALID_API_STRING.match(blueprint_names) is None: + return jsonify(status=False, errors=[{"id": INVALID_CHARS, "msg": "Invalid characters in API path"}]), 400 + + branch = request.args.get("branch", "master") + if VALID_API_STRING.match(branch) is None: + return jsonify(status=False, errors=[{"id": INVALID_CHARS, "msg": "Invalid characters in branch argument"}]), 400 + + blueprints = [] + errors = [] + for blueprint_name in [n.strip() for n in sorted(blueprint_names.split(","), key=lambda n: n.lower())]: + # get the blueprint + # Get the workspace version (if it exists) + blueprint = None + try: + with api.config["GITLOCK"].lock: + blueprint = workspace_read(api.config["GITLOCK"].repo, branch, blueprint_name) + except Exception: + pass + + if not blueprint: + # No workspace version, get the git version (if it exists) + try: + with api.config["GITLOCK"].lock: + blueprint = read_recipe_commit(api.config["GITLOCK"].repo, branch, blueprint_name) + except Exception as e: + errors.append({"id": BLUEPRINTS_ERROR, "msg": "%s: %s" % (blueprint_name, str(e))}) + log.error("(v0_blueprints_depsolve) %s", str(e)) + + # No blueprint found, skip it. + if not blueprint: + errors.append({"id": UNKNOWN_BLUEPRINT, "msg": "%s: blueprint not found" % blueprint_name}) + continue + + # Combine modules and packages and depsolve the list + # TODO include the version/glob in the depsolving + module_nver = blueprint.module_nver + package_nver = blueprint.package_nver + projects = sorted(set(module_nver+package_nver), key=lambda p: p[0].lower()) + deps = [] + try: + with api.config["DNFLOCK"].lock: + deps = projects_depsolve(api.config["DNFLOCK"].dbo, projects, blueprint.group_names) + except ProjectsError as e: + errors.append({"id": BLUEPRINTS_ERROR, "msg": "%s: %s" % (blueprint_name, str(e))}) + log.error("(v0_blueprints_depsolve) %s", str(e)) + + # Get the NEVRA's of the modules and projects, add as "modules" + modules = [] + for dep in deps: + if dep["name"] in projects: + modules.append(dep) + modules = sorted(modules, key=lambda m: m["name"].lower()) + + blueprints.append({"blueprint":blueprint, "dependencies":deps, "modules":modules}) + + return jsonify(blueprints=blueprints, errors=errors) + + @api.route("/api/v0/projects/list") + @crossdomain(origin="*") + def v0_projects_list(): + """List all of the available projects/packages""" + try: + limit = int(request.args.get("limit", "20")) + offset = int(request.args.get("offset", "0")) + except ValueError as e: + return jsonify(status=False, errors=[{"id": BAD_LIMIT_OR_OFFSET, "msg": str(e)}]), 400 + + try: + with api.config["DNFLOCK"].lock: + available = projects_list(api.config["DNFLOCK"].dbo) + except ProjectsError as e: + log.error("(v0_projects_list) %s", str(e)) + return jsonify(status=False, errors=[{"id": PROJECTS_ERROR, "msg": str(e)}]), 400 + + projects = take_limits(available, offset, limit) + return jsonify(projects=projects, offset=offset, limit=limit, total=len(available)) + + @api.route("/api/v0/projects/info", defaults={'project_names': ""}) + @api.route("/api/v0/projects/info/<project_names>") + @crossdomain(origin="*") + @checkparams([("project_names", "", "no project names given")]) + def v0_projects_info(project_names): + """Return detailed information about the listed projects""" + if VALID_API_STRING.match(project_names) is None: + return jsonify(status=False, errors=[{"id": INVALID_CHARS, "msg": "Invalid characters in API path"}]), 400 + + try: + with api.config["DNFLOCK"].lock: + projects = projects_info(api.config["DNFLOCK"].dbo, project_names.split(",")) + except ProjectsError as e: + log.error("(v0_projects_info) %s", str(e)) + return jsonify(status=False, errors=[{"id": PROJECTS_ERROR, "msg": str(e)}]), 400 + + if not projects: + msg = "one of the requested projects does not exist: %s" % project_names + log.error("(v0_projects_info) %s", msg) + return jsonify(status=False, errors=[{"id": UNKNOWN_PROJECT, "msg": msg}]), 400 + + return jsonify(projects=projects) + + @api.route("/api/v0/projects/depsolve", defaults={'project_names': ""}) + @api.route("/api/v0/projects/depsolve/<project_names>") + @crossdomain(origin="*") + @checkparams([("project_names", "", "no project names given")]) + def v0_projects_depsolve(project_names): + """Return detailed information about the listed projects""" + if VALID_API_STRING.match(project_names) is None: + return jsonify(status=False, errors=[{"id": INVALID_CHARS, "msg": "Invalid characters in API path"}]), 400 + + try: + with api.config["DNFLOCK"].lock: + deps = projects_depsolve(api.config["DNFLOCK"].dbo, [(n, "*") for n in project_names.split(",")], []) + except ProjectsError as e: + log.error("(v0_projects_depsolve) %s", str(e)) + return jsonify(status=False, errors=[{"id": PROJECTS_ERROR, "msg": str(e)}]), 400 + + if not deps: + msg = "one of the requested projects does not exist: %s" % project_names + log.error("(v0_projects_depsolve) %s", msg) + return jsonify(status=False, errors=[{"id": UNKNOWN_PROJECT, "msg": msg}]), 400 + + return jsonify(projects=deps) + + @api.route("/api/v0/projects/source/list") + @crossdomain(origin="*") + def v0_projects_source_list(): + """Return the list of source names""" + with api.config["DNFLOCK"].lock: + repos = list(api.config["DNFLOCK"].dbo.repos.iter_enabled()) + sources = sorted([r.id for r in repos]) + return jsonify(sources=sources) + + @api.route("/api/v0/projects/source/info", defaults={'source_names': ""}) + @api.route("/api/v0/projects/source/info/<source_names>") + @crossdomain(origin="*") + @checkparams([("source_names", "", "no source names given")]) + def v0_projects_source_info(source_names): + """Return detailed info about the list of sources""" + if VALID_API_STRING.match(source_names) is None: + return jsonify(status=False, errors=[{"id": INVALID_CHARS, "msg": "Invalid characters in API path"}]), 400 + + out_fmt = request.args.get("format", "json") + if VALID_API_STRING.match(out_fmt) is None: + return jsonify(status=False, errors=[{"id": INVALID_CHARS, "msg": "Invalid characters in format argument"}]), 400 + + # Return info on all of the sources + if source_names == "*": + with api.config["DNFLOCK"].lock: + source_names = ",".join(r.id for r in api.config["DNFLOCK"].dbo.repos.iter_enabled()) + + sources = {} + errors = [] + system_sources = get_repo_sources("/etc/yum.repos.d/*.repo") + for source in source_names.split(","): + with api.config["DNFLOCK"].lock: + repo = api.config["DNFLOCK"].dbo.repos.get(source, None) + if not repo: + errors.append({"id": UNKNOWN_SOURCE, "msg": "%s is not a valid source" % source}) + continue + sources[repo.id] = repo_to_source(repo, repo.id in system_sources) + + if out_fmt == "toml" and not errors: + # With TOML output we just want to dump the raw sources, skipping the errors + return toml.dumps(sources) + elif out_fmt == "toml" and errors: + # TOML requested, but there was an error + return jsonify(status=False, errors=errors), 400 + else: + return jsonify(sources=sources, errors=errors) + + @api.route("/api/v0/projects/source/new", methods=["POST"]) + @crossdomain(origin="*") + def v0_projects_source_new(): + """Add a new package source. Or change an existing one""" + if request.headers['Content-Type'] == "text/x-toml": + source = toml.loads(request.data) + else: + source = request.get_json(cache=False) + + system_sources = get_repo_sources("/etc/yum.repos.d/*.repo") + if source["name"] in system_sources: + return jsonify(status=False, errors=[{"id": SYSTEM_SOURCE, "msg": "%s is a system source, it cannot be changed." % source["name"]}]), 400 + + try: + # Remove it from the RepoDict (NOTE that this isn't explicitly supported by the DNF API) + with api.config["DNFLOCK"].lock: + dbo = api.config["DNFLOCK"].dbo + # If this repo already exists, delete it and replace it with the new one + repos = list(r.id for r in dbo.repos.iter_enabled()) + if source["name"] in repos: + del dbo.repos[source["name"]] + + repo = source_to_repo(source, dbo.conf) + dbo.repos.add(repo) + + log.info("Updating repository metadata after adding %s", source["name"]) + dbo.fill_sack(load_system_repo=False) + dbo.read_comps() + + # Write the new repo to disk, replacing any existing ones + repo_dir = api.config["COMPOSER_CFG"].get("composer", "repo_dir") + + # Remove any previous sources with this name, ignore it if it isn't found + try: + delete_repo_source(joinpaths(repo_dir, "*.repo"), source["name"]) + except ProjectsError: + pass + + # Make sure the source name can't contain a path traversal by taking the basename + source_path = joinpaths(repo_dir, os.path.basename("%s.repo" % source["name"])) + with open(source_path, "w") as f: + f.write(dnf_repo_to_file_repo(repo)) + except Exception as e: + log.error("(v0_projects_source_add) adding %s failed: %s", source["name"], str(e)) + + # Cleanup the mess, if loading it failed we don't want to leave it in memory + repos = list(r.id for r in dbo.repos.iter_enabled()) + if source["name"] in repos: + with api.config["DNFLOCK"].lock: + dbo = api.config["DNFLOCK"].dbo + del dbo.repos[source["name"]] + + log.info("Updating repository metadata after adding %s failed", source["name"]) + dbo.fill_sack(load_system_repo=False) + dbo.read_comps() + + return jsonify(status=False, errors=[{"id": PROJECTS_ERROR, "msg": str(e)}]), 400 + + return jsonify(status=True) + + @api.route("/api/v0/projects/source/delete", defaults={'source_name': ""}, methods=["DELETE"]) + @api.route("/api/v0/projects/source/delete/<source_name>", methods=["DELETE"]) + @crossdomain(origin="*") + @checkparams([("source_name", "", "no source name given")]) + def v0_projects_source_delete(source_name): + """Delete the named source and return a status response""" + if VALID_API_STRING.match(source_name) is None: + return jsonify(status=False, errors=[{"id": INVALID_CHARS, "msg": "Invalid characters in API path"}]), 400 + + system_sources = get_repo_sources("/etc/yum.repos.d/*.repo") + if source_name in system_sources: + return jsonify(status=False, errors=[{"id": SYSTEM_SOURCE, "msg": "%s is a system source, it cannot be deleted." % source_name}]), 400 + share_dir = api.config["COMPOSER_CFG"].get("composer", "repo_dir") + try: + # Remove the file entry for the source + delete_repo_source(joinpaths(share_dir, "*.repo"), source_name) + + # Remove it from the RepoDict (NOTE that this isn't explicitly supported by the DNF API) + with api.config["DNFLOCK"].lock: + if source_name in api.config["DNFLOCK"].dbo.repos: + del api.config["DNFLOCK"].dbo.repos[source_name] + log.info("Updating repository metadata after removing %s", source_name) + api.config["DNFLOCK"].dbo.fill_sack(load_system_repo=False) + api.config["DNFLOCK"].dbo.read_comps() + + except ProjectsError as e: + log.error("(v0_projects_source_delete) %s", str(e)) + return jsonify(status=False, errors=[{"id": UNKNOWN_SOURCE, "msg": str(e)}]), 400 + + return jsonify(status=True) + + @api.route("/api/v0/modules/list") + @api.route("/api/v0/modules/list/<module_names>") + @crossdomain(origin="*") + def v0_modules_list(module_names=None): + """List available modules, filtering by module_names""" + if module_names and VALID_API_STRING.match(module_names) is None: + return jsonify(status=False, errors=[{"id": INVALID_CHARS, "msg": "Invalid characters in API path"}]), 400 + + try: + limit = int(request.args.get("limit", "20")) + offset = int(request.args.get("offset", "0")) + except ValueError as e: + return jsonify(status=False, errors=[{"id": BAD_LIMIT_OR_OFFSET, "msg": str(e)}]), 400 + + if module_names: + module_names = module_names.split(",") + + try: + with api.config["DNFLOCK"].lock: + available = modules_list(api.config["DNFLOCK"].dbo, module_names) + except ProjectsError as e: + log.error("(v0_modules_list) %s", str(e)) + return jsonify(status=False, errors=[{"id": MODULES_ERROR, "msg": str(e)}]), 400 + + if module_names and not available: + msg = "one of the requested modules does not exist: %s" % module_names + log.error("(v0_modules_list) %s", msg) + return jsonify(status=False, errors=[{"id": UNKNOWN_MODULE, "msg": msg}]), 400 + + modules = take_limits(available, offset, limit) + return jsonify(modules=modules, offset=offset, limit=limit, total=len(available)) + + @api.route("/api/v0/modules/info", defaults={'module_names': ""}) + @api.route("/api/v0/modules/info/<module_names>") + @crossdomain(origin="*") + @checkparams([("module_names", "", "no module names given")]) + def v0_modules_info(module_names): + """Return detailed information about the listed modules""" + if VALID_API_STRING.match(module_names) is None: + return jsonify(status=False, errors=[{"id": INVALID_CHARS, "msg": "Invalid characters in API path"}]), 400 + try: + with api.config["DNFLOCK"].lock: + modules = modules_info(api.config["DNFLOCK"].dbo, module_names.split(",")) + except ProjectsError as e: + log.error("(v0_modules_info) %s", str(e)) + return jsonify(status=False, errors=[{"id": MODULES_ERROR, "msg": str(e)}]), 400 + + if not modules: + msg = "one of the requested modules does not exist: %s" % module_names + log.error("(v0_modules_info) %s", msg) + return jsonify(status=False, errors=[{"id": UNKNOWN_MODULE, "msg": msg}]), 400 + + return jsonify(modules=modules) + + @api.route("/api/v0/compose", methods=["POST"]) + @crossdomain(origin="*") + def v0_compose_start(): + """Start a compose + + The body of the post should have these fields: + blueprint_name - The blueprint name from /blueprints/list/ + compose_type - The type of output to create, from /compose/types + branch - Optional, defaults to master, selects the git branch to use for the blueprint. + """ + # Passing ?test=1 will generate a fake FAILED compose. + # Passing ?test=2 will generate a fake FINISHED compose. + try: + test_mode = int(request.args.get("test", "0")) + except ValueError: + test_mode = 0 + + compose = request.get_json(cache=False) + + errors = [] + if not compose: + return jsonify(status=False, errors=[{"id": MISSING_POST, "msg": "Missing POST body"}]), 400 + + if "blueprint_name" not in compose: + errors.append({"id": UNKNOWN_BLUEPRINT,"msg": "No 'blueprint_name' in the JSON request"}) + else: + blueprint_name = compose["blueprint_name"] + + if "branch" not in compose or not compose["branch"]: + branch = "master" + else: + branch = compose["branch"] + + if "compose_type" not in compose: + errors.append({"id": BAD_COMPOSE_TYPE, "msg": "No 'compose_type' in the JSON request"}) + else: + compose_type = compose["compose_type"] + + if VALID_API_STRING.match(blueprint_name) is None: + errors.append({"id": INVALID_CHARS, "msg": "Invalid characters in API path"}) + + if errors: + return jsonify(status=False, errors=errors), 400 + + try: + build_id = start_build(api.config["COMPOSER_CFG"], api.config["DNFLOCK"], api.config["GITLOCK"], + branch, blueprint_name, compose_type, test_mode) + except Exception as e: + if "Invalid compose type" in str(e): + return jsonify(status=False, errors=[{"id": BAD_COMPOSE_TYPE, "msg": str(e)}]), 400 + else: + return jsonify(status=False, errors=[{"id": BUILD_FAILED, "msg": str(e)}]), 400 + + return jsonify(status=True, build_id=build_id) + + @api.route("/api/v0/compose/types") + @crossdomain(origin="*") + def v0_compose_types(): + """Return the list of enabled output types + + (only enabled types are returned) + """ + share_dir = api.config["COMPOSER_CFG"].get("composer", "share_dir") + return jsonify(types=[{"name": k, "enabled": True} for k in compose_types(share_dir)]) + + @api.route("/api/v0/compose/queue") + @crossdomain(origin="*") + def v0_compose_queue(): + """Return the status of the new and running queues""" + return jsonify(queue_status(api.config["COMPOSER_CFG"])) + + @api.route("/api/v0/compose/finished") + @crossdomain(origin="*") + def v0_compose_finished(): + """Return the list of finished composes""" + return jsonify(finished=build_status(api.config["COMPOSER_CFG"], "FINISHED")) + + @api.route("/api/v0/compose/failed") + @crossdomain(origin="*") + def v0_compose_failed(): + """Return the list of failed composes""" + return jsonify(failed=build_status(api.config["COMPOSER_CFG"], "FAILED")) + + @api.route("/api/v0/compose/status", defaults={'uuids': ""}) + @api.route("/api/v0/compose/status/<uuids>") + @crossdomain(origin="*") + @checkparams([("uuids", "", "no UUIDs given")]) + def v0_compose_status(uuids): + """Return the status of the listed uuids""" + if VALID_API_STRING.match(uuids) is None: + return jsonify(status=False, errors=[{"id": INVALID_CHARS, "msg": "Invalid characters in API path"}]), 400 + + blueprint = request.args.get("blueprint", None) + status = request.args.get("status", None) + compose_type = request.args.get("type", None) + + results = [] + errors = [] + + if uuids.strip() == '*': + queue_status_dict = queue_status(api.config["COMPOSER_CFG"]) + queue_new = queue_status_dict["new"] + queue_running = queue_status_dict["run"] + candidates = queue_new + queue_running + build_status(api.config["COMPOSER_CFG"]) + else: + candidates = [] + for uuid in [n.strip().lower() for n in uuids.split(",")]: + details = uuid_status(api.config["COMPOSER_CFG"], uuid) + if details is None: + errors.append({"id": UNKNOWN_UUID, "msg": "%s is not a valid build uuid" % uuid}) + else: + candidates.append(details) + + for details in candidates: + if blueprint is not None and details['blueprint'] != blueprint: + continue + + if status is not None and details['queue_status'] != status: + continue + + if compose_type is not None and details['compose_type'] != compose_type: + continue + + results.append(details) + + return jsonify(uuids=results, errors=errors) + + @api.route("/api/v0/compose/cancel", defaults={'uuid': ""}, methods=["DELETE"]) + @api.route("/api/v0/compose/cancel/<uuid>", methods=["DELETE"]) + @crossdomain(origin="*") + @checkparams([("uuid", "", "no UUID given")]) + def v0_compose_cancel(uuid): + """Cancel a running compose and delete its results directory""" + if VALID_API_STRING.match(uuid) is None: + return jsonify(status=False, errors=[{"id": INVALID_CHARS, "msg": "Invalid characters in API path"}]), 400 + + status = uuid_status(api.config["COMPOSER_CFG"], uuid) + if status is None: + return jsonify(status=False, errors=[{"id": UNKNOWN_UUID, "msg": "%s is not a valid build uuid" % uuid}]), 400 + + if status["queue_status"] not in ["WAITING", "RUNNING"]: + return jsonify(status=False, errors=[{"id": BUILD_IN_WRONG_STATE, "msg": "Build %s is not in WAITING or RUNNING." % uuid}]) + + try: + uuid_cancel(api.config["COMPOSER_CFG"], uuid) + except Exception as e: + return jsonify(status=False, errors=[{"id": COMPOSE_ERROR, "msg": "%s: %s" % (uuid, str(e))}]),400 + else: + return jsonify(status=True, uuid=uuid) + + @api.route("/api/v0/compose/delete", defaults={'uuids': ""}, methods=["DELETE"]) + @api.route("/api/v0/compose/delete/<uuids>", methods=["DELETE"]) + @crossdomain(origin="*") + @checkparams([("uuids", "", "no UUIDs given")]) + def v0_compose_delete(uuids): + """Delete the compose results for the listed uuids""" + if VALID_API_STRING.match(uuids) is None: + return jsonify(status=False, errors=[{"id": INVALID_CHARS, "msg": "Invalid characters in API path"}]), 400 + + results = [] + errors = [] + for uuid in [n.strip().lower() for n in uuids.split(",")]: + status = uuid_status(api.config["COMPOSER_CFG"], uuid) + if status is None: + errors.append({"id": UNKNOWN_UUID, "msg": "%s is not a valid build uuid" % uuid}) + elif status["queue_status"] not in ["FINISHED", "FAILED"]: + errors.append({"id": BUILD_IN_WRONG_STATE, "msg": "Build %s is not in FINISHED or FAILED." % uuid}) + else: + try: + uuid_delete(api.config["COMPOSER_CFG"], uuid) + except Exception as e: + errors.append({"id": COMPOSE_ERROR, "msg": "%s: %s" % (uuid, str(e))}) + else: + results.append({"uuid":uuid, "status":True}) + return jsonify(uuids=results, errors=errors) + + @api.route("/api/v0/compose/info", defaults={'uuid': ""}) + @api.route("/api/v0/compose/info/<uuid>") + @crossdomain(origin="*") + @checkparams([("uuid", "", "no UUID given")]) + def v0_compose_info(uuid): + """Return detailed info about a compose""" + if VALID_API_STRING.match(uuid) is None: + return jsonify(status=False, errors=[{"id": INVALID_CHARS, "msg": "Invalid characters in API path"}]), 400 + + try: + info = uuid_info(api.config["COMPOSER_CFG"], uuid) + except Exception as e: + return jsonify(status=False, errors=[{"id": COMPOSE_ERROR, "msg": str(e)}]), 400 + + if info is None: + return jsonify(status=False, errors=[{"id": UNKNOWN_UUID, "msg": "%s is not a valid build uuid" % uuid}]), 400 + else: + return jsonify(**info) + + @api.route("/api/v0/compose/metadata", defaults={'uuid': ""}) + @api.route("/api/v0/compose/metadata/<uuid>") + @crossdomain(origin="*") + @checkparams([("uuid","", "no UUID given")]) + def v0_compose_metadata(uuid): + """Return a tar of the metadata for the build""" + if VALID_API_STRING.match(uuid) is None: + return jsonify(status=False, errors=[{"id": INVALID_CHARS, "msg": "Invalid characters in API path"}]), 400 + + status = uuid_status(api.config["COMPOSER_CFG"], uuid) + if status is None: + return jsonify(status=False, errors=[{"id": UNKNOWN_UUID, "msg": "%s is not a valid build uuid" % uuid}]), 400 + if status["queue_status"] not in ["FINISHED", "FAILED"]: + return jsonify(status=False, errors=[{"id": BUILD_IN_WRONG_STATE, "msg": "Build %s not in FINISHED or FAILED state." % uuid}]), 400 + else: + return Response(uuid_tar(api.config["COMPOSER_CFG"], uuid, metadata=True, image=False, logs=False), + mimetype="application/x-tar", + headers=[("Content-Disposition", "attachment; filename=%s-metadata.tar;" % uuid)], + direct_passthrough=True) + + @api.route("/api/v0/compose/results", defaults={'uuid': ""}) + @api.route("/api/v0/compose/results/<uuid>") + @crossdomain(origin="*") + @checkparams([("uuid","", "no UUID given")]) + def v0_compose_results(uuid): + """Return a tar of the metadata and the results for the build""" + if VALID_API_STRING.match(uuid) is None: + return jsonify(status=False, errors=[{"id": INVALID_CHARS, "msg": "Invalid characters in API path"}]), 400 + + status = uuid_status(api.config["COMPOSER_CFG"], uuid) + if status is None: + return jsonify(status=False, errors=[{"id": UNKNOWN_UUID, "msg": "%s is not a valid build uuid" % uuid}]), 400 + elif status["queue_status"] not in ["FINISHED", "FAILED"]: + return jsonify(status=False, errors=[{"id": BUILD_IN_WRONG_STATE, "msg": "Build %s not in FINISHED or FAILED state." % uuid}]), 400 + else: + return Response(uuid_tar(api.config["COMPOSER_CFG"], uuid, metadata=True, image=True, logs=True), + mimetype="application/x-tar", + headers=[("Content-Disposition", "attachment; filename=%s.tar;" % uuid)], + direct_passthrough=True) + + @api.route("/api/v0/compose/logs", defaults={'uuid': ""}) + @api.route("/api/v0/compose/logs/<uuid>") + @crossdomain(origin="*") + @checkparams([("uuid","", "no UUID given")]) + def v0_compose_logs(uuid): + """Return a tar of the metadata for the build""" + if VALID_API_STRING.match(uuid) is None: + return jsonify(status=False, errors=[{"id": INVALID_CHARS, "msg": "Invalid characters in API path"}]), 400 + + status = uuid_status(api.config["COMPOSER_CFG"], uuid) + if status is None: + return jsonify(status=False, errors=[{"id": UNKNOWN_UUID, "msg": "%s is not a valid build uuid" % uuid}]), 400 + elif status["queue_status"] not in ["FINISHED", "FAILED"]: + return jsonify(status=False, errors=[{"id": BUILD_IN_WRONG_STATE, "msg": "Build %s not in FINISHED or FAILED state." % uuid}]), 400 + else: + return Response(uuid_tar(api.config["COMPOSER_CFG"], uuid, metadata=False, image=False, logs=True), + mimetype="application/x-tar", + headers=[("Content-Disposition", "attachment; filename=%s-logs.tar;" % uuid)], + direct_passthrough=True) + + @api.route("/api/v0/compose/image", defaults={'uuid': ""}) + @api.route("/api/v0/compose/image/<uuid>") + @crossdomain(origin="*") + @checkparams([("uuid","", "no UUID given")]) + def v0_compose_image(uuid): + """Return the output image for the build""" + if VALID_API_STRING.match(uuid) is None: + return jsonify(status=False, errors=[{"id": INVALID_CHARS, "msg": "Invalid characters in API path"}]), 400 + + status = uuid_status(api.config["COMPOSER_CFG"], uuid) + if status is None: + return jsonify(status=False, errors=[{"id": UNKNOWN_UUID, "msg": "%s is not a valid build uuid" % uuid}]), 400 + elif status["queue_status"] not in ["FINISHED", "FAILED"]: + return jsonify(status=False, errors=[{"id": BUILD_IN_WRONG_STATE, "msg": "Build %s not in FINISHED or FAILED state." % uuid}]), 400 + else: + image_name, image_path = uuid_image(api.config["COMPOSER_CFG"], uuid) + + # Make sure it really exists + if not os.path.exists(image_path): + return jsonify(status=False, errors=[{"id": BUILD_MISSING_FILE, "msg": "Build %s is missing image file %s" % (uuid, image_name)}]), 400 + + # Make the image name unique + image_name = uuid + "-" + image_name + # XXX - Will mime type guessing work for all our output? + return send_file(image_path, as_attachment=True, attachment_filename=image_name, add_etags=False) + + @api.route("/api/v0/compose/log", defaults={'uuid': ""}) + @api.route("/api/v0/compose/log/<uuid>") + @crossdomain(origin="*") + @checkparams([("uuid","", "no UUID given")]) + def v0_compose_log_tail(uuid): + """Return the end of the main anaconda.log, defaults to 1Mbytes""" + if VALID_API_STRING.match(uuid) is None: + return jsonify(status=False, errors=[{"id": INVALID_CHARS, "msg": "Invalid characters in API path"}]), 400 + + try: + size = int(request.args.get("size", "1024")) + except ValueError as e: + return jsonify(status=False, errors=[{"id": COMPOSE_ERROR, "msg": str(e)}]), 400 + + status = uuid_status(api.config["COMPOSER_CFG"], uuid) + if status is None: + return jsonify(status=False, errors=[{"id": UNKNOWN_UUID, "msg": "%s is not a valid build uuid" % uuid}]), 400 + elif status["queue_status"] == "WAITING": + return jsonify(status=False, errors=[{"id": BUILD_IN_WRONG_STATE, "msg": "Build %s has not started yet. No logs to view" % uuid}]) + try: + return Response(uuid_log(api.config["COMPOSER_CFG"], uuid, size), direct_passthrough=True) + except RuntimeError as e: + return jsonify(status=False, errors=[{"id": COMPOSE_ERROR, "msg": str(e)}]), 400
+
+ +
+ +
+ + +
+
+ +
+ +
+ + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/f30-branch/_modules/pylorax/api/workspace.html b/f30-branch/_modules/pylorax/api/workspace.html new file mode 100644 index 00000000..f8531913 --- /dev/null +++ b/f30-branch/_modules/pylorax/api/workspace.html @@ -0,0 +1,299 @@ + + + + + + + + + + + pylorax.api.workspace — Lorax 30.19 documentation + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + +
+ + + + + +
+ +
+ + + + + + + + + + + + + + + + + +
+ + + + +
+
+
+
+ +

Source code for pylorax.api.workspace

+#
+# Copyright (C) 2017  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 os
+
+from pylorax.api.recipes import recipe_filename, recipe_from_toml, RecipeFileError
+from pylorax.sysutils import joinpaths
+
+
+
[docs]def workspace_dir(repo, branch): + """Create the workspace's path from a Repository and branch + + :param repo: Open repository + :type repo: Git.Repository + :param branch: Branch name + :type branch: str + :returns: The path to the branch's workspace directory + :rtype: str + + """ + repo_path = repo.get_location().get_path() + return joinpaths(repo_path, "workspace", branch)
+ + +
[docs]def workspace_read(repo, branch, recipe_name): + """Read a Recipe from the branch's workspace + + :param repo: Open repository + :type repo: Git.Repository + :param branch: Branch name + :type branch: str + :param recipe_name: The name of the recipe + :type recipe_name: str + :returns: The workspace copy of the recipe, or None if it doesn't exist + :rtype: Recipe or None + :raises: RecipeFileError + """ + ws_dir = workspace_dir(repo, branch) + if not os.path.isdir(ws_dir): + os.makedirs(ws_dir) + filename = joinpaths(ws_dir, recipe_filename(recipe_name)) + if not os.path.exists(filename): + return None + try: + f = open(filename, 'rb') + recipe = recipe_from_toml(f.read().decode("UTF-8")) + except IOError: + raise RecipeFileError + return recipe
+ + +
[docs]def workspace_write(repo, branch, recipe): + """Write a recipe to the workspace + + :param repo: Open repository + :type repo: Git.Repository + :param branch: Branch name + :type branch: str + :param recipe: The recipe to write to the workspace + :type recipe: Recipe + :returns: None + :raises: IO related errors + """ + ws_dir = workspace_dir(repo, branch) + if not os.path.isdir(ws_dir): + os.makedirs(ws_dir) + filename = joinpaths(ws_dir, recipe.filename) + open(filename, 'wb').write(recipe.toml().encode("UTF-8"))
+ + +
[docs]def workspace_delete(repo, branch, recipe_name): + """Delete the recipe from the workspace + + :param repo: Open repository + :type repo: Git.Repository + :param branch: Branch name + :type branch: str + :param recipe_name: The name of the recipe + :type recipe_name: str + :returns: None + :raises: IO related errors + """ + ws_dir = workspace_dir(repo, branch) + filename = joinpaths(ws_dir, recipe_filename(recipe_name)) + if os.path.exists(filename): + os.unlink(filename)
+
+ +
+ +
+ + +
+
+ +
+ +
+ + + + + + + + + + + + \ No newline at end of file diff --git a/f30-branch/_modules/pylorax/base.html b/f30-branch/_modules/pylorax/base.html new file mode 100644 index 00000000..2c4bd02e --- /dev/null +++ b/f30-branch/_modules/pylorax/base.html @@ -0,0 +1,267 @@ + + + + + + + + + + + pylorax.base — Lorax 30.19 documentation + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + +
+ + + + + +
+ +
+ + + + + + + + + + + + + + + + + +
+ + + + +
+
+
+
+ +

Source code for pylorax.base

+#
+# base.py
+#
+# Copyright (C) 2009-2015 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>
+#
+
+from abc import ABCMeta, abstractmethod
+import sys
+
+import pylorax.output as output
+
+
+
[docs]class BaseLoraxClass(object, metaclass=ABCMeta): + @abstractmethod + def __init__(self): + self.output = output.LoraxOutput() + +
[docs] def pcritical(self, msg, fobj=sys.stdout): + self.output.critical(msg, fobj)
+ +
[docs] def perror(self, msg, fobj=sys.stdout): + self.output.error(msg, fobj)
+ +
[docs] def pwarning(self, msg, fobj=sys.stdout): + self.output.warning(msg, fobj)
+ +
[docs] def pinfo(self, msg, fobj=sys.stdout): + self.output.info(msg, fobj)
+ +
[docs] def pdebug(self, msg, fobj=sys.stdout): + self.output.debug(msg, fobj)
+ + +
[docs]class DataHolder(dict): + + def __init__(self, **kwargs): + dict.__init__(self) + + for attr, value in kwargs.items(): + self[attr] = value + + def __getattr__(self, attr): + if attr in self: + return self[attr] + else: + raise AttributeError + + def __setattr__(self, attr, value): + self[attr] = value + +
[docs] def copy(self): + return DataHolder(**dict.copy(self))
+
+ +
+ +
+ + +
+
+ +
+ +
+ + + + + + + + + + + + \ No newline at end of file diff --git a/f30-branch/_modules/pylorax/buildstamp.html b/f30-branch/_modules/pylorax/buildstamp.html new file mode 100644 index 00000000..c702905d --- /dev/null +++ b/f30-branch/_modules/pylorax/buildstamp.html @@ -0,0 +1,266 @@ + + + + + + + + + + + pylorax.buildstamp — Lorax 30.19 documentation + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + +
+ + + + + +
+ +
+ + + + + + + + + + + + + + + + + +
+ + + + +
+
+
+
+ +

Source code for pylorax.buildstamp

+#
+# buildstamp.py
+#
+# Copyright (C) 2010-2015 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>
+#
+
+import logging
+logger = logging.getLogger("pylorax.buildstamp")
+
+import datetime
+import os
+
+
+
[docs]class BuildStamp(object): + + def __init__(self, product, version, bugurl, isfinal, buildarch, variant=""): + self.product = product + self.version = version + self.bugurl = bugurl + self.isfinal = isfinal + self.variant = variant + + if 'SOURCE_DATE_EPOCH' in os.environ: + now = datetime.datetime.utcfromtimestamp( + int(os.environ['SOURCE_DATE_EPOCH'])) + else: + now = datetime.datetime.now() + now = now.strftime("%Y%m%d%H%M") + self.uuid = "{0}.{1}".format(now, buildarch) + +
[docs] def write(self, outfile): + # get lorax version + try: + import pylorax.version + except ImportError: + vernum = "devel" + else: + vernum = pylorax.version.num + + logger.info("writing .buildstamp file") + with open(outfile, "w") as fobj: + fobj.write("[Main]\n") + fobj.write("Product={0.product}\n".format(self)) + fobj.write("Version={0.version}\n".format(self)) + fobj.write("BugURL={0.bugurl}\n".format(self)) + fobj.write("IsFinal={0.isfinal}\n".format(self)) + fobj.write("UUID={0.uuid}\n".format(self)) + if self.variant: + fobj.write("Variant={0.variant}\n".format(self)) + fobj.write("[Compose]\n") + fobj.write("Lorax={0}\n".format(vernum))
+
+ +
+ +
+ + +
+
+ +
+ +
+ + + + + + + + + + + + \ No newline at end of file diff --git a/f30-branch/_modules/pylorax/cmdline.html b/f30-branch/_modules/pylorax/cmdline.html new file mode 100644 index 00000000..5890a822 --- /dev/null +++ b/f30-branch/_modules/pylorax/cmdline.html @@ -0,0 +1,515 @@ + + + + + + + + + + + pylorax.cmdline — Lorax 30.19 documentation + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + +
+ + + + + +
+ +
+ + + + + + + + + + + + + + + + + +
+ + + + +
+
+
+
+ +

Source code for pylorax.cmdline

+#
+# cmdline.py
+#
+# Copyright (C) 2016  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):  Brian C. Lane <bcl@redhat.com>
+
+import os
+import sys
+import argparse
+
+from pylorax import vernum
+
+version = "{0}-{1}".format(os.path.basename(sys.argv[0]), vernum)
+
+
[docs]def lorax_parser(dracut_default=""): + """ Return the ArgumentParser for lorax""" + + parser = argparse.ArgumentParser(description="Create the Anaconda boot.iso") + + # required arguments for image creation + required = parser.add_argument_group("required arguments") + required.add_argument("-p", "--product", help="product name", required=True, metavar="PRODUCT") + required.add_argument("-v", "--version", help="version identifier", required=True, metavar="VERSION") + required.add_argument("-r", "--release", help="release information", required=True, metavar="RELEASE") + required.add_argument("-s", "--source", help="source repository (may be listed multiple times)", + metavar="REPOSITORY", action="append", default=[]) + required.add_argument("--repo", help="source dnf repository file", type=os.path.abspath, + dest="repos", metavar="REPOSITORY", action="append", default=[]) + + # optional arguments + optional = parser.add_argument_group("optional arguments") + optional.add_argument("-m", "--mirrorlist", + help="mirrorlist repository (may be listed multiple times)", + metavar="REPOSITORY", action="append", default=[]) + optional.add_argument("-t", "--variant", default="", + help="variant name", metavar="VARIANT") + optional.add_argument("-b", "--bugurl", + help="bug reporting URL for the product", metavar="URL", + default="your distribution provided bug reporting tool") + optional.add_argument("--isfinal", help="", + action="store_true", default=False, dest="isfinal") + optional.add_argument("-c", "--config", default="/etc/lorax/lorax.conf", + help="config file", metavar="CONFIGFILE") + optional.add_argument("--proxy", default=None, + help="repo proxy url:port", metavar="HOST") + optional.add_argument("-i", "--installpkgs", default=[], + action="append", metavar="PACKAGE", + help="package glob to install before runtime-install.tmpl runs. (may be listed multiple times)") + optional.add_argument("-e", "--excludepkgs", default=[], + action="append", metavar="PACKAGE", + help="package glob to remove before runtime-install.tmpl runs. (may be listed multiple times)") + optional.add_argument("--buildarch", default=None, + help="build architecture", metavar="ARCH") + optional.add_argument("--volid", default=None, + help="volume id", metavar="VOLID") + optional.add_argument("--macboot", help="", + action="store_true", default=True, dest="domacboot") + optional.add_argument("--nomacboot", help="", + action="store_false", dest="domacboot") + optional.add_argument("--noupgrade", help="", + action="store_false", default=True, dest="doupgrade") + optional.add_argument("--logfile", default="./lorax.log", type=os.path.abspath, + help="Path to logfile") + optional.add_argument("--tmp", default="/var/tmp/lorax", + help="Top level temporary directory" ) + optional.add_argument("--cachedir", default=None, type=os.path.abspath, + help="DNF cache directory. Default is a temporary dir.") + optional.add_argument("--workdir", default=None, type=os.path.abspath, + help="Work directory, overrides --tmp. Default is a temporary dir under /var/tmp/lorax") + optional.add_argument("--force", default=False, action="store_true", + help="Run even when the destination directory exists") + optional.add_argument("--add-template", dest="add_templates", + action="append", help="Additional template for runtime image", + default=[]) + optional.add_argument("--add-template-var", dest="add_template_vars", + action="append", help="Set variable for runtime image template", + default=[]) + optional.add_argument("--add-arch-template", dest="add_arch_templates", + action="append", help="Additional template for architecture-specific image", + default=[]) + optional.add_argument("--add-arch-template-var", dest="add_arch_template_vars", + action="append", help="Set variable for architecture-specific image", + default=[]) + optional.add_argument("--noverify", action="store_false", default=True, dest="verify", + help="Do not verify the install root") + optional.add_argument("--sharedir", metavar="SHAREDIR", type=os.path.abspath, + help="Directory containing all the templates. Overrides config file sharedir") + optional.add_argument("--enablerepo", action="append", default=[], dest="enablerepos", + metavar="[repo]", help="Names of repos to enable") + optional.add_argument("--disablerepo", action="append", default=[], dest="disablerepos", + metavar="[repo]", help="Names of repos to disable") + optional.add_argument("--rootfs-size", type=int, default=2, + help="Size of root filesystem in GiB. Defaults to 2.") + optional.add_argument("--noverifyssl", action="store_true", default=False, + help="Do not verify SSL certificates") + optional.add_argument("--dnfplugin", action="append", default=[], dest="dnfplugins", + help="Enable a DNF plugin by name/glob, or * to enable all of them.") + optional.add_argument("--squashfs-only", action="store_true", default=False, + help="Use a plain squashfs filesystem for the runtime.") + + # dracut arguments + dracut_group = parser.add_argument_group("dracut arguments") + dracut_group.add_argument("--dracut-arg", action="append", dest="dracut_args", + help="Argument to pass to dracut when " + "rebuilding the initramfs. Pass this " + "once for each argument. NOTE: this " + "overrides the default. (default: %s)" % dracut_default) + + # add the show version option + parser.add_argument("-V", help="show program's version number and exit", + action="version", version=version) + + parser.add_argument("outputdir", help="Output directory", metavar="OUTPUTDIR", type=os.path.abspath) + + return parser
+ + +
[docs]def lmc_parser(dracut_default=""): + """ Return a ArgumentParser object for live-media-creator.""" + parser = argparse.ArgumentParser(description="Create Live Install Media", + fromfile_prefix_chars="@") + + # These are mutually exclusive, one is required + action = parser.add_mutually_exclusive_group(required=True) + action.add_argument("--make-iso", action="store_true", + help="Build a live iso") + action.add_argument("--make-disk", action="store_true", + help="Build a partitioned disk image") + action.add_argument("--make-fsimage", action="store_true", + help="Build a filesystem image") + action.add_argument("--make-appliance", action="store_true", + help="Build an appliance image and XML description") + action.add_argument("--make-ami", action="store_true", + help="Build an ami image") + action.add_argument("--make-tar", action="store_true", + help="Build a tar of the root filesystem") + action.add_argument("--make-tar-disk", action="store_true", + help="Build a tar of a partitioned disk image") + action.add_argument("--make-pxe-live", action="store_true", + help="Build a live pxe boot squashfs image") + action.add_argument("--make-ostree-live", action="store_true", + help="Build a live pxe boot squashfs image of Atomic Host") + action.add_argument("--make-oci", action="store_true", + help="Build an Open Container Initiative image") + action.add_argument("--make-vagrant", action="store_true", + help="Build a Vagrant Box image") + + parser.add_argument("--iso", type=os.path.abspath, + help="Anaconda installation .iso path to use for qemu") + parser.add_argument("--iso-only", action="store_true", + help="Remove all iso creation artifacts except the boot.iso, " + "combine with --iso-name to rename the boot.iso") + parser.add_argument("--iso-name", default=None, + help="Name of output iso file for --iso-only. Default is boot.iso") + parser.add_argument("--ks", action="append", type=os.path.abspath, + help="Kickstart file defining the install.") + parser.add_argument("--image-only", action="store_true", + help="Exit after creating fs/disk image.") + + parser.add_argument("--no-virt", action="store_true", + help="Run anaconda directly on host instead of using qemu") + parser.add_argument("--proxy", + help="proxy URL to use for the install") + parser.add_argument("--anaconda-arg", action="append", dest="anaconda_args", + help="Additional argument to pass to anaconda (no-virt " + "mode). Pass once for each argument") + parser.add_argument("--armplatform", + help="the platform to use when creating images for ARM, " + "i.e., highbank, mvebu, omap, tegra, etc.") + parser.add_argument("--location", default=None, type=os.path.abspath, + help="location of iso directory tree with initrd.img " + "and vmlinuz. Used to run qemu with a newer initrd " + "than the iso.") + + parser.add_argument("--logfile", default="./livemedia.log", + type=os.path.abspath, + help="Name and path for primary logfile, other logs will " + "be created in the same directory.") + parser.add_argument("--lorax-templates", default=None, + type=os.path.abspath, + help="Path to mako templates for lorax") + parser.add_argument("--tmp", default="/var/tmp", type=os.path.abspath, + help="Top level temporary directory") + parser.add_argument("--resultdir", default=None, dest="result_dir", + type=os.path.abspath, + help="Directory to copy the resulting images and iso into. " + "Defaults to the temporary working directory") + + parser.add_argument("--macboot", action="store_true", default=True, + dest="domacboot") + parser.add_argument("--nomacboot", action="store_false", + dest="domacboot") + + parser.add_argument("--extra-boot-args", default="", dest="extra_boot_args", + help="Extra arguments to add to the bootloader kernel cmdline in the templates") + + image_group = parser.add_argument_group("disk/fs image arguments") + image_group.add_argument("--disk-image", type=os.path.abspath, + help="Path to existing disk image to use for creating final image.") + image_group.add_argument("--keep-image", action="store_true", + help="Keep raw disk image after .iso creation") + image_group.add_argument("--fs-image", type=os.path.abspath, + help="Path to existing filesystem image to use for creating final image.") + image_group.add_argument("--image-name", default=None, + help="Name of output file to create. Used for tar, fs and disk image. Default is a random name.") + image_group.add_argument("--tar-disk-name", default=None, + help="Name of the archive member for make-tar-disk.") + image_group.add_argument("--fs-label", default="Anaconda", + help="Label to set on fsimage, default is 'Anaconda'") + image_group.add_argument("--image-size-align", type=int, default=0, + help="Create a disk image with a size that is a multiple of this value in MiB.") + image_group.add_argument("--image-type", default=None, + help="Create an image with qemu-img. See qemu-img --help for supported formats.") + image_group.add_argument("--qemu-arg", action="append", dest="qemu_args", default=[], + help="Arguments to pass to qemu-img. Pass once for each argument, they will be used for ALL calls to qemu-img.") + image_group.add_argument("--qcow2", action="store_true", + help="Create qcow2 image instead of raw sparse image when making disk images.") + image_group.add_argument("--qcow2-arg", action="append", dest="qemu_args", default=[], + help="Arguments to pass to qemu-img. Pass once for each argument, they will be used for ALL calls to qemu-img.") + image_group.add_argument("--compression", default="xz", + help="Compression binary for make-tar. xz, lzma, gzip, and bzip2 are supported. xz is the default.") + image_group.add_argument("--compress-arg", action="append", dest="compress_args", default=[], + help="Arguments to pass to compression. Pass once for each argument") + # Group of arguments for appliance creation + app_group = parser.add_argument_group("appliance arguments") + app_group.add_argument("--app-name", default=None, + help="Name of appliance to pass to template") + app_group.add_argument("--app-template", default=None, + help="Path to template to use for appliance data.") + app_group.add_argument("--app-file", default="appliance.xml", + help="Appliance template results file.") + + # Group of arguments to pass to qemu + virt_group = parser.add_argument_group("qemu arguments") + virt_group.add_argument("--ram", metavar="MEMORY", type=int, default=2048, + help="Memory to allocate for installer in megabytes.") + virt_group.add_argument("--vcpus", type=int, default=None, + help="Passed to qemu -smp command") + virt_group.add_argument("--vnc", + help="Passed to qemu -display command. eg. vnc=127.0.0.1:5, default is to " + "choose the first unused vnc port.") + virt_group.add_argument("--arch", default=None, + help="System arch to build for. Used to select qemu-system-* command. " + "Defaults to qemu-system-<arch>") + virt_group.add_argument("--kernel-args", + help="Additional argument to pass to the installation kernel") + virt_group.add_argument("--ovmf-path", default="/usr/share/edk2/ovmf/", + help="Path to OVMF firmware") + virt_group.add_argument("--virt-uefi", action="store_true", default=False, + help="Use OVMF firmware to boot the VM in UEFI mode") + virt_group.add_argument("--no-kvm", action="store_true", default=False, + help="Skip using kvm with qemu even if it is available.") + virt_group.add_argument("--with-rng", default="/dev/random", + help="RNG device for QEMU (none for no RNG)") + + # dracut arguments + dracut_group = parser.add_argument_group("dracut arguments") + dracut_group.add_argument("--dracut-arg", action="append", dest="dracut_args", + help="Argument to pass to dracut when " + "rebuilding the initramfs. Pass this " + "once for each argument. NOTE: this " + "overrides the default. (default: %s)" % dracut_default) + + # pxe to live arguments + pxelive_group = parser.add_argument_group("pxe to live arguments") + pxelive_group.add_argument("--live-rootfs-size", type=int, default=0, + help="Size of root filesystem of live image in GiB") + pxelive_group.add_argument("--live-rootfs-keep-size", action="store_true", + help="Keep the original size of root filesystem in live image") + + # OCI specific commands + oci_group = parser.add_argument_group("OCI arguments") + oci_group.add_argument("--oci-config", + help="config.json OCI configuration file") + oci_group.add_argument("--oci-runtime", + help="runtime.json OCI configuration file") + + # Vagrant specific commands + vagrant_group = parser.add_argument_group("Vagrant arguments") + vagrant_group.add_argument("--vagrant-metadata", + help="optional metadata.json file") + vagrant_group.add_argument("--vagrantfile", + help="optional vagrantfile") + + parser.add_argument("--title", default="Linux Live Media", + help="Substituted for @TITLE@ in bootloader config files") + parser.add_argument("--project", default="Linux", + help="substituted for @PROJECT@ in bootloader config files") + parser.add_argument("--releasever", default="29", + help="substituted for @VERSION@ in bootloader config files") + parser.add_argument("--volid", default=None, help="volume id") + parser.add_argument("--squashfs_args", + help="additional squashfs args") + parser.add_argument("--timeout", default=None, type=int, + help="Cancel installer after X minutes") + + # add the show version option + parser.add_argument("-V", help="show program's version number and exit", + action="version", version=version) + + return parser
+
+ +
+ +
+ + +
+
+ +
+ +
+ + + + + + + + + + + + \ No newline at end of file diff --git a/f30-branch/_modules/pylorax/creator.html b/f30-branch/_modules/pylorax/creator.html new file mode 100644 index 00000000..17c974e0 --- /dev/null +++ b/f30-branch/_modules/pylorax/creator.html @@ -0,0 +1,931 @@ + + + + + + + + + + + pylorax.creator — Lorax 30.7 documentation + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + +
+ + + + + +
+ +
+ + + + + + + + + + + + + + + + + +
+ + + + +
+
+
+
+ +

Source code for pylorax.creator

+#
+# Copyright (C) 2011-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("pylorax")
+
+import os
+import tempfile
+import subprocess
+import shutil
+import hashlib
+import glob
+
+# Use Mako templates for appliance builder descriptions
+from mako.template import Template
+from mako.exceptions import text_error_template
+
+# Use pykickstart to calculate disk image size
+from pykickstart.parser import KickstartParser
+from pykickstart.constants import KS_SHUTDOWN
+from pykickstart.version import makeVersion
+
+# Use the Lorax treebuilder branch for iso creation
+from pylorax import ArchData
+from pylorax.base import DataHolder
+from pylorax.executils import execWithRedirect, runcmd
+from pylorax.imgutils import PartitionMount
+from pylorax.imgutils import mount, umount, Mount
+from pylorax.imgutils import mksquashfs, mkrootfsimg
+from pylorax.imgutils import copytree
+from pylorax.installer import novirt_install, virt_install, InstallError
+from pylorax.treebuilder import TreeBuilder, RuntimeBuilder
+from pylorax.treebuilder import findkernels
+from pylorax.sysutils import joinpaths, remove
+
+
+# Default parameters for rebuilding initramfs, override with --dracut-args
+DRACUT_DEFAULT = ["--xz", "--add", "livenet dmsquash-live convertfs pollcdrom qemu qemu-net",
+                  "--omit", "plymouth", "--no-hostonly", "--debug", "--no-early-microcode"]
+
+RUNTIME = "images/install.img"
+
+
[docs]class FakeDNF(object): + """ + A minimal DNF object suitable for passing to RuntimeBuilder + + lmc uses RuntimeBuilder to run the arch specific iso creation + templates, so the the installroot config value is the important part of + this. Everything else should be a nop. + """ + def __init__(self, conf): + self.conf = conf + +
[docs] def reset(self): + pass
+ +
[docs]def is_image_mounted(disk_img): + """ + Check to see if the disk_img is mounted + + :returns: True if disk_img is in /proc/mounts + :rtype: bool + """ + with open("/proc/mounts") as mounts: + for mnt in mounts: + fields = mnt.split() + if len(fields) > 2 and fields[1] == disk_img: + return True + return False
+ +
[docs]def find_ostree_root(phys_root): + """ + Find root of ostree deployment + + :param str phys_root: Path to physical root + :returns: Relative path of ostree deployment root + :rtype: str + :raise Exception: More than one deployment roots were found + """ + ostree_root = "" + ostree_sysroots = glob.glob(joinpaths(phys_root, "ostree/boot.?/*/*/0")) + log.debug("ostree_sysroots = %s", ostree_sysroots) + if ostree_sysroots: + if len(ostree_sysroots) > 1: + raise Exception("Too many deployment roots found: %s" % ostree_sysroots) + ostree_root = os.path.relpath(ostree_sysroots[0], phys_root) + return ostree_root
+ +
[docs]def get_arch(mount_dir): + """ + Get the kernel arch + + :returns: Arch of first kernel found at mount_dir/boot/ or i386 + :rtype: str + """ + kernels = findkernels(mount_dir) + if not kernels: + return "i386" + return kernels[0].arch
+ +
[docs]def squashfs_args(opts): + """ Returns the compression type and args to use when making squashfs + + :param opts: ArgumentParser object with compression and compressopts + :returns: tuple of compression type and args + :rtype: tuple + """ + compression = opts.compression or "xz" + arch = ArchData(opts.arch or os.uname().machine) + if compression == "xz" and arch.bcj: + compressargs = ["-Xbcj", arch.bcj] + else: + compressargs = [] + return (compression, compressargs)
+ + +
[docs]def make_appliance(disk_img, name, template, outfile, networks=None, ram=1024, + vcpus=1, arch=None, title="Linux", project="Linux", + releasever="29"): + """ + Generate an appliance description file + + :param str disk_img: Full path of the disk image + :param str name: Name of the appliance, passed to the template + :param str template: Full path of Mako template + :param str outfile: Full path of file to write, using template + :param list networks: List of networks(str) from the kickstart + :param int ram: Ram, in MiB, passed to template. Default is 1024 + :param int vcpus: CPUs, passed to template. Default is 1 + :param str arch: CPU architecture. Default is 'x86_64' + :param str title: Title, passed to template. Default is 'Linux' + :param str project: Project, passed to template. Default is 'Linux' + :param str releasever: Release version, passed to template. Default is 29 + """ + if not (disk_img and template and outfile): + return None + + log.info("Creating appliance definition using %s", template) + + if not arch: + arch = "x86_64" + + log.info("Calculating SHA256 checksum of %s", disk_img) + sha256 = hashlib.sha256() + with open(disk_img, "rb") as f: + while True: + data = f.read(1024**2) + if not data: + break + sha256.update(data) + log.info("SHA256 of %s is %s", disk_img, sha256.hexdigest()) + disk_info = DataHolder(name=os.path.basename(disk_img), format="raw", + checksum_type="sha256", checksum=sha256.hexdigest()) + try: + result = Template(filename=template).render(disks=[disk_info], name=name, + arch=arch, memory=ram, vcpus=vcpus, networks=networks, + title=title, project=project, releasever=releasever) + except Exception: + log.error(text_error_template().render()) + raise + + with open(outfile, "w") as f: + f.write(result)
+ + +
[docs]def make_runtime(opts, mount_dir, work_dir, size=None): + """ + Make the squashfs image from a directory + + :param opts: options passed to livemedia-creator + :type opts: argparse options + :param str mount_dir: Directory tree to compress + :param str work_dir: Output compressed image to work_dir+images/install.img + :param int size: Size of disk image, in GiB + """ + kernel_arch = get_arch(mount_dir) + + # Fake dnf object + fake_dbo = FakeDNF(conf=DataHolder(installroot=mount_dir)) + # Fake arch with only basearch set + arch = ArchData(kernel_arch) + # TODO: Need to get release info from someplace... + product = DataHolder(name=opts.project, version=opts.releasever, release="", + variant="", bugurl="", isfinal=False) + + # This is a mounted image partition, cannot hardlink to it, so just use it + # symlink mount_dir/images to work_dir/images so we don't run out of space + os.makedirs(joinpaths(work_dir, "images")) + + rb = RuntimeBuilder(product, arch, fake_dbo) + compression, compressargs = squashfs_args(opts) + log.info("Creating runtime") + rb.create_ext4_runtime(joinpaths(work_dir, RUNTIME), size=size, + compression=compression, compressargs=compressargs)
+ + +
[docs]def rebuild_initrds_for_live(opts, sys_root_dir, results_dir): + """ + Rebuild intrds for pxe live image (root=live:http://) + + :param opts: options passed to livemedia-creator + :type opts: argparse options + :param str sys_root_dir: Path to root of the system + :param str results_dir: Path of directory for storing results + """ + if not opts.dracut_args: + dracut_args = DRACUT_DEFAULT + else: + dracut_args = [] + for arg in opts.dracut_args: + dracut_args += arg.split(" ", 1) + log.info("dracut args = %s", dracut_args) + + dracut = ["dracut", "--nomdadmconf", "--nolvmconf"] + dracut_args + + kdir = "boot" + if opts.ostree: + kernels_dir = glob.glob(joinpaths(sys_root_dir, "boot/ostree/*")) + if kernels_dir: + kdir = os.path.relpath(kernels_dir[0], sys_root_dir) + + kernels = [kernel for kernel in findkernels(sys_root_dir, kdir)] + if not kernels: + raise Exception("No initrds found, cannot rebuild_initrds") + + # Hush some dracut warnings. TODO: bind-mount proc in place? + open(joinpaths(sys_root_dir,"/proc/modules"),"w") + + if opts.ostree: + # Dracut assumes to have some dirs in disk image + # /var/tmp for temp files + vartmp_dir = joinpaths(sys_root_dir, "var/tmp") + if not os.path.isdir(vartmp_dir): + os.mkdir(vartmp_dir) + # /root (maybe not fatal) + root_dir = joinpaths(sys_root_dir, "var/roothome") + if not os.path.isdir(root_dir): + os.mkdir(root_dir) + # /tmp (maybe not fatal) + tmp_dir = joinpaths(sys_root_dir, "sysroot/tmp") + if not os.path.isdir(tmp_dir): + os.mkdir(tmp_dir) + + # Write the new initramfs directly to the results directory + os.mkdir(joinpaths(sys_root_dir, "results")) + mount(results_dir, opts="bind", mnt=joinpaths(sys_root_dir, "results")) + # Dracut runs out of space inside the minimal rootfs image + mount("/var/tmp", opts="bind", mnt=joinpaths(sys_root_dir, "var/tmp")) + for kernel in kernels: + if hasattr(kernel, "initrd"): + outfile = os.path.basename(kernel.initrd.path) + else: + # Construct an initrd from the kernel name + outfile = os.path.basename(kernel.path.replace("vmlinuz-", "initrd-") + ".img") + log.info("rebuilding %s", outfile) + + kver = kernel.version + + cmd = dracut + ["/results/"+outfile, kver] + runcmd(cmd, root=sys_root_dir) + + shutil.copy2(joinpaths(sys_root_dir, kernel.path), results_dir) + umount(joinpaths(sys_root_dir, "var/tmp"), delete=False) + umount(joinpaths(sys_root_dir, "results"), delete=False) + os.unlink(joinpaths(sys_root_dir,"/proc/modules"))
+ +
[docs]def create_pxe_config(template, images_dir, live_image_name, add_args = None): + """ + Create template for pxe to live configuration + + :param str images_dir: Path of directory with images to be used + :param str live_image_name: Name of live rootfs image file + :param list add_args: Arguments to be added to initrd= pxe config + """ + + add_args = add_args or [] + + kernels = [kernel for kernel in findkernels(images_dir, kdir="") + if hasattr(kernel, "initrd")] + if not kernels: + return + + kernel = kernels[0] + + add_args_str = " ".join(add_args) + + + try: + result = Template(filename=template).render(kernel=kernel.path, + initrd=kernel.initrd.path, liveimg=live_image_name, + addargs=add_args_str) + except Exception: + log.error(text_error_template().render()) + raise + + with open (joinpaths(images_dir, "PXE_CONFIG"), "w") as f: + f.write(result)
+ + +
[docs]def make_livecd(opts, mount_dir, work_dir): + """ + Take the content from the disk image and make a livecd out of it + + :param opts: options passed to livemedia-creator + :type opts: argparse options + :param str mount_dir: Directory tree to compress + :param str work_dir: Output compressed image to work_dir+images/install.img + + This uses wwood's squashfs live initramfs method: + * put the real / into LiveOS/rootfs.img + * make a squashfs of the LiveOS/rootfs.img tree + * This is loaded by dracut when the cmdline is passed to the kernel: + root=live:CDLABEL=<volid> rd.live.image + """ + kernel_arch = get_arch(mount_dir) + + arch = ArchData(kernel_arch) + # TODO: Need to get release info from someplace... + product = DataHolder(name=opts.project, version=opts.releasever, release="", + variant="", bugurl="", isfinal=False) + + # Link /images to work_dir/images to make the templates happy + if os.path.islink(joinpaths(mount_dir, "images")): + os.unlink(joinpaths(mount_dir, "images")) + execWithRedirect("/bin/ln", ["-s", joinpaths(work_dir, "images"), + joinpaths(mount_dir, "images")]) + + # The templates expect the config files to be in /tmp/config_files + # I think these should be release specific, not from lorax, but for now + configdir = joinpaths(opts.lorax_templates,"live/config_files/") + configdir_path = "tmp/config_files" + fullpath = joinpaths(mount_dir, configdir_path) + if os.path.exists(fullpath): + remove(fullpath) + copytree(configdir, fullpath) + + isolabel = opts.volid or "{0.name}-{0.version}-{1.basearch}".format(product, arch) + if len(isolabel) > 32: + isolabel = isolabel[:32] + log.warning("Truncating isolabel to 32 chars: %s", isolabel) + + tb = TreeBuilder(product=product, arch=arch, domacboot=opts.domacboot, + inroot=mount_dir, outroot=work_dir, + runtime=RUNTIME, isolabel=isolabel, + templatedir=joinpaths(opts.lorax_templates,"live/")) + log.info("Rebuilding initrds") + if not opts.dracut_args: + dracut_args = DRACUT_DEFAULT + else: + dracut_args = [] + for arg in opts.dracut_args: + dracut_args += arg.split(" ", 1) + log.info("dracut args = %s", dracut_args) + tb.rebuild_initrds(add_args=dracut_args) + log.info("Building boot.iso") + tb.build() + + return work_dir
+ +
[docs]def mount_boot_part_over_root(img_mount): + """ + Mount boot partition to /boot of root fs mounted in img_mount + + Used for OSTree so it finds deployment configurations on live rootfs + + param img_mount: object with mounted disk image root partition + type img_mount: imgutils.PartitionMount + """ + root_dir = img_mount.mount_dir + is_boot_part = lambda dir: os.path.exists(dir+"/loader.0") + tmp_mount_dir = tempfile.mkdtemp(prefix="lmc-tmpdir-") + sysroot_boot_dir = None + for dev, _size in img_mount.loop_devices: + if dev is img_mount.mount_dev: + continue + try: + mount("/dev/mapper/"+dev, mnt=tmp_mount_dir) + if is_boot_part(tmp_mount_dir): + umount(tmp_mount_dir) + sysroot_boot_dir = joinpaths(root_dir, "boot") + mount("/dev/mapper/"+dev, mnt=sysroot_boot_dir) + break + else: + umount(tmp_mount_dir) + except subprocess.CalledProcessError as e: + log.debug("Looking for boot partition error: %s", e) + remove(tmp_mount_dir) + return sysroot_boot_dir
+ +
[docs]def make_squashfs(opts, disk_img, work_dir): + """ + Create a squashfs image of an unpartitioned filesystem disk image + + :param str disk_img: Path to the unpartitioned filesystem disk image + :param str work_dir: Output compressed image to work_dir+images/install.img + :param str compression: Compression type to use + :returns: True if squashfs creation was successful. False if there was an error. + :rtype: bool + + Take disk_img and put it into LiveOS/rootfs.img and squashfs this + tree into work_dir+images/install.img + + fsck.ext4 is run on the disk image to make sure there are no errors and to zero + out any deleted blocks to make it compress better. If this fails for any reason + it will return False and log the error. + """ + # Make sure free blocks are actually zeroed so it will compress + rc = execWithRedirect("/usr/sbin/fsck.ext4", ["-y", "-f", "-E", "discard", disk_img]) + if rc != 0: + log.error("Problem zeroing free blocks of %s", disk_img) + return False + + liveos_dir = joinpaths(work_dir, "runtime/LiveOS") + os.makedirs(liveos_dir) + os.makedirs(os.path.dirname(joinpaths(work_dir, RUNTIME))) + + rc = execWithRedirect("/bin/ln", [disk_img, joinpaths(liveos_dir, "rootfs.img")]) + if rc != 0: + shutil.copy2(disk_img, joinpaths(liveos_dir, "rootfs.img")) + + compression, compressargs = squashfs_args(opts) + mksquashfs(joinpaths(work_dir, "runtime"), + joinpaths(work_dir, RUNTIME), compression, compressargs) + remove(joinpaths(work_dir, "runtime")) + return True
+ +
[docs]def calculate_disk_size(opts, ks): + """ Calculate the disk size from the kickstart + + :param opts: options passed to livemedia-creator + :type opts: argparse options + :param str ks: Path to the kickstart to use for the installation + :returns: Disk size in MiB + :rtype: int + """ + # Disk size for a filesystem image should only be the size of / + # to prevent surprises when using the same kickstart for different installations. + unique_partitions = dict((p.mountpoint, p) for p in ks.handler.partition.partitions) + if opts.no_virt and (opts.make_iso or opts.make_fsimage): + disk_size = 2 + sum(p.size for p in unique_partitions.values() if p.mountpoint == "/") + else: + disk_size = 2 + sum(p.size for p in unique_partitions.values()) + log.info("Using disk size of %sMiB", disk_size) + return disk_size
+ +
[docs]def make_image(opts, ks): + """ + Install to a disk image + + :param opts: options passed to livemedia-creator + :type opts: argparse options + :param str ks: Path to the kickstart to use for the installation + :returns: Path of the image created + :rtype: str + + Use qemu+boot.iso or anaconda to install to a disk image. + """ + if opts.image_name: + disk_img = joinpaths(opts.result_dir, opts.image_name) + else: + disk_img = tempfile.mktemp(prefix="lmc-disk-", suffix=".img", dir=opts.result_dir) + log.info("disk_img = %s", disk_img) + disk_size = calculate_disk_size(opts, ks) + try: + if opts.no_virt: + novirt_install(opts, disk_img, disk_size) + else: + install_log = os.path.abspath(os.path.dirname(opts.logfile))+"/virt-install.log" + log.info("install_log = %s", install_log) + + virt_install(opts, install_log, disk_img, disk_size) + except InstallError as e: + log.error("Install failed: %s", e) + if not opts.keep_image and os.path.exists(disk_img): + log.info("Removing bad disk image") + os.unlink(disk_img) + raise + + log.info("Disk Image install successful") + return disk_img
+ + +
[docs]def make_live_images(opts, work_dir, disk_img): + """ + Create live images from direcory or rootfs image + + :param opts: options passed to livemedia-creator + :type opts: argparse options + :param str work_dir: Directory for storing results + :param str disk_img: Path to disk image (fsimage or partitioned) + :returns: Path of directory with created images or None + :rtype: str + + fsck.ext4 is run on the rootfs_image to make sure there are no errors and to zero + out any deleted blocks to make it compress better. If this fails for any reason + it will return None and log the error. + """ + sys_root = "" + + squashfs_root_dir = joinpaths(work_dir, "squashfs_root") + liveos_dir = joinpaths(squashfs_root_dir, "LiveOS") + os.makedirs(liveos_dir) + rootfs_img = joinpaths(liveos_dir, "rootfs.img") + + if opts.fs_image or opts.no_virt: + # Find the ostree root in the fsimage + if opts.ostree: + with Mount(disk_img, opts="loop") as mnt_dir: + sys_root = find_ostree_root(mnt_dir) + + # Try to hardlink the image, if that fails, copy it + rc = execWithRedirect("/bin/ln", [disk_img, rootfs_img]) + if rc != 0: + shutil.copy2(disk_img, rootfs_img) + else: + is_root_part = None + if opts.ostree: + is_root_part = lambda dir: os.path.exists(dir+"/ostree/deploy") + with PartitionMount(disk_img, mount_ok=is_root_part) as img_mount: + if img_mount and img_mount.mount_dir: + try: + mounted_sysroot_boot_dir = None + if opts.ostree: + sys_root = find_ostree_root(img_mount.mount_dir) + mounted_sysroot_boot_dir = mount_boot_part_over_root(img_mount) + if opts.live_rootfs_keep_size: + size = img_mount.mount_size / 1024**3 + else: + size = opts.live_rootfs_size or None + log.info("Creating live rootfs image") + mkrootfsimg(img_mount.mount_dir, rootfs_img, "LiveOS", size=size, sysroot=sys_root) + finally: + if mounted_sysroot_boot_dir: + umount(mounted_sysroot_boot_dir) + log.debug("sys_root = %s", sys_root) + + # Make sure free blocks are actually zeroed so it will compress + rc = execWithRedirect("/usr/sbin/fsck.ext4", ["-y", "-f", "-E", "discard", rootfs_img]) + if rc != 0: + log.error("Problem zeroing free blocks of %s", disk_img) + return None + + log.info("Packing live rootfs image") + add_pxe_args = [] + live_image_name = "live-rootfs.squashfs.img" + compression, compressargs = squashfs_args(opts) + mksquashfs(squashfs_root_dir, joinpaths(work_dir, live_image_name), compression, compressargs) + + log.info("Rebuilding initramfs for live") + with Mount(rootfs_img, opts="loop") as mnt_dir: + try: + mount(joinpaths(mnt_dir, "boot"), opts="bind", mnt=joinpaths(mnt_dir, sys_root, "boot")) + rebuild_initrds_for_live(opts, joinpaths(mnt_dir, sys_root), work_dir) + finally: + umount(joinpaths(mnt_dir, sys_root, "boot"), delete=False) + + remove(squashfs_root_dir) + + if opts.ostree: + add_pxe_args.append("ostree=/%s" % sys_root) + template = joinpaths(opts.lorax_templates, "pxe-live/pxe-config.tmpl") + create_pxe_config(template, work_dir, live_image_name, add_pxe_args) + + return work_dir
+ +
[docs]def run_creator(opts, callback_func=None): + """Run the image creator process + + :param opts: Commandline options to control the process + :type opts: Either a DataHolder or ArgumentParser + :returns: The result directory and the disk image path. + :rtype: Tuple of str + + This function takes the opts arguments and creates the selected output image. + See the cmdline --help for livemedia-creator for the possible options + + (Yes, this is not ideal, but we can fix that later) + """ + result_dir = None + + # Parse the kickstart + if opts.ks: + ks_version = makeVersion() + ks = KickstartParser(ks_version, errorsAreFatal=False, missingIncludeIsFatal=False) + ks.readKickstart(opts.ks[0]) + + # live iso usually needs dracut-live so warn the user if it is missing + if opts.ks and opts.make_iso: + if "dracut-live" not in ks.handler.packages.packageList: + log.error("dracut-live package is missing from the kickstart.") + raise RuntimeError("dracut-live package is missing from the kickstart.") + + # Make the disk or filesystem image + if not opts.disk_image and not opts.fs_image: + if not opts.ks: + raise RuntimeError("Image creation requires a kickstart file") + + errors = [] + if opts.no_virt and ks.handler.method.method not in ("url", "nfs") \ + and not ks.handler.ostreesetup.seen: + errors.append("Only url, nfs and ostreesetup install methods are currently supported." + "Please fix your kickstart file." ) + + if ks.handler.method.method in ("url", "nfs") and not ks.handler.network.seen: + errors.append("The kickstart must activate networking if " + "the url or nfs install method is used.") + + if ks.handler.displaymode.displayMode is not None: + errors.append("The kickstart must not set a display mode (text, cmdline, " + "graphical), this will interfere with livemedia-creator.") + + if opts.make_fsimage or (opts.make_pxe_live and opts.no_virt): + # Make sure the kickstart isn't using autopart and only has a / mountpoint + part_ok = not any(p for p in ks.handler.partition.partitions + if p.mountpoint not in ["/", "swap"]) + if not part_ok or ks.handler.autopart.seen: + errors.append("Filesystem images must use a single / part, not autopart or " + "multiple partitions. swap is allowed but not used.") + + if not opts.no_virt and ks.handler.reboot.action != KS_SHUTDOWN: + errors.append("The kickstart must include shutdown when using virt installation.") + + if errors: + list(log.error(e) for e in errors) + raise RuntimeError("\n".join(errors)) + + # Make the image. Output of this is either a partitioned disk image or a fsimage + try: + disk_img = make_image(opts, ks) + except InstallError as e: + log.error("ERROR: Image creation failed: %s", e) + raise RuntimeError("Image creation failed: %s" % e) + + if opts.image_only: + return (result_dir, disk_img) + + if opts.make_iso: + work_dir = tempfile.mkdtemp(prefix="lmc-work-") + log.info("working dir is %s", work_dir) + + if (opts.fs_image or opts.no_virt) and not opts.disk_image: + # Create iso from a filesystem image + disk_img = opts.fs_image or disk_img + + if not make_squashfs(opts, disk_img, work_dir): + log.error("squashfs.img creation failed") + raise RuntimeError("squashfs.img creation failed") + + with Mount(disk_img, opts="loop") as mount_dir: + result_dir = make_livecd(opts, mount_dir, work_dir) + else: + # Create iso from a partitioned disk image + disk_img = opts.disk_image or disk_img + with PartitionMount(disk_img) as img_mount: + if img_mount and img_mount.mount_dir: + make_runtime(opts, img_mount.mount_dir, work_dir, calculate_disk_size(opts, ks)/1024.0) + result_dir = make_livecd(opts, img_mount.mount_dir, work_dir) + + # --iso-only removes the extra build artifacts, keeping only the boot.iso + if opts.iso_only and result_dir: + boot_iso = joinpaths(result_dir, "images/boot.iso") + if not os.path.exists(boot_iso): + log.error("%s is missing, skipping --iso-only.", boot_iso) + else: + iso_dir = tempfile.mkdtemp(prefix="lmc-result-") + dest_file = joinpaths(iso_dir, opts.iso_name or "boot.iso") + shutil.move(boot_iso, dest_file) + shutil.rmtree(result_dir) + result_dir = iso_dir + + # cleanup the mess + # cleanup work_dir? + if disk_img and not (opts.keep_image or opts.disk_image or opts.fs_image): + os.unlink(disk_img) + log.info("Disk image erased") + disk_img = None + elif opts.make_appliance: + if not opts.ks: + networks = [] + else: + networks = ks.handler.network.network + make_appliance(opts.disk_image or disk_img, opts.app_name, + opts.app_template, opts.app_file, networks, opts.ram, + opts.vcpus or 1, opts.arch, opts.title, opts.project, opts.releasever) + elif opts.make_pxe_live: + work_dir = tempfile.mkdtemp(prefix="lmc-work-") + log.info("working dir is %s", work_dir) + disk_img = opts.fs_image or opts.disk_image or disk_img + log.debug("disk image is %s", disk_img) + + result_dir = make_live_images(opts, work_dir, disk_img) + if result_dir is None: + log.error("Creating PXE live image failed.") + raise RuntimeError("Creating PXE live image failed.") + + if opts.result_dir != opts.tmp and result_dir: + copytree(result_dir, opts.result_dir, preserve=False) + shutil.rmtree(result_dir) + result_dir = None + + return (result_dir, disk_img)
+
+ +
+ +
+ + +
+
+ +
+ +
+ + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/f30-branch/_modules/pylorax/decorators.html b/f30-branch/_modules/pylorax/decorators.html new file mode 100644 index 00000000..0b9e8225 --- /dev/null +++ b/f30-branch/_modules/pylorax/decorators.html @@ -0,0 +1,230 @@ + + + + + + + + + + + pylorax.decorators — Lorax 30.19 documentation + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + +
+ + + + + +
+ +
+ + + + + + + + + + + + + + + + + +
+ + + + +
+
+
+
+ +

Source code for pylorax.decorators

+#
+# decorators.py
+#
+# Copyright (C) 2009-2015 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>
+#
+
+
[docs]def singleton(cls): + instances = {} + + def get_instance(): + if cls not in instances: + instances[cls] = cls() + return instances[cls] + + return get_instance
+
+ +
+ +
+ + +
+
+ +
+ +
+ + + + + + + + + + + + \ No newline at end of file diff --git a/f30-branch/_modules/pylorax/discinfo.html b/f30-branch/_modules/pylorax/discinfo.html new file mode 100644 index 00000000..39994121 --- /dev/null +++ b/f30-branch/_modules/pylorax/discinfo.html @@ -0,0 +1,245 @@ + + + + + + + + + + + pylorax.discinfo — Lorax 30.19 documentation + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + +
+ + + + + +
+ +
+ + + + + + + + + + + + + + + + + +
+ + + + +
+
+
+
+ +

Source code for pylorax.discinfo

+#
+# discinfo.py
+#
+# Copyright (C) 2010-2015  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>
+#
+
+import logging
+logger = logging.getLogger("pylorax.discinfo")
+
+import os
+import time
+
+
+
[docs]class DiscInfo(object): + + def __init__(self, release, basearch): + self.release = release + self.basearch = basearch + +
[docs] def write(self, outfile): + if 'SOURCE_DATE_EPOCH' in os.environ: + timestamp = int(os.environ['SOURCE_DATE_EPOCH']) + else: + timestamp = time.time() + + logger.info("writing .discinfo file") + with open(outfile, "w") as fobj: + fobj.write("{0:f}\n".format(timestamp)) + fobj.write("{0.release}\n".format(self)) + fobj.write("{0.basearch}\n".format(self))
+
+ +
+ +
+ + +
+
+ +
+ +
+ + + + + + + + + + + + \ No newline at end of file diff --git a/f30-branch/_modules/pylorax/dnfbase.html b/f30-branch/_modules/pylorax/dnfbase.html new file mode 100644 index 00000000..91289fb5 --- /dev/null +++ b/f30-branch/_modules/pylorax/dnfbase.html @@ -0,0 +1,385 @@ + + + + + + + + + + + pylorax.dnfbase — Lorax 30.19 documentation + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + +
+ + + + + +
+ +
+ + + + + + + + + + + + + + + + + +
+ + + + +
+
+
+
+ +

Source code for pylorax.dnfbase

+# 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("pylorax")
+
+import dnf
+import os
+import shutil
+
+from pylorax import DEFAULT_PLATFORM_ID
+from pylorax.sysutils import flatconfig
+
+
[docs]def get_dnf_base_object(installroot, sources, mirrorlists=None, repos=None, + enablerepos=None, disablerepos=None, + tempdir="/var/tmp", proxy=None, releasever="29", + cachedir=None, logdir=None, sslverify=True, dnfplugins=None): + """ Create a dnf Base object and setup the repositories and installroot + + :param string installroot: Full path to the installroot + :param list sources: List of source repo urls to use for the installation + :param list enablerepos: List of repo names to enable + :param list disablerepos: List of repo names to disable + :param list mirrorlist: List of mirrors to use + :param string tempdir: Path of temporary directory + :param string proxy: http proxy to use when fetching packages + :param string releasever: Release version to pass to dnf + :param string cachedir: Directory to use for caching packages + :param bool noverifyssl: Set to True to ignore the CA of ssl certs. eg. use self-signed ssl for https repos. + + If tempdir is not set /var/tmp is used. + If cachedir is None a dnf.cache directory is created inside tmpdir + """ + def sanitize_repo(repo): + """Convert bare paths to file:/// URIs, and silently reject protocols unhandled by yum""" + if repo.startswith("/"): + return "file://{0}".format(repo) + elif any(repo.startswith(p) for p in ('http://', 'https://', 'ftp://', 'file://')): + return repo + else: + return None + + mirrorlists = mirrorlists or [] + + # sanitize the repositories + sources = list(sanitize_repo(r) for r in sources) + mirrorlists = list(sanitize_repo(r) for r in mirrorlists) + + # remove invalid repositories + sources = list(r for r in sources if r) + mirrorlists = list(r for r in mirrorlists if r) + + if not cachedir: + cachedir = os.path.join(tempdir, "dnf.cache") + if not os.path.isdir(cachedir): + os.mkdir(cachedir) + + if not logdir: + logdir = os.path.join(tempdir, "dnf.logs") + if not os.path.isdir(logdir): + os.mkdir(logdir) + + dnfbase = dnf.Base() + # Enable DNF pluings + # NOTE: These come from the HOST system's environment + if dnfplugins: + if dnfplugins[0] == "*": + # Enable them all + dnfbase.init_plugins() + else: + # Only enable the listed plugins + dnfbase.init_plugins(disabled_glob=["*"], enable_plugins=dnfplugins) + conf = dnfbase.conf + conf.logdir = logdir + conf.cachedir = cachedir + + conf.install_weak_deps = False + conf.releasever = releasever + conf.installroot = installroot + conf.prepend_installroot('persistdir') + # this is a weird 'AppendOption' thing that, when you set it, + # actually appends. Doing this adds 'nodocs' to the existing list + # of values, over in libdnf, it does not replace the existing values. + conf.tsflags = ['nodocs'] + + if proxy: + conf.proxy = proxy + + if sslverify == False: + conf.sslverify = False + + # DNF 3.2 needs to have module_platform_id set, otherwise depsolve won't work correctly + if not os.path.exists("/etc/os-release"): + log.warning("/etc/os-release is missing, cannot determine platform id, falling back to %s", DEFAULT_PLATFORM_ID) + platform_id = DEFAULT_PLATFORM_ID + else: + os_release = flatconfig("/etc/os-release") + platform_id = os_release.get("PLATFORM_ID", DEFAULT_PLATFORM_ID) + log.info("Using %s for module_platform_id", platform_id) + conf.module_platform_id = platform_id + + # Add .repo files + if repos: + reposdir = os.path.join(tempdir, "dnf.repos") + if not os.path.isdir(reposdir): + os.mkdir(reposdir) + for r in repos: + shutil.copy2(r, reposdir) + conf.reposdir = [reposdir] + dnfbase.read_all_repos() + + # add the sources + for i, r in enumerate(sources): + if "SRPM" in r or "srpm" in r: + log.info("Skipping source repo: %s", r) + continue + repo_name = "lorax-repo-%d" % i + repo = dnf.repo.Repo(repo_name, conf) + repo.baseurl = [r] + if proxy: + repo.proxy = proxy + repo.enable() + dnfbase.repos.add(repo) + log.info("Added '%s': %s", repo_name, r) + log.info("Fetching metadata...") + try: + repo.load() + except dnf.exceptions.RepoError as e: + log.error("Error fetching metadata for %s: %s", repo_name, e) + return None + + # add the mirrorlists + for i, r in enumerate(mirrorlists): + if "SRPM" in r or "srpm" in r: + log.info("Skipping source repo: %s", r) + continue + repo_name = "lorax-mirrorlist-%d" % i + repo = dnf.repo.Repo(repo_name, conf) + repo.mirrorlist = r + if proxy: + repo.proxy = proxy + repo.enable() + dnfbase.repos.add(repo) + log.info("Added '%s': %s", repo_name, r) + log.info("Fetching metadata...") + try: + repo.load() + except dnf.exceptions.RepoError as e: + log.error("Error fetching metadata for %s: %s", repo_name, e) + return None + + # Enable repos listed on the cmdline + for r in enablerepos: + repolist = dnfbase.repos.get_matching(r) + if not repolist: + log.warning("%s is an unknown repo, not enabling it", r) + else: + repolist.enable() + log.info("Enabled repo %s", r) + + # Disable repos listed on the cmdline + for r in disablerepos: + repolist = dnfbase.repos.get_matching(r) + if not repolist: + log.warning("%s is an unknown repo, not disabling it", r) + else: + repolist.disable() + log.info("Disabled repo %s", r) + + dnfbase.fill_sack(load_system_repo=False) + dnfbase.read_comps() + + return dnfbase
+
+ +
+ +
+ + +
+
+ +
+ +
+ + + + + + + + + + + + \ No newline at end of file diff --git a/f30-branch/_modules/pylorax/dnfhelper.html b/f30-branch/_modules/pylorax/dnfhelper.html new file mode 100644 index 00000000..c55155ad --- /dev/null +++ b/f30-branch/_modules/pylorax/dnfhelper.html @@ -0,0 +1,310 @@ + + + + + + + + + + + pylorax.dnfhelper — Lorax 30.19 documentation + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + +
+ + + + + +
+ +
+ + + + + + + + + + + + + + + + + +
+ + + + +
+
+
+
+ +

Source code for pylorax.dnfhelper

+#
+# dnfhelper.py
+#
+# Copyright (C) 2010-2015 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>
+#                     Brian C. Lane <bcl@redhat.com>
+#
+
+import logging
+logger = logging.getLogger("pylorax.dnfhelper")
+import dnf
+import dnf.transaction
+import collections
+import time
+import pylorax.output as output
+
+__all__ = ['LoraxDownloadCallback', 'LoraxRpmCallback']
+
+def _paced(fn):
+    """Execute `fn` no more often then every 2 seconds."""
+    def paced_fn(self, *args):
+        now = time.time()
+        if now - self.last_time < 2:
+            return
+        self.last_time = now
+        return fn(self, *args)
+    return paced_fn
+
+
+
[docs]class LoraxDownloadCallback(dnf.callback.DownloadProgress): + def __init__(self): + self.downloads = collections.defaultdict(int) + self.last_time = time.time() + self.total_files = 0 + self.total_size = 0 + + self.pkgno = 0 + self.total = 0 + + self.output = output.LoraxOutput() + + @_paced + def _update(self): + msg = "Downloading %(pkgno)s / %(total_files)s RPMs, " \ + "%(downloaded)s / %(total_size)s (%(percent)d%%) done.\n" + downloaded = sum(self.downloads.values()) + vals = { + 'downloaded' : downloaded, + 'percent' : int(100 * downloaded/self.total_size), + 'pkgno' : self.pkgno, + 'total_files' : self.total_files, + 'total_size' : self.total_size + } + self.output.write(msg % vals) + +
[docs] def end(self, payload, status, msg): + nevra = str(payload) + if status is dnf.callback.STATUS_OK: + self.downloads[nevra] = payload.download_size + self.pkgno += 1 + self._update() + return + logger.critical("Failed to download '%s': %d - %s", nevra, status, msg)
+ +
[docs] def progress(self, payload, done): + nevra = str(payload) + self.downloads[nevra] = done + self._update()
+ + # dnf 2.5.0 adds a new argument, accept it if it is passed + # pylint: disable=arguments-differ +
[docs] def start(self, total_files, total_size, total_drpms=0): + self.total_files = total_files + self.total_size = total_size
+ + +
[docs]class LoraxRpmCallback(dnf.callback.TransactionProgress): + def __init__(self): + super(LoraxRpmCallback, self).__init__() + self._last_ts = None + +
[docs] def progress(self, package, action, ti_done, ti_total, ts_done, ts_total): + if action == dnf.transaction.PKG_INSTALL: + # do not report same package twice + if self._last_ts == ts_done: + return + self._last_ts = ts_done + + msg = '(%d/%d) %s' % (ts_done, ts_total, package) + logger.info(msg) + elif action == dnf.transaction.TRANS_POST: + msg = "Performing post-installation setup tasks" + logger.info(msg)
+ +
[docs] def error(self, message): + logger.warning(message)
+
+ +
+ +
+ + +
+
+ +
+ +
+ + + + + + + + + + + + \ No newline at end of file diff --git a/f30-branch/_modules/pylorax/executils.html b/f30-branch/_modules/pylorax/executils.html new file mode 100644 index 00000000..ec109408 --- /dev/null +++ b/f30-branch/_modules/pylorax/executils.html @@ -0,0 +1,549 @@ + + + + + + + + + + + pylorax.executils — Lorax 30.19 documentation + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + +
+ + + + + +
+ +
+ + + + + + + + + + + + + + + + + +
+ + + + +
+
+
+
+ +

Source code for pylorax.executils

+#
+# executil.py - subprocess execution utility functions
+#
+# Copyright (C) 1999-2015
+# Red Hat, Inc.  All rights reserved.
+#
+# 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 os
+import subprocess
+from subprocess import TimeoutExpired
+import signal
+
+import logging
+log = logging.getLogger("pylorax")
+program_log = logging.getLogger("program")
+
+# pylint: disable=not-context-manager
+from threading import Lock
+program_log_lock = Lock()
+
+_child_env = {}
+
+
[docs]def setenv(name, value): + """ Set an environment variable to be used by child processes. + + This method does not modify os.environ for the running process, which + is not thread-safe. If setenv has already been called for a particular + variable name, the old value is overwritten. + + :param str name: The name of the environment variable + :param str value: The value of the environment variable + """ + + _child_env[name] = value
+ +
[docs]def augmentEnv(): + env = os.environ.copy() + env.update(_child_env) + return env
+ +
[docs]class ExecProduct(object): + def __init__(self, rc, stdout, stderr): + self.rc = rc + self.stdout = stdout + self.stderr = stderr
+ +
[docs]def startProgram(argv, root='/', stdin=None, stdout=subprocess.PIPE, stderr=subprocess.STDOUT, + env_prune=None, env_add=None, reset_handlers=True, reset_lang=True, **kwargs): + """ Start an external program and return the Popen object. + + The root and reset_handlers arguments are handled by passing a + preexec_fn argument to subprocess.Popen, but an additional preexec_fn + can still be specified and will be run. The user preexec_fn will be run + last. + + :param argv: The command to run and argument + :param root: The directory to chroot to before running command. + :param stdin: The file object to read stdin from. + :param stdout: The file object to write stdout to. + :param stderr: The file object to write stderr to. + :param env_prune: environment variables to remove before execution + :param env_add: environment variables to add before execution + :param reset_handlers: whether to reset to SIG_DFL any signal handlers set to SIG_IGN + :param reset_lang: whether to set the locale of the child process to C + :param kwargs: Additional parameters to pass to subprocess.Popen + :return: A Popen object for the running command. + :keyword preexec_fn: A function to run before execution starts. + """ + if env_prune is None: + env_prune = [] + + # Check for and save a preexec_fn argument + preexec_fn = kwargs.pop("preexec_fn", None) + + def preexec(): + # If a target root was specificed, chroot into it + if root and root != '/': + os.chroot(root) + os.chdir("/") + + # Signal handlers set to SIG_IGN persist across exec. Reset + # these to SIG_DFL if requested. In particular this will include the + # SIGPIPE handler set by python. + if reset_handlers: + for signum in range(1, signal.NSIG): + if signal.getsignal(signum) == signal.SIG_IGN: + signal.signal(signum, signal.SIG_DFL) + + # If the user specified an additional preexec_fn argument, run it + if preexec_fn is not None: + preexec_fn() + + with program_log_lock: + program_log.info("Running... %s", " ".join(argv)) + + env = augmentEnv() + for var in env_prune: + env.pop(var, None) + + if reset_lang: + env.update({"LC_ALL": "C"}) + + if env_add: + env.update(env_add) + + # pylint: disable=subprocess-popen-preexec-fn + return subprocess.Popen(argv, + stdin=stdin, + stdout=stdout, + stderr=stderr, + close_fds=True, + preexec_fn=preexec, cwd=root, env=env, **kwargs)
+ +def _run_program(argv, root='/', stdin=None, stdout=None, env_prune=None, log_output=True, + binary_output=False, filter_stderr=False, raise_err=False, callback=None, + env_add=None, reset_handlers=True, reset_lang=True): + """ Run an external program, log the output and return it to the caller + + :param argv: The command to run and argument + :param root: The directory to chroot to before running command. + :param stdin: The file object to read stdin from. + :param stdout: Optional file object to write the output to. + :param env_prune: environment variable to remove before execution + :param log_output: whether to log the output of command + :param binary_output: whether to treat the output of command as binary data + :param filter_stderr: whether to exclude the contents of stderr from the returned output + :param raise_err: whether to raise a CalledProcessError if the returncode is non-zero + :param callback: method to call while waiting for process to finish, passed Popen object + :param env_add: environment variables to add before execution + :param reset_handlers: whether to reset to SIG_DFL any signal handlers set to SIG_IGN + :param reset_lang: whether to set the locale of the child process to C + :return: The return code of the command and the output + :raises: OSError or CalledProcessError + """ + try: + if filter_stderr: + stderr = subprocess.PIPE + else: + stderr = subprocess.STDOUT + + proc = startProgram(argv, root=root, stdin=stdin, stdout=subprocess.PIPE, stderr=stderr, + env_prune=env_prune, universal_newlines=not binary_output, + env_add=env_add, reset_handlers=reset_handlers, reset_lang=reset_lang) + + output_string = None + err_string = None + if callback: + while callback(proc) and proc.poll() is None: + try: + (output_string, err_string) = proc.communicate(timeout=1) + break + except TimeoutExpired: + pass + else: + (output_string, err_string) = proc.communicate() + if output_string: + if binary_output: + output_lines = [output_string] + else: + if output_string[-1] != "\n": + output_string = output_string + "\n" + output_lines = output_string.splitlines(True) + + if log_output: + with program_log_lock: + for line in output_lines: + program_log.info(line.strip()) + + if stdout: + stdout.write(output_string) + + # If stderr was filtered, log it separately + if filter_stderr and err_string and log_output: + err_lines = err_string.splitlines(True) + + with program_log_lock: + for line in err_lines: + program_log.info(line.strip()) + + except OSError as e: + with program_log_lock: + program_log.error("Error running %s: %s", argv[0], e.strerror) + raise + + with program_log_lock: + program_log.debug("Return code: %s", proc.returncode) + + if proc.returncode and raise_err: + output = (output_string or "") + (err_string or "") + raise subprocess.CalledProcessError(proc.returncode, argv, output) + + return (proc.returncode, output_string) + +
[docs]def execWithRedirect(command, argv, stdin=None, stdout=None, root='/', env_prune=None, + log_output=True, binary_output=False, raise_err=False, callback=None, + env_add=None, reset_handlers=True, reset_lang=True): + """ Run an external program and redirect the output to a file. + + :param command: The command to run + :param argv: The argument list + :param stdin: The file object to read stdin from. + :param stdout: Optional file object to redirect stdout and stderr to. + :param root: The directory to chroot to before running command. + :param env_prune: environment variable to remove before execution + :param log_output: whether to log the output of command + :param binary_output: whether to treat the output of command as binary data + :param raise_err: whether to raise a CalledProcessError if the returncode is non-zero + :param callback: method to call while waiting for process to finish, passed Popen object + :param env_add: environment variables to add before execution + :param reset_handlers: whether to reset to SIG_DFL any signal handlers set to SIG_IGN + :param reset_lang: whether to set the locale of the child process to C + :return: The return code of the command + """ + argv = [command] + list(argv) + return _run_program(argv, stdin=stdin, stdout=stdout, root=root, env_prune=env_prune, + log_output=log_output, binary_output=binary_output, raise_err=raise_err, callback=callback, + env_add=env_add, reset_handlers=reset_handlers, reset_lang=reset_lang)[0]
+ +
[docs]def execWithCapture(command, argv, stdin=None, root='/', log_output=True, filter_stderr=False, + raise_err=False, callback=None, env_add=None, reset_handlers=True, reset_lang=True): + """ Run an external program and capture standard out and err. + + :param command: The command to run + :param argv: The argument list + :param stdin: The file object to read stdin from. + :param root: The directory to chroot to before running command. + :param log_output: Whether to log the output of command + :param filter_stderr: Whether stderr should be excluded from the returned output + :param callback: method to call while waiting for process to finish, passed Popen object + :param env_add: environment variables to add before execution + :param reset_handlers: whether to reset to SIG_DFL any signal handlers set to SIG_IGN + :param reset_lang: whether to set the locale of the child process to C + :return: The output of the command + """ + argv = [command] + list(argv) + return _run_program(argv, stdin=stdin, root=root, log_output=log_output, filter_stderr=filter_stderr, + raise_err=raise_err, callback=callback, env_add=env_add, + reset_handlers=reset_handlers, reset_lang=reset_lang)[1]
+ +
[docs]def execReadlines(command, argv, stdin=None, root='/', env_prune=None, filter_stderr=False, + callback=lambda x: True, env_add=None, reset_handlers=True, reset_lang=True): + """ Execute an external command and return the line output of the command + in real-time. + + This method assumes that there is a reasonably low delay between the + end of output and the process exiting. If the child process closes + stdout and then keeps on truckin' there will be problems. + + NOTE/WARNING: UnicodeDecodeError will be raised if the output of the + external command can't be decoded as UTF-8. + + :param command: The command to run + :param argv: The argument list + :param stdin: The file object to read stdin from. + :param stdout: Optional file object to redirect stdout and stderr to. + :param root: The directory to chroot to before running command. + :param env_prune: environment variable to remove before execution + :param filter_stderr: Whether stderr should be excluded from the returned output + :param callback: method to call while waiting for process to finish, passed Popen object + :param env_add: environment variables to add before execution + :param reset_handlers: whether to reset to SIG_DFL any signal handlers set to SIG_IGN + :param reset_lang: whether to set the locale of the child process to C + :return: Iterator of the lines from the command + + Output from the file is not logged to program.log + This returns an iterator with the lines from the command until it has finished + """ + + class ExecLineReader(object): + """Iterator class for returning lines from a process and cleaning + up the process when the output is no longer needed. + """ + + def __init__(self, proc, argv, callback): + self._proc = proc + self._argv = argv + self._callback = callback + + def __iter__(self): + return self + + def __del__(self): + # See if the process is still running + if self._proc.poll() is None: + # Stop the process and ignore any problems that might arise + try: + self._proc.terminate() + except OSError: + pass + + def __next__(self): + # Read the next line, blocking if a line is not yet available + line = self._proc.stdout.readline().decode("utf-8") + if line == '' or not self._callback(self._proc): + # Output finished, wait for the process to end + self._proc.communicate() + + # Check for successful exit + if self._proc.returncode < 0: + raise OSError("process '%s' was killed by signal %s" % + (self._argv, -self._proc.returncode)) + elif self._proc.returncode > 0: + raise OSError("process '%s' exited with status %s" % + (self._argv, self._proc.returncode)) + raise StopIteration + + return line.strip() + + argv = [command] + argv + + if filter_stderr: + stderr = subprocess.DEVNULL + else: + stderr = subprocess.STDOUT + + try: + proc = startProgram(argv, root=root, stdin=stdin, stdout=subprocess.PIPE, stderr=stderr, bufsize=1, + env_prune=env_prune, env_add=env_add, reset_handlers=reset_handlers, reset_lang=reset_lang) + except OSError as e: + with program_log_lock: + program_log.error("Error running %s: %s", argv[0], e.strerror) + raise + + return ExecLineReader(proc, argv, callback)
+ +
[docs]def runcmd(cmd, **kwargs): + """ run execWithRedirect with raise_err=True + """ + kwargs["raise_err"] = True + return execWithRedirect(cmd[0], cmd[1:], **kwargs)
+ +
[docs]def runcmd_output(cmd, **kwargs): + """ run execWithCapture with raise_err=True + """ + kwargs["raise_err"] = True + return execWithCapture(cmd[0], cmd[1:], **kwargs)
+
+ +
+ +
+ + +
+
+ +
+ +
+ + + + + + + + + + + + \ No newline at end of file diff --git a/f30-branch/_modules/pylorax/imgutils.html b/f30-branch/_modules/pylorax/imgutils.html new file mode 100644 index 00000000..983b62cb --- /dev/null +++ b/f30-branch/_modules/pylorax/imgutils.html @@ -0,0 +1,754 @@ + + + + + + + + + + + pylorax.imgutils — Lorax 30.19 documentation + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + +
+ + + + + +
+ +
+ + + + + + + + + + + + + + + + + +
+ + + + +
+
+
+
+ +

Source code for pylorax.imgutils

+# imgutils.py - utility functions/classes for building disk images
+#
+# Copyright (C) 2011-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/>.
+#
+# Author(s):  Will Woods <wwoods@redhat.com>
+
+import logging
+logger = logging.getLogger("pylorax.imgutils")
+
+import os, tempfile
+from os.path import join, dirname
+from subprocess import Popen, PIPE, CalledProcessError
+import sys
+import time
+import traceback
+import multiprocessing
+from time import sleep
+import shutil
+
+from pylorax.sysutils import cpfile
+from pylorax.executils import execWithRedirect, execWithCapture
+from pylorax.executils import runcmd, runcmd_output
+
+######## Functions for making container images (cpio, tar, squashfs) ##########
+
+
[docs]def compress(command, root, outfile, compression="xz", compressargs=None): + '''Make a compressed archive of the given rootdir or file. + command is a list of the archiver commands to run + compression should be "xz", "gzip", "lzma", "bzip2", or None. + compressargs will be used on the compression commandline.''' + if compression not in (None, "xz", "gzip", "lzma", "bzip2"): + raise ValueError("Unknown compression type %s" % compression) + compressargs = compressargs or ["-9"] + if compression == "xz": + compressargs.insert(0, "--check=crc32") + if compression is None: + compression = "cat" # this is a little silly + compressargs = [] + + # make compression run with multiple threads if possible + if compression in ("xz", "lzma"): + compressargs.insert(0, "-T%d" % multiprocessing.cpu_count()) + elif compression == "gzip": + compression = "pigz" + compressargs.insert(0, "-p%d" % multiprocessing.cpu_count()) + elif compression == "bzip2": + compression = "pbzip2" + compressargs.insert(0, "-p%d" % multiprocessing.cpu_count()) + + find, archive, comp = None, None, None + + try: + if os.path.isdir(root): + logger.debug("find %s -print0 |%s | %s %s > %s", root, " ".join(command), + compression, " ".join(compressargs), outfile) + + find = Popen(["find", ".", "-print0"], stdout=PIPE, cwd=root) + archive = Popen(command, stdin=find.stdout, stdout=PIPE, cwd=root) + else: + logger.debug("echo %s |%s | %s %s > %s", root, " ".join(command), + compression, " ".join(compressargs), outfile) + + archive = Popen(command, stdin=PIPE, stdout=PIPE, cwd=os.path.dirname(root)) + archive.stdin.write(os.path.basename(root).encode("utf-8") + b"\0") + archive.stdin.close() + + comp = Popen([compression] + compressargs, + stdin=archive.stdout, stdout=open(outfile, "wb")) + comp.wait() + return comp.returncode + except OSError as e: + logger.error(e) + # Kill off any hanging processes + list(p.kill() for p in (find, archive, comp) if p) + return 1
+ +
[docs]def mkcpio(root, outfile, compression="xz", compressargs=None): + compressargs = compressargs or ["-9"] + return compress(["cpio", "--null", "--quiet", "-H", "newc", "-o"], + root, outfile, compression, compressargs)
+ +
[docs]def mktar(root, outfile, compression="xz", compressargs=None, selinux=True): + compressargs = compressargs or ["-9"] + tar_cmd = ["tar", "--no-recursion"] + if selinux: + tar_cmd += ["--selinux", "--acls", "--xattrs"] + tar_cmd += ["-cf-", "--null", "-T-"] + return compress(tar_cmd, root, outfile, compression, compressargs)
+ +
[docs]def mksquashfs(rootdir, outfile, compression="default", compressargs=None): + '''Make a squashfs image containing the given rootdir.''' + compressargs = compressargs or [] + if compression != "default": + compressargs = ["-comp", compression] + compressargs + return execWithRedirect("mksquashfs", [rootdir, outfile] + compressargs)
+ +
[docs]def mkrootfsimg(rootdir, outfile, label, size=2, sysroot=""): + """ + Make rootfs image from a directory + + :param str rootdir: Root directory + :param str outfile: Path of output image file + :param str label: Filesystem label + :param int size: Size of the image in GiB, if None computed automatically + :param str sysroot: path to system (deployment) root relative to physical root + """ + if size: + fssize = size * (1024*1024*1024) # 2GB sparse file compresses down to nothin' + else: + fssize = None # Let mkext4img figure out the needed size + + mkext4img(rootdir, outfile, label=label, size=fssize)
+ + +######## Utility functions ############################################### + +
[docs]def mksparse(outfile, size): + '''use os.ftruncate to create a sparse file of the given size.''' + fobj = open(outfile, "w") + os.ftruncate(fobj.fileno(), size)
+ +
[docs]def mkqcow2(outfile, size, options=None): + '''use qemu-img to create a file of the given size. + options is a list of options passed to qemu-img + + Default format is qcow2, override by passing "-f", fmt + in options. + ''' + mkqemu_img(outfile, size, options)
+ +
[docs]def mkqemu_img(outfile, size, options=None): + '''use qemu-img to create a file of the given size. + options is a list of options passed to qemu-img + + Default format is qcow2, override by passing "-f", fmt + in options. + ''' + options = options or [] + if "-f" not in options: + options.extend(["-f", "qcow2"]) + runcmd(["qemu-img", "create"] + options + [outfile, str(size)])
+ +
[docs]def loop_waitfor(loop_dev, outfile): + """Make sure the loop device is attached to the outfile. + + It seems that on rare occasions losetup can return before the /dev/loopX is + ready for use, causing problems with mkfs. This tries to make sure that the + loop device really is associated with the backing file before continuing. + + Raise RuntimeError if it isn't setup after 5 tries. + """ + for _x in range(0,5): + runcmd(["udevadm", "settle", "--timeout", "300"]) + ## XXX Note that losetup --list output can be truncated to 64 bytes in some + ## situations. Don't use it to lookup backing file, go the other way + ## and lookup the loop for the backing file. See util-linux lib/loopdev.c + ## loopcxt_get_backing_file() + if get_loop_name(outfile) == os.path.basename(loop_dev): + return + + # If this really is a race, give it some time to settle down + time.sleep(1) + + raise RuntimeError("Unable to setup %s on %s" % (loop_dev, outfile))
+ +
[docs]def loop_attach(outfile): + """Attach a loop device to the given file. Return the loop device name. + + On rare occasions it appears that the device never shows up, some experiments + seem to indicate that it may be a race with another process using /dev/loop* devices. + + So we now try 3 times before actually failing. + + Raises CalledProcessError if losetup fails. + """ + retries = 0 + while True: + try: + retries += 1 + dev = runcmd_output(["losetup", "--find", "--show", outfile]).strip() + + # Sometimes the loop device isn't ready yet, make extra sure before returning + loop_waitfor(dev, outfile) + except RuntimeError: + # Try to setup the loop device 3 times + if retries == 3: + logger.error("loop_attach failed, retries exhausted.") + raise + logger.debug("Try %d failed, %s did not appear.", retries, dev) + break + return dev
+ +
[docs]def loop_detach(loopdev): + '''Detach the given loop device. Return False on failure.''' + return (execWithRedirect("losetup", ["--detach", loopdev]) == 0)
+ +
[docs]def get_loop_name(path): + '''Return the loop device associated with the path. + Raises RuntimeError if more than one loop is associated''' + buf = runcmd_output(["losetup", "-j", path]) + if len(buf.splitlines()) > 1: + # there should never be more than one loop device listed + raise RuntimeError("multiple loops associated with %s" % path) + name = os.path.basename(buf.split(":")[0]) + return name
+ +
[docs]def dm_attach(dev, size, name=None): + '''Attach a devicemapper device to the given device, with the given size. + If name is None, a random name will be chosen. Returns the device name. + raises CalledProcessError if dmsetup fails.''' + if name is None: + name = tempfile.mktemp(prefix="lorax.imgutils.", dir="") + runcmd(["dmsetup", "create", name, "--table", + "0 %i linear %s 0" % (size/512, dev)]) + return name
+ +
[docs]def dm_detach(dev): + '''Detach the named devicemapper device. Returns False if dmsetup fails.''' + dev = dev.replace("/dev/mapper/", "") # strip prefix, if it's there + return execWithRedirect("dmsetup", ["remove", dev])
+ +
[docs]def mount(dev, opts="", mnt=None): + '''Mount the given device at the given mountpoint, using the given opts. + opts should be a comma-separated string of mount options. + if mnt is none, a temporary directory will be created and its path will be + returned. + raises CalledProcessError if mount fails.''' + if mnt is None: + mnt = tempfile.mkdtemp(prefix="lorax.imgutils.") + logger.debug("make tmp mountdir %s", mnt) + cmd = ["mount"] + if opts: + cmd += ["-o", opts] + cmd += [dev, mnt] + runcmd(cmd) + return mnt
+ +
[docs]def umount(mnt, lazy=False, maxretry=3, retrysleep=1.0, delete=True): + '''Unmount the given mountpoint. If lazy is True, do a lazy umount (-l). + If the mount was a temporary dir created by mount, it will be deleted. + raises CalledProcessError if umount fails.''' + cmd = ["umount"] + if lazy: cmd += ["-l"] + cmd += [mnt] + count = 0 + while maxretry > 0: + try: + rv = runcmd(cmd) + except CalledProcessError: + count += 1 + if count == maxretry: + raise + logger.warning("failed to unmount %s. retrying (%d/%d)...", + mnt, count, maxretry) + if logger.getEffectiveLevel() <= logging.DEBUG: + fuser = execWithCapture("fuser", ["-vm", mnt]) + logger.debug("fuser -vm:\n%s\n", fuser) + sleep(retrysleep) + else: + break + if delete and 'lorax.imgutils' in mnt: + os.rmdir(mnt) + logger.debug("remove tmp mountdir %s", mnt) + return (rv == 0)
+ +
[docs]def copytree(src, dest, preserve=True): + '''Copy a tree of files using cp -a, thus preserving modes, timestamps, + links, acls, sparse files, xattrs, selinux contexts, etc. + If preserve is False, uses cp -R (useful for modeless filesystems) + raises CalledProcessError if copy fails.''' + logger.debug("copytree %s %s", src, dest) + cp = ["cp", "-a"] if preserve else ["cp", "-R", "-L", "--preserve=timestamps"] + cp += [join(src, "."), os.path.abspath(dest)] + runcmd(cp)
+ +
[docs]def do_grafts(grafts, dest, preserve=True): + '''Copy each of the items listed in grafts into dest. + If the key ends with '/' it's assumed to be a directory which should be + created, otherwise just the leading directories will be created.''' + for imgpath, filename in grafts.items(): + if imgpath[-1] == '/': + targetdir = join(dest, imgpath) + imgpath = imgpath[:-1] + else: + targetdir = join(dest, dirname(imgpath)) + if not os.path.isdir(targetdir): + os.makedirs(targetdir) + if os.path.isdir(filename): + copytree(filename, join(dest, imgpath), preserve) + else: + cpfile(filename, join(dest, imgpath))
+ +
[docs]def round_to_blocks(size, blocksize): + '''If size isn't a multiple of blocksize, round up to the next multiple''' + diff = size % blocksize + if diff or not size: + size += blocksize - diff + return size
+ +# TODO: move filesystem data outside this function +
[docs]def estimate_size(rootdir, graft=None, fstype=None, blocksize=4096, overhead=256): + graft = graft or {} + getsize = lambda f: os.lstat(f).st_size + if fstype == "btrfs": + overhead = 64*1024 # don't worry, it's all sparse + if fstype == "hfsplus": + overhead = 200 # hack to deal with two bootloader copies + if fstype in ("vfat", "msdos"): + blocksize = 2048 + getsize = lambda f: os.stat(f).st_size # no symlinks, count as copies + total = overhead*blocksize + dirlist = list(graft.values()) + if rootdir: + dirlist.append(rootdir) + for root in dirlist: + for top, dirs, files in os.walk(root): + for f in files + dirs: + total += round_to_blocks(getsize(join(top,f)), blocksize) + if fstype == "btrfs": + total = max(256*1024*1024, total) # btrfs minimum size: 256MB + logger.info("Size of %s block %s fs at %s estimated to be %s", blocksize, fstype, rootdir, total) + return total
+ +######## Execution contexts - use with the 'with' statement ############## + +
[docs]class LoopDev(object): + def __init__(self, filename, size=None): + self.loopdev = None + self.filename = filename + if size: + mksparse(self.filename, size) + def __enter__(self): + self.loopdev = loop_attach(self.filename) + return self.loopdev + def __exit__(self, exc_type, exc_value, tracebk): + loop_detach(self.loopdev)
+ +
[docs]class DMDev(object): + def __init__(self, dev, size, name=None): + self.mapperdev = None + (self.dev, self.size, self.name) = (dev, size, name) + def __enter__(self): + self.mapperdev = dm_attach(self.dev, self.size, self.name) + return self.mapperdev + def __exit__(self, exc_type, exc_value, tracebk): + dm_detach(self.mapperdev)
+ +
[docs]class Mount(object): + def __init__(self, dev, opts="", mnt=None): + (self.dev, self.opts, self.mnt) = (dev, opts, mnt) + def __enter__(self): + self.mnt = mount(self.dev, self.opts, self.mnt) + return self.mnt + def __exit__(self, exc_type, exc_value, tracebk): + umount(self.mnt)
+ +
[docs]def kpartx_disk_img(disk_img): + """Attach a disk image's partitions to /dev/loopX using kpartx + + :param disk_img: The full path to a partitioned disk image + :type disk_img: str + :returns: list of (loopXpN, size) + :rtype: list of tuples + """ + # Example kpartx output + # kpartx -p p -v -a /tmp/diskV2DiCW.im + # add map loop2p1 (253:2): 0 3481600 linear /dev/loop2 2048 + # add map loop2p2 (253:3): 0 614400 linear /dev/loop2 3483648 + kpartx_output = runcmd_output(["kpartx", "-v", "-a", "-s", disk_img]) + logger.debug(kpartx_output) + + # list of (deviceName, sizeInBytes) + loop_devices = [] + for line in kpartx_output.splitlines(): + # add map loop2p3 (253:4): 0 7139328 linear /dev/loop2 528384 + # 3rd element is size in 512 byte blocks + if line.startswith("add map "): + fields = line[8:].split() + loop_devices.append( (fields[0], int(fields[3])*512) ) + return loop_devices
+ +
[docs]class PartitionMount(object): + """ Mount a partitioned image file using kpartx """ + def __init__(self, disk_img, mount_ok=None, submount=None): + """ + :param str disk_img: The full path to a partitioned disk image + :param mount_ok: A function that is passed the mount point and + returns True if it should be mounted. + :param str submount: Directory inside mount_dir to mount at + + If mount_ok is not set it will look for /etc/passwd + + If the partition is found it will be mounted under a temporary + directory and self.temp_dir set to it. If submount is passed it will be + created and mounted there instead, with self.mount_dir set to point to + it. self.mount_dev is set to the loop device, and self.mount_size is + set to the size of the partition. + + When no subdir is passed self.temp_dir and self.mount_dir will be the same. + """ + self.mount_dev = None + self.mount_size = None + self.mount_dir = None + self.disk_img = disk_img + self.mount_ok = mount_ok + self.submount = submount + self.temp_dir = None + + # Default is to mount partition with /etc/passwd + if not self.mount_ok: + self.mount_ok = lambda mount_dir: os.path.isfile(mount_dir+"/etc/passwd") + + # list of (deviceName, sizeInBytes) + self.loop_devices = kpartx_disk_img(self.disk_img) + + def __enter__(self): + # Mount the device selected by mount_ok, if possible + self.temp_dir = tempfile.mkdtemp() + if self.submount: + mount_dir = os.path.normpath(os.path.sep.join([self.temp_dir, self.submount])) + os.makedirs(mount_dir, mode=0o755, exist_ok=True) + else: + mount_dir = self.temp_dir + for dev, size in self.loop_devices: + try: + mount( "/dev/mapper/"+dev, mnt=mount_dir ) + if self.mount_ok(mount_dir): + self.mount_dir = mount_dir + self.mount_dev = dev + self.mount_size = size + break + umount( mount_dir ) + except CalledProcessError: + logger.debug(traceback.format_exc()) + if self.mount_dir: + logger.info("Partition mounted on %s size=%s", self.mount_dir, self.mount_size) + else: + logger.debug("Unable to mount anything from %s", self.disk_img) + os.rmdir(self.temp_dir) + self.temp_dir = None + return self + + def __exit__(self, exc_type, exc_value, tracebk): + if self.temp_dir: + umount(self.mount_dir) + shutil.rmtree(self.temp_dir) + self.mount_dir = None + self.temp_dir = None + execWithRedirect("kpartx", ["-d", "-s", self.disk_img])
+ + +######## Functions for making filesystem images ########################## + +
[docs]def mkfsimage(fstype, rootdir, outfile, size=None, mkfsargs=None, mountargs="", graft=None): + '''Generic filesystem image creation function. + fstype should be a filesystem type - "mkfs.${fstype}" must exist. + graft should be a dict: {"some/path/in/image": "local/file/or/dir"}; + if the path ends with a '/' it's assumed to be a directory. + Will raise CalledProcessError if something goes wrong.''' + mkfsargs = mkfsargs or [] + graft = graft or {} + preserve = (fstype not in ("msdos", "vfat")) + if not size: + size = estimate_size(rootdir, graft, fstype) + with LoopDev(outfile, size) as loopdev: + try: + runcmd(["mkfs.%s" % fstype] + mkfsargs + [loopdev]) + except CalledProcessError as e: + logger.error("mkfs exited with a non-zero return code: %d", e.returncode) + logger.error(e.output) + sys.exit(e.returncode) + + with Mount(loopdev, mountargs) as mnt: + if rootdir: + copytree(rootdir, mnt, preserve) + do_grafts(graft, mnt, preserve) + + # Save information about filesystem usage + execWithRedirect("df", [mnt]) + + # Make absolutely sure that the data has been written + runcmd(["sync"])
+ +# convenience functions with useful defaults +
[docs]def mkdosimg(rootdir, outfile, size=None, label="", mountargs="shortname=winnt,umask=0077", graft=None): + graft = graft or {} + mkfsargs = ["-n", label] + if 'SOURCE_DATE_EPOCH' in os.environ: + mkfsargs.extend(["-i", + "{:x}".format(int(os.environ['SOURCE_DATE_EPOCH']))]) + mkfsimage("msdos", rootdir, outfile, size, mountargs=mountargs, + mkfsargs=mkfsargs, graft=graft)
+ +
[docs]def mkext4img(rootdir, outfile, size=None, label="", mountargs="", graft=None): + graft = graft or {} + mkfsimage("ext4", rootdir, outfile, size, mountargs=mountargs, + mkfsargs=["-L", label, "-b", "4096", "-m", "0"], graft=graft)
+ +
[docs]def mkbtrfsimg(rootdir, outfile, size=None, label="", mountargs="", graft=None): + graft = graft or {} + mkfsimage("btrfs", rootdir, outfile, size, mountargs=mountargs, + mkfsargs=["-L", label], graft=graft)
+ +
[docs]def mkhfsimg(rootdir, outfile, size=None, label="", mountargs="", graft=None): + graft = graft or {} + mkfsimage("hfsplus", rootdir, outfile, size, mountargs=mountargs, + mkfsargs=["-v", label], graft=graft)
+ +
[docs]def mkfsimage_from_disk(diskimage, fsimage, img_size=None, label="Anaconda"): + """ + Copy the / partition of a partitioned disk image to an un-partitioned + disk image. + + :param str diskimage: The full path to partitioned disk image with a / + :param str fsimage: The full path of the output fs image file + :param int img_size: Optional size of the fsimage in MiB or None to make + it as small as possible + :param str label: The label to apply to the image. Defaults to "Anaconda" + """ + with PartitionMount(diskimage) as img_mount: + if not img_mount or not img_mount.mount_dir: + return None + + logger.info("Creating fsimage %s (%s)", fsimage, img_size or "minimized") + if img_size: + # convert to Bytes + img_size *= 1024**2 + + mkext4img(img_mount.mount_dir, fsimage, size=img_size, label=label)
+ +
[docs]def default_image_name(compression, basename): + """ Return a default image name with the correct suffix for the compression type. + + :param str compression: Compression type + :param str basename: Base filename + :returns: basename with compression suffix + + If the compression is unknown it defaults to xz + """ + SUFFIXES = {"xz": ".xz", "gzip": ".gz", "bzip2": ".bz2", "lzma": ".lzma"} + return basename + SUFFIXES.get(compression, ".xz")
+
+ +
+ +
+ + +
+
+ +
+ +
+ + + + + + + + + + + + \ No newline at end of file diff --git a/f30-branch/_modules/pylorax/installer.html b/f30-branch/_modules/pylorax/installer.html new file mode 100644 index 00000000..efaa96b7 --- /dev/null +++ b/f30-branch/_modules/pylorax/installer.html @@ -0,0 +1,830 @@ + + + + + + + + + + + pylorax.installer — Lorax 30.7 documentation + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + +
+ + + + + +
+ +
+ + + + + + + + + + + + + + + + + +
+ + + + +
+
+
+
+ +

Source code for pylorax.installer

+#
+# Copyright (C) 2011-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("pylorax")
+
+import glob
+import json
+from math import ceil
+import os
+import subprocess
+import shutil
+import socket
+import tempfile
+
+# Use the Lorax treebuilder branch for iso creation
+from pylorax.executils import execWithRedirect, execReadlines
+from pylorax.imgutils import PartitionMount, mksparse, mkext4img, loop_detach
+from pylorax.imgutils import get_loop_name, dm_detach, mount, umount
+from pylorax.imgutils import mkqemu_img, mktar, mkcpio, mkfsimage_from_disk
+from pylorax.monitor import LogMonitor
+from pylorax.mount import IsoMountpoint
+from pylorax.sysutils import joinpaths
+from pylorax.treebuilder import udev_escape
+
+
+ROOT_PATH = "/mnt/sysimage/"
+
+
[docs]class InstallError(Exception): + pass
+ + +
[docs]def create_vagrant_metadata(path, size=0): + """ Create a default Vagrant metadata.json file + + :param str path: Path to metadata.json file + :param int size: Disk size in MiB + """ + metadata = { "provider":"libvirt", "format":"qcow2", "virtual_size": ceil(size / 1024) } + with open(path, "wt") as f: + json.dump(metadata, f, indent=4)
+ + +
[docs]def update_vagrant_metadata(path, size): + """ Update the Vagrant metadata.json file + + :param str path: Path to metadata.json file + :param int size: Disk size in MiB + + This function makes sure that the provider, format and virtual size of the + metadata file are set correctly. All other values are left untouched. + """ + with open(path, "rt") as f: + try: + metadata = json.load(f) + except ValueError as e: + log.error("Problem reading metadata file %s: %s", path, e) + return + + metadata["provider"] = "libvirt" + metadata["format"] = "qcow2" + metadata["virtual_size"] = ceil(size / 1024) + with open(path, "wt") as f: + json.dump(metadata, f, indent=4)
+ + +
[docs]def find_free_port(start=5900, end=5999, host="127.0.0.1"): + """ Return first free port in range. + + :param int start: Starting port number + :param int end: Ending port number + :param str host: Host IP to search + :returns: First free port or -1 if none found + :rtype: int + """ + s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) + for port in range(start, end+1): + try: + s.bind((host, port)) + s.close() + return port + except OSError: + pass + + return -1
+ +
[docs]def append_initrd(initrd, files): + """ Append files to an initrd. + + :param str initrd: Path to initrd + :param list files: list of file paths to add + :returns: Path to a new initrd + :rtype: str + + The files are added to the initrd by creating a cpio image + of the files (stored at /) and writing the cpio to the end of a + copy of the initrd. + + The initrd is not changed, a copy is made before appending the + cpio archive. + """ + qemu_initrd = tempfile.mktemp(prefix="lmc-initrd-", suffix=".img") + shutil.copy2(initrd, qemu_initrd) + ks_dir = tempfile.mkdtemp(prefix="lmc-ksdir-") + for ks in files: + shutil.copy2(ks, ks_dir) + ks_initrd = tempfile.mktemp(prefix="lmc-ks-", suffix=".img") + mkcpio(ks_dir, ks_initrd) + shutil.rmtree(ks_dir) + with open(qemu_initrd, "ab") as initrd_fp: + with open(ks_initrd, "rb") as ks_fp: + while True: + data = ks_fp.read(1024**2) + if not data: + break + initrd_fp.write(data) + os.unlink(ks_initrd) + + return qemu_initrd
+ +
[docs]class QEMUInstall(object): + """ + Run qemu using an iso and a kickstart + """ + # Mapping of arch to qemu command + QEMU_CMDS = {"x86_64": "qemu-system-x86_64", + "i386": "qemu-system-i386", + "arm": "qemu-system-arm", + "aarch64": "qemu-system-aarch64", + "ppc": "qemu-system-ppc", + "ppc64": "qemu-system-ppc64" + } + + def __init__(self, opts, iso, ks_paths, disk_img, img_size=2048, + kernel_args=None, memory=1024, vcpus=None, vnc=None, arch=None, + log_check=None, virtio_host="127.0.0.1", virtio_port=6080, + image_type=None, boot_uefi=False, ovmf_path=None): + """ + Start the installation + + :param iso: Information about the iso to use for the installation + :type iso: IsoMountpoint + :param list ks_paths: Paths to kickstart files. All are injected, the + first one is the one executed. + :param str disk_img: Path to a disk image, created it it doesn't exist + :param int img_size: The image size, in MiB, to create if it doesn't exist + :param str kernel_args: Extra kernel arguments to pass on the kernel cmdline + :param int memory: Amount of RAM to assign to the virt, in MiB + :param int vcpus: Number of virtual cpus + :param str vnc: Arguments to pass to qemu -display + :param str arch: Optional architecture to use in the virt + :param log_check: Method that returns True if the installation fails + :type log_check: method + :param str virtio_host: Hostname to connect virtio log to + :param int virtio_port: Port to connect virtio log to + :param str image_type: Type of qemu-img disk to create, or None. + :param bool boot_uefi: Use OVMF to boot the VM in UEFI mode + :param str ovmf_path: Path to the OVMF firmware + """ + # Lookup qemu-system- for arch if passed, or try to guess using host arch + qemu_cmd = [self.QEMU_CMDS.get(arch or os.uname().machine, "qemu-system-"+os.uname().machine)] + if not os.path.exists("/usr/bin/"+qemu_cmd[0]): + raise InstallError("%s does not exist, cannot run qemu" % qemu_cmd[0]) + + qemu_cmd += ["-nodefconfig"] + qemu_cmd += ["-m", str(memory)] + if vcpus: + qemu_cmd += ["-smp", str(vcpus)] + + if not opts.no_kvm and os.path.exists("/dev/kvm"): + qemu_cmd += ["--machine", "accel=kvm"] + + # Copy the initrd from the iso, create a cpio archive of the kickstart files + # and append it to the temporary initrd. + qemu_initrd = append_initrd(iso.initrd, ks_paths) + qemu_cmd += ["-kernel", iso.kernel] + qemu_cmd += ["-initrd", qemu_initrd] + + # Add the disk and cdrom + if not os.path.isfile(disk_img): + mksparse(disk_img, img_size * 1024**2) + drive_args = "file=%s" % disk_img + drive_args += ",cache=unsafe,discard=unmap" + if image_type: + drive_args += ",format=%s" % image_type + else: + drive_args += ",format=raw" + qemu_cmd += ["-drive", drive_args] + + drive_args = "file=%s,media=cdrom,readonly=on" % iso.iso_path + qemu_cmd += ["-drive", drive_args] + + # Setup the cmdline args + # ====================== + cmdline_args = "ks=file:/%s" % os.path.basename(ks_paths[0]) + cmdline_args += " inst.stage2=hd:LABEL=%s" % udev_escape(iso.label) + if opts.proxy: + cmdline_args += " inst.proxy=%s" % opts.proxy + if kernel_args: + cmdline_args += " "+kernel_args + cmdline_args += " inst.text inst.cmdline" + + qemu_cmd += ["-append", cmdline_args] + + if not opts.vnc: + vnc_port = find_free_port() + if vnc_port == -1: + raise InstallError("No free VNC ports") + display_args = "vnc=127.0.0.1:%d" % (vnc_port - 5900) + else: + display_args = opts.vnc + log.info("qemu %s", display_args) + qemu_cmd += ["-nographic", "-display", display_args ] + + # Setup the virtio log port + qemu_cmd += ["-device", "virtio-serial-pci,id=virtio-serial0"] + qemu_cmd += ["-device", "virtserialport,bus=virtio-serial0.0,nr=1,chardev=charchannel0" + ",id=channel0,name=org.fedoraproject.anaconda.log.0"] + qemu_cmd += ["-chardev", "socket,id=charchannel0,host=%s,port=%s" % (virtio_host, virtio_port)] + + # PAss through rng from host + if opts.with_rng != "none": + qemu_cmd += ["-object", "rng-random,id=virtio-rng0,filename=%s" % opts.with_rng] + qemu_cmd += ["-device", "virtio-rng-pci,rng=virtio-rng0,id=rng0,bus=pci.0,addr=0x9"] + + if boot_uefi and ovmf_path: + qemu_cmd += ["-drive", "file=%s/OVMF_CODE.fd,if=pflash,format=raw,unit=0,readonly=on" % ovmf_path] + + # Make a copy of the OVMF_VARS.fd for this run + ovmf_vars = tempfile.mktemp(prefix="lmc-OVMF_VARS-", suffix=".fd") + shutil.copy2(joinpaths(ovmf_path, "/OVMF_VARS.fd"), ovmf_vars) + + qemu_cmd += ["-drive", "file=%s,if=pflash,format=raw,unit=1" % ovmf_vars] + + log.info("Running qemu") + log.debug(qemu_cmd) + try: + execWithRedirect(qemu_cmd[0], qemu_cmd[1:], reset_lang=False, raise_err=True, + callback=lambda p: not log_check()) + except subprocess.CalledProcessError as e: + log.error("Running qemu failed:") + log.error("cmd: %s", " ".join(e.cmd)) + log.error("output: %s", e.output or "") + raise InstallError("QEMUInstall failed") + except (OSError, KeyboardInterrupt) as e: + log.error("Running qemu failed: %s", str(e)) + raise InstallError("QEMUInstall failed") + finally: + os.unlink(qemu_initrd) + if boot_uefi and ovmf_path: + os.unlink(ovmf_vars) + + if log_check(): + log.error("Installation error detected. See logfile for details.") + raise InstallError("QEMUInstall failed") + else: + log.info("Installation finished without errors.")
+ + +
[docs]def novirt_log_check(log_check, proc): + """ + Check to see if there has been an error in the logs + + :param log_check: method to call to check for an error in the logs + :param proc: Popen object for the anaconda process + :returns: True if the process has been terminated + + The log_check method should return a True if an error has been detected. + When an error is detected the process is terminated and this returns True + """ + if log_check(): + proc.terminate() + return True + return False
+ + +
[docs]def anaconda_cleanup(dirinstall_path): + """ + Cleanup any leftover mounts from anaconda + + :param str dirinstall_path: Path where anaconda mounts things + :returns: True if cleanups were successful. False if any of them failed. + + If anaconda crashes it may leave things mounted under this path. It will + typically be set to /mnt/sysimage/ + + Attempts to cleanup may also fail. Catch these and continue trying the + other mountpoints. + """ + rc = True + dirinstall_path = os.path.abspath(dirinstall_path) + # unmount filesystems + for mounted in reversed(open("/proc/mounts").readlines()): + (_device, mountpoint, _rest) = mounted.split(" ", 2) + if mountpoint.startswith(dirinstall_path) and os.path.ismount(mountpoint): + try: + umount(mountpoint) + except subprocess.CalledProcessError: + log.error("Cleanup of %s failed. See program.log for details", mountpoint) + rc = False + return rc
+ + +
[docs]def novirt_install(opts, disk_img, disk_size): + """ + Use Anaconda to install to a disk image + + :param opts: options passed to livemedia-creator + :type opts: argparse options + :param str disk_img: The full path to the disk image to be created + :param int disk_size: The size of the disk_img in MiB + + This method runs anaconda to create the image and then based on the opts + passed creates a qemu disk image or tarfile. + """ + dirinstall_path = ROOT_PATH + + # Clean up /tmp/ from previous runs to prevent stale info from being used + for path in ["/tmp/yum.repos.d/", "/tmp/yum.cache/"]: + if os.path.isdir(path): + shutil.rmtree(path) + + args = ["--kickstart", opts.ks[0], "--cmdline", "--loglevel", "debug"] + if opts.anaconda_args: + for arg in opts.anaconda_args: + args += arg.split(" ", 1) + if opts.proxy: + args += ["--proxy", opts.proxy] + if opts.armplatform: + args += ["--armplatform", opts.armplatform] + + if opts.make_iso or opts.make_fsimage or opts.make_pxe_live: + # Make a blank fs image + args += ["--dirinstall"] + + mkext4img(None, disk_img, label=opts.fs_label, size=disk_size * 1024**2) + if not os.path.isdir(dirinstall_path): + os.mkdir(dirinstall_path) + mount(disk_img, opts="loop", mnt=dirinstall_path) + elif opts.make_tar or opts.make_oci: + # Install under dirinstall_path, make sure it starts clean + if os.path.exists(dirinstall_path): + shutil.rmtree(dirinstall_path) + + if opts.make_oci: + # OCI installs under /rootfs/ + dirinstall_path = joinpaths(dirinstall_path, "rootfs") + args += ["--dirinstall", dirinstall_path] + else: + args += ["--dirinstall"] + + os.makedirs(dirinstall_path) + else: + args += ["--image", disk_img] + + # Create the sparse image + mksparse(disk_img, disk_size * 1024**2) + + log_monitor = LogMonitor(timeout=opts.timeout) + args += ["--remotelog", "%s:%s" % (log_monitor.host, log_monitor.port)] + + # Make sure anaconda has the right product and release + log.info("Running anaconda.") + try: + for line in execReadlines("anaconda", args, reset_lang=False, + env_add={"ANACONDA_PRODUCTNAME": opts.project, + "ANACONDA_PRODUCTVERSION": opts.releasever}, + callback=lambda p: not novirt_log_check(log_monitor.server.log_check, p)): + log.info(line) + + # Make sure the new filesystem is correctly labeled + setfiles_args = ["-e", "/proc", "-e", "/sys", "-e", "/dev", + "/etc/selinux/targeted/contexts/files/file_contexts", "/"] + + if "--dirinstall" in args: + # setfiles may not be available, warn instead of fail + try: + execWithRedirect("setfiles", setfiles_args, root=dirinstall_path) + except (subprocess.CalledProcessError, OSError) as e: + log.warning("Running setfiles on install tree failed: %s", str(e)) + else: + with PartitionMount(disk_img) as img_mount: + if img_mount and img_mount.mount_dir: + try: + execWithRedirect("setfiles", setfiles_args, root=img_mount.mount_dir) + except (subprocess.CalledProcessError, OSError) as e: + log.warning("Running setfiles on install tree failed: %s", str(e)) + + # For image installs, run fstrim to discard unused blocks. This way + # unused blocks do not need to be allocated for sparse image types + execWithRedirect("fstrim", [img_mount.mount_dir]) + + except (subprocess.CalledProcessError, OSError) as e: + log.error("Running anaconda failed: %s", e) + raise InstallError("novirt_install failed") + finally: + log_monitor.shutdown() + + # Move the anaconda logs over to a log directory + log_dir = os.path.abspath(os.path.dirname(opts.logfile)) + log_anaconda = joinpaths(log_dir, "anaconda") + if not os.path.isdir(log_anaconda): + os.mkdir(log_anaconda) + for l in glob.glob("/tmp/*log")+glob.glob("/tmp/anaconda-tb-*"): + shutil.copy2(l, log_anaconda) + os.unlink(l) + + # Make sure any leftover anaconda mounts have been cleaned up + if not anaconda_cleanup(dirinstall_path): + raise InstallError("novirt_install cleanup of anaconda mounts failed.") + + if not opts.make_iso and not opts.make_fsimage and not opts.make_pxe_live: + dm_name = os.path.splitext(os.path.basename(disk_img))[0] + dm_path = "/dev/mapper/"+dm_name + if os.path.exists(dm_path): + dm_detach(dm_path) + loop_detach(get_loop_name(disk_img)) + + # qemu disk image is used by bare qcow2 images and by Vagrant + if opts.image_type: + log.info("Converting %s to %s", disk_img, opts.image_type) + qemu_args = [] + for arg in opts.qemu_args: + qemu_args += arg.split(" ", 1) + + # convert the image to the selected format + if "-O" not in qemu_args: + qemu_args.extend(["-O", opts.image_type]) + qemu_img = tempfile.mktemp(prefix="lmc-disk-", suffix=".img") + execWithRedirect("qemu-img", ["convert"] + qemu_args + [disk_img, qemu_img], raise_err=True) + if not opts.make_vagrant: + execWithRedirect("mv", ["-f", qemu_img, disk_img], raise_err=True) + else: + # Take the new qcow2 image and package it up for Vagrant + compress_args = [] + for arg in opts.compress_args: + compress_args += arg.split(" ", 1) + + vagrant_dir = tempfile.mkdtemp(prefix="lmc-tmpdir-") + metadata_path = joinpaths(vagrant_dir, "metadata.json") + execWithRedirect("mv", ["-f", qemu_img, joinpaths(vagrant_dir, "box.img")], raise_err=True) + if opts.vagrant_metadata: + shutil.copy2(opts.vagrant_metadata, metadata_path) + else: + create_vagrant_metadata(metadata_path) + update_vagrant_metadata(metadata_path, disk_size) + if opts.vagrantfile: + shutil.copy2(opts.vagrantfile, joinpaths(vagrant_dir, "vagrantfile")) + + log.info("Creating Vagrant image") + rc = mktar(vagrant_dir, disk_img, opts.compression, compress_args, selinux=False) + if rc: + raise InstallError("novirt_install mktar failed: rc=%s" % rc) + shutil.rmtree(vagrant_dir) + elif opts.make_tar: + compress_args = [] + for arg in opts.compress_args: + compress_args += arg.split(" ", 1) + + rc = mktar(dirinstall_path, disk_img, opts.compression, compress_args) + shutil.rmtree(dirinstall_path) + + if rc: + raise InstallError("novirt_install mktar failed: rc=%s" % rc) + elif opts.make_oci: + # An OCI image places the filesystem under /rootfs/ and adds the json files at the top + # And then creates a tar of the whole thing. + compress_args = [] + for arg in opts.compress_args: + compress_args += arg.split(" ", 1) + + shutil.copy2(opts.oci_config, ROOT_PATH) + shutil.copy2(opts.oci_runtime, ROOT_PATH) + rc = mktar(ROOT_PATH, disk_img, opts.compression, compress_args) + + if rc: + raise InstallError("novirt_install mktar failed: rc=%s" % rc) + else: + # For raw disk images, use fallocate to deallocate unused space + execWithRedirect("fallocate", ["--dig-holes", disk_img], raise_err=True)
+ + +
[docs]def virt_install(opts, install_log, disk_img, disk_size): + """ + Use qemu to install to a disk image + + :param opts: options passed to livemedia-creator + :type opts: argparse options + :param str install_log: The path to write the log from qemu + :param str disk_img: The full path to the disk image to be created + :param int disk_size: The size of the disk_img in MiB + + This uses qemu with a boot.iso and a kickstart to create a disk + image and then optionally, based on the opts passed, creates tarfile. + """ + iso_mount = IsoMountpoint(opts.iso, opts.location) + if not iso_mount.stage2: + iso_mount.umount() + raise InstallError("ISO is missing stage2, cannot continue") + + log_monitor = LogMonitor(install_log, timeout=opts.timeout) + + kernel_args = "" + if opts.kernel_args: + kernel_args += opts.kernel_args + if opts.proxy: + kernel_args += " proxy="+opts.proxy + + if opts.image_type and not opts.make_fsimage: + qemu_args = [] + for arg in opts.qemu_args: + qemu_args += arg.split(" ", 1) + if "-f" not in qemu_args: + qemu_args += ["-f", opts.image_type] + + mkqemu_img(disk_img, disk_size*1024**2, qemu_args) + + if opts.make_fsimage or opts.make_tar or opts.make_oci: + diskimg_path = tempfile.mktemp(prefix="lmc-disk-", suffix=".img") + else: + diskimg_path = disk_img + + try: + QEMUInstall(opts, iso_mount, opts.ks, diskimg_path, disk_size, + kernel_args, opts.ram, opts.vcpus, opts.vnc, opts.arch, + log_check = log_monitor.server.log_check, + virtio_host = log_monitor.host, + virtio_port = log_monitor.port, + image_type=opts.image_type, boot_uefi=opts.virt_uefi, + ovmf_path=opts.ovmf_path) + log_monitor.shutdown() + except InstallError as e: + log.error("VirtualInstall failed: %s", e) + raise + finally: + log.info("unmounting the iso") + iso_mount.umount() + + if log_monitor.server.log_check(): + if not log_monitor.server.error_line and opts.timeout: + msg = "virt_install failed due to timeout" + else: + msg = "virt_install failed on line: %s" % log_monitor.server.error_line + raise InstallError(msg) + + if opts.make_fsimage: + mkfsimage_from_disk(diskimg_path, disk_img, disk_size, label=opts.fs_label) + os.unlink(diskimg_path) + elif opts.make_tar: + compress_args = [] + for arg in opts.compress_args: + compress_args += arg.split(" ", 1) + + with PartitionMount(diskimg_path) as img_mount: + if img_mount and img_mount.mount_dir: + rc = mktar(img_mount.mount_dir, disk_img, opts.compression, compress_args) + else: + rc = 1 + os.unlink(diskimg_path) + + if rc: + raise InstallError("virt_install failed") + elif opts.make_oci: + # An OCI image places the filesystem under /rootfs/ and adds the json files at the top + # And then creates a tar of the whole thing. + compress_args = [] + for arg in opts.compress_args: + compress_args += arg.split(" ", 1) + + with PartitionMount(diskimg_path, submount="rootfs") as img_mount: + if img_mount and img_mount.temp_dir: + shutil.copy2(opts.oci_config, img_mount.temp_dir) + shutil.copy2(opts.oci_runtime, img_mount.temp_dir) + rc = mktar(img_mount.temp_dir, disk_img, opts.compression, compress_args) + else: + rc = 1 + os.unlink(diskimg_path) + + if rc: + raise InstallError("virt_install failed") + elif opts.make_vagrant: + compress_args = [] + for arg in opts.compress_args: + compress_args += arg.split(" ", 1) + + vagrant_dir = tempfile.mkdtemp(prefix="lmc-tmpdir-") + metadata_path = joinpaths(vagrant_dir, "metadata.json") + execWithRedirect("mv", ["-f", disk_img, joinpaths(vagrant_dir, "box.img")], raise_err=True) + if opts.vagrant_metadata: + shutil.copy2(opts.vagrant_metadata, metadata_path) + else: + create_vagrant_metadata(metadata_path) + update_vagrant_metadata(metadata_path, disk_size) + if opts.vagrantfile: + shutil.copy2(opts.vagrantfile, joinpaths(vagrant_dir, "vagrantfile")) + + rc = mktar(vagrant_dir, disk_img, opts.compression, compress_args, selinux=False) + if rc: + raise InstallError("virt_install failed") + shutil.rmtree(vagrant_dir)
+
+ +
+ +
+ + +
+
+ +
+ +
+ + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/f30-branch/_modules/pylorax/ltmpl.html b/f30-branch/_modules/pylorax/ltmpl.html new file mode 100644 index 00000000..3e83b832 --- /dev/null +++ b/f30-branch/_modules/pylorax/ltmpl.html @@ -0,0 +1,1087 @@ + + + + + + + + + + + pylorax.ltmpl — Lorax 30.19 documentation + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + +
+ + + + + +
+ +
+ + + + + + + + + + + + + + + + + +
+ + + + +
+
+
+
+ +

Source code for pylorax.ltmpl

+#
+# ltmpl.py
+#
+# Copyright (C) 2009-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/>.
+#
+# Red Hat Author(s):  Martin Gracik <mgracik@redhat.com>
+#                     Will Woods <wwoods@redhat.com>
+#
+
+import logging
+logger = logging.getLogger("pylorax.ltmpl")
+
+import os, re, glob, shlex, fnmatch
+from os.path import basename, isdir
+from subprocess import CalledProcessError
+import shutil
+
+from pylorax.sysutils import joinpaths, cpfile, mvfile, replace, remove
+from pylorax.dnfhelper import LoraxDownloadCallback, LoraxRpmCallback
+from pylorax.base import DataHolder
+from pylorax.executils import runcmd, runcmd_output
+from pylorax.imgutils import mkcpio
+
+from mako.lookup import TemplateLookup
+from mako.exceptions import text_error_template
+import sys, traceback
+import struct
+import dnf
+import collections
+
+
[docs]class LoraxTemplate(object): + def __init__(self, directories=None): + directories = directories or ["/usr/share/lorax"] + # we have to add ["/"] to the template lookup directories or the + # file includes won't work properly for absolute paths + self.directories = ["/"] + directories + +
[docs] def parse(self, template_file, variables): + lookup = TemplateLookup(directories=self.directories) + template = lookup.get_template(template_file) + + try: + textbuf = template.render(**variables) + except: + logger.error("Problem rendering %s (%s):", template_file, variables) + logger.error(text_error_template().render()) + raise + + # split, strip and remove empty lines + lines = textbuf.splitlines() + lines = [line.strip() for line in lines] + lines = [line for line in lines if line] + + # remove comments + lines = [line for line in lines if not line.startswith("#")] + + # split with shlex and perform brace expansion. This can fail, so we unroll the loop + # for better error reporting. + expanded_lines = [] + try: + for line in lines: + expanded_lines.append(split_and_expand(line)) + except Exception as e: + logger.error('shlex error processing "%s": %s', line, str(e)) + raise + return expanded_lines
+ +
[docs]def split_and_expand(line): + return [exp for word in shlex.split(line) for exp in brace_expand(word)]
+ +
[docs]def brace_expand(s): + if not ('{' in s and ',' in s and '}' in s): + yield s + else: + right = s.find('}') + left = s[:right].rfind('{') + (prefix, choices, suffix) = (s[:left], s[left+1:right], s[right+1:]) + for choice in choices.split(','): + for alt in brace_expand(prefix+choice+suffix): + yield alt
+ +
[docs]def rglob(pathname, root="/", fatal=False): + seen = set() + rootlen = len(root)+1 + for f in glob.iglob(joinpaths(root, pathname)): + if f not in seen: + seen.add(f) + yield f[rootlen:] # remove the root to produce relative path + if fatal and not seen: + raise IOError("nothing matching %s in %s" % (pathname, root))
+ +
[docs]def rexists(pathname, root=""): + # Generator is always True, even with no values; + # bool(rglob(...)) won't work here. + for _path in rglob(pathname, root): + return True + return False
+ +
[docs]class TemplateRunner(object): + ''' + This class parses and executes Lorax templates. Sample usage: + + # install a bunch of packages + runner = LoraxTemplateRunner(inroot=rundir, outroot=rundir, dbo=dnf_obj) + runner.run("install-packages.ltmpl") + NOTES: + + * Parsing procedure is roughly: + 1. Mako template expansion (on the whole file) + 2. For each line of the result, + + a. Whitespace splitting (using shlex.split()) + b. Brace expansion (using brace_expand()) + c. If the first token is the name of a function, call that function + with the rest of the line as arguments + + * Parsing and execution are *separate* passes - so you can't use the result + of a command in an %if statement (or any other control statements)! + ''' + def __init__(self, fatalerrors=True, templatedir=None, defaults=None, builtins=None): + self.fatalerrors = fatalerrors + self.templatedir = templatedir or "/usr/share/lorax" + self.templatefile = None + self.builtins = builtins or {} + self.defaults = defaults or {} + + +
[docs] def run(self, templatefile, **variables): + for k,v in list(self.defaults.items()) + list(self.builtins.items()): + variables.setdefault(k,v) + logger.debug("executing %s with variables=%s", templatefile, variables) + self.templatefile = templatefile + t = LoraxTemplate(directories=[self.templatedir]) + commands = t.parse(templatefile, variables) + self._run(commands)
+ + + def _run(self, parsed_template): + logger.info("running %s", self.templatefile) + for (num, line) in enumerate(parsed_template,1): + logger.debug("template line %i: %s", num, " ".join(line)) + skiperror = False + (cmd, args) = (line[0], line[1:]) + # Following Makefile convention, if the command is prefixed with + # a dash ('-'), we'll ignore any errors on that line. + if cmd.startswith('-'): + cmd = cmd[1:] + skiperror = True + try: + # grab the method named in cmd and pass it the given arguments + f = getattr(self, cmd, None) + if cmd[0] == '_' or cmd == 'run' or not isinstance(f, collections.Callable): + raise ValueError("unknown command %s" % cmd) + f(*args) + except Exception: # pylint: disable=broad-except + if skiperror: + logger.debug("ignoring error") + continue + logger.error("template command error in %s:", self.templatefile) + logger.error(" %s", " ".join(line)) + # format the exception traceback + exclines = traceback.format_exception(*sys.exc_info()) + # skip the bit about "ltmpl.py, in _run()" - we know that + exclines.pop(1) + # log the "ErrorType: this is what happened" line + logger.error(" %s", exclines[-1].strip()) + # and log the entire traceback to the debug log + for _line in ''.join(exclines).splitlines(): + logger.debug(" %s", _line) + if self.fatalerrors: + raise
+ + +# TODO: operate inside an actual chroot for safety? Not that RPM bothers.. +
[docs]class LoraxTemplateRunner(TemplateRunner): + ''' + This class parses and executes Lorax templates. Sample usage: + + # install a bunch of packages + runner = LoraxTemplateRunner(inroot=rundir, outroot=rundir, dbo=dnf_obj) + runner.run("install-packages.ltmpl") + + # modify a runtime dir + runner = LoraxTemplateRunner(inroot=rundir, outroot=newrun) + runner.run("runtime-transmogrify.ltmpl") + + NOTES: + + * Commands that run external programs (e.g. systemctl) currently use + the *host*'s copy of that program, which may cause problems if there's a + big enough difference between the host and the image you're modifying. + + * The commands are not executed under a real chroot, so absolute symlinks + will point *outside* the inroot/outroot. Be careful with symlinks! + + ADDING NEW COMMANDS: + + * Each template command is just a method of the LoraxTemplateRunner + object - so adding a new command is as easy as adding a new function. + + * Each function gets arguments that correspond to the rest of the tokens + on that line (after word splitting and brace expansion) + + * Commands should raise exceptions for errors - don't use sys.exit() + ''' + def __init__(self, inroot, outroot, dbo=None, fatalerrors=True, + templatedir=None, defaults=None): + self.inroot = inroot + self.outroot = outroot + self.dbo = dbo + builtins = DataHolder(exists=lambda p: rexists(p, root=inroot), + glob=lambda g: list(rglob(g, root=inroot))) + self.results = DataHolder(treeinfo=dict()) # just treeinfo for now + + super(LoraxTemplateRunner, self).__init__(fatalerrors, templatedir, defaults, builtins) + # TODO: set up custom logger with a filter to add line info + + def _out(self, path): + return joinpaths(self.outroot, path) + def _in(self, path): + return joinpaths(self.inroot, path) + + def _filelist(self, *pkgs): + """ Return the list of files in the packages """ + pkglist = [] + for pkg_glob in pkgs: + pkglist += list(self.dbo.sack.query().installed().filter(name__glob=pkg_glob)) + + # dnf/hawkey doesn't make any distinction between file, dir or ghost like yum did + # so only return the files. + return set(f for pkg in pkglist for f in pkg.files if not os.path.isdir(self._out(f))) + + def _getsize(self, *files): + return sum(os.path.getsize(self._out(f)) for f in files if os.path.isfile(self._out(f))) + + def _write_debuginfo_log(self): + """ + Write a list of debuginfo packages to /root/debug-pkgs.log + + If lorax is called with a debug repo find the corresponding debuginfo package + names and write them to /root/debubg-pkgs.log on the boot.iso + """ + for repo in self.dbo.repos: + repo = self.dbo.repos[repo] + if any(True for url in repo.baseurl if "debug" in url): + break + if repo.metalink and "debug" in repo.metalink: + break + if repo.mirrorlist and "debug" in repo.mirrorlist: + break + else: + # No debug repos + return + + available = self.dbo.sack.query().available() + debug_pkgs = [] + for p in list(self.dbo.transaction.install_set): + if available.filter(name=p.name+"-debuginfo"): + debug_pkgs += ["{0.name}-debuginfo-{0.epoch}:{0.version}-{0.release}".format(p)] + + os.makedirs(self._out("root/"), exist_ok=True) + with open(self._out("root/debug-pkgs.log"), "w") as f: + for pkg in debug_pkgs: + f.write("%s\n" % pkg) + +
[docs] def install(self, srcglob, dest): + ''' + install SRC DEST + Copy the given file (or files, if a glob is used) from the input + tree to the given destination in the output tree. + The path to DEST must exist in the output tree. + If DEST is a directory, SRC will be copied into that directory. + If DEST doesn't exist, SRC will be copied to a file with that name, + assuming the rest of the path exists. + This is pretty much like how the 'cp' command works. + + Examples: + install usr/share/myconfig/grub.conf /boot + install /usr/share/myconfig/grub.conf.in /boot/grub.conf + ''' + for src in rglob(self._in(srcglob), fatal=True): + try: + cpfile(src, self._out(dest)) + except shutil.Error as e: + logger.error(e)
+ +
[docs] def installimg(self, *args): + ''' + installimg [--xz|--gzip|--bzip2|--lzma] [-ARG|--ARG=OPTION] SRCDIR DESTFILE + Create a compressed cpio archive of the contents of SRCDIR and place + it in DESTFILE. + + If SRCDIR doesn't exist or is empty nothing is created. + + Examples: + installimg ${LORAXDIR}/product/ images/product.img + installimg ${LORAXDIR}/updates/ images/updates.img + installimg --xz -6 ${LORAXDIR}/updates/ images/updates.img + installimg --xz -9 --memlimit-compress=3700MiB ${LORAXDIR}/updates/ images/updates.img + + Optionally use a different compression type and override the default args + passed to it. The default is xz -9 + ''' + COMPRESSORS = ("--xz", "--gzip", "--bzip2", "--lzma") + if len(args) < 2: + raise ValueError("Not enough args for installimg.") + + srcdir = args[-2] + destfile = args[-1] + if not os.path.isdir(self._in(srcdir)) or not os.listdir(self._in(srcdir)): + return + + compression = "xz" + compressargs = [] + if args[0] in COMPRESSORS: + compression = args[0][2:] + + for arg in args[1:-2]: + if arg.startswith('-'): + compressargs.append(arg) + else: + raise ValueError("Argument is missing -") + + logger.info("Creating image file %s from contents of %s", self._out(destfile), self._in(srcdir)) + logger.debug("Using %s %s compression", compression, compressargs or "") + mkcpio(self._in(srcdir), self._out(destfile), compression=compression, compressargs=compressargs)
+ +
[docs] def mkdir(self, *dirs): + ''' + mkdir DIR [DIR ...] + Create the named DIR(s). Will create leading directories as needed. + + Example: + mkdir /images + ''' + for d in dirs: + d = self._out(d) + if not isdir(d): + os.makedirs(d)
+ +
[docs] def replace(self, pat, repl, *fileglobs): + ''' + replace PATTERN REPLACEMENT FILEGLOB [FILEGLOB ...] + Find-and-replace the given PATTERN (Python-style regex) with the given + REPLACEMENT string for each of the files listed. + + Example: + replace @VERSION@ ${product.version} /boot/grub.conf /boot/isolinux.cfg + ''' + match = False + for g in fileglobs: + for f in rglob(self._out(g)): + match = True + replace(f, pat, repl) + if not match: + raise IOError("no files matched %s" % " ".join(fileglobs))
+ +
[docs] def append(self, filename, data): + ''' + append FILE STRING + Append STRING (followed by a newline character) to FILE. + Python character escape sequences ('\\n', '\\t', etc.) will be + converted to the appropriate characters. + + Examples: + + append /etc/depmod.d/dd.conf "search updates built-in" + append /etc/resolv.conf "" + ''' + with open(self._out(filename), "a") as fobj: + fobj.write(bytes(data, "utf8").decode('unicode_escape')+"\n")
+ +
[docs] def treeinfo(self, section, key, *valuetoks): + ''' + treeinfo SECTION KEY ARG [ARG ...] + Add an item to the treeinfo data store. + The given SECTION will have a new item added where + KEY = ARG ARG ... + + Example: + treeinfo images-${kernel.arch} boot.iso images/boot.iso + ''' + if section not in self.results.treeinfo: + self.results.treeinfo[section] = dict() + self.results.treeinfo[section][key] = " ".join(valuetoks)
+ +
[docs] def installkernel(self, section, src, dest): + ''' + installkernel SECTION SRC DEST + Install the kernel from SRC in the input tree to DEST in the output + tree, and then add an item to the treeinfo data store, in the named + SECTION, where "kernel" = DEST. + + Equivalent to: + install SRC DEST + treeinfo SECTION kernel DEST + ''' + self.install(src, dest) + self.treeinfo(section, "kernel", dest)
+ +
[docs] def installinitrd(self, section, src, dest): + ''' + installinitrd SECTION SRC DEST + Same as installkernel, but for "initrd". + ''' + self.install(src, dest) + self.chmod(dest, '644') + self.treeinfo(section, "initrd", dest)
+ +
[docs] def installupgradeinitrd(self, section, src, dest): + ''' + installupgradeinitrd SECTION SRC DEST + Same as installkernel, but for "upgrade". + ''' + self.install(src, dest) + self.chmod(dest, '644') + self.treeinfo(section, "upgrade", dest)
+ + + + + +
[docs] def copy(self, src, dest): + ''' + copy SRC DEST + Copy SRC to DEST. + If DEST is a directory, SRC will be copied inside it. + If DEST doesn't exist, SRC will be copied to a file with + that name, if the path leading to it exists. + ''' + try: + cpfile(self._out(src), self._out(dest)) + except shutil.Error as e: + logger.error(e)
+ +
[docs] def move(self, src, dest): + ''' + move SRC DEST + Move SRC to DEST. + ''' + mvfile(self._out(src), self._out(dest))
+ +
[docs] def remove(self, *fileglobs): + ''' + remove FILEGLOB [FILEGLOB ...] + Remove all the named files or directories. + Will *not* raise exceptions if the file(s) are not found. + ''' + for g in fileglobs: + for f in rglob(self._out(g)): + remove(f) + logger.debug("removed %s", f)
+ +
[docs] def chmod(self, fileglob, mode): + ''' + chmod FILEGLOB OCTALMODE + Change the mode of all the files matching FILEGLOB to OCTALMODE. + ''' + for f in rglob(self._out(fileglob), fatal=True): + os.chmod(f, int(mode,8))
+ +
[docs] def log(self, msg): + ''' + log MESSAGE + Emit the given log message. Be sure to put it in quotes! + + Example: + log "Reticulating splines, please wait..." + ''' + logger.info(msg)
+ + # TODO: add ssh-keygen, mkisofs(?), find, and other useful commands +
[docs] def runcmd(self, *cmdlist): + ''' + runcmd CMD [ARG ...] + Run the given command with the given arguments. + + NOTE: All paths given MUST be COMPLETE, ABSOLUTE PATHS to the file + or files mentioned. ${root}/${inroot}/${outroot} are good for + constructing these paths. + + FURTHER NOTE: Please use this command only as a last resort! + Whenever possible, you should use the existing template commands. + If the existing commands don't do what you need, fix them! + + Examples: + (this should be replaced with a "find" function) + runcmd find ${root} -name "*.pyo" -type f -delete + %for f in find(root, name="*.pyo"): + remove ${f} + %endfor + ''' + cmd = cmdlist + logger.debug('running command: %s', cmd) + if cmd[0].startswith("--chdir="): + logger.error("--chdir is no longer supported for runcmd.") + raise ValueError("--chdir is no longer supported for runcmd.") + + try: + stdout = runcmd_output(cmd) + if stdout: + logger.debug('command output:\n%s', stdout) + logger.debug("command finished successfully") + except CalledProcessError as e: + if e.output: + logger.error('command output:\n%s', e.output) + logger.error('command returned failure (%d)', e.returncode) + raise
+ +
[docs] def installpkg(self, *pkgs): + ''' + installpkg [--required|--optional] [--except PKGGLOB [--except PKGGLOB ...]] PKGGLOB [PKGGLOB ...] + Request installation of all packages matching the given globs. + Note that this is just a *request* - nothing is *actually* installed + until the 'run_pkg_transaction' command is given. + + --required is now the default. If the PKGGLOB can be missing pass --optional + ''' + if pkgs[0] == '--optional': + pkgs = pkgs[1:] + required = False + elif pkgs[0] == '--required': + pkgs = pkgs[1:] + required = True + else: + required = True + + excludes = [] + while '--except' in pkgs: + idx = pkgs.index('--except') + if len(pkgs) == idx+1: + raise ValueError("installpkg needs an argument after --except") + + excludes.append(pkgs[idx+1]) + pkgs = pkgs[:idx] + pkgs[idx+2:] + + errors = False + for p in pkgs: + try: + # Start by using Subject to generate a package query, which will + # give us a query object similar to what dbo.install would select, + # minus the handling for multilib. This query may contain + # multiple arches. Pull the package names out of that, filter any + # that match the excludes patterns, and pass those names back to + # dbo.install to do the actual, arch and version and multilib + # aware, package selction. + + # dnf queries don't have a concept of negative globs which is why + # the filtering is done the hard way. + + pkgnames = [pkg for pkg in dnf.subject.Subject(p).get_best_query(self.dbo.sack).filter(latest=True)] + if not pkgnames: + raise dnf.exceptions.PackageNotFoundError("no package matched", p) + + # Apply excludes to the name only + for exclude in excludes: + pkgnames = [pkg for pkg in pkgnames if not fnmatch.fnmatch(pkg.name, exclude)] + + # Convert to a sorted NVR list for installation + pkgnvrs = sorted(["{}-{}-{}".format(pkg.name, pkg.version, pkg.release) for pkg in pkgnames]) + + # If the request is a glob, expand it in the log + if any(g for g in ['*','?','.'] if g in p): + logger.info("installpkg: %s expands to %s", p, ",".join(pkgnvrs)) + + for pkgnvr in pkgnvrs: + try: + self.dbo.install(pkgnvr) + except Exception as e: # pylint: disable=broad-except + if required: + raise + # Not required, log it and continue processing pkgs + logger.error("installpkg %s failed: %s", pkgnvr, str(e)) + except Exception as e: # pylint: disable=broad-except + logger.error("installpkg %s failed: %s", p, str(e)) + errors = True + + if errors and required: + raise Exception("Required installpkg failed.")
+ +
[docs] def removepkg(self, *pkgs): + ''' + removepkg PKGGLOB [PKGGLOB...] + Delete the named package(s). + + IMPLEMENTATION NOTES: + RPM scriptlets (%preun/%postun) are *not* run. + Files are deleted, but directories are left behind. + ''' + for p in pkgs: + filepaths = [f.lstrip('/') for f in self._filelist(p)] + # TODO: also remove directories that aren't owned by anything else + if filepaths: + logger.debug("removepkg %s: %ikb", p, self._getsize(*filepaths)/1024) + self.remove(*filepaths) + else: + logger.debug("removepkg %s: no files to remove!", p)
+ +
[docs] def run_pkg_transaction(self): + ''' + run_pkg_transaction + Actually install all the packages requested by previous 'installpkg' + commands. + ''' + try: + logger.info("Checking dependencies") + self.dbo.resolve() + except dnf.exceptions.DepsolveError as e: + logger.error("Dependency check failed: %s", e) + raise + logger.info("%d packages selected", len(self.dbo.transaction)) + if len(self.dbo.transaction) == 0: + raise Exception("No packages in transaction") + + # If a debug repo has been included, write out a list of debuginfo packages + self._write_debuginfo_log() + + pkgs_to_download = self.dbo.transaction.install_set + logger.info("Downloading packages") + progress = LoraxDownloadCallback() + try: + self.dbo.download_packages(pkgs_to_download, progress) + except dnf.exceptions.DownloadError as e: + logger.error("Failed to download the following packages: %s", e) + raise + + logger.info("Preparing transaction from installation source") + try: + display = LoraxRpmCallback() + self.dbo.do_transaction(display=display) + except BaseException as e: + logger.error("The transaction process has ended abruptly: %s", e) + raise + + # Reset the package sack to pick up the installed packages + self.dbo.reset(repos=False) + self.dbo.fill_sack(load_system_repo=True, load_available_repos=False) + + # At this point dnf should know about the installed files. Double check that it really does. + if len(self._filelist("anaconda-core")) == 0: + raise Exception("Failed to reset dbo to installed package set")
+ +
[docs] def removefrom(self, pkg, *globs): + ''' + removefrom PKGGLOB [--allbut] FILEGLOB [FILEGLOB...] + Remove all files matching the given file globs from the package + (or packages) named. + If '--allbut' is used, all the files from the given package(s) will + be removed *except* the ones which match the file globs. + + Examples: + removefrom usbutils /usr/bin/* + removefrom xfsprogs --allbut /sbin/* + ''' + cmd = "%s %s" % (pkg, " ".join(globs)) # save for later logging + keepmatches = False + if globs[0] == '--allbut': + keepmatches = True + globs = globs[1:] + # get pkg filelist and find files that match the globs + filelist = self._filelist(pkg) + matches = set() + for g in globs: + globs_re = re.compile(fnmatch.translate(g)) + m = [f for f in filelist if globs_re.match(f)] + if m: + matches.update(m) + else: + logger.debug("removefrom %s %s: no files matched!", pkg, g) + # are we removing the matches, or keeping only the matches? + if keepmatches: + remove_files = filelist.difference(matches) + else: + remove_files = matches + # remove the files + if remove_files: + logger.debug("removefrom %s: removed %i/%i files, %ikb/%ikb", cmd, + len(remove_files), len(filelist), + self._getsize(*remove_files)/1024, self._getsize(*filelist)/1024) + self.remove(*remove_files) + else: + logger.debug("removefrom %s: no files to remove!", cmd)
+ + # pylint: disable=anomalous-backslash-in-string +
[docs] def removekmod(self, *globs): + ''' + removekmod GLOB [GLOB...] [--allbut] KEEPGLOB [KEEPGLOB...] + Remove all files and directories matching the given file globs from the kernel + modules directory. + + If '--allbut' is used, all the files from the modules will be removed *except* + the ones which match the file globs. There must be at least one initial GLOB + to search and one KEEPGLOB to keep. The KEEPGLOB is expanded to be *KEEPGLOB* + so that it will match anywhere in the path. + + This only removes files from under /lib/modules/\*/kernel/ + + Examples: + removekmod sound drivers/media drivers/hwmon drivers/video + removekmod drivers/char --allbut virtio_console hw_random + ''' + cmd = " ".join(globs) + if "--allbut" in globs: + idx = globs.index("--allbut") + if idx == 0: + raise ValueError("removekmod needs at least one GLOB before --allbut") + + # Apply keepglobs anywhere they appear in the path + keepglobs = globs[idx+1:] + if len(keepglobs) == 0: + raise ValueError("removekmod needs at least one GLOB after --allbut") + + globs = globs[:idx] + else: + # Nothing to keep + keepglobs = [] + + filelist = set() + for g in globs: + for top_dir in rglob(self._out("/lib/modules/*/kernel/"+g)): + for root, _dirs, files in os.walk(top_dir): + filelist.update(root+"/"+f for f in files) + + # Remove anything matching keepglobs from the list + matches = set() + for g in keepglobs: + globs_re = re.compile(fnmatch.translate("*"+g+"*")) + m = [f for f in filelist if globs_re.match(f)] + if m: + matches.update(m) + else: + logger.debug("removekmod %s: no files matched!", g) + remove_files = filelist.difference(matches) + + if remove_files: + logger.debug("removekmod: removing %d files", len(remove_files)) + list(remove(f) for f in remove_files) + else: + logger.debug("removekmod %s: no files to remove!", cmd)
+ +
[docs] def createaddrsize(self, addr, src, dest): + ''' + createaddrsize INITRD_ADDRESS INITRD ADDRSIZE + Create the initrd.addrsize file required in LPAR boot process. + + Examples: + createaddrsize ${INITRD_ADDRESS} ${outroot}/${BOOTDIR}/initrd.img ${outroot}/${BOOTDIR}/initrd.addrsize + ''' + addrsize = open(dest, "wb") + addrsize_data = struct.pack(">iiii", 0, int(addr, 16), 0, os.stat(src).st_size) + addrsize.write(addrsize_data) + addrsize.close()
+ +
[docs] def systemctl(self, cmd, *units): + ''' + systemctl [enable|disable|mask] UNIT [UNIT...] + Enable, disable, or mask the given systemd units. + + Examples: + systemctl disable lvm2-monitor.service + systemctl mask fedora-storage-init.service fedora-configure.service + ''' + if cmd not in ('enable', 'disable', 'mask'): + raise ValueError('unsupported systemctl cmd: %s' % cmd) + if not units: + logger.debug("systemctl: no units given for %s, ignoring", cmd) + return + self.mkdir("/run/systemd/system") # XXX workaround for systemctl bug + systemctl = ['systemctl', '--root', self.outroot, '--no-reload', cmd] + # When a unit doesn't exist systemd aborts the command. Run them one at a time. + # XXX for some reason 'systemctl enable/disable' always returns 1 + for unit in units: + try: + cmd = systemctl + [unit] + runcmd(cmd) + except CalledProcessError: + pass
+ +
[docs]class LiveTemplateRunner(TemplateRunner): + """ + This class parses and executes a limited Lorax template. Sample usage: + + # install a bunch of packages + runner = LiveTemplateRunner(dbo, templatedir, defaults) + runner.run("live-install.tmpl") + + It is meant to be used with the live-install.tmpl which lists the per-arch + pacages needed to build the live-iso output. + """ + def __init__(self, dbo, fatalerrors=True, templatedir=None, defaults=None): + self.dbo = dbo + self.pkgs = [] + self.pkgnames = [] + + super(LiveTemplateRunner, self).__init__(fatalerrors, templatedir, defaults) + +
[docs] def installpkg(self, *pkgs): + ''' + installpkg [--required|--optional] [--except PKGGLOB [--except PKGGLOB ...]] PKGGLOB [PKGGLOB ...] + Request installation of all packages matching the given globs. + Note that this is just a *request* - nothing is *actually* installed + until the 'run_pkg_transaction' command is given. + + --required is now the default. If the PKGGLOB can be missing pass --optional + ''' + if pkgs[0] == '--optional': + pkgs = pkgs[1:] + required = False + elif pkgs[0] == '--required': + pkgs = pkgs[1:] + required = True + else: + required = True + + excludes = [] + while '--except' in pkgs: + idx = pkgs.index('--except') + if len(pkgs) == idx+1: + raise ValueError("installpkg needs an argument after --except") + + excludes.append(pkgs[idx+1]) + pkgs = pkgs[:idx] + pkgs[idx+2:] + + errors = False + for p in pkgs: + try: + # Start by using Subject to generate a package query, which will + # give us a query object similar to what dbo.install would select, + # minus the handling for multilib. This query may contain + # multiple arches. Pull the package names out of that, filter any + # that match the excludes patterns, and pass those names back to + # dbo.install to do the actual, arch and version and multilib + # aware, package selction. + + # dnf queries don't have a concept of negative globs which is why + # the filtering is done the hard way. + + pkgnames = [pkg for pkg in dnf.subject.Subject(p).get_best_query(self.dbo.sack).filter(latest=True)] + if not pkgnames: + raise dnf.exceptions.PackageNotFoundError("no package matched", p) + + # Apply excludes to the name only + for exclude in excludes: + pkgnames = [pkg for pkg in pkgnames if not fnmatch.fnmatch(pkg.name, exclude)] + + # Convert to a sorted NVR list for installation + pkgnvrs = sorted(["{}-{}-{}".format(pkg.name, pkg.version, pkg.release) for pkg in pkgnames]) + + # If the request is a glob, expand it in the log + if any(g for g in ['*','?','.'] if g in p): + logger.info("installpkg: %s expands to %s", p, ",".join(pkgnvrs)) + + self.pkgs.extend(pkgnvrs) + self.pkgnames.extend([pkg.name for pkg in pkgnames]) + except Exception as e: # pylint: disable=broad-except + logger.error("installpkg %s failed: %s", p, str(e)) + errors = True + + if errors and required: + raise Exception("Required installpkg failed.")
+
+ +
+ +
+ + +
+
+ +
+ +
+ + + + + + + + + + + + \ No newline at end of file diff --git a/f30-branch/_modules/pylorax/monitor.html b/f30-branch/_modules/pylorax/monitor.html new file mode 100644 index 00000000..02a71c41 --- /dev/null +++ b/f30-branch/_modules/pylorax/monitor.html @@ -0,0 +1,403 @@ + + + + + + + + + + + pylorax.monitor — Lorax 30.19 documentation + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + +
+ + + + + +
+ +
+ + + + + + + + + + + + + + + + + +
+ + + + +
+
+
+
+ +

Source code for pylorax.monitor

+# monitor.py
+#
+# Copyright (C) 2011-2015  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/>.
+#
+# Author(s): Brian C. Lane <bcl@redhat.com>
+#
+import logging
+log = logging.getLogger("livemedia-creator")
+
+import re
+import socket
+import socketserver
+import threading
+import time
+
+
[docs]class LogRequestHandler(socketserver.BaseRequestHandler): + """ + Handle monitoring and saving the logfiles from the virtual install + + Incoming data is written to self.server.log_path and each line is checked + for patterns that would indicate that the installation failed. + self.server.log_error is set True when this happens. + """ + + simple_tests = [ + "Traceback (", + "traceback script(s) have been run", + "Out of memory:", + "Call Trace:", + "insufficient disk space:", + "Not enough disk space to download the packages", + "error populating transaction after", + "crashed on signal", + "packaging: Missed: NoSuchPackage", + "packaging: Installation failed", + "The following error occurred while installing. This is a fatal error" + ] + + re_tests = [ + r"packaging: base repo .* not valid", + r"packaging: .* requires .*" + ] + +
[docs] def setup(self): + """Start writing to self.server.log_path""" + + if self.server.log_path: + self.fp = open(self.server.log_path, "w") # pylint: disable=attribute-defined-outside-init + else: + self.fp = None + self.request.settimeout(10)
+ +
[docs] def handle(self): + """ + Write incoming data to a logfile and check for errors + + Split incoming data into lines and check for any Tracebacks or other + errors that indicate that the install failed. + + Loops until self.server.kill is True + """ + log.info("Processing logs from %s", self.client_address) + line = "" + while True: + if self.server.kill: + break + + try: + data = str(self.request.recv(4096), "utf8") + if self.fp: + self.fp.write(data) + self.fp.flush() + + # check the data for errors and set error flag + # need to assemble it into lines so we can test for the error + # string. + while data: + more = data.split("\n", 1) + line += more[0] + if len(more) > 1: + self.iserror(line) + line = "" + data = more[1] + else: + data = None + + except socket.timeout: + pass + except Exception as e: # pylint: disable=broad-except + log.info("log processing killed by exception: %s", e) + break
+ +
[docs] def finish(self): + log.info("Shutting down log processing") + self.request.close() + if self.fp: + self.fp.close()
+ +
[docs] def iserror(self, line): + """ + Check a line to see if it contains an error indicating installation failure + + :param str line: log line to check for failure + + If the line contains IGNORED it will be skipped. + """ + if "IGNORED" in line: + return + + for t in self.simple_tests: + if t in line: + self.server.log_error = True + self.server.error_line = line + return + for t in self.re_tests: + if re.search(t, line): + self.server.log_error = True + self.server.error_line = line + return
+ + +
[docs]class LogServer(socketserver.TCPServer): + """A TCP Server that listens for log data""" + + # Number of seconds to wait for a connection after startup + timeout = 60 + + def __init__(self, log_path, *args, **kwargs): + """ + Setup the log server + + :param str log_path: Path to the log file to write + """ + self.kill = False + self.log_error = False + self.error_line = "" + self.log_path = log_path + self._timeout = kwargs.pop("timeout", None) + if self._timeout: + self._start_time = time.time() + socketserver.TCPServer.__init__(self, *args, **kwargs) + +
[docs] def log_check(self): + """ + Check to see if an error has been found in the log + + :returns: True if there has been an error + :rtype: bool + """ + if self._timeout: + taking_too_long = time.time() > self._start_time + (self._timeout * 60) + if taking_too_long: + log.error("Canceling installation due to timeout") + else: + taking_too_long = False + return self.log_error or taking_too_long
+ + +
[docs]class LogMonitor(object): + """ + Setup a server to monitor the logs output by the installation + + This needs to be running before the virt-install runs, it expects + there to be a listener on the port used for the virtio log port. + """ + def __init__(self, log_path=None, host="localhost", port=0, timeout=None, log_request_handler_class=LogRequestHandler): + """ + Start a thread to monitor the logs. + + :param str log_path: Path to the logfile to write + :param str host: Host to bind to. Default is localhost. + :param int port: Port to listen to or 0 to pick a port + + If 0 is passed for the port the dynamically assigned port will be + available as self.port + + If log_path isn't set then it only monitors the logs, instead of + also writing them to disk. + """ + self.server = LogServer(log_path, (host, port), log_request_handler_class, timeout=timeout) + self.host, self.port = self.server.server_address + self.log_path = log_path + self.server_thread = threading.Thread(target=self.server.handle_request) + self.server_thread.daemon = True + self.server_thread.start() + +
[docs] def shutdown(self): + """Force shutdown of the monitoring thread""" + self.server.kill = True + self.server_thread.join()
+
+ +
+ +
+ + +
+
+ +
+ +
+ + + + + + + + + + + + \ No newline at end of file diff --git a/f30-branch/_modules/pylorax/mount.html b/f30-branch/_modules/pylorax/mount.html new file mode 100644 index 00000000..d98e9af0 --- /dev/null +++ b/f30-branch/_modules/pylorax/mount.html @@ -0,0 +1,319 @@ + + + + + + + + + + + pylorax.mount — Lorax 30.7 documentation + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + +
+ + + + + +
+ +
+ + + + + + + + + + + + + + + + + +
+ + + + +
+
+
+
+ +

Source code for pylorax.mount

+# mount.py
+#
+# Copyright (C) 2011-2015  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/>.
+#
+# Author(s): Brian C. Lane <bcl@redhat.com>
+#
+import logging
+log = logging.getLogger("livemedia-creator")
+
+import os
+
+from pylorax.imgutils import mount, umount
+from pylorax.executils import execWithCapture
+
+
[docs]class IsoMountpoint(object): + """ + Mount the iso and check to make sure the vmlinuz and initrd.img files exist + + Also check the iso for a a stage2 image and set a flag and extract the + iso's label. + + stage2 can be either LiveOS/squashfs.img or images/install.img + """ + def __init__(self, iso_path, initrd_path=None): + """ + Mount the iso + + :param str iso_path: Path to the iso to mount + :param str initrd_path: Optional path to initrd + + initrd_path can be used to point to a tree with a newer + initrd.img than the iso has. The iso is still used for stage2. + + self.kernel and self.initrd point to the kernel and initrd. + self.stage2 is set to True if there is a stage2 image. + self.repo is the path to the mounted iso if there is a /repodata dir. + """ + self.label = None + self.iso_path = iso_path + self.initrd_path = initrd_path + + if not self.initrd_path: + self.mount_dir = mount(self.iso_path, opts="loop") + else: + self.mount_dir = self.initrd_path + + kernel_list = [("/isolinux/vmlinuz", "/isolinux/initrd.img"), + ("/ppc/ppc64/vmlinuz", "/ppc/ppc64/initrd.img")] + + if os.path.isdir(self.mount_dir+"/repodata"): + self.repo = self.mount_dir + else: + self.repo = None + self.stage2 = os.path.exists(self.mount_dir+"/LiveOS/squashfs.img") or \ + os.path.exists(self.mount_dir+"/images/install.img") + + try: + for kernel, initrd in kernel_list: + if (os.path.isfile(self.mount_dir+kernel) and + os.path.isfile(self.mount_dir+initrd)): + self.kernel = self.mount_dir+kernel + self.initrd = self.mount_dir+initrd + break + else: + raise Exception("Missing kernel and initrd file in iso, failed" + " to search under: {0}".format(kernel_list)) + except: + self.umount() + raise + + self.get_iso_label() + +
[docs] def umount( self ): + """Unmount the iso""" + if not self.initrd_path: + umount(self.mount_dir)
+ +
[docs] def get_iso_label(self): + """ + Get the iso's label using isoinfo + + Sets self.label if one is found + """ + isoinfo_output = execWithCapture("isoinfo", ["-d", "-i", self.iso_path]) + log.debug(isoinfo_output) + for line in isoinfo_output.splitlines(): + if line.startswith("Volume id: "): + self.label = line[11:] + return
+
+ +
+ +
+ + +
+
+ +
+ +
+ + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/f30-branch/_modules/pylorax/sysutils.html b/f30-branch/_modules/pylorax/sysutils.html new file mode 100644 index 00000000..f0d5b096 --- /dev/null +++ b/f30-branch/_modules/pylorax/sysutils.html @@ -0,0 +1,332 @@ + + + + + + + + + + + pylorax.sysutils — Lorax 30.19 documentation + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + +
+ + + + + +
+ +
+ + + + + + + + + + + + + + + + + +
+ + + + +
+
+
+
+ +

Source code for pylorax.sysutils

+#
+# sysutils.py
+#
+# Copyright (C) 2009-2015 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>
+#
+
+__all__ = ["joinpaths", "touch", "replace", "chown_", "chmod_", "remove",
+           "linktree"]
+
+import sys
+import os
+import re
+import fileinput
+import pwd
+import grp
+import glob
+import shutil
+import shlex
+from configparser import ConfigParser
+
+from pylorax.executils import runcmd
+
+
[docs]def joinpaths(*args, **kwargs): + path = os.path.sep.join(args) + + if kwargs.get("follow_symlinks"): + return os.path.realpath(path) + else: + return path
+ + +
[docs]def touch(fname): + # python closes the file when it goes out of scope + open(fname, "w").write("")
+ + +
[docs]def replace(fname, find, sub): + fin = fileinput.input(fname, inplace=1) + pattern = re.compile(find) + + for line in fin: + line = pattern.sub(sub, line) + sys.stdout.write(line) + + fin.close()
+ + +
[docs]def chown_(path, user=None, group=None, recursive=False): + uid = gid = -1 + + if user is not None: + uid = pwd.getpwnam(user)[2] + if group is not None: + gid = grp.getgrnam(group)[2] + + for fname in glob.iglob(path): + os.chown(fname, uid, gid) + + if recursive and os.path.isdir(fname): + for nested in os.listdir(fname): + nested = joinpaths(fname, nested) + chown_(nested, user, group, recursive)
+ + +
[docs]def chmod_(path, mode, recursive=False): + for fname in glob.iglob(path): + os.chmod(fname, mode) + + if recursive and os.path.isdir(fname): + for nested in os.listdir(fname): + nested = joinpaths(fname, nested) + chmod_(nested, mode, recursive)
+ + +def cpfile(src, dst): + shutil.copy2(src, dst) + if os.path.isdir(dst): + dst = joinpaths(dst, os.path.basename(src)) + + return dst + +def mvfile(src, dst): + if os.path.isdir(dst): + dst = joinpaths(dst, os.path.basename(src)) + os.rename(src, dst) + return dst + +
[docs]def remove(target): + if os.path.isdir(target) and not os.path.islink(target): + shutil.rmtree(target) + else: + os.unlink(target)
+ +
[docs]def linktree(src, dst): + runcmd(["/bin/cp", "-alx", src, dst])
+ +def unquote(s): + return ' '.join(shlex.split(s)) + +class UnquotingConfigParser(ConfigParser): + """A ConfigParser, only with unquoting of the values.""" + # pylint: disable=arguments-differ + def get(self, *args, **kwargs): + ret = super().get(*args, **kwargs) + if ret: + ret = unquote(ret) + return ret + +def flatconfig(filename): + """Use UnquotingConfigParser to read a flat config file (without + section headers) by adding a section header. + """ + with open (filename, 'r') as conffh: + conftext = "[main]\n" + conffh.read() + config = UnquotingConfigParser() + config.read_string(conftext) + return config['main'] +
+ +
+ +
+ + +
+
+ +
+ +
+ + + + + + + + + + + + \ No newline at end of file diff --git a/f30-branch/_modules/pylorax/treebuilder.html b/f30-branch/_modules/pylorax/treebuilder.html new file mode 100644 index 00000000..15862c39 --- /dev/null +++ b/f30-branch/_modules/pylorax/treebuilder.html @@ -0,0 +1,603 @@ + + + + + + + + + + + pylorax.treebuilder — Lorax 30.19 documentation + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + +
+ + + + + +
+ +
+ + + + + + + + + + + + + + + + + +
+ + + + +
+
+
+
+ +

Source code for pylorax.treebuilder

+# treebuilder.py - handle arch-specific tree building stuff using templates
+#
+# Copyright (C) 2011-2015 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/>.
+#
+# Author(s):  Will Woods <wwoods@redhat.com>
+
+import logging
+logger = logging.getLogger("pylorax.treebuilder")
+
+import os, re
+from os.path import basename
+from shutil import copytree, copy2
+from pathlib import Path
+import itertools
+
+from pylorax.sysutils import joinpaths, remove
+from pylorax.base import DataHolder
+from pylorax.ltmpl import LoraxTemplateRunner
+import pylorax.imgutils as imgutils
+from pylorax.executils import runcmd, runcmd_output, execWithCapture
+
+templatemap = {
+    'i386':    'x86.tmpl',
+    'x86_64':  'x86.tmpl',
+    'ppc64le': 'ppc64le.tmpl',
+    's390':    's390.tmpl',
+    's390x':   's390.tmpl',
+    'aarch64': 'aarch64.tmpl',
+    'arm':     'arm.tmpl',
+    'armhfp':  'arm.tmpl',
+}
+
+
[docs]def generate_module_info(moddir, outfile=None): + def module_desc(mod): + output = runcmd_output(["modinfo", "-F", "description", mod]) + return output.strip() + def read_module_set(name): + return set(l.strip() for l in open(joinpaths(moddir,name)) if ".ko" in l) + modsets = {'scsi':read_module_set("modules.block"), + 'eth':read_module_set("modules.networking")} + + modinfo = list() + for root, _dirs, files in os.walk(moddir): + for modtype, modset in modsets.items(): + for mod in modset.intersection(files): # modules in this dir + (name, _ext) = os.path.splitext(mod) # foo.ko -> (foo, .ko) + desc = module_desc(joinpaths(root,mod)) or "%s driver" % name + modinfo.append(dict(name=name, type=modtype, desc=desc)) + + out = open(outfile or joinpaths(moddir,"module-info"), "w") + out.write("Version 0\n") + for mod in sorted(modinfo, key=lambda m: m.get('name')): + out.write('{name}\n\t{type}\n\t"{desc:.65}"\n'.format(**mod))
+ +
[docs]class RuntimeBuilder(object): + '''Builds the anaconda runtime image.''' + def __init__(self, product, arch, dbo, templatedir=None, + installpkgs=None, excludepkgs=None, + add_templates=None, + add_template_vars=None): + root = dbo.conf.installroot + # use a copy of product so we can modify it locally + product = product.copy() + product.name = product.name.lower() + self.vars = DataHolder(arch=arch, product=product, dbo=dbo, root=root, + basearch=arch.basearch, libdir=arch.libdir) + self.dbo = dbo + self._runner = LoraxTemplateRunner(inroot=root, outroot=root, + dbo=dbo, templatedir=templatedir) + self.add_templates = add_templates or [] + self.add_template_vars = add_template_vars or {} + self._installpkgs = installpkgs or [] + self._excludepkgs = excludepkgs or [] + self._runner.defaults = self.vars + self.dbo.reset() + + def _install_branding(self): + release = None + q = self.dbo.sack.query() + a = q.available() + for pkg in a.filter(provides='system-release'): + logger.debug("Found release package %s", pkg) + if pkg.name.startswith('generic'): + continue + else: + release = pkg.name + break + + if not release: + logger.error('could not get the release') + return + + # release + logger.info('got release: %s', release) + self._runner.installpkg(release) + + # logos + release, _suffix = release.split('-', 1) + self._runner.installpkg('%s-logos' % release) + +
[docs] def install(self): + '''Install packages and do initial setup with runtime-install.tmpl''' + self._install_branding() + if len(self._installpkgs) > 0: + self._runner.installpkg(*self._installpkgs) + if len(self._excludepkgs) > 0: + self._runner.removepkg(*self._excludepkgs) + self._runner.run("runtime-install.tmpl") + for tmpl in self.add_templates: + self._runner.run(tmpl, **self.add_template_vars)
+ +
[docs] def writepkglists(self, pkglistdir): + '''debugging data: write out lists of package contents''' + if not os.path.isdir(pkglistdir): + os.makedirs(pkglistdir) + q = self.dbo.sack.query() + for pkgobj in q.installed(): + with open(joinpaths(pkglistdir, pkgobj.name), "w") as fobj: + for fname in pkgobj.files: + fobj.write("{0}\n".format(fname))
+ +
[docs] def postinstall(self): + '''Do some post-install setup work with runtime-postinstall.tmpl''' + # copy configdir into runtime root beforehand + configdir = joinpaths(self._runner.templatedir,"config_files") + configdir_path = "tmp/config_files" + fullpath = joinpaths(self.vars.root, configdir_path) + if os.path.exists(fullpath): + remove(fullpath) + copytree(configdir, fullpath) + self._runner.run("runtime-postinstall.tmpl", configdir=configdir_path)
+ +
[docs] def cleanup(self): + '''Remove unneeded packages and files with runtime-cleanup.tmpl''' + self._runner.run("runtime-cleanup.tmpl")
+ +
[docs] def verify(self): + '''Ensure that contents of the installroot can run''' + status = True + + ELF_MAGIC = b'\x7fELF' + + # Iterate over all files in /usr/bin and /usr/sbin + # For ELF files, gather them into a list and we'll check them all at + # the end. For files with a #!, check them as we go + elf_files = [] + usr_bin = Path(self.vars.root + '/usr/bin') + usr_sbin = Path(self.vars.root + '/usr/sbin') + for path in (str(x) for x in itertools.chain(usr_bin.iterdir(), usr_sbin.iterdir()) \ + if x.is_file()): + with open(path, "rb") as f: + magic = f.read(4) + if magic == ELF_MAGIC: + # Save the path, minus the chroot prefix + elf_files.append(path[len(self.vars.root):]) + elif magic[:2] == b'#!': + # Reopen the file as text and read the first line. + # Open as latin-1 so that stray 8-bit characters don't make + # things blow up. We only really care about ASCII parts. + with open(path, "rt", encoding="latin-1") as f_text: + # Remove the #!, split on space, and take the first part + shabang = f_text.readline()[2:].split()[0] + + # Does the path exist? + if not os.path.exists(self.vars.root + shabang): + logger.error('%s, needed by %s, does not exist', shabang, path) + status = False + + # Now, run ldd on all the ELF files + # Just run ldd once on everything so it isn't logged a million times. + # At least one thing in the list isn't going to be a dynamic executable, + # so use execWithCapture to ignore the exit code. + filename = '' + for line in execWithCapture('ldd', elf_files, root=self.vars.root, + log_output=False, filter_stderr=True).split('\n'): + if line and not line[0].isspace(): + # New filename header, strip the : at the end and save + filename = line[:-1] + elif 'not found' in line: + logger.error('%s, needed by %s, not found', line.split()[0], filename) + status = False + + return status
+ +
[docs] def writepkgsizes(self, pkgsizefile): + '''debugging data: write a big list of pkg sizes''' + fobj = open(pkgsizefile, "w") + getsize = lambda f: os.lstat(f).st_size if os.path.exists(f) else 0 + q = self.dbo.sack.query() + for p in sorted(q.installed()): + pkgsize = sum(getsize(joinpaths(self.vars.root,f)) for f in p.files) + fobj.write("{0.name}.{0.arch}: {1}\n".format(p, pkgsize))
+ +
[docs] def generate_module_data(self): + root = self.vars.root + moddir = joinpaths(root, "lib/modules/") + for kernel in findkernels(root=root): + ksyms = joinpaths(root, "boot/System.map-%s" % kernel.version) + logger.info("doing depmod and module-info for %s", kernel.version) + runcmd(["depmod", "-a", "-F", ksyms, "-b", root, kernel.version]) + generate_module_info(moddir+kernel.version, outfile=moddir+"module-info")
+ +
[docs] def create_squashfs_runtime(self, outfile="/var/tmp/squashfs.img", compression="xz", compressargs=None, size=2): + """Create a plain squashfs runtime""" + compressargs = compressargs or [] + os.makedirs(os.path.dirname(outfile)) + + # squash the rootfs + imgutils.mksquashfs(self.vars.root, outfile, compression, compressargs)
+ +
[docs] def create_ext4_runtime(self, outfile="/var/tmp/squashfs.img", compression="xz", compressargs=None, size=2): + """Create a squashfs compressed ext4 runtime""" + # make live rootfs image - must be named "LiveOS/rootfs.img" for dracut + compressargs = compressargs or [] + workdir = joinpaths(os.path.dirname(outfile), "runtime-workdir") + os.makedirs(joinpaths(workdir, "LiveOS")) + + imgutils.mkrootfsimg(self.vars.root, joinpaths(workdir, "LiveOS/rootfs.img"), + "Anaconda", size=size) + + # squash the live rootfs and clean up workdir + imgutils.mksquashfs(workdir, outfile, compression, compressargs) + remove(workdir)
+ +
[docs] def finished(self): + """ Done using RuntimeBuilder + + Close the dnf base object + """ + self.dbo.close()
+ +
[docs]class TreeBuilder(object): + '''Builds the arch-specific boot images. + inroot should be the installtree root (the newly-built runtime dir)''' + def __init__(self, product, arch, inroot, outroot, runtime, isolabel, domacboot=True, doupgrade=True, + templatedir=None, add_templates=None, add_template_vars=None, workdir=None, extra_boot_args=""): + + # NOTE: if you pass an arg named "runtime" to a mako template it'll + # clobber some mako internal variables - hence "runtime_img". + self.vars = DataHolder(arch=arch, product=product, runtime_img=runtime, + runtime_base=basename(runtime), + inroot=inroot, outroot=outroot, + basearch=arch.basearch, libdir=arch.libdir, + isolabel=isolabel, udev=udev_escape, domacboot=domacboot, doupgrade=doupgrade, + workdir=workdir, lower=string_lower, + extra_boot_args=extra_boot_args) + self._runner = LoraxTemplateRunner(inroot, outroot, templatedir=templatedir) + self._runner.defaults = self.vars + self.add_templates = add_templates or [] + self.add_template_vars = add_template_vars or {} + self.templatedir = templatedir + self.treeinfo_data = None + + @property + def kernels(self): + return findkernels(root=self.vars.inroot) + +
[docs] def rebuild_initrds(self, add_args=None, backup="", prefix=""): + '''Rebuild all the initrds in the tree. If backup is specified, each + initrd will be renamed with backup as a suffix before rebuilding. + If backup is empty, the existing initrd files will be overwritten. + If suffix is specified, the existing initrd is untouched and a new + image is built with the filename "${prefix}-${kernel.version}.img" + + If the initrd doesn't exist its name will be created based on the + name of the kernel. + ''' + add_args = add_args or [] + dracut = ["dracut", "--nomdadmconf", "--nolvmconf"] + add_args + if not backup: + dracut.append("--force") + + if not self.kernels: + raise Exception("No kernels found, cannot rebuild_initrds") + + # Hush some dracut warnings. TODO: bind-mount proc in place? + open(joinpaths(self.vars.inroot,"/proc/modules"),"w") + for kernel in self.kernels: + if prefix: + idir = os.path.dirname(kernel.path) + outfile = joinpaths(idir, prefix+'-'+kernel.version+'.img') + elif hasattr(kernel, "initrd"): + # If there is an existing initrd, use that + outfile = kernel.initrd.path + else: + # Construct an initrd from the kernel name + outfile = kernel.path.replace("vmlinuz-", "initrd-") + ".img" + logger.info("rebuilding %s", outfile) + if backup: + initrd = joinpaths(self.vars.inroot, outfile) + if os.path.exists(initrd): + os.rename(initrd, initrd + backup) + cmd = dracut + [outfile, kernel.version] + runcmd(cmd, root=self.vars.inroot) + + os.unlink(joinpaths(self.vars.inroot,"/proc/modules"))
+ +
[docs] def build(self): + templatefile = templatemap[self.vars.arch.basearch] + for tmpl in self.add_templates: + self._runner.run(tmpl, **self.add_template_vars) + self._runner.run(templatefile, kernels=self.kernels) + self.treeinfo_data = self._runner.results.treeinfo + self.implantisomd5()
+ +
[docs] def implantisomd5(self): + for _section, data in self.treeinfo_data.items(): + if 'boot.iso' in data: + iso = joinpaths(self.vars.outroot, data['boot.iso']) + runcmd(["implantisomd5", iso])
+ + @property + def dracut_hooks_path(self): + """ Return the path to the lorax dracut hooks scripts + + Use the configured share dir if it is setup, + otherwise default to /usr/share/lorax/dracut_hooks + """ + if self.templatedir: + return joinpaths(self.templatedir, "dracut_hooks") + else: + return "/usr/share/lorax/dracut_hooks" + +
[docs] def copy_dracut_hooks(self, hooks): + """ Copy the hook scripts in hooks into the installroot's /tmp/ + and return a list of commands to pass to dracut when creating the + initramfs + + hooks is a list of tuples with the name of the hook script and the + target dracut hook directory + (eg. [("99anaconda-copy-ks.sh", "/lib/dracut/hooks/pre-pivot")]) + """ + dracut_commands = [] + for hook_script, dracut_path in hooks: + src = joinpaths(self.dracut_hooks_path, hook_script) + if not os.path.exists(src): + logger.error("Missing lorax dracut hook script %s", (src)) + continue + dst = joinpaths(self.vars.inroot, "/tmp/", hook_script) + copy2(src, dst) + dracut_commands += ["--include", joinpaths("/tmp/", hook_script), + dracut_path] + return dracut_commands
+ +#### TreeBuilder helper functions + +
[docs]def findkernels(root="/", kdir="boot"): + # To find possible flavors, awk '/BuildKernel/ { print $4 }' kernel.spec + flavors = ('debug', 'PAE', 'PAEdebug', 'smp', 'xen', 'lpae') + kre = re.compile(r"vmlinuz-(?P<version>.+?\.(?P<arch>[a-z0-9_]+)" + r"(.(?P<flavor>{0}))?)$".format("|".join(flavors))) + kernels = [] + bootfiles = os.listdir(joinpaths(root, kdir)) + for f in bootfiles: + match = kre.match(f) + if match: + kernel = DataHolder(path=joinpaths(kdir, f)) + kernel.update(match.groupdict()) # sets version, arch, flavor + kernels.append(kernel) + + # look for associated initrd/initramfs/etc. + for kernel in kernels: + for f in bootfiles: + if f.endswith('-'+kernel.version+'.img'): + imgtype, _rest = f.split('-',1) + # special backwards-compat case + if imgtype == 'initramfs': + imgtype = 'initrd' + kernel[imgtype] = DataHolder(path=joinpaths(kdir, f)) + + logger.debug("kernels=%s", kernels) + return kernels
+ +# udev whitelist: 'a-zA-Z0-9#+.:=@_-' (see is_whitelisted in libudev-util.c) +udev_blacklist=' !"$%&\'()*,/;<>?[\\]^`{|}~' # ASCII printable, minus whitelist +udev_blacklist += ''.join(chr(i) for i in range(32)) # ASCII non-printable +
[docs]def udev_escape(label): + out = '' + for ch in label: + out += ch if ch not in udev_blacklist else '\\x%02x' % ord(ch) + return out
+ +
[docs]def string_lower(string): + """ Return a lowercase string. + + :param string: String to lowercase + + This is used as a filter in the templates. + """ + return string.lower()
+
+ +
+ +
+ + +
+
+ +
+ +
+ + + + + + + + + + + + \ No newline at end of file diff --git a/f30-branch/_modules/pylorax/treeinfo.html b/f30-branch/_modules/pylorax/treeinfo.html new file mode 100644 index 00000000..ee1cf2bc --- /dev/null +++ b/f30-branch/_modules/pylorax/treeinfo.html @@ -0,0 +1,263 @@ + + + + + + + + + + + pylorax.treeinfo — Lorax 30.19 documentation + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + +
+ + + + + +
+ +
+ + + + + + + + + + + + + + + + + +
+ + + + +
+
+
+
+ +

Source code for pylorax.treeinfo

+#
+# treeinfo.py
+#
+# Copyright (C) 2010-2015 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>
+#
+
+import logging
+logger = logging.getLogger("pylorax.treeinfo")
+
+import configparser
+import os
+import time
+
+
+
[docs]class TreeInfo(object): + + def __init__(self, product, version, variant, basearch, + packagedir=""): + + self.c = configparser.ConfigParser() + + if 'SOURCE_DATE_EPOCH' in os.environ: + timestamp = os.environ['SOURCE_DATE_EPOCH'] + else: + timestamp = str(time.time()) + + section = "general" + data = {"timestamp": timestamp, + "family": product, + "version": version, + "name": "%s-%s" % (product, version), + "variant": variant or "", + "arch": basearch, + "packagedir": packagedir} + + self.c.add_section(section) + list(self.c.set(section, key, value) for key, value in data.items()) + +
[docs] def add_section(self, section, data): + if not self.c.has_section(section): + self.c.add_section(section) + + list(self.c.set(section, key, value) for key, value in data.items())
+ +
[docs] def write(self, outfile): + logger.info("writing .treeinfo file") + with open(outfile, "w") as fobj: + self.c.write(fobj)
+
+ +
+ +
+ + +
+
+ +
+ +
+ + + + + + + + + + + + \ No newline at end of file diff --git a/f30-branch/_sources/composer-cli.rst.txt b/f30-branch/_sources/composer-cli.rst.txt new file mode 100644 index 00000000..36cc92ec --- /dev/null +++ b/f30-branch/_sources/composer-cli.rst.txt @@ -0,0 +1,62 @@ +composer-cli +============ + +:Authors: + Brian C. Lane + +``composer-cli`` is used to interact with the ``lorax-composer`` API server, managing blueprints, exploring available packages, and building new images. + +It requires `lorax-composer `_ to be installed on the +local system, and the user running it needs to be a member of the ``weldr`` +group. They do not need to be root, but all of the `security precautions +`_ apply. + +composer-cli cmdline arguments +------------------------------ + +.. argparse:: + :ref: composer.cli.cmdline.composer_cli_parser + :prog: composer-cli + +Edit a Blueprint +---------------- + +Start out by listing the available blueprints using ``composer-cli blueprints +list``, pick one and save it to the local directory by running ``composer-cli +blueprints save http-server``. If there are no blueprints available you can +copy one of the examples `from the test suite +`_. + +Edit the file (it will be saved with a .toml extension) and change the +description, add a package or module to it. Send it back to the server by +running ``composer-cli blueprints push http-server.toml``. You can verify that it was +saved by viewing the changelog - ``composer-cli blueprints changes http-server``. + +Build an image +---------------- + +Build a ``qcow2`` disk image from this blueprint by running ``composer-cli +compose start http-server qcow2``. It will print a UUID that you can use to +keep track of the build. You can also cancel the build if needed. + +The available types of images is displayed by ``composer-cli compose types``. +Currently this consists of: alibaba, ami, ext4-filesystem, google, hyper-v, +live-iso, openstack, partitioned-disk, qcow2, tar, vhd, vmdk + +Monitor the build status +------------------------ + +Monitor it using ``composer-cli compose status``, which will show the status of +all the builds on the system. You can view the end of the anaconda build logs +once it is in the ``RUNNING`` state using ``composer-cli compose log UUID`` +where UUID is the UUID returned by the start command. + +Once the build is in the ``FINISHED`` state you can download the image. + +Download the image +------------------ + +Downloading the final image is done with ``composer-cli compose image UUID`` and it will +save the qcow2 image as ``UUID-disk.qcow2`` which you can then use to boot a VM like this:: + + qemu-kvm --name test-image -m 1024 -hda ./UUID-disk.qcow2 diff --git a/f30-branch/_sources/composer.cli.rst.txt b/f30-branch/_sources/composer.cli.rst.txt new file mode 100644 index 00000000..81a5776f --- /dev/null +++ b/f30-branch/_sources/composer.cli.rst.txt @@ -0,0 +1,86 @@ +composer.cli package +==================== + +Submodules +---------- + +composer.cli.blueprints module +------------------------------ + +.. automodule:: composer.cli.blueprints + :members: + :undoc-members: + :show-inheritance: + +composer.cli.cmdline module +--------------------------- + +.. automodule:: composer.cli.cmdline + :members: + :undoc-members: + :show-inheritance: + +composer.cli.compose module +--------------------------- + +.. automodule:: composer.cli.compose + :members: + :undoc-members: + :show-inheritance: + +composer.cli.help module +------------------------ + +.. automodule:: composer.cli.help + :members: + :undoc-members: + :show-inheritance: + +composer.cli.modules module +--------------------------- + +.. automodule:: composer.cli.modules + :members: + :undoc-members: + :show-inheritance: + +composer.cli.projects module +---------------------------- + +.. automodule:: composer.cli.projects + :members: + :undoc-members: + :show-inheritance: + +composer.cli.sources module +--------------------------- + +.. automodule:: composer.cli.sources + :members: + :undoc-members: + :show-inheritance: + +composer.cli.status module +-------------------------- + +.. automodule:: composer.cli.status + :members: + :undoc-members: + :show-inheritance: + +composer.cli.utilities module +----------------------------- + +.. automodule:: composer.cli.utilities + :members: + :undoc-members: + :show-inheritance: + + +Module contents +--------------- + +.. automodule:: composer.cli + :members: + :undoc-members: + :show-inheritance: diff --git a/f30-branch/_sources/composer.rst.txt b/f30-branch/_sources/composer.rst.txt new file mode 100644 index 00000000..5314fc0c --- /dev/null +++ b/f30-branch/_sources/composer.rst.txt @@ -0,0 +1,37 @@ +composer package +================ + +Subpackages +----------- + +.. toctree:: + + composer.cli + +Submodules +---------- + +composer.http\_client module +---------------------------- + +.. automodule:: composer.http_client + :members: + :undoc-members: + :show-inheritance: + +composer.unix\_socket module +---------------------------- + +.. automodule:: composer.unix_socket + :members: + :undoc-members: + :show-inheritance: + + +Module contents +--------------- + +.. automodule:: composer + :members: + :undoc-members: + :show-inheritance: diff --git a/f30-branch/_sources/index.rst.txt b/f30-branch/_sources/index.rst.txt new file mode 100644 index 00000000..40acd5a6 --- /dev/null +++ b/f30-branch/_sources/index.rst.txt @@ -0,0 +1,34 @@ +.. Lorax documentation master file, created by + sphinx-quickstart on Wed Apr 8 13:46:00 2015. + You can adapt this file completely to your liking, but it should at least + contain the root `toctree` directive. + +Welcome to Lorax's documentation! +================================= + +Contents: + +.. toctree:: + :maxdepth: 1 + + intro + lorax + livemedia-creator + lorax-composer + composer-cli + product-images + modules + +Documentation for other Lorax Branches +====================================== + +* `Fedora 28 `_ +* `RHEL7 lorax-composer `_ + +Indices and tables +================== + +* :ref:`genindex` +* :ref:`modindex` +* :ref:`search` + diff --git a/f30-branch/_sources/intro.rst.txt b/f30-branch/_sources/intro.rst.txt new file mode 100644 index 00000000..01857ee9 --- /dev/null +++ b/f30-branch/_sources/intro.rst.txt @@ -0,0 +1,67 @@ +Introduction to Lorax +===================== + +I am the Lorax. I speak for the trees [and images]. + +Lorax is used to build the Anaconda Installer boot.iso, it consists of a +library, pylorax, a set of templates, and the lorax script. Its operation +is driven by a customized set of Mako templates that lists the packages +to be installed, steps to execute to remove unneeded files, and creation +of the iso for all of the supported architectures. + + + + + + +Before Lorax +============ + +Tree building tools such as pungi and revisor rely on 'buildinstall' in +anaconda/scripts/ to produce the boot images and other such control files +in the final tree. The existing buildinstall scripts written in a mix of +bash and Python are unmaintainable. Lorax is an attempt to replace them +with something more flexible. + + +EXISTING WORKFLOW: + +pungi and other tools call scripts/buildinstall, which in turn call other +scripts to do the image building and data generation. Here's how it +currently looks: + + -> buildinstall + * process command line options + * write temporary yum.conf to point to correct repo + * find anaconda release RPM + * unpack RPM, pull in those versions of upd-instroot, mk-images, + maketreeinfo.py, makestamp.py, and buildinstall + + -> call upd-instroot + + -> call maketreeinfo.py + + -> call mk-images (which figures out which mk-images.ARCH to call) + + -> call makestamp.py + + * clean up + + +PROBLEMS: + +The existing workflow presents some problems with maintaining the scripts. +First, almost all knowledge of what goes in to the stage 1 and stage 2 +images lives in upd-instroot. The mk-images* scripts copy things from the +root created by upd-instroot in order to build the stage 1 image, though +it's not completely clear from reading the scripts. + + +NEW IDEAS: + +Create a new central driver with all information living in Python modules. +Configuration files will provide the knowledge previously contained in the +upd-instroot and mk-images* scripts. + + + diff --git a/f30-branch/_sources/livemedia-creator.rst.txt b/f30-branch/_sources/livemedia-creator.rst.txt new file mode 100644 index 00000000..4b4fea7f --- /dev/null +++ b/f30-branch/_sources/livemedia-creator.rst.txt @@ -0,0 +1,657 @@ +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 qemu 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 qemu. + +``--ks`` to select the kickstart file describing what to install. + +To use livemedia-creator with virtualization you will need to have qemu 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 +----------------------------------- + +.. argparse:: + :ref: pylorax.cmdline.lmc_parser + :prog: livemedia-creator + + +Quickstart +---------- + +Run this to create a bootable live iso:: + + sudo livemedia-creator --make-iso \ + --iso=/extra/iso/boot.iso --ks=./docs/fedora-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/fedora-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 qemu 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. + +.. note:: + The output from --make-iso includes the artifacts used to create the boot.iso; + the kernel, initrd, the squashfs filesystem, etc. If you only want the + boot.iso you can pass ``--iso-only`` and the other files will be removed. You + can also name the iso by using ``--iso-name my-live.iso``. + + +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 + grub2-efi + memtest86+ + syslinux + +User created repositories +~~~~~~~~~~~~~~~~~~~~~~~~~ + +If you are using your own repositories and installing groups (eg. @core) make +sure you create the repodata with groups like this ``createrepo -g +/path/to/groups.xml /path/to/rpms`` + +Using a Proxy with repos +~~~~~~~~~~~~~~~~~~~~~~~~ + +One drawback to using qemu 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. You can use all of the `kickstart commands `_ in your kickstart. Make sure there +is only one ``url`` command, other repos have to use the ``repo`` command and cannot be +named ``updates`` which is reserved for Anaconda's use. eg.:: + + url --url=PRIMARY-REPO-URL --proxy=PROXY-URL + repo --name="repo1" --baseurl=FIRST-REPO-URL --proxy=PROXY-URL + repo --name="repo2" --baseurl=SECOND-REPO_URL --proxy=PROXY-URL + + +Anaconda image install (no-virt) +-------------------------------- + +You can create images without using qemu 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. 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=./fedora-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. + +.. note:: + As of version 30.7 SELinux can be set to Enforcing. The current state is + logged for debugging purposes and if there are SELinux denials they should + be reported as a bug. + +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/fedora-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
+ + + +
+ + + + + +
+ +
+ + + + + + + + + + + + + + + + + +
+ + + + +
+
+
+
+ +

Source code for composer.cli

+#
+# 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.blueprints import blueprints_cmd
+from composer.cli.modules import modules_cmd
+from composer.cli.projects import projects_cmd
+from composer.cli.compose import compose_cmd
+from composer.cli.sources import sources_cmd
+from composer.cli.status import status_cmd
+
+command_map = {
+    "blueprints": blueprints_cmd,
+    "modules": modules_cmd,
+    "projects": projects_cmd,
+    "compose": compose_cmd,
+    "sources": sources_cmd,
+    "status": status_cmd
+    }
+
+
+
[docs]def main(opts): + """ Main program execution + + :param opts: Cmdline arguments + :type opts: argparse.Namespace + """ + + # Making sure opts.args is not empty (thus, has a command and subcommand) + # is already handled in src/bin/composer-cli. + if opts.args[0] not in command_map: + log.error("Unknown command %s", opts.args[0]) + return 1 + else: + try: + return command_map[opts.args[0]](opts) + except Exception as e: + log.error(str(e)) + return 1
+
+ +
+ +
+ + +
+
+ +
+ +