foreman_remote_execution 0.0.1 → 0.0.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (36) hide show
  1. checksums.yaml +5 -13
  2. data/doc/Gemfile +7 -0
  3. data/doc/Gemfile.lock +90 -0
  4. data/doc/Rakefile +41 -0
  5. data/doc/_config.yml +33 -0
  6. data/doc/plugins/alert_block.rb +27 -0
  7. data/doc/plugins/div_tag.rb +24 -0
  8. data/doc/plugins/graphviz.rb +121 -0
  9. data/doc/plugins/plantuml.rb +84 -0
  10. data/doc/plugins/play.rb +13 -0
  11. data/doc/plugins/tags.rb +138 -0
  12. data/doc/plugins/toc.rb +20 -0
  13. data/doc/source/404.md +6 -0
  14. data/doc/source/_includes/footer.html +21 -0
  15. data/doc/source/_includes/header.html +59 -0
  16. data/doc/source/_includes/tocify.html +6 -0
  17. data/doc/source/_layouts/default.html +9 -0
  18. data/doc/source/_layouts/page.html +25 -0
  19. data/doc/source/atom.xml +32 -0
  20. data/doc/source/design/index.md +1317 -0
  21. data/doc/source/index.md +18 -0
  22. data/doc/source/static/css/bootstrap-responsive.min.css +9 -0
  23. data/doc/source/static/css/bootstrap.min.css +866 -0
  24. data/doc/source/static/css/jquery.tocify.css +128 -0
  25. data/doc/source/static/css/style.css +285 -0
  26. data/doc/source/static/css/syntax.css +60 -0
  27. data/doc/source/static/images/foreman.png +0 -0
  28. data/doc/source/static/images/glyphicons-halflings-white.png +0 -0
  29. data/doc/source/static/images/glyphicons-halflings.png +0 -0
  30. data/doc/source/static/js/bootstrap.min.js +7 -0
  31. data/doc/source/static/js/jquery-ui-1.9.2.custom.min.js +6 -0
  32. data/doc/source/static/js/jquery.js +2 -0
  33. data/doc/source/static/js/jquery.tocify.min.js +3 -0
  34. data/doc/source/static/js/scroll.js +24 -0
  35. data/lib/foreman_remote_execution/version.rb +1 -1
  36. metadata +90 -23
