foreman_remote_execution 0.0.7 → 0.0.8

Sign up to get free protection for your applications and to get access to all the features.
Files changed (227) hide show
  1. checksums.yaml +13 -5
  2. data/app/assets/javascripts/template_invocation.js +13 -0
  3. data/app/controllers/job_invocations_controller.rb +2 -0
  4. data/app/helpers/remote_execution_helper.rb +11 -0
  5. data/app/models/concerns/foreman_remote_execution/errors_flattener.rb +62 -0
  6. data/app/models/job_invocation.rb +13 -4
  7. data/app/models/job_invocation_api_composer.rb +8 -1
  8. data/app/models/targeting.rb +4 -0
  9. data/app/models/template_invocation.rb +10 -3
  10. data/app/models/template_invocation_input_value.rb +1 -1
  11. data/app/views/job_invocations/_host_actions_td.html.erb +3 -0
  12. data/app/views/job_invocations/_host_provider_td.html.erb +3 -0
  13. data/app/views/job_invocations/_host_status_td.html.erb +1 -0
  14. data/app/views/job_invocations/_tab_hosts.html.erb +29 -11
  15. data/app/views/job_invocations/show.html.erb +6 -6
  16. data/app/views/job_invocations/show.js.erb +17 -1
  17. data/doc/public/404.html +101 -0
  18. data/doc/public/atom.xml +15 -0
  19. data/doc/public/design/design.pml +159 -0
  20. data/doc/public/design/design.png +0 -0
  21. data/doc/public/design/index.html +1034 -0
  22. data/doc/public/images/plantuml/0afe1cad9989f8eeb83d64ddd8ad966b.png +0 -0
  23. data/doc/public/images/plantuml/1b3aebc565b29cbfc24e17bb82e1755f.png +0 -0
  24. data/doc/public/images/plantuml/22d999e1bab9125ee2bee01061a0613d.png +0 -0
  25. data/doc/public/images/plantuml/37a3d54b72b5fb424c66f20823096b45.png +0 -0
  26. data/doc/public/images/plantuml/3d5233cf81d5b4e7ff1a0cb83ff8dd3c.png +0 -0
  27. data/doc/public/images/plantuml/85afad842176d44784ab10b21523523a.png +0 -0
  28. data/doc/public/images/plantuml/c06658361624b9745f8c30b3adab8d55.png +0 -0
  29. data/doc/public/images/plantuml/c3f37e9cedeb193116d385903ac5f4ec.png +0 -0
  30. data/doc/public/images/plantuml/c3f94a3f18228ce7b36d0660912c120a.png +0 -0
  31. data/doc/public/images/plantuml/cafad29b096c88cfd52b039c721138e8.png +0 -0
  32. data/doc/public/images/plantuml/ea3d02987c95f57c24dbdcae663f3d04.png +0 -0
  33. data/doc/public/images/plantuml/f1836392f5e62d2b20c7e4ba87865dfd.png +0 -0
  34. data/doc/public/images/plantuml/f4af9732163057c0b67bdc611f22c8c0.png +0 -0
  35. data/doc/public/images/plantuml/f658ac705f379599a491287b25604faa.png +0 -0
  36. data/doc/public/index.html +89 -0
  37. data/doc/public/static/css/bootstrap-responsive.min.css +9 -0
  38. data/doc/public/static/css/bootstrap.min.css +866 -0
  39. data/doc/public/static/css/jquery.tocify.css +128 -0
  40. data/doc/public/static/css/style.css +285 -0
  41. data/doc/public/static/css/syntax.css +60 -0
  42. data/doc/public/static/images/foreman.png +0 -0
  43. data/doc/public/static/images/glyphicons-halflings-white.png +0 -0
  44. data/doc/public/static/images/glyphicons-halflings.png +0 -0
  45. data/doc/public/static/js/bootstrap.min.js +7 -0
  46. data/doc/public/static/js/jquery-ui-1.9.2.custom.min.js +6 -0
  47. data/doc/public/static/js/jquery.js +2 -0
  48. data/doc/public/static/js/jquery.tocify.min.js +3 -0
  49. data/doc/public/static/js/scroll.js +24 -0
  50. data/doc/tmp/images/plantuml/048413aa0ebe983d6d4c8b723d44ef54.png +0 -0
  51. data/doc/tmp/images/plantuml/058267eeb70d4349b612d467c60f8f14.png +0 -0
  52. data/doc/tmp/images/plantuml/09a2e6a267933b8e8c2279a0aafa9552.png +0 -0
  53. data/doc/tmp/images/plantuml/09a756ef3ceb8f5f90b22c59a3363efb.png +0 -0
  54. data/doc/tmp/images/plantuml/0a4171455a0c5af78de5794e3728a152.png +0 -0
  55. data/doc/tmp/images/plantuml/0afe1cad9989f8eeb83d64ddd8ad966b.png +0 -0
  56. data/doc/tmp/images/plantuml/0b197179e1818607f0286eaf824274ac.png +0 -0
  57. data/doc/tmp/images/plantuml/0b593326b50006bd521b5e5cca3604b3.png +0 -0
  58. data/doc/tmp/images/plantuml/0b884643df43305b1506c084a54f7d62.png +0 -0
  59. data/doc/tmp/images/plantuml/0bf587f8eb742566e15f05ada951fcf3.png +0 -0
  60. data/doc/tmp/images/plantuml/0cd39df66435b755c12e7f691f89ccb9.png +0 -0
  61. data/doc/tmp/images/plantuml/12f54e758726d7c77c2d7734618ade9b.png +0 -0
  62. data/doc/tmp/images/plantuml/151c158cf5e99816811684f1f7af4add.png +0 -0
  63. data/doc/tmp/images/plantuml/1521fcbd2bb979ee2dd2cd86df6b4a4c.png +0 -0
  64. data/doc/tmp/images/plantuml/16aebbe8eb0ca9ed26fbd3ea2a7cb704.png +0 -0
  65. data/doc/tmp/images/plantuml/1b3aebc565b29cbfc24e17bb82e1755f.png +0 -0
  66. data/doc/tmp/images/plantuml/1b3c153aa58dd2c5b70603ae29268b42.png +0 -0
  67. data/doc/tmp/images/plantuml/1ddbe7dfddb328821a68a786de29d2cc.png +0 -0
  68. data/doc/tmp/images/plantuml/2082b2c3d29c6b4ab2f757f5efed5cf8.png +0 -0
  69. data/doc/tmp/images/plantuml/21833320c937cbc514f1c1248ca7d4db.png +0 -0
  70. data/doc/tmp/images/plantuml/22d999e1bab9125ee2bee01061a0613d.png +0 -0
  71. data/doc/tmp/images/plantuml/22e29148dc9824f3e0cc3d9c58591923.png +0 -0
  72. data/doc/tmp/images/plantuml/244893af77c602647475415cc99e481d.png +0 -0
  73. data/doc/tmp/images/plantuml/244af716a7f432ebe016cb4182617efc.png +0 -0
  74. data/doc/tmp/images/plantuml/26684bb52065f9727d2333f7a7cd6de9.png +0 -0
  75. data/doc/tmp/images/plantuml/271444cd542bae5faa48a9d6188e7016.png +0 -0
  76. data/doc/tmp/images/plantuml/27344a60dad90d0f298dd5c4cd4d93f0.png +0 -0
  77. data/doc/tmp/images/plantuml/280625d6da4746b3b2d3303029d5fc4f.png +0 -0
  78. data/doc/tmp/images/plantuml/289ec5cf505c6dc6e58d4581aef813fe.png +0 -0
  79. data/doc/tmp/images/plantuml/28fe7ea8ff31ec776c470ddba9465488.png +0 -0
  80. data/doc/tmp/images/plantuml/2911c81d093198df67454fae05026221.png +0 -0
  81. data/doc/tmp/images/plantuml/2aba8300fb9c286bae4761d2bdb7a856.png +0 -0
  82. data/doc/tmp/images/plantuml/2b4f2436047875223ff9217259e75eb9.png +0 -0
  83. data/doc/tmp/images/plantuml/2b6d5b3236658251a0f37b3c5b1213b1.png +0 -0
  84. data/doc/tmp/images/plantuml/2c8c0b91888b0312eb75c4652548ed9b.png +0 -0
  85. data/doc/tmp/images/plantuml/2c9447bd16be8887bee0536dac95cef6.png +0 -0
  86. data/doc/tmp/images/plantuml/2fc1023e86f067275073e41f12ae52d4.png +0 -0
  87. data/doc/tmp/images/plantuml/2fda3d4d4e80f49622f759fcc9e57fd1.png +0 -0
  88. data/doc/tmp/images/plantuml/33b3f0ba0e98a1ef3005a4d2d2736137.png +0 -0
  89. data/doc/tmp/images/plantuml/3661b2e20356651b79c50c347c791f11.png +0 -0
  90. data/doc/tmp/images/plantuml/37a3d54b72b5fb424c66f20823096b45.png +0 -0
  91. data/doc/tmp/images/plantuml/37af5b8d47aad5fda54758685a85926c.png +0 -0
  92. data/doc/tmp/images/plantuml/3972ae58834f466ccee2b403487c6b0b.png +0 -0
  93. data/doc/tmp/images/plantuml/3a465f203961262224ed05be8a69eed5.png +0 -0
  94. data/doc/tmp/images/plantuml/3d191f8c61d742c60b9c27c5a4afd231.png +0 -0
  95. data/doc/tmp/images/plantuml/3d5233cf81d5b4e7ff1a0cb83ff8dd3c.png +0 -0
  96. data/doc/tmp/images/plantuml/3d6f85d2d931ded5ecae64f2bc89c11a.png +0 -0
  97. data/doc/tmp/images/plantuml/3de6b456b9fae31dde8f8d65dc965a0b.png +0 -0
  98. data/doc/tmp/images/plantuml/3f250c15112d48b6364791c0d3e00fb4.png +0 -0
  99. data/doc/tmp/images/plantuml/40c71a2b081db9c25ff88fda3fb8485d.png +0 -0
  100. data/doc/tmp/images/plantuml/45599cee5eb08d0a3950625d5ec3926a.png +0 -0
  101. data/doc/tmp/images/plantuml/497445a1debfcc5c52fe92b94305ccb7.png +0 -0
  102. data/doc/tmp/images/plantuml/4bbaa7bb555b0c4594f6d6d15825518d.png +0 -0
  103. data/doc/tmp/images/plantuml/4db606c9f871c96e361709f299223fc6.png +0 -0
  104. data/doc/tmp/images/plantuml/51eab226644fbfdb90d771c82c2d32e7.png +0 -0
  105. data/doc/tmp/images/plantuml/56eb220f166aaec84e573d393f21441a.png +0 -0
  106. data/doc/tmp/images/plantuml/5989a2986aa2641cf6b7a251082b0ed5.png +0 -0
  107. data/doc/tmp/images/plantuml/5f1a066b1d50befe6fadc45f10625dfe.png +0 -0
  108. data/doc/tmp/images/plantuml/61f9c2c4b9984de108a2198d8f779c0c.png +0 -0
  109. data/doc/tmp/images/plantuml/621822e563b5766a3d59897d19df8713.png +0 -0
  110. data/doc/tmp/images/plantuml/64f5b6d463af5ade70cfa17ba657c3f1.png +0 -0
  111. data/doc/tmp/images/plantuml/67ff01dde24e12f82a8e999d571958be.png +0 -0
  112. data/doc/tmp/images/plantuml/6b249a571b18352445467823e93ac3d5.png +0 -0
  113. data/doc/tmp/images/plantuml/6c5529902d6fb5d7db3b0007afedebc7.png +0 -0
  114. data/doc/tmp/images/plantuml/6cc111c35c0814efa0a9a191181e1200.png +0 -0
  115. data/doc/tmp/images/plantuml/6f0a6390ef7b3356e38a6fa6bba96e15.png +0 -0
  116. data/doc/tmp/images/plantuml/711b546eed61e45999c8b1fb84023529.png +0 -0
  117. data/doc/tmp/images/plantuml/712ec401d83fa8795c9c1b0eae016aad.png +0 -0
  118. data/doc/tmp/images/plantuml/73ae8c3303bc976f248a36e0c9b0c9a1.png +0 -0
  119. data/doc/tmp/images/plantuml/748444baf352084db872b0907a5d4e05.png +0 -0
  120. data/doc/tmp/images/plantuml/7516cd6e6a0d1b88ae213a6783ed1fad.png +0 -0
  121. data/doc/tmp/images/plantuml/76172d21de8c809c7c93b1e57198f327.png +0 -0
  122. data/doc/tmp/images/plantuml/7a958293f7a810646f4996e69d5c539e.png +0 -0
  123. data/doc/tmp/images/plantuml/7b9e4cd6ddfab153267f4e76ef71a8d7.png +0 -0
  124. data/doc/tmp/images/plantuml/7c9165a218b29a6d1e35c01a8a034a62.png +0 -0
  125. data/doc/tmp/images/plantuml/7ce9d3a0abf7c8bdae44e8078df89d3a.png +0 -0
  126. data/doc/tmp/images/plantuml/7f04522bca62dda77dbae9f6d6fa0c03.png +0 -0
  127. data/doc/tmp/images/plantuml/7fe1802627ae83d228dd02fb258e1e2a.png +0 -0
  128. data/doc/tmp/images/plantuml/805c9e1236d9a8ae5a084bc4ea5c4986.png +0 -0
  129. data/doc/tmp/images/plantuml/842b17abcd4a5f154837fa1682574ec1.png +0 -0
  130. data/doc/tmp/images/plantuml/85afad842176d44784ab10b21523523a.png +0 -0
  131. data/doc/tmp/images/plantuml/864507c1788f9f4d995cf2f9a4d5c6f8.png +0 -0
  132. data/doc/tmp/images/plantuml/8706e05f1d3ef4fb5bd6eb104c9c7bd1.png +0 -0
  133. data/doc/tmp/images/plantuml/88fd0980f15b8b15898172ba05aaa41f.png +0 -0
  134. data/doc/tmp/images/plantuml/8d17e36f709592799543d492c740f536.png +0 -0
  135. data/doc/tmp/images/plantuml/8d99db858d323a4d28a800c24d69321e.png +0 -0
  136. data/doc/tmp/images/plantuml/8fb41bf0ac7f49a037f28c7188982a21.png +0 -0
  137. data/doc/tmp/images/plantuml/90692212f6fa45bebff1c098f59081d4.png +0 -0
  138. data/doc/tmp/images/plantuml/921cfd5e27136bc11a05da16d87ec5e1.png +0 -0
  139. data/doc/tmp/images/plantuml/92ca26960a9e4d2dbc9e5099d0d9aa47.png +0 -0
  140. data/doc/tmp/images/plantuml/92f06afd5a5fce9b76f46d0f2a4301c8.png +0 -0
  141. data/doc/tmp/images/plantuml/9401b435b322426270569cfead3aaec6.png +0 -0
  142. data/doc/tmp/images/plantuml/964570783a5e6f760039695567799251.png +0 -0
  143. data/doc/tmp/images/plantuml/96bd58456219af9a60cc68323d3e42e1.png +0 -0
  144. data/doc/tmp/images/plantuml/98e94e848a193d0278890233f2d43aaa.png +0 -0
  145. data/doc/tmp/images/plantuml/9a40b332e4c2775ae08163ea68fccaae.png +0 -0
  146. data/doc/tmp/images/plantuml/a134bf02dcd57358aca553be5427cc8d.png +0 -0
  147. data/doc/tmp/images/plantuml/a3dd22bc181ea18e0270cc38d299c64e.png +0 -0
  148. data/doc/tmp/images/plantuml/a6f86e5ab17a4b01984e131552ba9d2d.png +0 -0
  149. data/doc/tmp/images/plantuml/a89e5c53f9c64a2d50d28011b16eeca8.png +0 -0
  150. data/doc/tmp/images/plantuml/ab99822b7b6d395f02025fc294acdbf6.png +0 -0
  151. data/doc/tmp/images/plantuml/ac3250afab3a13ac5a9fe26d3fa76f4e.png +0 -0
  152. data/doc/tmp/images/plantuml/aeef134198459523e8ce5ca546e05bd9.png +0 -0
  153. data/doc/tmp/images/plantuml/af1d95b2618fd3b925100f3ed30cae1f.png +0 -0
  154. data/doc/tmp/images/plantuml/af65578ed6bf4754899c00fc937a9ca7.png +0 -0
  155. data/doc/tmp/images/plantuml/b502ecd2b04efe6d65d5ab4f42529723.png +0 -0
  156. data/doc/tmp/images/plantuml/b5775ac7d4be15c8d1d4dc916c57b1fc.png +0 -0
  157. data/doc/tmp/images/plantuml/b5d351333c33e237e975e416665c45f6.png +0 -0
  158. data/doc/tmp/images/plantuml/b65b5a9f2110e85fe657fd34a7d4e914.png +0 -0
  159. data/doc/tmp/images/plantuml/b7fe7bc142c939ab06a0457b20d15c75.png +0 -0
  160. data/doc/tmp/images/plantuml/ba43bac82453feba943d5c1daea6631f.png +0 -0
  161. data/doc/tmp/images/plantuml/ba53dcf3a4cffbf21843b4832c205ba8.png +0 -0
  162. data/doc/tmp/images/plantuml/bc11747b6e90ad5983be919d312a94fe.png +0 -0
  163. data/doc/tmp/images/plantuml/bda113efbc8950f52d6025f4c2566dce.png +0 -0
  164. data/doc/tmp/images/plantuml/be01cbbb3b421294fd727b375fb0d6e5.png +0 -0
  165. data/doc/tmp/images/plantuml/be93da05489725928bf0366658baadb6.png +0 -0
  166. data/doc/tmp/images/plantuml/bfce7af69eec0df8b52740af0645c31f.png +0 -0
  167. data/doc/tmp/images/plantuml/c054b73ffa0504758ea4eb0c7dc8ca81.png +0 -0
  168. data/doc/tmp/images/plantuml/c0626bf19526c0962d595925438e9673.png +0 -0
  169. data/doc/tmp/images/plantuml/c06658361624b9745f8c30b3adab8d55.png +0 -0
  170. data/doc/tmp/images/plantuml/c3b98674c79ce06ee0769dcbaa9d94c3.png +0 -0
  171. data/doc/tmp/images/plantuml/c3d72dde65d5101037047b98c6cb818d.png +0 -0
  172. data/doc/tmp/images/plantuml/c3f37e9cedeb193116d385903ac5f4ec.png +0 -0
  173. data/doc/tmp/images/plantuml/c3f94a3f18228ce7b36d0660912c120a.png +0 -0
  174. data/doc/tmp/images/plantuml/c58114cfff1f79e4bd5745c19abb7b0a.png +0 -0
  175. data/doc/tmp/images/plantuml/c94423daecb58043ed4a2d03eb9e7fbb.png +0 -0
  176. data/doc/tmp/images/plantuml/c9f310ef5b79590646b80dc981fa5e57.png +0 -0
  177. data/doc/tmp/images/plantuml/ca3a0f9122751ad0979be05a573a6608.png +0 -0
  178. data/doc/tmp/images/plantuml/cafad29b096c88cfd52b039c721138e8.png +0 -0
  179. data/doc/tmp/images/plantuml/cb29a529eb88b13ff4f8f063b54135c8.png +0 -0
  180. data/doc/tmp/images/plantuml/cb5c68d0f0ff412347978b498e5673c2.png +0 -0
  181. data/doc/tmp/images/plantuml/cc33b6eaa2f4035b4b4dbbc56b1b5727.png +0 -0
  182. data/doc/tmp/images/plantuml/cd8243645f8d93d6f2578a1b08b6f423.png +0 -0
  183. data/doc/tmp/images/plantuml/cda431cfd134c2e8ed1d99955df2847c.png +0 -0
  184. data/doc/tmp/images/plantuml/ce6e23abaa619800bda4724d32c5b174.png +0 -0
  185. data/doc/tmp/images/plantuml/cf4c537923c278133d9584f5cb30257b.png +0 -0
  186. data/doc/tmp/images/plantuml/cf5dfb9db2c8a84071e0615e4aa6a17f.png +0 -0
  187. data/doc/tmp/images/plantuml/cfe17537e2e1bd3339ecf69538b1bedf.png +0 -0
  188. data/doc/tmp/images/plantuml/d029ad0926f2d7017652f8806cceecf5.png +0 -0
  189. data/doc/tmp/images/plantuml/d34e8760198c9bb5b2452b2982ae8797.png +0 -0
  190. data/doc/tmp/images/plantuml/d4774af1e3f76853eab95959ad4e0e08.png +0 -0
  191. data/doc/tmp/images/plantuml/d55d250a9ae94d2242570689bcde2bd7.png +0 -0
  192. data/doc/tmp/images/plantuml/d5a5326ed3240acd44d98f6545d9666b.png +0 -0
  193. data/doc/tmp/images/plantuml/da292996f3b3552d5def31282a728aa6.png +0 -0
  194. data/doc/tmp/images/plantuml/dc3a1b0baa6db1cab59eedbfb8e6af75.png +0 -0
  195. data/doc/tmp/images/plantuml/dd7c64391b456f5eaab7eff30f211890.png +0 -0
  196. data/doc/tmp/images/plantuml/dd7f0c2542cddecaff7d00177ffc7895.png +0 -0
  197. data/doc/tmp/images/plantuml/dd902c7d5084f0c402ce75a544d7197d.png +0 -0
  198. data/doc/tmp/images/plantuml/df6507a5cb7e9f9fe4203669e758a918.png +0 -0
  199. data/doc/tmp/images/plantuml/e3407a41d8da8f9d699cb8142a0d777f.png +0 -0
  200. data/doc/tmp/images/plantuml/e38fc5cd0e5eb7f29cca7c367f500ea2.png +0 -0
  201. data/doc/tmp/images/plantuml/e75ceb1ce105508b5219c35b7dda63c4.png +0 -0
  202. data/doc/tmp/images/plantuml/e92887836f3471bbb60d5811a4f56d3c.png +0 -0
  203. data/doc/tmp/images/plantuml/ea03a745369cb2849c041b5ebf0b30ed.png +0 -0
  204. data/doc/tmp/images/plantuml/ea3d02987c95f57c24dbdcae663f3d04.png +0 -0
  205. data/doc/tmp/images/plantuml/ea972643782c0ac7abf5b1ae6ae666ed.png +0 -0
  206. data/doc/tmp/images/plantuml/ed437670bf13bdb12bc79b4bccbdd2d3.png +0 -0
  207. data/doc/tmp/images/plantuml/ed5834a2cc5ae4c19d37a49963ba2149.png +0 -0
  208. data/doc/tmp/images/plantuml/edc4fd9652deda112f17db2861588d83.png +0 -0
  209. data/doc/tmp/images/plantuml/f0bbb549ad5634ca0f1d09f89bde3364.png +0 -0
  210. data/doc/tmp/images/plantuml/f0e610accad41289c5f984fe72148d12.png +0 -0
  211. data/doc/tmp/images/plantuml/f1836392f5e62d2b20c7e4ba87865dfd.png +0 -0
  212. data/doc/tmp/images/plantuml/f2f65926bbdde9e1e80d44ee633ed98d.png +0 -0
  213. data/doc/tmp/images/plantuml/f4098f2c227db897eb4dc67bb4842200.png +0 -0
  214. data/doc/tmp/images/plantuml/f4af9732163057c0b67bdc611f22c8c0.png +0 -0
  215. data/doc/tmp/images/plantuml/f658ac705f379599a491287b25604faa.png +0 -0
  216. data/doc/tmp/images/plantuml/f72930a4d8c5d8d5e95cd3c99687e14b.png +0 -0
  217. data/doc/tmp/images/plantuml/f754b731d8ea2c1764363570b78defcb.png +0 -0
  218. data/doc/tmp/images/plantuml/f90e4db6e7f787dd9833be86854125a3.png +0 -0
  219. data/doc/tmp/images/plantuml/f942161223c00f35e38950be1b12beac.png +0 -0
  220. data/doc/tmp/images/plantuml/fa54e9771c4537d1e8e0103de6865f15.png +0 -0
  221. data/doc/tmp/images/plantuml/fb777bb04ce1e2a8e5b9a345dd199aa7.png +0 -0
  222. data/doc/tmp/images/plantuml/fbf8cbe3aa6a95ad4c90d5596c769f34.png +0 -0
  223. data/doc/tmp/images/plantuml/fbf9f7958bc1d1f6f9ae966791e9c11b.png +0 -0
  224. data/lib/foreman_remote_execution/version.rb +1 -1
  225. data/test/unit/job_invocation_api_composer_test.rb +36 -10
  226. data/test/unit/targeting_test.rb +13 -0
  227. metadata +471 -52
