— layout: page title: Design countheads: true toc: true comments: true —
As a user I want to run jobs in parallel across large number of hosts
As a user I want to run jobs on a host in a different network segment (the host doesn't see the Foreman server/the Foreman server doesn't see the host directly)
As a user I want to manage a host without installing an agent on it (just plain old ssh)
As a community user I want to already existing remote execution technologies in combination with the Foreman
Although specific providers are mentioned in the design, it's used mainly for distinguishing different approaches to the remote execution than to choose specific technologies
{% plantuml %} actor User participant “Foreman Server” as Foreman participant “Foreman Proxy” as Proxy participant “Host” as Host
autonumber User -> Foreman : JobInvocation Foreman -> Proxy : ProxyCommand Proxy -> Host : SshScript Activate Host Host –> Proxy : ProgressReport[1, Running] Host –> Proxy : ProgressReport[2, Running] Proxy –> Foreman : AccumulatedProgressReport[1, Running] Host –> Proxy : ProgressReport[3, Running] Host –> Proxy : ProgressReport[4, Finished] Deactivate Host Proxy –> Foreman : AccumulatedProgressReport[2, Finished] {% endplantuml %}
JobInvocation: see see scheduling
ProxyCommand:
host: host.example.com
provider: ssh
input: “yum install -y vim-X11”
SSHScript:
host: host.example.com
input: “yum install -y vim-X11”
ProgressReport[1, Running]:
output: “Resolving depednencies”
ProgressReport[2, Running]:
output: “installing libXt”
AccumulatedProgressReport[1, Running]:
output: { stdout: “Resolving depednenciesninstalling libXt” }
ProgressReport[3, Running]:
output: “installing vim-X11”
ProgressReport[4, Finished]:
output: “operation finished successfully”
exit_code: 0
AccumulatedProgressReport[2, Finished]:
output: { stdout: “installing vim-X11noperation finished successfully”, exit_code: 0 }
success: true
This case allows to handle the case, when the host is offline by the time of job invocation: the list of jobs for the host is stored on the Foreman server side for running once the host is online.
This approach is not limited to the ssh provider only.
{% plantuml %} actor User participant “Foreman Server” as Foreman participant “Foreman Proxy” as Proxy participant “Host” as Host
autonumber User -> Foreman : JobInvocation Host -> Proxy : CheckIn Proxy -> Foreman : CheckIn Foreman -> Proxy : ProxyCommand Proxy -> Host : SshScript {% endplantuml %}
{% plantuml %} actor User participant “Foreman Server” as Foreman participant “Foreman Proxy” as Proxy participant “Host 1” as Host1 participant “Host 2” as Host2
autonumber User -> Foreman : JobInvocation Foreman -> Proxy : ProxyCommand Foreman -> Proxy : ProxyCommand Proxy -> Host1 : SSHScript Proxy -> Host2 : SSHScript {% endplantuml %}
host: host-1.example.com
provider: ssh
input: “yum install -y vim-X11”
host: host-2.example.com
provider: ssh
input: “yum install -y vim-X11”
{% info_block %} we might want to optimize the communication between server and the proxy (sending collection of ProxyCommands in bulk, as well as the AccumulatedProgerssReports). That would could also be utilized by the Ansible implementation, where there might be optimization on the invoking the ansible commands at once (the same might apply to mcollective). On the other hand, this is more an optimization, not required to be implemented from the day one: but it's good to have this in mind {% endinfo_block %}
{% plantuml %} actor User participant “Foreman Server” as Foreman participant “Foreman Proxy” as Proxy participant “AMQP” as AMQP participant “Host” as Host
autonumber User -> Foreman : JobInvocation Foreman -> Proxy : ProxyCommand Proxy -> AMQP : MCOCommand AMQP -> Host : MCOCommand Activate Host Host –> AMQP : ProgressReport Deactivate Host AMQP –> Proxy : ProgressReport Proxy –> Foreman : AccumulatedProgressReport {% endplantuml %}
JobInvocation:
hosts: [host.example.com]
template: install-packages-mco
input: { packages: ['vim-X11'] }
ProxyCommand:
host: host.example.com
provider: mcollective
input: { agent: package, args: { package => 'vim-X11' } }
MCOCommand:
host: host.example.com
input: { agent: package, args: { package => 'vim-X11' } }
output: [ {“name”:“vim-X11”,“tries”:1,“version”:“7.4.160-1”,“status”:0,“release”:“1.el7”}, {“name”:“libXt”,“tries”:1,“version”:“1.1.4-6”,“status”:0,“release”:“1.el7”} ]
output: [ {“name”:“vim-X11”,“tries”:1,“version”:“7.4.160-1”,“status”:0,“release”:“1.el7”}, {“name”:“libXt”,“tries”:1,“version”:“1.1.4-6”,“status”:0,“release”:“1.el7”} ]
success: true
{% plantuml %} actor User participant “Foreman Server” as Foreman participant “Foreman Proxy” as Proxy participant “Host” as Host
autonumber User -> Foreman : JobInvocation Foreman -> Proxy : ProxyCommand Proxy -> Host : AnsibleCommand Activate Host Host –> Proxy : ProgressReport Deactivate Host Proxy –> Foreman : AccumulatedProgressReport
{% endplantuml %}
JobInvocation:
hosts: [host.example.com]
template: install-packages-ansible
input: { packages: ['vim-X11'] }
ProxyCommand:
host: host.example.com
provider: ansible
input: { module: yum, args: { name: 'vim-X11', state: installed } }
AnsibleCommand:
host: host.example.com
provider: ansible
input: { module: yum, args: { name: 'vim-X11', state: installed } }
output: { changed: true, rc: 0, results: [“Resolving depednenciesninstalling libXtninstalling vim-X11noperation finished successfully”] }
output: { changed: true, rc: 0, results: [“Resolving depednenciesninstalling libXtninstalling vim-X11noperation finished successfully”] }
success: true
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
As a user these job templates should be audited and versioned
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.
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.
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.
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.
As a user I want to be able to specify default number of tries per job template.
As a user I want to be able to specify default retry interval per job template.
As a user I want to be able to specify default splay time per job template.
As a user I want to setup default timeout per job template.
As a user I want to preview a rendered job template for a host (providing needed inputs)
Creating a job template
given I'm on new template form
I select from a list of existing job names or fill in a new job name
I select some option to add an input
Give the input a name
Select the type 'user input'
Give the input a description (space separated package list)
I select from a list of known providers (ssh, mco, salt, ansible)
I am shown an example of how to use the input in the template
I am able to see some simple example for the selected provider??
I fill in the template
I select one or more organizations and locations (if enabled)
I click save
Creating a smart variable based input
given i am creating or editing a job template
I select to add a new input
Give the input a name
Define a smart variable name
{% plantuml %}
class JobTemplate { name:string job_name: string retry_count: integer retry_interval: integer splay: integer provider_type: string == has_and_belongs_to_many :taxonomies has_many :inputs has_many :audits }
class ConfigTemplateInput { name: string required: bool input_type: USER_INPUT | FACT | SMART_VARIABLE fact_name: string smart_variable_name: string description: string == has_one :job_template }
ConfigTemplate “1” – “N” ConfigTemplateInput {% endplantuml %}
As a user I would like to invoke a job on a single host
As a user I would like to invoke a job on a set of hosts, based on search filter
As a user I want to be able to reuse existing bookmarks for job invocation
As a user, when setting a job in future, I want to decide if the search criteria should be evaluated now or on the execution time
As a user I want to reuse the target of previous jobs for next execution
As a CLI user I want to be able to invoke a job via hammer CLI
As a user, I want to be able to invoke the job on a specific set of hosts (by using checkboxes in the hosts table)
As a user, when planning future job execution, I want to see a warning with the info about unreachable hosts
As a user I want to be able to override default values like (number of tries, retry interval, splay time, timeout, effective user...) when I plan an execution of command.
As a user I expect to see a the description of an input whenever i am being requested to provide the value for the input.
As a user I want to be able to re-invoke the jobs based on success/failure of previous task
Fill in target for a job
when I'm on job invocation form
then I can specify the target of the job using the scoped search syntax
the target might influence the list of providers available for the invocation: although, in delayed execution and dynamic targeting the current list of providers based on the hosts might not be final and we should count on that.
Fill in template inputs for a job
given I'm on job invocation form
when I choose the job to execute
then I'm given a list of providers that I have enabled and has a template available for the job
and each provider allows to choose which template to use for this invocation (if more templates for the job and provider are available)
and every template has input fields generated based on the input defined on the template (such as list of packages for install package job)
See the calculated template inputs for a job
given I'm on job invocation form
when I choose the job to execute
and I'm using a template with inputs calculated base on fact data template available for the job
then the preview of the current value for this input should be displayed
but for the execution the value that the fact has by the time of execution will be used.
Fill in job description for the execution
given I'm on job invocation form
there should be a field for task description, that will be used for listing the jobs
the description value should be pregenerated based on the job name and specified input (something like “Package install: zsh”)
Fill in execution properties of the job
when I'm on job invocation form
I can override the default values for number of tries, retry interval, splay time, timeout, effective user...
the overrides are common for all the templates
Set the execution time into future (see scheduling for more scenarios)
when I'm on a job invocation form
then I can specify the time to start the execution at (now by default)
and I can specify if the targeting should be calculated now or postponed to the execution time
Run a job from host detail
given I'm on a host details page
when I click “Run job”
then a user dialog opens with job invocation form, with pre-filled targeting pointing to this particular host
Run a job from host index
given I'm on a host index page
when I click “Run job”
then a user dialog opens with job invocation form, with prefiled targeting using the same search that was used in the host index page
Invoke a job with single remote execution provider
given I have only one provider available in my installation
and I'm on job invocation form
when I choose the job to execute
then only the template for this provider is available to run and asking for user inputs
Invoke a job with hammer
given I'm using CLI
then I can run a job with ability to specify:
targeting with scoped search or bookmark_id
job name to run
templates to use for the job
inputs on per-template basis
execution properties as overrides for the defaults coming from the template
start_at
value for execution in future
in case of the start_at value, if the targeting should be static vs. dynamic
whether to wait for the job or exit after invocation (–async option)
Re-invoke a job
given I'm in job details page
when I choose re-run
then a user dialog opens with job invocation form, with prefiled targeting parameters from the previous execution 1 and I can override all the values (including targeting, job, templates and inputs)
Re-invoke a job for failed hosts
given I'm in job details page
when I choose re-run
then a user dialog opens with job invocation form, with prefiled targeting parameters from the previous execution 1 and I can override all the values (including targeting, job, templates and inputs)
I can choose in the targeting to only run on hosts that failed with the job previously
Edit a bookmark referenced by pending job invocation
given I have a pending execution task which targeting was created from a bookmark
when I edit the bookmark
then I should be notified about the existence of the pending tasks with ability to update the targeting (or cancel and recreate the invocation)
Email notification: opt in
given I haven't configured to send email notifications about my executions
then the job invocation should have the 'send email notification' turned off by default
Email notification: opt out
given I haven't configured to send email notifications about my executions
then the job invocation should have the 'send email notification' turned off by default
Class diagram of Foreman classes
{% plantuml %}
class Bookmark { name:string query:string controller:string public:bool owner_id:integer owner_type:string }
class Targeting { query: string dynamic: bool }
class Host class User
class TemplateInvocation { inputs }
class JobInvocation { tries retry_interval splay concurrency effective_user email_notification: bool }
class JobTask { start_at: datetime }
Bookmark “1” - “N” Targeting Targeting “M” - “N” Host : (polymorphic) Targeting “N” – “1” User JobInvocation “1” – “1” Targeting JobInvocation “1” – “N” TemplateInvocation TemplateInvocation “N” – “1” JobTemplate JobInvocation “1” – “N” JobTask
{% endplantuml %}
Query is copied to Targeting, we don't want to propagate any later changes to Bookmark to already planned job executions.
We can store link to original bookmark to be able to compare changes later.
For JobInvocation we forbid later editing of Targeting.
should we unify the common inputs in all templates to specify them only once or scoping the input by template?
Maybe an inputs catalog (with both defined name and semantic) might help with keeping the inputs consistent across templates/providers
As a user I want to be able to cancel job which hasn't been started yet.
As a user I want to be able to cancel job which is in progress (if supported by specific provider…)
As a user I want job execution to fail after timeout limit.
As a user I want to job execution to be re-tried based on the tries and retry interval values given in the invocation
As a user I want to job execution on multiple hosts to be spread using the splay time value: the execution of the jobs will be spread randomly across the time interval
As a user I want to job execution on multiple hosts to be limited by a concurrency level: the number of concurrently running jobs will not exceed the limit.
As a user I want the job execution to be performed as a user that was specified on the job invocation
As a user I want an ability to retry the job execution when the host checks in (support of hosts that are offline by the time the execution).
Cancel pending bulk task: all at once
given I've set a job to run in future on multiple hosts
when I click 'cancel' on the corresponding bulk task
then the whole task should be canceled (including all the sub-tasks on all the hosts)
Cancel pending bulk task: task on specific host
given I've set a job to run in future on multiple hosts
when I show the task representation on a host details page
when I click 'cancel' on the task
then I should be offered whether I should cancel just this instance or the whole bulk task on all hosts
Fail after timeout
given I've invoked a job
when the job fails to start in given specified timeout
then the job should be marked as failed due to timeout
Retried task
given I've invoked a job
when the job fails to start at first attemt
then the executor should wait for retry_timeout period
and it should reiterate with the attempt based on the tries number
and I should see the information about the number of retries
Class diagram for jobs running on multiple hosts
{% plantuml %}
class Host { get_provider(type) }
class BulkJobTask { state: $TaskState start_at: datetime started_at: datetime ended_at datetime cancel() }
class JobTask { retry: integer retry_interval: integer timeout: integer splay: integer concurrency: integer type: string state: $TaskState start_at: datetime started_at: datetime tried_count: integer ended_at datetime {abstract} support_cancel?() {abstract} proxy_endpoint() cancel() }
abstract class ProxyCommand { }
class SSHProxyCommand { {static} support_cancel?() proxy_endpoint():string }
class MCollectiveProxyCommand { {static} support_cancel?() proxy_endpoint():string }
BulkJobTask “N” - “1” JobInvocation BulkJobTask “1” – “N” JobTask TemplateInvocation “N” - “1” JobInvocation TemplateInvocation “1” — “N” JobTask JobTask “1” – “1” ProxyCommand JobTask “N” – “1” Host
ProxyCommand <|– SSHProxyCommand ProxyCommand <|– MCollectiveProxyCommand
{% endplantuml %}
Class diagram for jobs running a single host
{% plantuml %}
class Host { get_provider(type) }
class JobTask { retry: integer retry_interval: integer timeout: integer splay: integer concurrency: integer type: string state: $TaskState started_at: datetime start_at: datetime tried_count: integer ended_at datetime cancel() {abstract} support_cancel?() {abstract} proxy_endpoint() plan() cancel() }
class ProxyCommand { }
JobTask “N” - “1” JobInvocation TemplateInvocation “N” - “1” JobInvocation TemplateInvocation “1” - “N” JobTask JobTask “1” – “1” ProxyCommand JobTask “1” – “1” Host {% endplantuml %}
As a user I would like to monitor the current state of the job running against a single host, including the output and exit status
As a user I would like to monitor the status of bulk job, including the number of successful, failed and pending tasks
As a user I would like to see the history of all job run on a host
As a user I would like to see the history of all tasks that I've invoked
As a user I would like to be able to get an email notification with execution report
Track the job running on a set of hosts
given I've set a job to run in future on multiple hosts
then I can watch the progress of the job (number of successful/failed/pending tasks)
and I can get to the list of jobs per host
and I'm able to filter on the host that it was run against and state
Track the job running on a single host
given I've set a job to run on a specific host
when I show the task representation page
then I can watch the progress of the job (updated log), status
History of jobs run on a host
given I'm on host jobs page
when I can see all the jobs run against the host
and I'm able to filter on the host that it was run against and state, owner etc.
History of invoked jobs
given I'm on job invocation history page
when I can see all the jobs invoked in the system
scoped by a taxonomy (based on the hosts the jobs were run against)
and I'm able to filter on the host that it was run against and state, owner etc.
Email notification: send after finish
given I've invoked a job with email notification turned on
when the job finishes
then I should get the email with report from the job after it finishes
Class diagram for jobs running on multiple hosts
{% plantuml %}
class Host { }
class JobInvocation { email_notification: bool }
class BulkJobTask { state: $TaskState start_at: datetime started_at: datetime ended_at datetime }
class JobTask { type: string state: $TaskState start_at: datetime started_at: datetime ended_at datetime tried_count: integer command: string output: string exit_code: string }
BulkJobTask “N” - “1” JobInvocation BulkJobTask “1” – “N” JobTask JobTask “1” – “1” Host
{% endplantuml %}
As a user I want to be able go execute a job at future time
As a user I want to set the job to reoccur with specified frequency
Job set for the future
given I've invoked a job at future time
when the time comes
the job gets executed
Creating reoccurring job
given I'm in job invocation form
when I check 'reoccurring job'
then I can set the frequency and valid until date
Showing the tasks with reoccurring logic
when I list the jobs
I can see the information about the reoccurring logic at every job
and I can filter the jobs for those with the reoccurring logic
Canceling the reoccurring job
given I have reoccurring job configured
when I cancel the next instance of the job
then I'm offered to cancel the reoccurring of the job in the future
{% plantuml %}
class Schedule { start_at: datetime end_at: datetime cronline: string }
class JobTask { }
JobTask “N” – “1” JobInvocation JobInvocation “1” – “1” Schedule
{% endplantuml %}
As a Foreman developer, I want to be able to use remote execution plugin to help with other Foreman features such as:
puppet run
grubby reprovision
content actions (package install/update/remove/downgrade, group install/uninstall, package profile refresh)
subscription actions (refresh)
OpenSCAP content update
Defining a predefined job without provided inputs
given I'm a Foreman developer
and I want to expose 'puppet run' feature to the user
then define the 'Puppet Run' as predefined job in the code
and specify the default job name to be used for the mapping
Defining a predefined job with provided inputs
given I'm a Katello developer
and I want to expose 'package install' feature to the user
then I define the 'Package Install' predefined job with list of packages as provided input in the code
and I specify default job name to be used for the mapping
and I specify default mapping of the provided inputs to template inputs
Preseeding the predefined jobs
given I've defined the 'Package Install' predefined job
when the seed script is run as part of the Foreman installation
the systems tries to create the default mapping from the predefined job to the existing templates based on the developer-provided defaults
Configuring the predefined jobs mapping
given I'm the administrator of the Foreman instance
then I can see all the predefined jobs mapping
when I edit existing mapping
then I can choose job name, template and provided input -> template inputs mapping
Configuring the predefined jobs mapping with organizations
given I'm the administrator of the Foreman instance
then I can scope the mapping of the predefined job to a specific organization
and the system doesn't let me to create two mappings for the same predefined job and provider visible in one organization
Using the predefined jobs without provided inputs
given I'm a Foreman user
when I'm on host details page
and I press 'Puppet Run'
the job is invoked on the host based on the predefined mapping
Using the predefined jobs with provided inputs
given I'm a Katello user
when I'm on host applicable errata list
and I select a set of errata to install on the host
and I click 'Install errata'
the job will be invoked to install the packages belonging to this errata
Using the predefined jobs with customization
given I'm a Katello user
when I'm on host applicable errata list
and I select a set of errata to install on the host
and I click 'Install errata (customize)'
then the job invocation form will be opened with pre-filled values based on the mapping
and I can update the values, including setting the start_at time or reoccurring logic
{% plantuml %}
class PredefinedJob { predefined_job_name: string == has_and_belongs_to_many :taxonomies }
class PredefinedJobInputMapping { provided_input_name: string }
PredefinedJob “1” – “N” PredefinedJobInputMapping PredefinedJobInputMapping “N” – “1” ConfigTemplateInput PredefinedJob “M” – “N” JobTemplate note on link red: 1:1 per organization and provider
{% endplantuml %}
As a user I want to be able to plan job invocation for any host that I can view (view_host permission).
As a user I want to be able to plan a job invocation of job that I can view (view_job permission)
As a user I want to restrict other users which combination of host and job name they can execute (execute permission on job_task resource).
As a user I want to be warned if I planned job invocation on hosts on which the execution of this job is not allowed to me.
As a user I want to see refused job invocations (based on permissions) as failed when they are executed.
As a user I want to set limit filter with execute permission by host attributes such as hostgroup, environment, fqdn, id, lifecycle environment (if applicable), content view (if applicable).
As a user I want to specify effective_user for JobInvocation if at least one provider supports it.
As a user I want to restrict other users to execute job under specific user as a part of filter condition. If the provider does not allow this, execution should be refused.
As a job template provider I want to be able to specify default effective user
Allow user A to invoke package installation on host B
given user A can view all hosts and job templates
when he invoke package installation job on host B
then his job task fails because he does not have execution permission for such job task
Allow user A to run package installation on host B
given I've permissions to assign other user permissions
and user A can view all hosts and job templates
and user A can create job invocations
when I grant user A execution permission on resource JobTask
and I set related filter condition to “host_name = B and job_name = package_install”
and user A invokes package install execution on hosts B and C
then the job gets executed successfully on host B
and job execution will fail on host C
User can set effective user
given the provider of job template supports changing effective user
when user invokes a job
then he can set effective user under which job is executed on target host
User can disallow running job as different effective user
given I've permissions to assign other user permissions
and user A can view all hosts and job templates
and user A can create job invocations
when I grant user A execution permission on resource JobTask
and I set related filter condition to “effective_user = user_a”
and user A invokes job execution with effective user set to different user (e.g. root)
then the job execution fails
JobInvocation
Create
View
Cancel
Edit (Schedule, never can change targetting)
JobTask
Execute
(filter can be: effective_user = 'joe' and host_id = 1 or host_id = 2 and script_name = 'foobar')
{% plantuml %}
class JobTemplate { effective_user: string }
class JobInvocation { effective_user: string }
{% endplantuml %}
katello-agent provides three main functions aside from remote management:
package profile yum plugin - pushes a new package profile after any yum transaction
Split out into its own package (yum-plugin-katello-profile)
enabled repository monitoring
monitors /etc/yum.repos.d/redhat.repo file for changes and sends newly enabled repos whenever it does change
Split out into its own package (katello-errata-profile) with a service to do the same
On the capsule, goferd runs to recieve commands to sync repositories, possible solutions:
katello-agent can remain (but possibly renamed), with a lot of the existing functionality removed
pulp changes to a rest api method for initiating capsule syncs, katello needs to store some auth credentials per capsule
As a user I want to group a number of jobs together and treat them as an executable unit. (i.e. run this script to stop the app, install these errata, reboot the system)
As a user I want to run a set of jobs in a rolling fashion. (i.e.,patch server 1, reboot it, if it succeeds, proceed to server 2 & repeat. Otherwise raise exception)
As a user I want to define a rollback job in case the execution fails
As a sysadmin I would like to orchestrate several actions across a collection of machines. (e.g. install a DB on this machine, and pass the ip address into an install of a web server on another machine)
TBD after the simple support is implemented, possible cooperation with multi-host deployments feature
Some of the features might be solved by advanced remote execution technology integration (such as ansible playbook)
{% plantuml %} class Host { get_provider(type) }
package “Job Preparation” { class JobTemplate { name: string job_name: string retry_count: integer retry_interval: integer splay: integer provider_type: string effective_user: string == has_and_belongs_to_many :taxonomies has_many :inputs has_many :audits }
class ConfigTemplateInput { name: string required: bool input_type: USER_INPUT | FACT | SMART_VARIABLE fact_name: string smart_variable_name: string description: string == has_one :job_template }
JobTemplate “1” – “N” ConfigTemplateInput }
package “Job Invocation” { class Bookmark { name:string query:string controller:string public:bool owner_id:integer owner_type:string }
class Targeting { query: string dynamic: bool }
class TemplateInvocation { inputs }
class JobInvocation { tries retry_interval splay concurrency email_notification: bool effective_user: string }
class User
Bookmark “1” -DOWN- “N” Targeting Targeting “M” -DOWN- “N” Host Targeting “N” -UP- “1” User JobInvocation “1” -LEFT- “1” Targeting JobInvocation “1” -DOWN- “N” TemplateInvocation TemplateInvocation “N” -LEFT- “1” JobTemplate
}
package “Scheduling” { class Schedule { start_at: datetime end_at: datetime cronline: string }
JobInvocation “1” -UP- “0..1” Schedule }
package “Execution” { class BulkJobTask { state: $TaskState start_at: datetime started_at: datetime ended_at datetime cancel() }
class JobTask { state: $TaskState start_at: datetime started_at: datetime tried_count: integer ended_at datetime retry: integer retry_interval: integer timeout: integer splay: integer concurrency: integer provider: string
command: string output: string exit_code: string {abstract} support_cancel?() {abstract} proxy_endpoint() cancel()
}
abstract class ProxyCommand { }
class SSHProxyCommand { {static} support_cancel?() proxy_endpoint():string }
class MCollectiveProxyCommand { {static} support_cancel?() proxy_endpoint():string }
BulkJobTask “N” -LEFT- “1” JobInvocation BulkJobTask “1” – “N” JobTask TemplateInvocation “1” – “N” JobTask JobTask “1” -RIGHT- “1” ProxyCommand JobTask “N” -UP- “1” Host }
ProxyCommand <|– SSHProxyCommand ProxyCommand <|– MCollectiveProxyCommand
package “Developer API” { class PredefinedJob { predefined_job_name: string == has_and_belongs_to_many :taxonomies }
class PredefinedJobInputMapping { provided_input_name: string }
PredefinedJob “1” – “N” PredefinedJobInputMapping PredefinedJobInputMapping “N” -RIGHT- “1” ConfigTemplateInput PredefinedJob “M” -RIGHT- “N” JobTemplate }
{% endplantuml %}