@@ -0,0 +1,1317 @@
1
+ ---
2
+ layout: page
3
+ title: Design
4
+ countheads: true
5
+ toc: true
6
+ comments: true
7
+ ---
8
+
9
+ Remote Execution Technology
10
+ ===========================
11
+
12
+ User Stories
13
+ ------------
14
+
15
+ - As a user I want to run jobs in parallel across large number of
16
+ hosts
17
+
18
+ - As a user I want to run jobs on a host in a different network
19
+ segment (the host doesn't see the Foreman server/the Foreman server
20
+ doesn't see the host directly)
21
+
22
+ - As a user I want to manage a host without installing an agent on it
23
+ (just plain old ssh)
24
+
25
+ - As a community user I want to already existing remote execution
26
+ technologies in combination with the Foreman
27
+
28
+ Design
29
+ ------
30
+
31
+ Although specific providers are mentioned in the design, it's used
32
+ mainly for distinguishing different approaches to the remote execution
33
+ than to choose specific technologies
34
+
35
+ ### Ssh Single Host Push
36
+
37
+ {% plantuml %}
38
+ actor User
39
+ participant "Foreman Server" as Foreman
40
+ participant "Foreman Proxy" as Proxy
41
+ participant "Host" as Host
42
+
43
+ autonumber
44
+ User -> Foreman : JobInvocation
45
+ Foreman -> Proxy : ProxyCommand
46
+ Proxy -> Host : SshScript
47
+ Activate Host
48
+ Host --> Proxy : ProgressReport[1, Running]
49
+ Host --> Proxy : ProgressReport[2, Running]
50
+ Proxy --> Foreman : AccumulatedProgressReport[1, Running]
51
+ Host --> Proxy : ProgressReport[3, Running]
52
+ Host --> Proxy : ProgressReport[4, Finished]
53
+ Deactivate Host
54
+ Proxy --> Foreman : AccumulatedProgressReport[2, Finished]
55
+ {% endplantuml %}
56
+
57
+ JobInvocation: see see [scheduling](design#job-invocation)
58
+
59
+ ProxyCommand:
60
+
61
+ * host: host.example.com
62
+ * provider: ssh
63
+ * input: "yum install -y vim-X11"
64
+
65
+ SSHScript:
66
+
67
+ * host: host.example.com
68
+ * input: "yum install -y vim-X11"
69
+
70
+ ProgressReport[1, Running]:
71
+
72
+ * output: "Resolving depednencies"
73
+
74
+ ProgressReport[2, Running]:
75
+
76
+ * output: "installing libXt"
77
+
78
+ AccumulatedProgressReport[1, Running]:
79
+
80
+ * output: { stdout: "Resolving depednencies\ninstalling libXt" }
81
+
82
+ ProgressReport[3, Running]:
83
+
84
+ * output: "installing vim-X11"
85
+
86
+ ProgressReport[4, Finished]:
87
+
88
+ * output: "operation finished successfully"
89
+ * exit_code: 0
90
+
91
+ AccumulatedProgressReport[2, Finished]:
92
+
93
+ * output: { stdout: "installing vim-X11\noperation finished successfully", exit_code: 0 }
94
+ * success: true
95
+
96
+ ### Ssh Single Host Check-in
97
+
98
+ This case allows to handle the case, when the host is offline by the
99
+ time of job invocation: the list of jobs for the host is stored on the
100
+ Foreman server side for running once the host is online.
101
+
102
+ This approach is not limited to the ssh provider only.
103
+
104
+ {% plantuml %}
105
+ actor User
106
+ participant "Foreman Server" as Foreman
107
+ participant "Foreman Proxy" as Proxy
108
+ participant "Host" as Host
109
+
110
+ autonumber
111
+ User -> Foreman : JobInvocation
112
+ Host -> Proxy : CheckIn
113
+ Proxy -> Foreman : CheckIn
114
+ Foreman -> Proxy : ProxyCommand
115
+ Proxy -> Host : SshScript
116
+ {% endplantuml %}
117
+
118
+ ### Ssh Multi Host
119
+
120
+ {% plantuml %}
121
+ actor User
122
+ participant "Foreman Server" as Foreman
123
+ participant "Foreman Proxy" as Proxy
124
+ participant "Host 1" as Host1
125
+ participant "Host 2" as Host2
126
+
127
+ autonumber
128
+ User -> Foreman : JobInvocation
129
+ Foreman -> Proxy : ProxyCommand[host1]
130
+ Foreman -> Proxy : ProxyCommand[host2]
131
+ Proxy -> Host1 : SSHScript
132
+ Proxy -> Host2 : SSHScript
133
+ {% endplantuml %}
134
+
135
+ ProxyCommand[host1]:
136
+
137
+ * host: host-1.example.com
138
+ * provider: ssh
139
+ * input: "yum install -y vim-X11"
140
+
141
+ ProxyCommand[host2]:
142
+
143
+ * host: host-2.example.com
144
+ * provider: ssh
145
+ * input: "yum install -y vim-X11"
146
+
147
+ {% info_block %}
148
+ we might want to optimize the communication between server and
149
+ the proxy (sending collection of ProxyCommands in bulk, as well as
150
+ the AccumulatedProgerssReports). That would could also be utilized
151
+ by the Ansible implementation, where there might be optimization
152
+ on the invoking the ansible commands at once (the same might apply
153
+ to mcollective). On the other hand, this is more an optimization,
154
+ not required to be implemented from the day one: but it's good to have
155
+ this in mind
156
+ {% endinfo_block %}
157
+
158
+ ### MCollective Single Host
159
+
160
+ {% plantuml %}
161
+ actor User
162
+ participant "Foreman Server" as Foreman
163
+ participant "Foreman Proxy" as Proxy
164
+ participant "AMQP" as AMQP
165
+ participant "Host" as Host
166
+
167
+ autonumber
168
+ User -> Foreman : JobInvocation
169
+ Foreman -> Proxy : ProxyCommand
170
+ Proxy -> AMQP : MCOCommand
171
+ AMQP -> Host : MCOCommand
172
+ Activate Host
173
+ Host --> AMQP : ProgressReport[Finished]
174
+ Deactivate Host
175
+ AMQP --> Proxy : ProgressReport[Finished]
176
+ Proxy --> Foreman : AccumulatedProgressReport[Finished]
177
+ {% endplantuml %}
178
+
179
+ JobInvocation:
180
+
181
+ * hosts: [host.example.com]
182
+ * template: install-packages-mco
183
+ * input: { packages: ['vim-X11'] }
184
+
185
+ ProxyCommand:
186
+
187
+ * host: host.example.com
188
+ * provider: mcollective
189
+ * input: { agent: package, args: { package => 'vim-X11' } }
190
+
191
+ MCOCommand:
192
+
193
+ * host: host.example.com
194
+ * input: { agent: package, args: { package => 'vim-X11' } }
195
+
196
+ ProgressReport[Finished]:
197
+
198
+ * output: [ {"name":"vim-X11","tries":1,"version":"7.4.160-1","status":0,"release":"1.el7"},
199
+ {"name":"libXt","tries":1,"version":"1.1.4-6","status":0,"release":"1.el7"} ]
200
+
201
+ AccumulatedProgressReport[Finished]:
202
+
203
+ * output: [ {"name":"vim-X11","tries":1,"version":"7.4.160-1","status":0,"release":"1.el7"},
204
+ {"name":"libXt","tries":1,"version":"1.1.4-6","status":0,"release":"1.el7"} ]
205
+ * success: true
206
+
207
+ ### Ansible Single Host
208
+
209
+ {% plantuml %}
210
+ actor User
211
+ participant "Foreman Server" as Foreman
212
+ participant "Foreman Proxy" as Proxy
213
+ participant "Host" as Host
214
+
215
+ autonumber
216
+ User -> Foreman : JobInvocation
217
+ Foreman -> Proxy : ProxyCommand
218
+ Proxy -> Host : AnsibleCommand
219
+ Activate Host
220
+ Host --> Proxy : ProgressReport[Finished]
221
+ Deactivate Host
222
+ Proxy --> Foreman : AccumulatedProgressReport[Finished]
223
+
224
+ {% endplantuml %}
225
+
226
+ JobInvocation:
227
+
228
+ * hosts: [host.example.com]
229
+ * template: install-packages-ansible
230
+ * input: { packages: ['vim-X11'] }
231
+
232
+ ProxyCommand:
233
+
234
+ * host: host.example.com
235
+ * provider: ansible
236
+ * input: { module: yum, args: { name: 'vim-X11', state: installed } }
237
+
238
+ AnsibleCommand:
239
+
240
+ * host: host.example.com
241
+ * provider: ansible
242
+ * input: { module: yum, args: { name: 'vim-X11', state: installed } }
243
+
244
+ ProgressReport[Finished]:
245
+
246
+ * output: { changed: true,
247
+ rc: 0,
248
+ results: ["Resolving depednencies\ninstalling libXt\ninstalling vim-X11\noperation finished successfully"] }
249
+
250
+ AccumulatedProgressReport[Finished]:
251
+
252
+ * output: { changed: true,
253
+ rc: 0,
254
+ results: ["Resolving depednencies\ninstalling libXt\ninstalling vim-X11\noperation finished successfully"] }
255
+ * success: true
256
+
257
+
258
+ Job Preparation
259
+ ===============
260
+
261
+ User Stories
262
+ ------------
263
+
264
+ - 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
265
+
266
+ - As a user these job templates should be audited and versioned
267
+
268
+ - 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.
269
+
270
+ - 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.
271
+
272
+ - 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.
273
+
274
+ - 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.
275
+
276
+ - As a user I want to be able to specify default number of tries per job template.
277
+
278
+ - As a user I want to be able to specify default retry interval per job template.
279
+
280
+ - As a user I want to be able to specify default splay time per job template.
281
+
282
+ - As a user I want to setup default timeout per job template.
283
+
284
+ - As a user I want to preview a rendered job template for a host (providing needed inputs)
285
+
286
+ Scenarios
287
+ ---------
288
+ **Creating a job template**
289
+
290
+ 1. given I'm on new template form
291
+ 1. I select from a list of existing job names or fill in a new job name
292
+ 1. I select some option to add an input
293
+ 1. Give the input a name
294
+ 1. Select the type 'user input'
295
+ 1. Give the input a description (space separated package list)
296
+ 1. I select from a list of known providers (ssh, mco, salt, ansible)
297
+ 1. I am shown an example of how to use the input in the template
298
+ 1. I am able to see some simple example for the selected provider??
299
+ 1. I fill in the template
300
+ 1. I select one or more organizations and locations (if enabled)
301
+ 1. I click save
302
+
303
+
304
+ **Creating a smart variable based input**
305
+
306
+ 1. given i am creating or editing a job template
307
+ 1. I select to add a new input
308
+ 1. Give the input a name
309
+ 1. Define a smart variable name
310
+
311
+ Design
312
+ ------
313
+
314
+ {% plantuml %}
315
+
316
+ class JobTemplate {
317
+ name:string
318
+ job_name: string
319
+ retry_count: integer
320
+ retry_interval: integer
321
+ splay: integer
322
+ provider_type: string
323
+ ==
324
+ has_and_belongs_to_many :taxonomies
325
+ has_many :inputs
326
+ has_many :audits
327
+ }
328
+
329
+ class ConfigTemplateInput {
330
+ name: string
331
+ required: bool
332
+ input_type: USER_INPUT | FACT | SMART_VARIABLE
333
+ fact_name: string
334
+ smart_variable_name: string
335
+ description: string
336
+ ==
337
+ has_one :job_template
338
+ }
339
+
340
+ ConfigTemplate "1" -- "N" ConfigTemplateInput
341
+ {% endplantuml %}
342
+
343
+
344
+ Job Invocation
345
+ ==============
346
+
347
+ User Stories
348
+ ------------
349
+
350
+ - As a user I would like to invoke a job on a single host
351
+
352
+ - As a user I would like to invoke a job on a set of hosts, based on
353
+ search filter
354
+
355
+ - As a user I want to be able to reuse existing bookmarks for job
356
+ invocation
357
+
358
+ - As a user, when setting a job in future, I want to decide if the
359
+ search criteria should be evaluated now or on the execution time
360
+
361
+ - As a user I want to reuse the target of previous jobs for next execution
362
+
363
+ - As a CLI user I want to be able to invoke a job via hammer CLI
364
+
365
+ - As a user, I want to be able to invoke the job on a specific set of hosts
366
+ (by using checkboxes in the hosts table)
367
+
368
+ - As a user, when planning future job execution, I want to see a
369
+ warning with the info about unreachable hosts
370
+
371
+ - As a user I want to be able to override default values like (number
372
+ of tries, retry interval, splay time, timeout, effective user...) when I plan an execution of command.
373
+
374
+ - As a user I expect to see a the description of an input whenever i am being requested to
375
+ provide the value for the input.
376
+
377
+ - As a user I want to be able to re-invoke the jobs based on
378
+ success/failure of previous task
379
+
380
+ Scenarios
381
+ ---------
382
+
383
+ **Fill in target for a job**
384
+
385
+ 1. when I'm on job invocation form
386
+ 1. then I can specify the target of the job using the scoped search
387
+ syntax
388
+ 1. the target might influence the list of providers available for the
389
+ invocation: although, in delayed execution and dynamic targeting the
390
+ current list of providers based on the hosts might not be final and
391
+ we should count on that.
392
+
393
+ **Fill in template inputs for a job**
394
+
395
+ 1. given I'm on job invocation form
396
+ 1. when I choose the job to execute
397
+ 1. then I'm given a list of providers that I have enabled and has a
398
+ template available for the job
399
+ 1. and each provider allows to choose which template to use for this
400
+ invocation (if more templates for the job and provider are available)
401
+ 1. and every template has input fields generated based on the input
402
+ defined on the template (such as list of packages for install package
403
+ job)
404
+
405
+ **See the calculated template inputs for a job**
406
+
407
+ 1. given I'm on job invocation form
408
+ 1. when I choose the job to execute
409
+ 1. and I'm using a template with inputs calculated base on fact data
410
+ template available for the job
411
+ 1. then the preview of the current value for this input should be displayed
412
+ 1. but for the execution the value that the fact has by the time of
413
+ execution will be used.
414
+
415
+ **Fill in job description for the execution**
416
+
417
+ 1. given I'm on job invocation form
418
+ 1. there should be a field for task description, that will be used for
419
+ listing the jobs
420
+ 1. the description value should be pregenerated based on the job name
421
+ and specified input (something like "Package install: zsh")
422
+
423
+ **Fill in execution properties of the job**
424
+
425
+ 1. when I'm on job invocation form
426
+ 1. I can override the default values for number of tries, retry
427
+ interval, splay time, timeout, effective user...
428
+ 1. the overrides are common for all the templates
429
+
430
+ **Set the execution time into future** (see [scheduling](design#scheduling)
431
+ for more scenarios)
432
+
433
+ 1. when I'm on a job invocation form
434
+ 1. then I can specify the time to start the execution at (now by
435
+ default)
436
+ 1. and I can specify if the targeting should be calculated now or
437
+ postponed to the execution time
438
+
439
+ **Run a job from host detail**
440
+
441
+ 1. given I'm on a host details page
442
+ 1. when I click "Run job"
443
+ 1. then a user dialog opens with job invocation form, with pre-filled
444
+ targeting pointing to this particular host
445
+
446
+ **Run a job from host index**
447
+
448
+ 1. given I'm on a host index page
449
+ 1. when I click "Run job"
450
+ 1. then a user dialog opens with job invocation form, with prefiled
451
+ targeting using the same search that was used in the host index page
452
+
453
+ **Invoke a job with single remote execution provider**
454
+
455
+ 1. given I have only one provider available in my installation
456
+ 1. and I'm on job invocation form
457
+ 1. when I choose the job to execute
458
+ 1. then only the template for this provider is available to run and
459
+ asking for user inputs
460
+
461
+ **Invoke a job with hammer**
462
+
463
+ 1. given I'm using CLI
464
+ 1. then I can run a job with ability to specify:
465
+ - targeting with scoped search or bookmark_id
466
+ - job name to run
467
+ - templates to use for the job
468
+ - inputs on per-template basis
469
+ - execution properties as overrides for the defaults coming from the template
470
+ - ``start_at`` value for execution in future
471
+ - in case of the start_at value, if the targeting should be static
472
+ vs. dynamic
473
+ - whether to wait for the job or exit after invocation (--async
474
+ option)
475
+
476
+ **Re-invoke a job**
477
+
478
+ 1. given I'm in job details page
479
+ 1. when I choose re-run
480
+ 1. then a user dialog opens with job invocation form, with prefiled
481
+ targeting parameters from the previous execution
482
+ 1 and I can override all the values (including targeting, job,
483
+ templates and inputs)
484
+
485
+ **Re-invoke a job for failed hosts**
486
+
487
+ 1. given I'm in job details page
488
+ 1. when I choose re-run
489
+ 1. then a user dialog opens with job invocation form, with prefiled
490
+ targeting parameters from the previous execution
491
+ 1 and I can override all the values (including targeting, job,
492
+ templates and inputs)
493
+ 1. I can choose in the targeting to only run on hosts that failed with
494
+ the job previously
495
+
496
+ **Edit a bookmark referenced by pending job invocation**
497
+
498
+ 1. given I have a pending execution task which targeting was created
499
+ from a bookmark
500
+ 2. when I edit the bookmark
501
+ 3. then I should be notified about the existence of the pending tasks
502
+ with ability to update the targeting (or cancel and recreate the
503
+ invocation)
504
+
505
+ **Email notification: opt in**
506
+
507
+ 1. given I haven't configured to send email notifications about my executions
508
+ 1. then the job invocation should have the 'send email notification'
509
+ turned off by default
510
+
511
+ **Email notification: opt out**
512
+
513
+ 1. given I haven't configured to send email notifications about my executions
514
+ 1. then the job invocation should have the 'send email notification'
515
+ turned off by default
516
+
517
+ Design
518
+ ------
519
+
520
+ Class diagram of Foreman classes
521
+
522
+ {% plantuml %}
523
+
524
+ class Bookmark {
525
+ name:string
526
+ query:string
527
+ controller:string
528
+ public:bool
529
+ owner_id:integer
530
+ owner_type:string
531
+ }
532
+
533
+ class Targeting {
534
+ query: string
535
+ dynamic: bool
536
+ }
537
+
538
+ class Host
539
+ class User
540
+
541
+ class TemplateInvocation {
542
+ inputs
543
+ }
544
+
545
+ class JobInvocation {
546
+ tries
547
+ retry_interval
548
+ splay
549
+ concurrency
550
+ effective_user
551
+ email_notification: bool
552
+ }
553
+
554
+ class JobTask {
555
+ start_at: datetime
556
+ }
557
+
558
+ Bookmark "1" - "N" Targeting
559
+ Targeting "M" - "N" Host : (polymorphic)
560
+ Targeting "N" -- "1" User
561
+ JobInvocation "1" -- "1" Targeting
562
+ JobInvocation "1" -- "N" TemplateInvocation
563
+ TemplateInvocation "N" -- "1" JobTemplate
564
+ JobInvocation "1" -- "N" JobTask
565
+
566
+ {% endplantuml %}
567
+
568
+ Query is copied to Targeting, we don't want to propagate any later
569
+ changes to Bookmark to already planned job executions.
570
+
571
+ We can store link to original bookmark to be able to
572
+ compare changes later.
573
+
574
+ For JobInvocation we forbid later editing of Targeting.
575
+
576
+ Open questions
577
+ --------------
578
+
579
+ * should we unify the common inputs in all templates to specify them
580
+ only once or scoping the input by template?
581
+
582
+ * Maybe an inputs catalog (with both defined name and semantic) might
583
+ help with keeping the inputs consistent across templates/providers
584
+
585
+
586
+ Job Execution
587
+ =============
588
+
589
+ User Stories
590
+ ------------
591
+
592
+ - As a user I want to be able to cancel job which hasn't been started yet.
593
+
594
+ - As a user I want to be able to cancel job which is in progress
595
+ (if supported by specific provider…)
596
+
597
+ - As a user I want job execution to fail after timeout limit.
598
+
599
+ - As a user I want to job execution to be re-tried
600
+ based on the tries and retry interval values given in the invocation
601
+
602
+ - As a user I want to job execution on multiple hosts to be spread
603
+ using the splay time value: the execution of the jobs will be spread
604
+ randomly across the time interval
605
+
606
+ - As a user I want to job execution on multiple hosts to be limited
607
+ by a concurrency level: the number of concurrently running jobs will
608
+ not exceed the limit.
609
+
610
+ - As a user I want the job execution to be performed as a user that
611
+ was specified on the job invocation
612
+
613
+ - As a user I want an ability to retry the job execution when the host
614
+ checks in (support of hosts that are offline by the time the
615
+ execution).
616
+
617
+ Scenarios
618
+ ---------
619
+
620
+ **Cancel pending bulk task: all at once**
621
+
622
+ 1. given I've set a job to run in future on multiple hosts
623
+ 1. when I click 'cancel' on the corresponding bulk task
624
+ 1. then the whole task should be canceled (including all the sub-tasks
625
+ on all the hosts)
626
+
627
+ **Cancel pending bulk task: task on specific host**
628
+
629
+ 1. given I've set a job to run in future on multiple hosts
630
+ 1. when I show the task representation on a host details page
631
+ 1. when I click 'cancel' on the task
632
+ 1. then I should be offered whether I should cancel just this
633
+ instance or the whole bulk task on all hosts
634
+
635
+ **Fail after timeout**
636
+
637
+ 1. given I've invoked a job
638
+ 1. when the job fails to start in given specified timeout
639
+ 1. then the job should be marked as failed due to timeout
640
+
641
+ **Retried task**
642
+
643
+ 1. given I've invoked a job
644
+ 1. when the job fails to start at first attemt
645
+ 1. then the executor should wait for retry_timeout period
646
+ 1. and it should reiterate with the attempt based on the tries number
647
+ 1. and I should see the information about the number of retries
648
+
649
+ Design
650
+ ------
651
+
652
+ Class diagram for jobs running on multiple hosts
653
+
654
+ {% plantuml %}
655
+
656
+
657
+ class Host {
658
+ get_provider(type)
659
+ }
660
+
661
+ class BulkJobTask {
662
+ state: $TaskState
663
+ start_at: datetime
664
+ started_at: datetime
665
+ ended_at datetime
666
+ cancel()
667
+ }
668
+
669
+ class JobTask {
670
+ retry: integer
671
+ retry_interval: integer
672
+ timeout: integer
673
+ splay: integer
674
+ concurrency: integer
675
+ type: string
676
+ state: $TaskState
677
+ start_at: datetime
678
+ started_at: datetime
679
+ tried_count: integer
680
+ ended_at datetime
681
+ {abstract} support_cancel?()
682
+ {abstract} proxy_endpoint()
683
+ cancel()
684
+ }
685
+
686
+ abstract class ProxyCommand {
687
+ }
688
+
689
+ class SSHProxyCommand {
690
+ {static} support_cancel?()
691
+ proxy_endpoint():string
692
+ }
693
+
694
+ class MCollectiveProxyCommand {
695
+ {static} support_cancel?()
696
+ proxy_endpoint():string
697
+ }
698
+
699
+ BulkJobTask "N" - "1" JobInvocation
700
+ BulkJobTask "1" -- "N" JobTask
701
+ TemplateInvocation "N" - "1" JobInvocation
702
+ TemplateInvocation "1" --- "N" JobTask
703
+ JobTask "1" -- "1" ProxyCommand
704
+ JobTask "N" -- "1" Host
705
+
706
+ ProxyCommand <|-- SSHProxyCommand
707
+ ProxyCommand <|-- MCollectiveProxyCommand
708
+
709
+ {% endplantuml %}
710
+
711
+ Class diagram for jobs running a single host
712
+
713
+ {% plantuml %}
714
+
715
+ class Host {
716
+ get_provider(type)
717
+ }
718
+
719
+ class JobTask {
720
+ retry: integer
721
+ retry_interval: integer
722
+ timeout: integer
723
+ splay: integer
724
+ concurrency: integer
725
+ type: string
726
+ state: $TaskState
727
+ started_at: datetime
728
+ start_at: datetime
729
+ tried_count: integer
730
+ ended_at datetime
731
+ cancel()
732
+ {abstract} support_cancel?()
733
+ {abstract} proxy_endpoint()
734
+ plan()
735
+ cancel()
736
+ }
737
+
738
+ class ProxyCommand {
739
+ }
740
+
741
+ JobTask "N" - "1" JobInvocation
742
+ TemplateInvocation "N" - "1" JobInvocation
743
+ TemplateInvocation "1" - "N" JobTask
744
+ JobTask "1" -- "1" ProxyCommand
745
+ JobTask "1" -- "1" Host
746
+ {% endplantuml %}
747
+
748
+ Reporting
749
+ =========
750
+
751
+ User Stories
752
+ ------------
753
+
754
+ - As a user I would like to monitor the current state of the job
755
+ running against a single host, including the output and exit status
756
+
757
+ - As a user I would like to monitor the status of bulk job,
758
+ including the number of successful, failed and pending tasks
759
+
760
+ - As a user I would like to see the history of all job run on a
761
+ host
762
+
763
+ - As a user I would like to see the history of all tasks that I've
764
+ invoked
765
+
766
+ - As a user I would like to be able to get an email notification with
767
+ execution report
768
+
769
+ Scenarios
770
+ ---------
771
+
772
+ **Track the job running on a set of hosts**
773
+
774
+ 1. given I've set a job to run in future on multiple hosts
775
+ 1. then I can watch the progress of the job (number of
776
+ successful/failed/pending tasks)
777
+ 1. and I can get to the list of jobs per host
778
+ 1. and I'm able to filter on the host that it was run against and
779
+ state
780
+
781
+ **Track the job running on a single host**
782
+
783
+ 1. given I've set a job to run on a specific host
784
+ 1. when I show the task representation page
785
+ 1. then I can watch the progress of the job (updated log), status
786
+
787
+ **History of jobs run on a host**
788
+
789
+ 1. given I'm on host jobs page
790
+ 1. when I can see all the jobs run against the host
791
+ 1. and I'm able to filter on the host that it was run against and
792
+ state, owner etc.
793
+
794
+ **History of invoked jobs**
795
+
796
+ 1. given I'm on job invocation history page
797
+ 1. when I can see all the jobs invoked in the system
798
+ 1. scoped by a taxonomy (based on the hosts the jobs were run against)
799
+ 1. and I'm able to filter on the host that it was run against and
800
+ state, owner etc.
801
+
802
+ **Email notification: send after finish**
803
+
804
+ 1. given I've invoked a job with email notification turned on
805
+ 1. when the job finishes
806
+ 1. then I should get the email with report from the job after it finishes
807
+
808
+ Design
809
+ ------
810
+
811
+ Class diagram for jobs running on multiple hosts
812
+
813
+ {% plantuml %}
814
+
815
+
816
+ class Host {
817
+ }
818
+
819
+ class JobInvocation {
820
+ email_notification: bool
821
+ }
822
+
823
+ class BulkJobTask {
824
+ state: $TaskState
825
+ start_at: datetime
826
+ started_at: datetime
827
+ ended_at datetime
828
+ }
829
+
830
+ class JobTask {
831
+ type: string
832
+ state: $TaskState
833
+ start_at: datetime
834
+ started_at: datetime
835
+ ended_at datetime
836
+ tried_count: integer
837
+ command: string
838
+ output: string
839
+ exit_code: string
840
+ }
841
+
842
+ BulkJobTask "N" - "1" JobInvocation
843
+ BulkJobTask "1" -- "N" JobTask
844
+ JobTask "1" -- "1" Host
845
+
846
+ {% endplantuml %}
847
+
848
+ Scheduling
849
+ ==========
850
+
851
+ User Stories
852
+ ------------
853
+
854
+ - As a user I want to be able go execute a job at future time
855
+
856
+ - As a user I want to set the job to reoccur with specified
857
+ frequency
858
+
859
+ Scenarios
860
+ ---------
861
+
862
+ **Job set for the future**
863
+
864
+ 1. given I've invoked a job at future time
865
+ 1. when the time comes
866
+ 1. the job gets executed
867
+
868
+ **Creating reoccurring job**
869
+
870
+ 1. given I'm in job invocation form
871
+ 1. when I check 'reoccurring job'
872
+ 1. then I can set the frequency and valid until date
873
+
874
+ **Showing the tasks with reoccurring logic**
875
+
876
+ 1. when I list the jobs
877
+ 1. I can see the information about the reoccurring logic at every job
878
+ 1. and I can filter the jobs for those with the reoccurring logic
879
+
880
+ **Canceling the reoccurring job**
881
+
882
+ 1. given I have reoccurring job configured
883
+ 1. when I cancel the next instance of the job
884
+ 1. then I'm offered to cancel the reoccurring of the job in the future
885
+
886
+ Design
887
+ ------
888
+
889
+ {% plantuml %}
890
+
891
+ class Schedule {
892
+ start_at: datetime
893
+ end_at: datetime
894
+ cronline: string
895
+ }
896
+
897
+ class JobTask {
898
+ }
899
+
900
+ JobTask "N" -- "1" JobInvocation
901
+ JobInvocation "1" -- "1" Schedule
902
+
903
+ {% endplantuml %}
904
+
905
+ Developer API
906
+ =============
907
+
908
+ User Stories
909
+ ------------
910
+
911
+ - As a Foreman developer, I want to be able to use remote execution
912
+ plugin to help with other Foreman features such as:
913
+ - puppet run
914
+ - grubby reprovision
915
+ - content actions (package install/update/remove/downgrade, group
916
+ install/uninstall, package profile refresh)
917
+ - subscription actions (refresh)
918
+ - OpenSCAP content update
919
+
920
+ Scenarios
921
+ ---------
922
+
923
+ **Defining a predefined job without provided inputs**
924
+
925
+ 1. given I'm a Foreman developer
926
+ 1. and I want to expose 'puppet run' feature to the user
927
+ 1. then define the 'Puppet Run' as predefined job in the code
928
+ 1. and specify the default job name to be used for the mapping
929
+
930
+ **Defining a predefined job with provided inputs**
931
+
932
+ 1. given I'm a Katello developer
933
+ 1. and I want to expose 'package install' feature to the user
934
+ 1. then I define the 'Package Install' predefined job with list of
935
+ packages as provided input in the code
936
+ 1. and I specify default job name to be used for the mapping
937
+ 1. and I specify default mapping of the provided inputs to template
938
+ inputs
939
+
940
+ **Preseeding the predefined jobs**
941
+
942
+ 1. given I've defined the 'Package Install' predefined job
943
+ 1. when the seed script is run as part of the Foreman installation
944
+ 1. the systems tries to create the default mapping from the predefined job to
945
+ the existing templates based on the developer-provided defaults
946
+
947
+ **Configuring the predefined jobs mapping**
948
+
949
+ 1. given I'm the administrator of the Foreman instance
950
+ 1. then I can see all the predefined jobs mapping
951
+ 1. when I edit existing mapping
952
+ 1. then I can choose job name, template and provided input -> template
953
+ inputs mapping
954
+
955
+ **Configuring the predefined jobs mapping with organizations**
956
+
957
+ 1. given I'm the administrator of the Foreman instance
958
+ 1. then I can scope the mapping of the predefined job to a specific
959
+ organization
960
+ 1. and the system doesn't let me to create two mappings for the same
961
+ predefined job and provider visible in one organization
962
+
963
+ **Using the predefined jobs without provided inputs**
964
+
965
+ 1. given I'm a Foreman user
966
+ 1. when I'm on host details page
967
+ 1. and I press 'Puppet Run'
968
+ 1. the job is invoked on the host based on the predefined mapping
969
+
970
+ **Using the predefined jobs with provided inputs**
971
+
972
+ 1. given I'm a Katello user
973
+ 1. when I'm on host applicable errata list
974
+ 1. and I select a set of errata to install on the host
975
+ 1. and I click 'Install errata'
976
+ 1. the job will be invoked to install the packages belonging to this
977
+ errata
978
+
979
+ **Using the predefined jobs with customization**
980
+
981
+ 1. given I'm a Katello user
982
+ 1. when I'm on host applicable errata list
983
+ 1. and I select a set of errata to install on the host
984
+ 1. and I click 'Install errata (customize)'
985
+ 1. then the job invocation form will be opened with pre-filled values
986
+ based on the mapping
987
+ 1. and I can update the values, including setting the start_at time or
988
+ reoccurring logic
989
+
990
+ Design
991
+ ------
992
+
993
+ {% plantuml %}
994
+
995
+ class PredefinedJob {
996
+ predefined_job_name: string
997
+ ==
998
+ has_and_belongs_to_many :taxonomies
999
+ }
1000
+
1001
+ class PredefinedJobInputMapping {
1002
+ provided_input_name: string
1003
+ }
1004
+
1005
+ PredefinedJob "1" -- "N" PredefinedJobInputMapping
1006
+ PredefinedJobInputMapping "N" -- "1" ConfigTemplateInput
1007
+ PredefinedJob "M" -- "N" JobTemplate
1008
+ note on link #red: 1:1 per organization and provider
1009
+
1010
+ {% endplantuml %}
1011
+
1012
+
1013
+ Security
1014
+ ========
1015
+
1016
+ User Stories
1017
+ ------------
1018
+
1019
+ - As a user I want to be able to plan job invocation for any host that I
1020
+ can view (view_host permission).
1021
+
1022
+ - As a user I want to be able to plan a job invocation of job that I can
1023
+ view (view_job permission)
1024
+
1025
+ - As a user I want to restrict other users which combination of host and job
1026
+ name they can execute (execute permission on job_task resource).
1027
+
1028
+ - As a user I want to be warned if I planned job invocation on hosts on
1029
+ which the execution of this job is not allowed to me.
1030
+
1031
+ - As a user I want to see refused job invocations (based on permissions) as
1032
+ failed when they are executed.
1033
+
1034
+ - As a user I want to set limit filter with execute permission by host attributes
1035
+ such as hostgroup, environment, fqdn, id, lifecycle environment (if applicable),
1036
+ content view (if applicable).
1037
+
1038
+ - As a user I want to specify effective_user for JobInvocation if at least one
1039
+ provider supports it.
1040
+
1041
+ - As a user I want to restrict other users to execute job under specific user
1042
+ as a part of filter condition. If the provider does not allow this, execution
1043
+ should be refused.
1044
+
1045
+ - As a job template provider I want to be able to specify default effective user
1046
+
1047
+ Scenarios
1048
+ ---------
1049
+
1050
+ **Allow user A to invoke package installation on host B**
1051
+
1052
+ 1. given user A can view all hosts and job templates
1053
+ 1. when he invoke package installation job on host B
1054
+ 1. then his job task fails because he does not have execution
1055
+ permission for such job task
1056
+
1057
+ **Allow user A to run package installation on host B**
1058
+
1059
+ 1. given I've permissions to assign other user permissions
1060
+ 1. and user A can view all hosts and job templates
1061
+ 1. and user A can create job invocations
1062
+ 1. when I grant user A execution permission on resource JobTask
1063
+ 1. and I set related filter condition to "host_name = B and job_name = package_install"
1064
+ 1. and user A invokes package install execution on hosts B and C
1065
+ 1. then the job gets executed successfully on host B
1066
+ 1. and job execution will fail on host C
1067
+
1068
+ **User can set effective user**
1069
+
1070
+ 1. given the provider of job template supports changing effective user
1071
+ 1. when user invokes a job
1072
+ 1. then he can set effective user under which job is executed on target host
1073
+
1074
+ **User can disallow running job as different effective user**
1075
+
1076
+ 1. given I've permissions to assign other user permissions
1077
+ 1. and user A can view all hosts and job templates
1078
+ 1. and user A can create job invocations
1079
+ 1. when I grant user A execution permission on resource JobTask
1080
+ 1. and I set related filter condition to "effective_user = user_a"
1081
+ 1. and user A invokes job execution with effective user set to different user (e.g. root)
1082
+ 1. then the job execution fails
1083
+
1084
+ New permissions introduced
1085
+ --------------------------
1086
+
1087
+ - JobInvocation
1088
+ - Create
1089
+ - View
1090
+ - Cancel
1091
+ - Edit (Schedule, never can change targetting)
1092
+ - JobTask
1093
+ - Execute
1094
+ - (filter can be: effective_user = 'joe' and host_id = 1 or host_id = 2 and script_name = 'foobar')
1095
+
1096
+
1097
+ Design
1098
+ ------
1099
+
1100
+ {% plantuml %}
1101
+
1102
+ class JobTemplate {
1103
+ effective_user: string
1104
+ }
1105
+
1106
+ class JobInvocation {
1107
+ effective_user: string
1108
+ }
1109
+
1110
+ {% endplantuml %}
1111
+
1112
+ Katello Client Utilities
1113
+ ========================
1114
+
1115
+ Design
1116
+ ------
1117
+
1118
+ katello-agent provides three main functions aside from remote management:
1119
+
1120
+ * package profile yum plugin - pushes a new package profile after any yum transaction
1121
+ * Split out into its own package (yum-plugin-katello-profile)
1122
+ * enabled repository monitoring
1123
+ * monitors /etc/yum.repos.d/redhat.repo file for changes and sends newly enabled repos whenever it does change
1124
+ * Split out into its own package (katello-errata-profile) with a service to do the same
1125
+ * On the capsule, goferd runs to recieve commands to sync repositories, possible solutions:
1126
+ * katello-agent can remain (but possibly renamed), with a lot of the existing functionality removed
1127
+ * pulp changes to a rest api method for initiating capsule syncs, katello needs to store some auth credentials per capsule
1128
+
1129
+ Orchestration
1130
+ =============
1131
+
1132
+ User Stories
1133
+ ------------
1134
+
1135
+ - As a user I want to group a number of jobs together and treat them
1136
+ as an executable unit. (i.e. run this script to stop the app, install
1137
+ these errata, reboot the system)
1138
+
1139
+ - As a user I want to run a set of jobs in a rolling fashion.
1140
+ (i.e.,patch server 1, reboot it, if it succeeds, proceed to server 2
1141
+ & repeat. Otherwise raise exception)
1142
+
1143
+ - As a user I want to define a rollback job in case the execution
1144
+ fails
1145
+
1146
+ - As a sysadmin I would like to orchestrate several actions across a
1147
+ collection of machines. (e.g. install a DB on this machine, and pass
1148
+ the ip address into an install of a web server on another machine)
1149
+
1150
+ Design
1151
+ ------
1152
+
1153
+ - TBD after the simple support is implemented, possible cooperation with
1154
+ multi-host deployments feature
1155
+
1156
+ - Some of the features might be solved by advanced remote execution
1157
+ technology integration (such as ansible playbook)
1158
+
1159
+
1160
+ Design: the whole picture
1161
+ ======================
1162
+
1163
+ {% plantuml %}
1164
+ class Host {
1165
+ get_provider(type)
1166
+ }
1167
+
1168
+ package "Job Preparation" {
1169
+ class JobTemplate {
1170
+ name: string
1171
+ job_name: string
1172
+ retry_count: integer
1173
+ retry_interval: integer
1174
+ splay: integer
1175
+ provider_type: string
1176
+ effective_user: string
1177
+ ==
1178
+ has_and_belongs_to_many :taxonomies
1179
+ has_many :inputs
1180
+ has_many :audits
1181
+ }
1182
+
1183
+ class ConfigTemplateInput {
1184
+ name: string
1185
+ required: bool
1186
+ input_type: USER_INPUT | FACT | SMART_VARIABLE
1187
+ fact_name: string
1188
+ smart_variable_name: string
1189
+ description: string
1190
+ ==
1191
+ has_one :job_template
1192
+ }
1193
+
1194
+ JobTemplate "1" -- "N" ConfigTemplateInput
1195
+ }
1196
+
1197
+ package "Job Invocation" {
1198
+ class Bookmark {
1199
+ name:string
1200
+ query:string
1201
+ controller:string
1202
+ public:bool
1203
+ owner_id:integer
1204
+ owner_type:string
1205
+ }
1206
+
1207
+ class Targeting {
1208
+ query: string
1209
+ dynamic: bool
1210
+ }
1211
+
1212
+ class TemplateInvocation {
1213
+ inputs
1214
+ }
1215
+
1216
+ class JobInvocation {
1217
+ tries
1218
+ retry_interval
1219
+ splay
1220
+ concurrency
1221
+ email_notification: bool
1222
+ effective_user: string
1223
+ }
1224
+
1225
+ class User
1226
+
1227
+ Bookmark "1" -DOWN- "N" Targeting
1228
+ Targeting "M" -DOWN- "N" Host
1229
+ Targeting "N" -UP- "1" User
1230
+ JobInvocation "1" -LEFT- "1" Targeting
1231
+ JobInvocation "1" -DOWN- "N" TemplateInvocation
1232
+ TemplateInvocation "N" -LEFT- "1" JobTemplate
1233
+
1234
+ }
1235
+
1236
+ package "Scheduling" {
1237
+ class Schedule {
1238
+ start_at: datetime
1239
+ end_at: datetime
1240
+ cronline: string
1241
+ }
1242
+
1243
+ JobInvocation "1" -UP- "0..1" Schedule
1244
+ }
1245
+
1246
+ package "Execution" {
1247
+ class BulkJobTask {
1248
+ state: $TaskState
1249
+ start_at: datetime
1250
+ started_at: datetime
1251
+ ended_at datetime
1252
+ cancel()
1253
+ }
1254
+
1255
+ class JobTask {
1256
+ state: $TaskState
1257
+ start_at: datetime
1258
+ started_at: datetime
1259
+ tried_count: integer
1260
+ ended_at datetime
1261
+ retry: integer
1262
+ retry_interval: integer
1263
+ timeout: integer
1264
+ splay: integer
1265
+ concurrency: integer
1266
+ provider: string
1267
+
1268
+ command: string
1269
+ output: string
1270
+ exit_code: string
1271
+
1272
+ {abstract} support_cancel?()
1273
+ {abstract} proxy_endpoint()
1274
+ cancel()
1275
+ }
1276
+
1277
+ abstract class ProxyCommand {
1278
+ }
1279
+
1280
+ class SSHProxyCommand {
1281
+ {static} support_cancel?()
1282
+ proxy_endpoint():string
1283
+ }
1284
+
1285
+ class MCollectiveProxyCommand {
1286
+ {static} support_cancel?()
1287
+ proxy_endpoint():string
1288
+ }
1289
+
1290
+ BulkJobTask "N" -LEFT- "1" JobInvocation
1291
+ BulkJobTask "1" -- "N" JobTask
1292
+ TemplateInvocation "1" -- "N" JobTask
1293
+ JobTask "1" -RIGHT- "1" ProxyCommand
1294
+ JobTask "N" -UP- "1" Host
1295
+ }
1296
+
1297
+
1298
+ ProxyCommand <|-- SSHProxyCommand
1299
+ ProxyCommand <|-- MCollectiveProxyCommand
1300
+
1301
+ package "Developer API" {
1302
+ class PredefinedJob {
1303
+ predefined_job_name: string
1304
+ ==
1305
+ has_and_belongs_to_many :taxonomies
1306
+ }
1307
+
1308
+ class PredefinedJobInputMapping {
1309
+ provided_input_name: string
1310
+ }
1311
+
1312
+ PredefinedJob "1" -- "N" PredefinedJobInputMapping
1313
+ PredefinedJobInputMapping "N" -RIGHT- "1" ConfigTemplateInput
1314
+ PredefinedJob "M" -RIGHT- "N" JobTemplate
1315
+ }
1316
+
1317
+ {% endplantuml %}