@@ -0,0 +1,15 @@
1
+ <?xml version="1.0" encoding="utf-8"?>
2
+ <feed xmlns="http://www.w3.org/2005/Atom">
3
+
4
+ <title><![CDATA[Foreman Remote Execution]]></title>
5
+ <link href="http://theforeman.github.io/atom.xml" rel="self"/>
6
+ <link href="http://theforeman.github.io/"/>
7
+ <updated>2015-04-30T14:14:32+02:00</updated>
8
+ <id>http://theforeman.github.io/</id>
9
+ <author>
10
+ <name><![CDATA[]]></name>
11
+
12
+ </author>
13
+
14
+
15
+ </feed>
@@ -0,0 +1,159 @@
1
+ @startuml
2
+
3
+ class Host {
4
+ get_provider(type)
5
+ }
6
+
7
+ package "Job Preparation" {
8
+ class JobTemplate {
9
+ name: string
10
+ job_name: string
11
+ retry_count: integer
12
+ retry_interval: integer
13
+ splay: integer
14
+ provider_type: string
15
+ effective_user: string
16
+ ==
17
+ has_many :taxonomies
18
+ has_many :inputs
19
+ has_many :audits
20
+ }
21
+
22
+ class ConfigTemplateInput {
23
+ name: string
24
+ required: bool
25
+ input_type: USER_INPUT | FACT | SMART_VARIABLE
26
+ fact_name: string
27
+ smart_variable_name: string
28
+ description: string
29
+ ==
30
+ has_one :job_template
31
+ }
32
+
33
+ JobTemplate "1" --> "N" Taxonomy
34
+ JobTemplate "1" --> "N" ConfigTemplateInput
35
+ JobTemplate "1" --> "N" Audit
36
+
37
+ class Taxonomy
38
+ class Audit
39
+ }
40
+
41
+ package "Job Invocation" {
42
+ class Bookmark {
43
+ name:string
44
+ query:string
45
+ controller:string
46
+ public:bool
47
+ owner_id:integer
48
+ owner_type:string
49
+ }
50
+
51
+ class Targeting {
52
+ query: string
53
+ dynamic: bool
54
+ }
55
+
56
+ class TemplateInvocation {
57
+ inputs
58
+ }
59
+
60
+ class JobInvocation {
61
+ tries
62
+ retry_interval
63
+ splay
64
+ concurrency
65
+ email_notification: bool
66
+ effective_user: string
67
+ }
68
+
69
+ class User
70
+
71
+ Bookmark "1" <-UP- "N" Targeting
72
+ Targeting "M" -DOWN- "N" Host
73
+ Targeting "N" -UP-> "1" User
74
+ JobInvocation "1" -LEFT-> "1" Targeting
75
+ JobInvocation "1" <-UP- "N" TemplateInvocation
76
+ TemplateInvocation "N" -LEFT-> "1" JobTemplate
77
+
78
+ }
79
+
80
+ package "Scheduling" {
81
+ class Schedule {
82
+ start_at: datetime
83
+ end_at: datetime
84
+ cronline: string
85
+ }
86
+
87
+ JobInvocation "1" -UP- "0..1" Schedule
88
+ }
89
+
90
+ package "Execution" {
91
+ class BulkJobTask {
92
+ state: $TaskState
93
+ start_at: datetime
94
+ started_at: datetime
95
+ ended_at datetime
96
+ cancel()
97
+ }
98
+
99
+ class JobTask {
100
+ state: $TaskState
101
+ start_at: datetime
102
+ started_at: datetime
103
+ tried_count: integer
104
+ ended_at datetime
105
+ retry: integer
106
+ retry_interval: integer
107
+ timeout: integer
108
+ splay: integer
109
+ concurrency: integer
110
+ provider: string
111
+
112
+ command: string
113
+ output: string
114
+ exit_code: string
115
+
116
+ {abstract} support_cancel?()
117
+ {abstract} proxy_endpoint()
118
+ cancel()
119
+ }
120
+
121
+ abstract class ProxyCommand {
122
+ }
123
+
124
+ class SSHProxyCommand {
125
+ {static} support_cancel?()
126
+ proxy_endpoint():string
127
+ }
128
+
129
+ class MCollectiveProxyCommand {
130
+ {static} support_cancel?()
131
+ proxy_endpoint():string
132
+ }
133
+
134
+ BulkJobTask "N" -RIGHT-> "1" JobInvocation
135
+ BulkJobTask "1" <-- "N" JobTask
136
+ TemplateInvocation "1" <-- "N" JobTask
137
+ JobTask "1" -RIGHT- "1" ProxyCommand
138
+ JobTask "1" -UP- "1" Host
139
+ }
140
+
141
+
142
+ ProxyCommand <|-- SSHProxyCommand
143
+ ProxyCommand <|-- MCollectiveProxyCommand
144
+
145
+ package "Developer API" {
146
+ class PredefinedJob {
147
+ predefined_job_name: string
148
+ }
149
+
150
+ class PredefinedJobInputMapping {
151
+ provided_input_name: string
152
+ }
153
+
154
+ PredefinedJob "1" -- "N" PredefinedJobInputMapping
155
+ PredefinedJobInputMapping "1" -RIGHT- "N" ConfigTemplateInput
156
+ PredefinedJob "M" -RIGHT- "N" JobTemplate
157
+ }
158
+
159
+ @enduml
Binary file
@@ -0,0 +1,1034 @@
1
+ <!doctype html>
2
+ <html itemscope itemtype="http://schema.org/Organization" lang="en">
3
+ <head>
4
+ <base href="/foreman_remote_execution/">
5
+
6
+
7
+
8
+
9
+
10
+
11
+
12
+ <title> :: Foreman Remote Execution</title>
13
+
14
+ <script type="text/javascript" src="/foreman_remote_execution/static/js/jquery.js"></script>
15
+ <script type="text/javascript" src="/foreman_remote_execution/static/js/bootstrap.min.js"></script>
16
+ <script type="text/javascript" src="/foreman_remote_execution/static/js/jquery-ui-1.9.2.custom.min.js"></script>
17
+ <script type="text/javascript" src="/foreman_remote_execution/static/js/jquery.tocify.min.js"></script>
18
+ <script type="text/javascript" src="/foreman_remote_execution/static/js/scroll.js"></script>
19
+
20
+ <link rel="stylesheet" media="all" href="/foreman_remote_execution/static/css/bootstrap.min.css"/>
21
+ <link rel="stylesheet" media="all" href="/foreman_remote_execution/static/css/bootstrap-responsive.min.css"/>
22
+ <link rel="stylesheet" media="all" href="/foreman_remote_execution/static/css/jquery.tocify.css"/>
23
+ <link rel="stylesheet" media="all" href="/foreman_remote_execution/static/css/syntax.css" />
24
+ <link rel="stylesheet" media="all" href="/foreman_remote_execution/static/css/style.css"/>
25
+ <meta name="apple-mobile-web-app-capable" content="yes">
26
+ <meta name="viewport" content="user-scalable=no, width=device-width, initial-scale=1.0, maximum-scale=1.0;" />
27
+ <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
28
+ </head>
29
+
30
+ <body>
31
+ <div class="navbar navbar-fixed-top">
32
+ <div class="navbar-inner">
33
+ <div class="container">
34
+ <button type="button" class="btn btn-navbar" data-toggle="collapse" data-target=".nav-collapse">
35
+ <span class="icon-bar"></span>
36
+ <span class="icon-bar"></span>
37
+ <span class="icon-bar"></span>
38
+ </button>
39
+ <img alt="Foreman" class="logo" src="/foreman_remote_execution/static/images/foreman.png" />
40
+ <div class="brand">
41
+ <a href="/foreman_remote_execution/">Foreman</a>
42
+ <a class="subtitle" href="/foreman_remote_execution/">Remote Execution</a>
43
+ </div>
44
+ <div class="nav-collapse collapse">
45
+ <ul class="nav">
46
+ <li><a href="/foreman_remote_execution/">About</a></li>
47
+ <li><a href="/foreman_remote_execution/design/">Design</a></li>
48
+ </ul>
49
+ </div>
50
+ </div>
51
+ </div>
52
+ </div>
53
+ <script>
54
+ $(function(){
55
+ $('.nav > li a[href="'+window.location.pathname+'"]').parents('.nav > li').addClass('active')
56
+ })
57
+ </script>
58
+
59
+
60
+ <div id="main">
61
+ <div id="content" class="container">
62
+ <div class="row">
63
+ <div id="toc" class="span3">
64
+ </div>
65
+ <div id="doc" class="offset3 span9">
66
+ <h1 id="remote-execution-technology">Remote Execution Technology</h1>
67
+
68
+ <h2 id="user-stories">User Stories</h2>
69
+
70
+ <ul>
71
+ <li><p>As a user I want to run jobs in parallel across large number of
72
+ hosts</p></li>
73
+ <li><p>As a user I want to run jobs on a host in a different network
74
+ segment (the host doesn&#39;t see the Foreman server/the Foreman server
75
+ doesn&#39;t see the host directly)</p></li>
76
+ <li><p>As a user I want to manage a host without installing an agent on it
77
+ (just plain old ssh)</p></li>
78
+ <li><p>As a community user I want to already existing remote execution
79
+ technologies in combination with the Foreman</p></li>
80
+ </ul>
81
+
82
+ <h2 id="design">Design</h2>
83
+
84
+ <p>Although specific providers are mentioned in the design, it&#39;s used
85
+ mainly for distinguishing different approaches to the remote execution
86
+ than to choose specific technologies</p>
87
+
88
+ <h3 id="ssh-single-host-push">Ssh Single Host Push</h3>
89
+
90
+ <p><img src='/foreman_remote_execution///images/plantuml/1b3aebc565b29cbfc24e17bb82e1755f.png'></p>
91
+
92
+ <p>JobInvocation: see see <a href="design#job-invocation">scheduling</a></p>
93
+
94
+ <p>ProxyCommand:</p>
95
+
96
+ <ul>
97
+ <li>host: host.example.com</li>
98
+ <li>provider: ssh</li>
99
+ <li>input: &quot;yum install -y vim-X11&quot;</li>
100
+ </ul>
101
+
102
+ <p>SSHScript:</p>
103
+
104
+ <ul>
105
+ <li>host: host.example.com</li>
106
+ <li>input: &quot;yum install -y vim-X11&quot;</li>
107
+ </ul>
108
+
109
+ <p>ProgressReport[1, Running]:</p>
110
+
111
+ <ul>
112
+ <li>output: &quot;Resolving depednencies&quot;</li>
113
+ </ul>
114
+
115
+ <p>ProgressReport[2, Running]:</p>
116
+
117
+ <ul>
118
+ <li>output: &quot;installing libXt&quot;</li>
119
+ </ul>
120
+
121
+ <p>AccumulatedProgressReport[1, Running]:</p>
122
+
123
+ <ul>
124
+ <li>output: { stdout: &quot;Resolving depednencies\ninstalling libXt&quot; }</li>
125
+ </ul>
126
+
127
+ <p>ProgressReport[3, Running]:</p>
128
+
129
+ <ul>
130
+ <li>output: &quot;installing vim-X11&quot;</li>
131
+ </ul>
132
+
133
+ <p>ProgressReport[4, Finished]:</p>
134
+
135
+ <ul>
136
+ <li>output: &quot;operation finished successfully&quot;</li>
137
+ <li>exit_code: 0</li>
138
+ </ul>
139
+
140
+ <p>AccumulatedProgressReport[2, Finished]:</p>
141
+
142
+ <ul>
143
+ <li>output: { stdout: &quot;installing vim-X11\noperation finished successfully&quot;, exit_code: 0 }</li>
144
+ <li>success: true</li>
145
+ </ul>
146
+
147
+ <h3 id="ssh-single-host-check-in">Ssh Single Host Check-in</h3>
148
+
149
+ <p>This case allows to handle the case, when the host is offline by the
150
+ time of job invocation: the list of jobs for the host is stored on the
151
+ Foreman server side for running once the host is online.</p>
152
+
153
+ <p>This approach is not limited to the ssh provider only.</p>
154
+
155
+ <p><img src='/foreman_remote_execution///images/plantuml/f658ac705f379599a491287b25604faa.png'></p>
156
+
157
+ <h3 id="ssh-multi-host">Ssh Multi Host</h3>
158
+
159
+ <p><img src='/foreman_remote_execution///images/plantuml/c3f94a3f18228ce7b36d0660912c120a.png'></p>
160
+
161
+ <p>ProxyCommand[host1]:</p>
162
+
163
+ <ul>
164
+ <li>host: host-1.example.com</li>
165
+ <li>provider: ssh</li>
166
+ <li>input: &quot;yum install -y vim-X11&quot;</li>
167
+ </ul>
168
+
169
+ <p>ProxyCommand[host2]:</p>
170
+
171
+ <ul>
172
+ <li>host: host-2.example.com</li>
173
+ <li>provider: ssh</li>
174
+ <li>input: &quot;yum install -y vim-X11&quot;</li>
175
+ </ul>
176
+
177
+ <div class=" alert alert-info">
178
+ <p><strong>Note</strong> </p>
179
+
180
+ <p>we might want to optimize the communication between server and
181
+ the proxy (sending collection of ProxyCommands in bulk, as well as
182
+ the AccumulatedProgerssReports). That would could also be utilized
183
+ by the Ansible implementation, where there might be optimization
184
+ on the invoking the ansible commands at once (the same might apply
185
+ to mcollective). On the other hand, this is more an optimization,
186
+ not required to be implemented from the day one: but it&#39;s good to have
187
+ this in mind</p>
188
+
189
+ </div>
190
+
191
+ <h3 id="mcollective-single-host">MCollective Single Host</h3>
192
+
193
+ <p><img src='/foreman_remote_execution///images/plantuml/f1836392f5e62d2b20c7e4ba87865dfd.png'></p>
194
+
195
+ <p>JobInvocation:</p>
196
+
197
+ <ul>
198
+ <li>hosts: [host.example.com]</li>
199
+ <li>template: install-packages-mco</li>
200
+ <li>input: { packages: [&#39;vim-X11&#39;] }</li>
201
+ </ul>
202
+
203
+ <p>ProxyCommand:</p>
204
+
205
+ <ul>
206
+ <li>host: host.example.com</li>
207
+ <li>provider: mcollective</li>
208
+ <li>input: { agent: package, args: { package =&gt; &#39;vim-X11&#39; } }</li>
209
+ </ul>
210
+
211
+ <p>MCOCommand:</p>
212
+
213
+ <ul>
214
+ <li>host: host.example.com</li>
215
+ <li>input: { agent: package, args: { package =&gt; &#39;vim-X11&#39; } }</li>
216
+ </ul>
217
+
218
+ <p>ProgressReport[Finished]:</p>
219
+
220
+ <ul>
221
+ <li>output: [ {&quot;name&quot;:&quot;vim-X11&quot;,&quot;tries&quot;:1,&quot;version&quot;:&quot;7.4.160-1&quot;,&quot;status&quot;:0,&quot;release&quot;:&quot;1.el7&quot;},
222
+ {&quot;name&quot;:&quot;libXt&quot;,&quot;tries&quot;:1,&quot;version&quot;:&quot;1.1.4-6&quot;,&quot;status&quot;:0,&quot;release&quot;:&quot;1.el7&quot;} ]</li>
223
+ </ul>
224
+
225
+ <p>AccumulatedProgressReport[Finished]:</p>
226
+
227
+ <ul>
228
+ <li>output: [ {&quot;name&quot;:&quot;vim-X11&quot;,&quot;tries&quot;:1,&quot;version&quot;:&quot;7.4.160-1&quot;,&quot;status&quot;:0,&quot;release&quot;:&quot;1.el7&quot;},
229
+ {&quot;name&quot;:&quot;libXt&quot;,&quot;tries&quot;:1,&quot;version&quot;:&quot;1.1.4-6&quot;,&quot;status&quot;:0,&quot;release&quot;:&quot;1.el7&quot;} ]</li>
230
+ <li>success: true</li>
231
+ </ul>
232
+
233
+ <h3 id="ansible-single-host">Ansible Single Host</h3>
234
+
235
+ <p><img src='/foreman_remote_execution///images/plantuml/c06658361624b9745f8c30b3adab8d55.png'></p>
236
+
237
+ <p>JobInvocation:</p>
238
+
239
+ <ul>
240
+ <li>hosts: [host.example.com]</li>
241
+ <li>template: install-packages-ansible</li>
242
+ <li>input: { packages: [&#39;vim-X11&#39;] }</li>
243
+ </ul>
244
+
245
+ <p>ProxyCommand:</p>
246
+
247
+ <ul>
248
+ <li>host: host.example.com</li>
249
+ <li>provider: ansible</li>
250
+ <li>input: { module: yum, args: { name: &#39;vim-X11&#39;, state: installed } }</li>
251
+ </ul>
252
+
253
+ <p>AnsibleCommand:</p>
254
+
255
+ <ul>
256
+ <li>host: host.example.com</li>
257
+ <li>provider: ansible</li>
258
+ <li>input: { module: yum, args: { name: &#39;vim-X11&#39;, state: installed } }</li>
259
+ </ul>
260
+
261
+ <p>ProgressReport[Finished]:</p>
262
+
263
+ <ul>
264
+ <li>output: { changed: true,
265
+ rc: 0,
266
+ results: [&quot;Resolving depednencies\ninstalling libXt\ninstalling vim-X11\noperation finished successfully&quot;] }</li>
267
+ </ul>
268
+
269
+ <p>AccumulatedProgressReport[Finished]:</p>
270
+
271
+ <ul>
272
+ <li>output: { changed: true,
273
+ rc: 0,
274
+ results: [&quot;Resolving depednencies\ninstalling libXt\ninstalling vim-X11\noperation finished successfully&quot;] }</li>
275
+ <li>success: true</li>
276
+ </ul>
277
+
278
+ <h1 id="job-preparation">Job Preparation</h1>
279
+
280
+ <h2 id="user-stories">User Stories</h2>
281
+
282
+ <ul>
283
+ <li><p>As a user I want to be able to create a template to run some command for a given remote execution provider for a specific job</p></li>
284
+ <li><p>As a user these job templates should be audited and versioned</p></li>
285
+ <li><p>As a user I want to be able to define inputs into the template that consist of user input at execution time. I should be able to use these inputs within my template.</p></li>
286
+ <li><p>As a user I want to be able to define an input for a template that uses a particular fact about the host being executed on at execution time.</p></li>
287
+ <li><p>As a user I want to be able to define an input for a template that uses a particular smart variable that is resolved at execution time.</p></li>
288
+ <li><p>As a user I want to be able to define a description of each input in order to help describe the format and meaning of an input.</p></li>
289
+ <li><p>As a user I want to be able to specify default number of tries per job template.</p></li>
290
+ <li><p>As a user I want to be able to specify default retry interval per job template.</p></li>
291
+ <li><p>As a user I want to be able to specify default splay time per job template.</p></li>
292
+ <li><p>As a user I want to setup default timeout per job template.</p></li>
293
+ <li><p>As a user I want to preview a rendered job template for a host (providing needed inputs)</p></li>
294
+ </ul>
295
+
296
+ <h2 id="scenarios">Scenarios</h2>
297
+
298
+ <p><strong>Creating a job template</strong></p>
299
+
300
+ <ol>
301
+ <li>given I&#39;m on new template form</li>
302
+ <li>I select from a list of existing job names or fill in a new job name</li>
303
+ <li>I select some option to add an input
304
+
305
+ <ol>
306
+ <li>Give the input a name</li>
307
+ <li>Select the type &#39;user input&#39;</li>
308
+ <li>Give the input a description (space separated package list)</li>
309
+ </ol></li>
310
+ <li>I select from a list of known providers (ssh, mco, salt, ansible)</li>
311
+ <li>I am shown an example of how to use the input in the template</li>
312
+ <li>I am able to see some simple example for the selected provider??</li>
313
+ <li>I fill in the template</li>
314
+ <li>I select one or more organizations and locations (if enabled)</li>
315
+ <li>I click save</li>
316
+ </ol>
317
+
318
+ <p><strong>Creating a smart variable based input</strong></p>
319
+
320
+ <ol>
321
+ <li>given i am creating or editing a job template</li>
322
+ <li>I select to add a new input
323
+
324
+ <ol>
325
+ <li>Give the input a name</li>
326
+ <li>Define a smart variable name</li>
327
+ </ol></li>
328
+ </ol>
329
+
330
+ <h2 id="design">Design</h2>
331
+
332
+ <p><img src='/foreman_remote_execution///images/plantuml/37a3d54b72b5fb424c66f20823096b45.png'></p>
333
+
334
+ <h1 id="job-invocation">Job Invocation</h1>
335
+
336
+ <h2 id="user-stories">User Stories</h2>
337
+
338
+ <ul>
339
+ <li><p>As a user I would like to invoke a job on a single host</p></li>
340
+ <li><p>As a user I would like to invoke a job on a set of hosts, based on
341
+ search filter</p></li>
342
+ <li><p>As a user I want to be able to reuse existing bookmarks for job
343
+ invocation</p></li>
344
+ <li><p>As a user, when setting a job in future, I want to decide if the
345
+ search criteria should be evaluated now or on the execution time</p></li>
346
+ <li><p>As a user I want to reuse the target of previous jobs for next execution</p></li>
347
+ <li><p>As a CLI user I want to be able to invoke a job via hammer CLI</p></li>
348
+ <li><p>As a user, I want to be able to invoke the job on a specific set of hosts
349
+ (by using checkboxes in the hosts table)</p></li>
350
+ <li><p>As a user, when planning future job execution, I want to see a
351
+ warning with the info about unreachable hosts</p></li>
352
+ <li><p>As a user I want to be able to override default values like (number
353
+ of tries, retry interval, splay time, timeout, effective user...) when I plan an execution of command.</p></li>
354
+ <li><p>As a user I expect to see a the description of an input whenever i am being requested to
355
+ provide the value for the input.</p></li>
356
+ <li><p>As a user I want to be able to re-invoke the jobs based on
357
+ success/failure of previous task</p></li>
358
+ </ul>
359
+
360
+ <h2 id="scenarios">Scenarios</h2>
361
+
362
+ <p><strong>Fill in target for a job</strong></p>
363
+
364
+ <ol>
365
+ <li>when I&#39;m on job invocation form</li>
366
+ <li>then I can specify the target of the job using the scoped search
367
+ syntax</li>
368
+ <li>the target might influence the list of providers available for the
369
+ invocation: although, in delayed execution and dynamic targeting the
370
+ current list of providers based on the hosts might not be final and
371
+ we should count on that.</li>
372
+ </ol>
373
+
374
+ <p><strong>Fill in template inputs for a job</strong></p>
375
+
376
+ <ol>
377
+ <li>given I&#39;m on job invocation form</li>
378
+ <li>when I choose the job to execute</li>
379
+ <li>then I&#39;m given a list of providers that I have enabled and has a
380
+ template available for the job</li>
381
+ <li>and each provider allows to choose which template to use for this
382
+ invocation (if more templates for the job and provider are available)</li>
383
+ <li>and every template has input fields generated based on the input
384
+ defined on the template (such as list of packages for install package
385
+ job)</li>
386
+ </ol>
387
+
388
+ <p><strong>See the calculated template inputs for a job</strong></p>
389
+
390
+ <ol>
391
+ <li>given I&#39;m on job invocation form</li>
392
+ <li>when I choose the job to execute</li>
393
+ <li>and I&#39;m using a template with inputs calculated base on fact data
394
+ template available for the job</li>
395
+ <li>then the preview of the current value for this input should be displayed</li>
396
+ <li>but for the execution the value that the fact has by the time of
397
+ execution will be used.</li>
398
+ </ol>
399
+
400
+ <p><strong>Fill in job description for the execution</strong></p>
401
+
402
+ <ol>
403
+ <li>given I&#39;m on job invocation form</li>
404
+ <li>there should be a field for task description, that will be used for
405
+ listing the jobs</li>
406
+ <li>the description value should be pregenerated based on the job name
407
+ and specified input (something like &quot;Package install: zsh&quot;)</li>
408
+ </ol>
409
+
410
+ <p><strong>Fill in execution properties of the job</strong></p>
411
+
412
+ <ol>
413
+ <li>when I&#39;m on job invocation form</li>
414
+ <li>I can override the default values for number of tries, retry
415
+ interval, splay time, timeout, effective user...</li>
416
+ <li>the overrides are common for all the templates</li>
417
+ </ol>
418
+
419
+ <p><strong>Set the execution time into future</strong> (see <a href="design#scheduling">scheduling</a>
420
+ for more scenarios)</p>
421
+
422
+ <ol>
423
+ <li>when I&#39;m on a job invocation form</li>
424
+ <li>then I can specify the time to start the execution at (now by
425
+ default)</li>
426
+ <li>and I can specify if the targeting should be calculated now or
427
+ postponed to the execution time</li>
428
+ </ol>
429
+
430
+ <p><strong>Run a job from host detail</strong></p>
431
+
432
+ <ol>
433
+ <li>given I&#39;m on a host details page</li>
434
+ <li>when I click &quot;Run job&quot;</li>
435
+ <li>then a user dialog opens with job invocation form, with pre-filled
436
+ targeting pointing to this particular host</li>
437
+ </ol>
438
+
439
+ <p><strong>Run a job from host index</strong></p>
440
+
441
+ <ol>
442
+ <li>given I&#39;m on a host index page</li>
443
+ <li>when I click &quot;Run job&quot;</li>
444
+ <li>then a user dialog opens with job invocation form, with prefiled
445
+ targeting using the same search that was used in the host index page</li>
446
+ </ol>
447
+
448
+ <p><strong>Invoke a job with single remote execution provider</strong></p>
449
+
450
+ <ol>
451
+ <li>given I have only one provider available in my installation</li>
452
+ <li>and I&#39;m on job invocation form</li>
453
+ <li>when I choose the job to execute</li>
454
+ <li>then only the template for this provider is available to run and
455
+ asking for user inputs</li>
456
+ </ol>
457
+
458
+ <p><strong>Invoke a job with hammer</strong></p>
459
+
460
+ <ol>
461
+ <li>given I&#39;m using CLI</li>
462
+ <li>then I can run a job with ability to specify:
463
+
464
+ <ul>
465
+ <li>targeting with scoped search or bookmark_id</li>
466
+ <li>job name to run</li>
467
+ <li>templates to use for the job</li>
468
+ <li>inputs on per-template basis</li>
469
+ <li>execution properties as overrides for the defaults coming from the template</li>
470
+ <li><code>start_at</code> value for execution in future</li>
471
+ <li>in case of the start_at value, if the targeting should be static
472
+ vs. dynamic</li>
473
+ <li>whether to wait for the job or exit after invocation (--async
474
+ option)</li>
475
+ </ul></li>
476
+ </ol>
477
+
478
+ <p><strong>Re-invoke a job</strong></p>
479
+
480
+ <ol>
481
+ <li>given I&#39;m in job details page</li>
482
+ <li>when I choose re-run</li>
483
+ <li>then a user dialog opens with job invocation form, with prefiled
484
+ targeting parameters from the previous execution
485
+ 1 and I can override all the values (including targeting, job,
486
+ templates and inputs)</li>
487
+ </ol>
488
+
489
+ <p><strong>Re-invoke a job for failed hosts</strong></p>
490
+
491
+ <ol>
492
+ <li>given I&#39;m in job details page</li>
493
+ <li>when I choose re-run</li>
494
+ <li>then a user dialog opens with job invocation form, with prefiled
495
+ targeting parameters from the previous execution
496
+ 1 and I can override all the values (including targeting, job,
497
+ templates and inputs)</li>
498
+ <li>I can choose in the targeting to only run on hosts that failed with
499
+ the job previously</li>
500
+ </ol>
501
+
502
+ <p><strong>Edit a bookmark referenced by pending job invocation</strong></p>
503
+
504
+ <ol>
505
+ <li>given I have a pending execution task which targeting was created
506
+ from a bookmark</li>
507
+ <li>when I edit the bookmark</li>
508
+ <li>then I should be notified about the existence of the pending tasks
509
+ with ability to update the targeting (or cancel and recreate the
510
+ invocation)</li>
511
+ </ol>
512
+
513
+ <p><strong>Email notification: opt in</strong></p>
514
+
515
+ <ol>
516
+ <li>given I haven&#39;t configured to send email notifications about my executions</li>
517
+ <li>then the job invocation should have the &#39;send email notification&#39;
518
+ turned off by default</li>
519
+ </ol>
520
+
521
+ <p><strong>Email notification: opt out</strong></p>
522
+
523
+ <ol>
524
+ <li>given I haven&#39;t configured to send email notifications about my executions</li>
525
+ <li>then the job invocation should have the &#39;send email notification&#39;
526
+ turned off by default</li>
527
+ </ol>
528
+
529
+ <h2 id="design">Design</h2>
530
+
531
+ <p>Class diagram of Foreman classes</p>
532
+
533
+ <p><img src='/foreman_remote_execution///images/plantuml/3d5233cf81d5b4e7ff1a0cb83ff8dd3c.png'></p>
534
+
535
+ <p>Query is copied to Targeting, we don&#39;t want to propagate any later
536
+ changes to Bookmark to already planned job executions.</p>
537
+
538
+ <p>We can store link to original bookmark to be able to
539
+ compare changes later.</p>
540
+
541
+ <p>For JobInvocation we forbid later editing of Targeting.</p>
542
+
543
+ <h2 id="open-questions">Open questions</h2>
544
+
545
+ <ul>
546
+ <li><p>should we unify the common inputs in all templates to specify them
547
+ only once or scoping the input by template?</p></li>
548
+ <li><p>Maybe an inputs catalog (with both defined name and semantic) might
549
+ help with keeping the inputs consistent across templates/providers</p></li>
550
+ </ul>
551
+
552
+ <h1 id="job-execution">Job Execution</h1>
553
+
554
+ <h2 id="user-stories">User Stories</h2>
555
+
556
+ <ul>
557
+ <li><p>As a user I want to be able to cancel job which hasn&#39;t been started yet.</p></li>
558
+ <li><p>As a user I want to be able to cancel job which is in progress
559
+ (if supported by specific provider…)</p></li>
560
+ <li><p>As a user I want job execution to fail after timeout limit.</p></li>
561
+ <li><p>As a user I want to job execution to be re-tried
562
+ based on the tries and retry interval values given in the invocation</p></li>
563
+ <li><p>As a user I want to job execution on multiple hosts to be spread
564
+ using the splay time value: the execution of the jobs will be spread
565
+ randomly across the time interval</p></li>
566
+ <li><p>As a user I want to job execution on multiple hosts to be limited
567
+ by a concurrency level: the number of concurrently running jobs will
568
+ not exceed the limit.</p></li>
569
+ <li><p>As a user I want the job execution to be performed as a user that
570
+ was specified on the job invocation</p></li>
571
+ <li><p>As a user I want an ability to retry the job execution when the host
572
+ checks in (support of hosts that are offline by the time the
573
+ execution).</p></li>
574
+ </ul>
575
+
576
+ <h2 id="scenarios">Scenarios</h2>
577
+
578
+ <p><strong>Cancel pending bulk task: all at once</strong></p>
579
+
580
+ <ol>
581
+ <li>given I&#39;ve set a job to run in future on multiple hosts</li>
582
+ <li>when I click &#39;cancel&#39; on the corresponding bulk task</li>
583
+ <li>then the whole task should be canceled (including all the sub-tasks
584
+ on all the hosts)</li>
585
+ </ol>
586
+
587
+ <p><strong>Cancel pending bulk task: task on specific host</strong></p>
588
+
589
+ <ol>
590
+ <li>given I&#39;ve set a job to run in future on multiple hosts</li>
591
+ <li>when I show the task representation on a host details page</li>
592
+ <li>when I click &#39;cancel&#39; on the task</li>
593
+ <li>then I should be offered whether I should cancel just this
594
+ instance or the whole bulk task on all hosts</li>
595
+ </ol>
596
+
597
+ <p><strong>Fail after timeout</strong></p>
598
+
599
+ <ol>
600
+ <li>given I&#39;ve invoked a job</li>
601
+ <li>when the job fails to start in given specified timeout</li>
602
+ <li>then the job should be marked as failed due to timeout</li>
603
+ </ol>
604
+
605
+ <p><strong>Retried task</strong></p>
606
+
607
+ <ol>
608
+ <li>given I&#39;ve invoked a job</li>
609
+ <li>when the job fails to start at first attemt</li>
610
+ <li>then the executor should wait for retry_timeout period</li>
611
+ <li>and it should reiterate with the attempt based on the tries number</li>
612
+ <li>and I should see the information about the number of retries</li>
613
+ </ol>
614
+
615
+ <h2 id="design">Design</h2>
616
+
617
+ <p>Class diagram for jobs running on multiple hosts</p>
618
+
619
+ <p><img src='/foreman_remote_execution///images/plantuml/85afad842176d44784ab10b21523523a.png'></p>
620
+
621
+ <p>Class diagram for jobs running a single host</p>
622
+
623
+ <p><img src='/foreman_remote_execution///images/plantuml/f4af9732163057c0b67bdc611f22c8c0.png'></p>
624
+
625
+ <h1 id="reporting">Reporting</h1>
626
+
627
+ <h2 id="user-stories">User Stories</h2>
628
+
629
+ <ul>
630
+ <li><p>As a user I would like to monitor the current state of the job
631
+ running against a single host, including the output and exit status</p></li>
632
+ <li><p>As a user I would like to monitor the status of bulk job,
633
+ including the number of successful, failed and pending tasks</p></li>
634
+ <li><p>As a user I would like to see the history of all job run on a
635
+ host</p></li>
636
+ <li><p>As a user I would like to see the history of all tasks that I&#39;ve
637
+ invoked</p></li>
638
+ <li><p>As a user I would like to be able to get an email notification with
639
+ execution report</p></li>
640
+ </ul>
641
+
642
+ <h2 id="scenarios">Scenarios</h2>
643
+
644
+ <p><strong>Track the job running on a set of hosts</strong></p>
645
+
646
+ <ol>
647
+ <li>given I&#39;ve set a job to run in future on multiple hosts</li>
648
+ <li>then I can watch the progress of the job (number of
649
+ successful/failed/pending tasks)</li>
650
+ <li>and I can get to the list of jobs per host</li>
651
+ <li>and I&#39;m able to filter on the host that it was run against and
652
+ state</li>
653
+ </ol>
654
+
655
+ <p><strong>Track the job running on a single host</strong></p>
656
+
657
+ <ol>
658
+ <li>given I&#39;ve set a job to run on a specific host</li>
659
+ <li>when I show the task representation page</li>
660
+ <li>then I can watch the progress of the job (updated log), status</li>
661
+ </ol>
662
+
663
+ <p><strong>History of jobs run on a host</strong></p>
664
+
665
+ <ol>
666
+ <li>given I&#39;m on host jobs page</li>
667
+ <li>when I can see all the jobs run against the host</li>
668
+ <li>and I&#39;m able to filter on the host that it was run against and
669
+ state, owner etc.</li>
670
+ </ol>
671
+
672
+ <p><strong>History of invoked jobs</strong></p>
673
+
674
+ <ol>
675
+ <li>given I&#39;m on job invocation history page</li>
676
+ <li>when I can see all the jobs invoked in the system</li>
677
+ <li>scoped by a taxonomy (based on the hosts the jobs were run against)</li>
678
+ <li>and I&#39;m able to filter on the host that it was run against and
679
+ state, owner etc.</li>
680
+ </ol>
681
+
682
+ <p><strong>Email notification: send after finish</strong></p>
683
+
684
+ <ol>
685
+ <li>given I&#39;ve invoked a job with email notification turned on</li>
686
+ <li>when the job finishes</li>
687
+ <li>then I should get the email with report from the job after it finishes</li>
688
+ </ol>
689
+
690
+ <h2 id="design">Design</h2>
691
+
692
+ <p>Class diagram for jobs running on multiple hosts</p>
693
+
694
+ <p><img src='/foreman_remote_execution///images/plantuml/ea3d02987c95f57c24dbdcae663f3d04.png'></p>
695
+
696
+ <h1 id="scheduling">Scheduling</h1>
697
+
698
+ <h2 id="user-stories">User Stories</h2>
699
+
700
+ <ul>
701
+ <li><p>As a user I want to be able go execute a job at future time</p></li>
702
+ <li><p>As a user I want to set the job to reoccur with specified
703
+ frequency</p></li>
704
+ </ul>
705
+
706
+ <h2 id="scenarios">Scenarios</h2>
707
+
708
+ <p><strong>Job set for the future</strong></p>
709
+
710
+ <ol>
711
+ <li>given I&#39;ve invoked a job at future time</li>
712
+ <li>when the time comes</li>
713
+ <li>the job gets executed</li>
714
+ </ol>
715
+
716
+ <p><strong>Creating reoccurring job</strong></p>
717
+
718
+ <ol>
719
+ <li>given I&#39;m in job invocation form</li>
720
+ <li>when I check &#39;reoccurring job&#39;</li>
721
+ <li>then I can set the frequency and valid until date</li>
722
+ </ol>
723
+
724
+ <p><strong>Showing the tasks with reoccurring logic</strong></p>
725
+
726
+ <ol>
727
+ <li>when I list the jobs</li>
728
+ <li>I can see the information about the reoccurring logic at every job</li>
729
+ <li>and I can filter the jobs for those with the reoccurring logic</li>
730
+ </ol>
731
+
732
+ <p><strong>Canceling the reoccurring job</strong></p>
733
+
734
+ <ol>
735
+ <li>given I have reoccurring job configured</li>
736
+ <li>when I cancel the next instance of the job</li>
737
+ <li>then I&#39;m offered to cancel the reoccurring of the job in the future</li>
738
+ </ol>
739
+
740
+ <h2 id="design">Design</h2>
741
+
742
+ <p><img src='/foreman_remote_execution///images/plantuml/cafad29b096c88cfd52b039c721138e8.png'></p>
743
+
744
+ <h1 id="developer-api">Developer API</h1>
745
+
746
+ <h2 id="user-stories">User Stories</h2>
747
+
748
+ <ul>
749
+ <li>As a Foreman developer, I want to be able to use remote execution
750
+ plugin to help with other Foreman features such as:
751
+
752
+ <ul>
753
+ <li>puppet run</li>
754
+ <li>grubby reprovision</li>
755
+ <li>content actions (package install/update/remove/downgrade, group
756
+ install/uninstall, package profile refresh)</li>
757
+ <li>subscription actions (refresh)</li>
758
+ <li>OpenSCAP content update</li>
759
+ </ul></li>
760
+ </ul>
761
+
762
+ <h2 id="scenarios">Scenarios</h2>
763
+
764
+ <p><strong>Defining a predefined job without provided inputs</strong></p>
765
+
766
+ <ol>
767
+ <li>given I&#39;m a Foreman developer</li>
768
+ <li>and I want to expose &#39;puppet run&#39; feature to the user</li>
769
+ <li>then define the &#39;Puppet Run&#39; as predefined job in the code</li>
770
+ <li>and specify the default job name to be used for the mapping</li>
771
+ </ol>
772
+
773
+ <p><strong>Defining a predefined job with provided inputs</strong></p>
774
+
775
+ <ol>
776
+ <li>given I&#39;m a Katello developer</li>
777
+ <li>and I want to expose &#39;package install&#39; feature to the user</li>
778
+ <li>then I define the &#39;Package Install&#39; predefined job with list of
779
+ packages as provided input in the code</li>
780
+ <li>and I specify default job name to be used for the mapping</li>
781
+ <li>and I specify default mapping of the provided inputs to template
782
+ inputs</li>
783
+ </ol>
784
+
785
+ <p><strong>Preseeding the predefined jobs</strong></p>
786
+
787
+ <ol>
788
+ <li>given I&#39;ve defined the &#39;Package Install&#39; predefined job</li>
789
+ <li>when the seed script is run as part of the Foreman installation</li>
790
+ <li>the systems tries to create the default mapping from the predefined job to
791
+ the existing templates based on the developer-provided defaults</li>
792
+ </ol>
793
+
794
+ <p><strong>Configuring the predefined jobs mapping</strong></p>
795
+
796
+ <ol>
797
+ <li>given I&#39;m the administrator of the Foreman instance</li>
798
+ <li>then I can see all the predefined jobs mapping</li>
799
+ <li>when I edit existing mapping</li>
800
+ <li>then I can choose job name, template and provided input -&gt; template
801
+ inputs mapping</li>
802
+ </ol>
803
+
804
+ <p><strong>Configuring the predefined jobs mapping with organizations</strong></p>
805
+
806
+ <ol>
807
+ <li>given I&#39;m the administrator of the Foreman instance</li>
808
+ <li>then I can scope the mapping of the predefined job to a specific
809
+ organization</li>
810
+ <li>and the system doesn&#39;t let me to create two mappings for the same
811
+ predefined job and provider visible in one organization</li>
812
+ </ol>
813
+
814
+ <p><strong>Using the predefined jobs without provided inputs</strong></p>
815
+
816
+ <ol>
817
+ <li>given I&#39;m a Foreman user</li>
818
+ <li>when I&#39;m on host details page</li>
819
+ <li>and I press &#39;Puppet Run&#39;</li>
820
+ <li>the job is invoked on the host based on the predefined mapping</li>
821
+ </ol>
822
+
823
+ <p><strong>Using the predefined jobs with provided inputs</strong></p>
824
+
825
+ <ol>
826
+ <li>given I&#39;m a Katello user</li>
827
+ <li>when I&#39;m on host applicable errata list</li>
828
+ <li>and I select a set of errata to install on the host</li>
829
+ <li>and I click &#39;Install errata&#39;</li>
830
+ <li>the job will be invoked to install the packages belonging to this
831
+ errata</li>
832
+ </ol>
833
+
834
+ <p><strong>Using the predefined jobs with customization</strong></p>
835
+
836
+ <ol>
837
+ <li>given I&#39;m a Katello user</li>
838
+ <li>when I&#39;m on host applicable errata list</li>
839
+ <li>and I select a set of errata to install on the host</li>
840
+ <li>and I click &#39;Install errata (customize)&#39;</li>
841
+ <li>then the job invocation form will be opened with pre-filled values
842
+ based on the mapping</li>
843
+ <li>and I can update the values, including setting the start_at time or
844
+ reoccurring logic</li>
845
+ </ol>
846
+
847
+ <h2 id="design">Design</h2>
848
+
849
+ <p><img src='/foreman_remote_execution///images/plantuml/0afe1cad9989f8eeb83d64ddd8ad966b.png'></p>
850
+
851
+ <h1 id="security">Security</h1>
852
+
853
+ <h2 id="user-stories">User Stories</h2>
854
+
855
+ <ul>
856
+ <li><p>As a user I want to be able to plan job invocation for any host that I
857
+ can view (view_host permission).</p></li>
858
+ <li><p>As a user I want to be able to plan a job invocation of job that I can
859
+ view (view_job permission)</p></li>
860
+ <li><p>As a user I want to restrict other users which combination of host and job
861
+ name they can execute (execute permission on job_task resource).</p></li>
862
+ <li><p>As a user I want to be warned if I planned job invocation on hosts on
863
+ which the execution of this job is not allowed to me.</p></li>
864
+ <li><p>As a user I want to see refused job invocations (based on permissions) as
865
+ failed when they are executed.</p></li>
866
+ <li><p>As a user I want to set limit filter with execute permission by host attributes
867
+ such as hostgroup, environment, fqdn, id, lifecycle environment (if applicable),
868
+ content view (if applicable).</p></li>
869
+ <li><p>As a user I want to specify effective_user for JobInvocation if at least one
870
+ provider supports it.</p></li>
871
+ <li><p>As a user I want to restrict other users to execute job under specific user
872
+ as a part of filter condition. If the provider does not allow this, execution
873
+ should be refused.</p></li>
874
+ <li><p>As a job template provider I want to be able to specify default effective user</p></li>
875
+ </ul>
876
+
877
+ <h2 id="scenarios">Scenarios</h2>
878
+
879
+ <p><strong>Allow user A to invoke package installation on host B</strong></p>
880
+
881
+ <ol>
882
+ <li>given user A can view all hosts and job templates</li>
883
+ <li>when he invoke package installation job on host B</li>
884
+ <li>then his job task fails because he does not have execution
885
+ permission for such job task</li>
886
+ </ol>
887
+
888
+ <p><strong>Allow user A to run package installation on host B</strong></p>
889
+
890
+ <ol>
891
+ <li>given I&#39;ve permissions to assign other user permissions</li>
892
+ <li>and user A can view all hosts and job templates</li>
893
+ <li>and user A can create job invocations</li>
894
+ <li>when I grant user A execution permission on resource JobTask</li>
895
+ <li>and I set related filter condition to &quot;host<em>name = B and job</em>name = package_install&quot;</li>
896
+ <li>and user A invokes package install execution on hosts B and C</li>
897
+ <li>then the job gets executed successfully on host B</li>
898
+ <li>and job execution will fail on host C</li>
899
+ </ol>
900
+
901
+ <p><strong>User can set effective user</strong></p>
902
+
903
+ <ol>
904
+ <li>given the provider of job template supports changing effective user</li>
905
+ <li>when user invokes a job</li>
906
+ <li>then he can set effective user under which job is executed on target host</li>
907
+ </ol>
908
+
909
+ <p><strong>User can disallow running job as different effective user</strong></p>
910
+
911
+ <ol>
912
+ <li>given I&#39;ve permissions to assign other user permissions</li>
913
+ <li>and user A can view all hosts and job templates</li>
914
+ <li>and user A can create job invocations</li>
915
+ <li>when I grant user A execution permission on resource JobTask</li>
916
+ <li>and I set related filter condition to &quot;effective<em>user = user</em>a&quot;</li>
917
+ <li>and user A invokes job execution with effective user set to different user (e.g. root)</li>
918
+ <li>then the job execution fails</li>
919
+ </ol>
920
+
921
+ <h2 id="new-permissions-introduced">New permissions introduced</h2>
922
+
923
+ <ul>
924
+ <li>JobInvocation
925
+
926
+ <ul>
927
+ <li>Create</li>
928
+ <li>View</li>
929
+ <li>Cancel</li>
930
+ <li>Edit (Schedule, never can change targetting)</li>
931
+ </ul></li>
932
+ <li>JobTask
933
+
934
+ <ul>
935
+ <li>Execute</li>
936
+ <li>(filter can be: effective<em>user = &#39;joe&#39; and host</em>id = 1 or host<em>id = 2 and script</em>name = &#39;foobar&#39;)</li>
937
+ </ul></li>
938
+ </ul>
939
+
940
+ <h2 id="design">Design</h2>
941
+
942
+ <p><img src='/foreman_remote_execution///images/plantuml/c3f37e9cedeb193116d385903ac5f4ec.png'></p>
943
+
944
+ <h1 id="katello-client-utilities">Katello Client Utilities</h1>
945
+
946
+ <h2 id="design">Design</h2>
947
+
948
+ <p>katello-agent provides three main functions aside from remote management:</p>
949
+
950
+ <ul>
951
+ <li>package profile yum plugin - pushes a new package profile after any yum transaction
952
+
953
+ <ul>
954
+ <li>Split out into its own package (yum-plugin-katello-profile)</li>
955
+ </ul></li>
956
+ <li>enabled repository monitoring
957
+
958
+ <ul>
959
+ <li>monitors /etc/yum.repos.d/redhat.repo file for changes and sends newly enabled repos whenever it does change</li>
960
+ <li>Split out into its own package (katello-errata-profile) with a service to do the same</li>
961
+ </ul></li>
962
+ <li>On the capsule, goferd runs to recieve commands to sync repositories, possible solutions:
963
+
964
+ <ul>
965
+ <li>katello-agent can remain (but possibly renamed), with a lot of the existing functionality removed</li>
966
+ <li>pulp changes to a rest api method for initiating capsule syncs, katello needs to store some auth credentials per capsule</li>
967
+ </ul></li>
968
+ </ul>
969
+
970
+ <h1 id="orchestration">Orchestration</h1>
971
+
972
+ <h2 id="user-stories">User Stories</h2>
973
+
974
+ <ul>
975
+ <li><p>As a user I want to group a number of jobs together and treat them
976
+ as an executable unit. (i.e. run this script to stop the app, install
977
+ these errata, reboot the system)</p></li>
978
+ <li><p>As a user I want to run a set of jobs in a rolling fashion.
979
+ (i.e.,patch server 1, reboot it, if it succeeds, proceed to server 2
980
+ &amp; repeat. Otherwise raise exception)</p></li>
981
+ <li><p>As a user I want to define a rollback job in case the execution
982
+ fails</p></li>
983
+ <li><p>As a sysadmin I would like to orchestrate several actions across a
984
+ collection of machines. (e.g. install a DB on this machine, and pass
985
+ the ip address into an install of a web server on another machine)</p></li>
986
+ </ul>
987
+
988
+ <h2 id="design">Design</h2>
989
+
990
+ <ul>
991
+ <li><p>TBD after the simple support is implemented, possible cooperation with
992
+ multi-host deployments feature</p></li>
993
+ <li><p>Some of the features might be solved by advanced remote execution
994
+ technology integration (such as ansible playbook)</p></li>
995
+ </ul>
996
+
997
+ <h1 id="design:-the-whole-picture">Design: the whole picture</h1>
998
+
999
+ <p><img src='/foreman_remote_execution///images/plantuml/22d999e1bab9125ee2bee01061a0613d.png'></p>
1000
+
1001
+ </div>
1002
+ </div>
1003
+ </div>
1004
+ </div>
1005
+
1006
+ <script type="text/javascript">
1007
+ $(function() {
1008
+ //Calls the tocify method on your HTML div.
1009
+ $("#toc").tocify({context: '#doc', scrollTo: '60', selectors: "h1,h2,h3"});
1010
+ });
1011
+ </script>
1012
+
1013
+ <div class="footer">
1014
+ <div class="container">
1015
+ <p>This web site is licensed under a <a rel="license" href="http://creativecommons.org/licenses/by-sa/3.0/deed.en_GB">Creative Commons Attribution-ShareAlike 3.0 Unported License</a>. Source available: <a xmlns:dct="http://purl.org/dc/terms/" href="https://github.com/theforeman/theforeman.org" rel="dct:source">github/theforeman/theforeman.org</a>.</p>
1016
+ <a href="https://plus.google.com/102496134326414788199" rel="publisher">Google+ community</a>
1017
+ </div>
1018
+ </div>
1019
+
1020
+ <script type="text/javascript">
1021
+ var _gaq = _gaq || [];
1022
+ _gaq.push(['_setAccount', 'UA-2134730-5']);
1023
+ _gaq.push(['_trackPageview']);
1024
+
1025
+ (function() {
1026
+ var ga = document.createElement('script'); ga.type = 'text/javascript'; ga.async = true;
1027
+ ga.src = ('https:' == document.location.protocol ? 'https://ssl' : 'http://www') + '.google-analytics.com/ga.js';
1028
+ var s = document.getElementsByTagName('script')[0]; s.parentNode.insertBefore(ga, s);
1029
+ })();
1030
+ </script>
1031
+
1032
+ </body>
1033
+ </html>
1034
+