You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@ambari.apache.org by ol...@apache.org on 2017/05/31 18:07:33 UTC

ambari git commit: AMBARI-21155. Design Ambari Infra Component & workflows (oleewere)

Repository: ambari
Updated Branches:
  refs/heads/trunk 753f8aacc -> 23e23afd8


AMBARI-21155. Design Ambari Infra Component & workflows (oleewere)


Project: http://git-wip-us.apache.org/repos/asf/ambari/repo
Commit: http://git-wip-us.apache.org/repos/asf/ambari/commit/23e23afd
Tree: http://git-wip-us.apache.org/repos/asf/ambari/tree/23e23afd
Diff: http://git-wip-us.apache.org/repos/asf/ambari/diff/23e23afd

Branch: refs/heads/trunk
Commit: 23e23afd884449812d05c490804dc4845b01ac21
Parents: 753f8aa
Author: oleewere <ol...@gmail.com>
Authored: Wed May 31 12:21:34 2017 +0200
Committer: oleewere <ol...@gmail.com>
Committed: Wed May 31 20:03:27 2017 +0200

----------------------------------------------------------------------
 ambari-infra/ambari-infra-manager/README.md     |  92 ++-
 .../ambari-infra-manager/docs/api/swagger.yaml  | 784 +++++++++++++++++++
 .../docs/images/batch-1.png                     | Bin 0 -> 20521 bytes
 .../docs/images/batch-2.png                     | Bin 0 -> 29388 bytes
 .../docs/images/batch-3.png                     | Bin 0 -> 14105 bytes
 .../docs/images/batch-4.png                     | Bin 0 -> 23277 bytes
 .../infra/common/InfraManagerConstants.java     |   2 +-
 .../infra/conf/InfraManagerApiDocConfig.java    |  35 +-
 .../conf/batch/InfraManagerBatchConfig.java     |   8 +-
 .../ambari/infra/job/dummy/DummyItemWriter.java |  13 +
 .../infra/job/dummy/DummyJobListener.java       |  39 +
 .../infra/job/dummy/DummyStepListener.java      |  41 +
 .../apache/ambari/infra/rest/JobResource.java   |   2 +-
 13 files changed, 1002 insertions(+), 14 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/ambari/blob/23e23afd/ambari-infra/ambari-infra-manager/README.md
----------------------------------------------------------------------
diff --git a/ambari-infra/ambari-infra-manager/README.md b/ambari-infra/ambari-infra-manager/README.md
index d3527c4..dd2854c 100644
--- a/ambari-infra/ambari-infra-manager/README.md
+++ b/ambari-infra/ambari-infra-manager/README.md
@@ -18,13 +18,99 @@ limitations under the License.
 -->
 
 # Ambari Infra Manager
-TODO
-## Build & Run Application
+
+## Overview
+
+Ambari Infra Manager is a REST based management application for Ambari Infra services (like Infra Solr). The API is built on top of [Spring Batch] (http://docs.spring.io/spring-batch/reference/html/)
+
+### Architecture
+![batch-1](docs/images/batch-1.png)
+
+### Job execution overview
+![batch-2](docs/images/batch-2.png)
+
+### Job workflow
+![batch-3](docs/images/batch-3.png)
+
+### Step workflow
+![batch-4](docs/images/batch-4.png)
+
+(images originally from [here] (http://docs.spring.io/spring-batch/reference/html/))
+
+## API documentation
+
+Infra Manager uses [Swagger] (http://swagger.io/), generated yaml file can be downloaded from [here] (docs/api/swagger.yaml)
+
+
+## Development guide
+
+### Adding a new custom job
+
+As Infra Manager is a Spring based application and using Java configurations, if it is needed to add a new custom Job, the Jobs/Steps/Configurations are need to be on the classpath. Spring beans are registered only in a specific package, so for writing a plugin, all the added Java classes needs to be added inside "org.apache.ambari.infra" package.
+
+For the plugin it will be needed to add all Spring & Spring batch dependencies. For adding a new Job you will need to define a new Configuration object. There you can define your own jobs/steps/writers/readers/processors, as you can see in that example:
+```java
+@Configuration
+@EnableBatchProcessing
+public class MyJobConfig {
+
+  @Inject
+  private StepBuilderFactory steps;
+
+  @Inject
+  private JobBuilderFactory jobs;
+  
+  
+  @Bean(name = "dummyStep")
+  protected Step dummyStep(ItemReader<DummyObject> reader,
+                         ItemProcessor<DummyObject, String> processor,
+                         ItemWriter<String> writer) {
+    return steps.get("dummyStep").listener(new DummyStepListener()).<DummyObject, String> chunk(2)
+      .reader(reader).processor(processor).writer(writer).build();
+  }
+  
+  @Bean(name = "dummyJob")
+  public Job job(@Qualifier("dummyStep") Step dummyStep) {
+    return jobs.get("dummyJob").listener(new DummyJobListener()).start(dummyStep).build();
+  }
+
+}
+```
+As you can see it will require to implement [ItemWriter] (https://docs.spring.io/spring-batch/apidocs/org/springframework/batch/item/ItemWriter.html), [ItemReader] (http://docs.spring.io/spring-batch/trunk/apidocs/org/springframework/batch/item/ItemReader.html) and [ItemProcessor] (https://docs.spring.io/spring-batch/apidocs/org/springframework/batch/item/ItemProcessor.html)
+
+### Schedule custom jobs
+
+It can be needed based on business requirements to schedule jobs (e.g. daily) instead of run manually through the REST API. It can be done with adding a custom bean to "org.apache.ambari.infra" package with using [@Scheduled] (http://docs.spring.io/spring-framework/docs/current/javadoc-api/org/springframework/scheduling/annotation/Scheduled.html):
+```java
+@Named
+public class MySchedulerObject {
+
+   @Inject
+   private JobService jobService; // or JobOperator jobOperator if spring-batch-admin manager dependecy is not included
+   
+   @Value("${infra-manager.batch.my.param:defaultString}")
+   private String myParamFromLogSearchProperties;
+   
+   @Scheduled(cron = "*/5 * * * * MON-FRI")
+   public void doSomething() {
+      // setup job params
+      jobService.launch(jobName, jobParameters, TimeZone.getDefault());
+   }
+   
+   @Scheduled(cron = "${infra.manager.my.prop}")
+   public void doSomethingBasedOnInfraProperty() {
+      // do something ...
+   }
+}
+```
+
+You can put your cron expression inside infra-manager.properties file just make it configuratble.
+### Build & Run Application
 ```bash
 mvn clean package exec:java
 ```
 
-## Build & Run Application in docker container
+### Build & Run Application in docker container
 ```bash
 cd docker
 ./infra-manager-docker.sh

http://git-wip-us.apache.org/repos/asf/ambari/blob/23e23afd/ambari-infra/ambari-infra-manager/docs/api/swagger.yaml
----------------------------------------------------------------------
diff --git a/ambari-infra/ambari-infra-manager/docs/api/swagger.yaml b/ambari-infra/ambari-infra-manager/docs/api/swagger.yaml
new file mode 100644
index 0000000..824629f
--- /dev/null
+++ b/ambari-infra/ambari-infra-manager/docs/api/swagger.yaml
@@ -0,0 +1,784 @@
+---
+swagger: "2.0"
+info:
+  description: "Manager component for Ambari Infra"
+  version: "1.0.0"
+  title: "Infra Manager REST API"
+  license:
+    name: "Apache 2.0"
+    url: "http://www.apache.org/licenses/LICENSE-2.0.html"
+basePath: "/api/v1"
+tags:
+- name: "jobs"
+schemes:
+- "http"
+- "https"
+paths:
+  /jobs:
+    get:
+      tags:
+      - "jobs"
+      summary: "Get all jobs"
+      description: ""
+      operationId: "getAllJobs"
+      produces:
+      - "application/json"
+      parameters:
+      - name: "page"
+        in: "query"
+        required: false
+        type: "integer"
+        default: 0
+        format: "int32"
+      - name: "size"
+        in: "query"
+        required: false
+        type: "integer"
+        default: 20
+        format: "int32"
+      responses:
+        200:
+          description: "successful operation"
+          schema:
+            type: "array"
+            items:
+              $ref: "#/definitions/JobInfo"
+  /jobs/executions:
+    delete:
+      tags:
+      - "jobs"
+      summary: "Stop all job executions."
+      description: ""
+      operationId: "stopAll"
+      produces:
+      - "application/json"
+      parameters: []
+      responses:
+        200:
+          description: "successful operation"
+          schema:
+            type: "integer"
+            format: "int32"
+  /jobs/executions/{jobExecutionId}:
+    get:
+      tags:
+      - "jobs"
+      summary: "Get job and step details for job execution instance."
+      description: ""
+      operationId: "getExectionInfo"
+      produces:
+      - "application/json"
+      parameters:
+      - name: "jobExecutionId"
+        in: "path"
+        required: true
+        type: "integer"
+        format: "int64"
+      responses:
+        200:
+          description: "successful operation"
+          schema:
+            $ref: "#/definitions/JobExecutionDetailsResponse"
+    delete:
+      tags:
+      - "jobs"
+      summary: "Stop or abandon a running job execution."
+      description: ""
+      operationId: "stopOrAbandonJobExecution"
+      produces:
+      - "application/json"
+      parameters:
+      - name: "jobExecutionId"
+        in: "path"
+        required: true
+        type: "integer"
+        format: "int64"
+      - name: "operation"
+        in: "query"
+        required: true
+        type: "string"
+        enum:
+        - "STOP"
+        - "ABANDON"
+      responses:
+        200:
+          description: "successful operation"
+          schema:
+            $ref: "#/definitions/JobExecutionInfoResponse"
+  /jobs/executions/{jobExecutionId}/context:
+    get:
+      tags:
+      - "jobs"
+      summary: "Get execution context for specific job."
+      description: ""
+      operationId: "getExecutionContextByJobExecId"
+      produces:
+      - "application/json"
+      parameters:
+      - name: "jobExecutionId"
+        in: "path"
+        required: true
+        type: "integer"
+        format: "int64"
+      responses:
+        200:
+          description: "successful operation"
+          schema:
+            $ref: "#/definitions/ExecutionContextResponse"
+  /jobs/executions/{jobExecutionId}/steps/{stepExecutionId}:
+    get:
+      tags:
+      - "jobs"
+      summary: "Get step execution details."
+      description: ""
+      operationId: "getStepExecution"
+      produces:
+      - "application/json"
+      parameters:
+      - name: "jobExecutionId"
+        in: "path"
+        required: true
+        type: "integer"
+        format: "int64"
+      - name: "stepExecutionId"
+        in: "path"
+        required: true
+        type: "integer"
+        format: "int64"
+      responses:
+        200:
+          description: "successful operation"
+          schema:
+            $ref: "#/definitions/StepExecutionInfoResponse"
+  /jobs/executions/{jobExecutionId}/steps/{stepExecutionId}/execution-context:
+    get:
+      tags:
+      - "jobs"
+      summary: "Get the execution context of step execution."
+      description: ""
+      operationId: "getStepExecutionContext"
+      produces:
+      - "application/json"
+      parameters:
+      - name: "jobExecutionId"
+        in: "path"
+        required: true
+        type: "integer"
+        format: "int64"
+      - name: "stepExecutionId"
+        in: "path"
+        required: true
+        type: "integer"
+        format: "int64"
+      responses:
+        200:
+          description: "successful operation"
+          schema:
+            $ref: "#/definitions/StepExecutionContextResponse"
+  /jobs/executions/{jobExecutionId}/steps/{stepExecutionId}/progress:
+    get:
+      tags:
+      - "jobs"
+      summary: "Get progress of step execution."
+      description: ""
+      operationId: "getStepExecutionProgress"
+      produces:
+      - "application/json"
+      parameters:
+      - name: "jobExecutionId"
+        in: "path"
+        required: true
+        type: "integer"
+        format: "int64"
+      - name: "stepExecutionId"
+        in: "path"
+        required: true
+        type: "integer"
+        format: "int64"
+      responses:
+        200:
+          description: "successful operation"
+          schema:
+            $ref: "#/definitions/StepExecutionProgressResponse"
+  /jobs/info/names:
+    get:
+      tags:
+      - "jobs"
+      summary: "Get all job names"
+      description: ""
+      operationId: "getAllJobNames"
+      produces:
+      - "application/json"
+      parameters: []
+      responses:
+        200:
+          description: "successful operation"
+          schema:
+            type: "array"
+            uniqueItems: true
+            items:
+              type: "string"
+  /jobs/{jobName}:
+    post:
+      tags:
+      - "jobs"
+      summary: "Start a new job instance by job name."
+      description: ""
+      operationId: "startJob"
+      produces:
+      - "application/json"
+      parameters:
+      - name: "jobName"
+        in: "path"
+        required: true
+        type: "string"
+      - name: "params"
+        in: "query"
+        required: false
+        type: "string"
+      responses:
+        200:
+          description: "successful operation"
+          schema:
+            $ref: "#/definitions/JobExecutionInfoResponse"
+  /jobs/{jobName}/executions:
+    get:
+      tags:
+      - "jobs"
+      summary: "Get the id values of all the running job instances."
+      description: ""
+      operationId: "getExecutionIdsByJobName"
+      produces:
+      - "application/json"
+      parameters:
+      - name: "jobName"
+        in: "path"
+        required: true
+        type: "string"
+      responses:
+        200:
+          description: "successful operation"
+          schema:
+            type: "array"
+            uniqueItems: true
+            items:
+              type: "integer"
+              format: "int64"
+  /jobs/{jobName}/info:
+    get:
+      tags:
+      - "jobs"
+      summary: "Get job details by job name."
+      description: ""
+      operationId: "getJobDetails"
+      produces:
+      - "application/json"
+      parameters:
+      - name: "page"
+        in: "query"
+        required: false
+        type: "integer"
+        default: 0
+        format: "int32"
+      - name: "size"
+        in: "query"
+        required: false
+        type: "integer"
+        default: 20
+        format: "int32"
+      - name: "jobName"
+        in: "path"
+        required: true
+        type: "string"
+      responses:
+        200:
+          description: "successful operation"
+          schema:
+            $ref: "#/definitions/JobDetailsResponse"
+  /jobs/{jobName}/{jobInstanceId}/executions:
+    get:
+      tags:
+      - "jobs"
+      summary: "Get execution for job instance."
+      description: ""
+      operationId: "getExecutionsForInstance"
+      produces:
+      - "application/json"
+      parameters:
+      - name: "jobName"
+        in: "path"
+        required: true
+        type: "string"
+      - name: "jobInstanceId"
+        in: "path"
+        required: true
+        type: "integer"
+        format: "int64"
+      responses:
+        200:
+          description: "successful operation"
+          schema:
+            type: "array"
+            items:
+              $ref: "#/definitions/JobExecutionInfoResponse"
+    post:
+      tags:
+      - "jobs"
+      summary: "Restart job instance."
+      description: ""
+      operationId: "restartJobInstance"
+      produces:
+      - "application/json"
+      parameters:
+      - in: "body"
+        name: "body"
+        required: false
+        schema:
+          $ref: "#/definitions/JobExecutionRestartRequest"
+      responses:
+        200:
+          description: "successful operation"
+          schema:
+            $ref: "#/definitions/JobExecutionInfoResponse"
+definitions:
+  JobExecutionData:
+    type: "object"
+    properties:
+      id:
+        type: "integer"
+        format: "int64"
+      executionContext:
+        $ref: "#/definitions/ExecutionContext"
+      jobInstance:
+        $ref: "#/definitions/JobInstance"
+      jobId:
+        type: "integer"
+        format: "int64"
+      jobParameters:
+        $ref: "#/definitions/JobParameters"
+      failureExceptions:
+        type: "array"
+        items:
+          $ref: "#/definitions/Throwable"
+      endTime:
+        type: "string"
+        format: "date-time"
+      exitStatus:
+        $ref: "#/definitions/ExitStatus"
+      createTime:
+        type: "string"
+        format: "date-time"
+      lastUpdated:
+        type: "string"
+        format: "date-time"
+      jobConfigurationName:
+        type: "string"
+      startTime:
+        type: "string"
+        format: "date-time"
+      status:
+        type: "string"
+        enum:
+        - "COMPLETED"
+        - "STARTING"
+        - "STARTED"
+        - "STOPPING"
+        - "STOPPED"
+        - "FAILED"
+        - "ABANDONED"
+        - "UNKNOWN"
+      stepExecutionDataList:
+        type: "array"
+        items:
+          $ref: "#/definitions/StepExecutionData"
+  JobInstance:
+    type: "object"
+    properties:
+      id:
+        type: "integer"
+        format: "int64"
+      version:
+        type: "integer"
+        format: "int32"
+      jobName:
+        type: "string"
+      instanceId:
+        type: "integer"
+        format: "int64"
+  StepExecutionData:
+    type: "object"
+    properties:
+      id:
+        type: "integer"
+        format: "int64"
+      jobExecutionId:
+        type: "integer"
+        format: "int64"
+      executionContext:
+        $ref: "#/definitions/ExecutionContext"
+      stepName:
+        type: "string"
+      terminateOnly:
+        type: "boolean"
+        default: false
+      failureExceptions:
+        type: "array"
+        items:
+          $ref: "#/definitions/Throwable"
+      endTime:
+        type: "string"
+        format: "date-time"
+      exitStatus:
+        $ref: "#/definitions/ExitStatus"
+      lastUpdated:
+        type: "string"
+        format: "date-time"
+      commitCount:
+        type: "integer"
+        format: "int32"
+      readCount:
+        type: "integer"
+        format: "int32"
+      filterCount:
+        type: "integer"
+        format: "int32"
+      writeCount:
+        type: "integer"
+        format: "int32"
+      readSkipCount:
+        type: "integer"
+        format: "int32"
+      writeSkipCount:
+        type: "integer"
+        format: "int32"
+      processSkipCount:
+        type: "integer"
+        format: "int32"
+      rollbackCount:
+        type: "integer"
+        format: "int32"
+      startTime:
+        type: "string"
+        format: "date-time"
+      status:
+        type: "string"
+        enum:
+        - "COMPLETED"
+        - "STARTING"
+        - "STARTED"
+        - "STOPPING"
+        - "STOPPED"
+        - "FAILED"
+        - "ABANDONED"
+        - "UNKNOWN"
+  StackTraceElement:
+    type: "object"
+    properties:
+      methodName:
+        type: "string"
+      fileName:
+        type: "string"
+      lineNumber:
+        type: "integer"
+        format: "int32"
+      className:
+        type: "string"
+      nativeMethod:
+        type: "boolean"
+        default: false
+  JobExecutionDetailsResponse:
+    type: "object"
+    properties:
+      jobExecutionInfoResponse:
+        $ref: "#/definitions/JobExecutionInfoResponse"
+      stepExecutionInfoList:
+        type: "array"
+        items:
+          $ref: "#/definitions/StepExecutionInfoResponse"
+  StepExecutionContextResponse:
+    type: "object"
+    properties:
+      executionContextMap:
+        type: "object"
+        additionalProperties:
+          type: "object"
+      jobExecutionId:
+        type: "integer"
+        format: "int64"
+      stepExecutionId:
+        type: "integer"
+        format: "int64"
+      stepName:
+        type: "string"
+  StepExecutionProgress:
+    type: "object"
+    properties:
+      estimatedPercentCompleteMessage:
+        $ref: "#/definitions/MessageSourceResolvable"
+      estimatedPercentComplete:
+        type: "number"
+        format: "double"
+  ExitStatus:
+    type: "object"
+    properties:
+      exitCode:
+        type: "string"
+      exitDescription:
+        type: "string"
+      running:
+        type: "boolean"
+        default: false
+  ExecutionContextResponse:
+    type: "object"
+    properties:
+      jobExecutionId:
+        type: "integer"
+        format: "int64"
+      executionContextMap:
+        type: "object"
+        additionalProperties:
+          type: "object"
+  StepExecutionHistory:
+    type: "object"
+    properties:
+      stepName:
+        type: "string"
+      count:
+        type: "integer"
+        format: "int32"
+      commitCount:
+        $ref: "#/definitions/CumulativeHistory"
+      rollbackCount:
+        $ref: "#/definitions/CumulativeHistory"
+      readCount:
+        $ref: "#/definitions/CumulativeHistory"
+      writeCount:
+        $ref: "#/definitions/CumulativeHistory"
+      filterCount:
+        $ref: "#/definitions/CumulativeHistory"
+      readSkipCount:
+        $ref: "#/definitions/CumulativeHistory"
+      writeSkipCount:
+        $ref: "#/definitions/CumulativeHistory"
+      processSkipCount:
+        $ref: "#/definitions/CumulativeHistory"
+      duration:
+        $ref: "#/definitions/CumulativeHistory"
+      durationPerRead:
+        $ref: "#/definitions/CumulativeHistory"
+  TimeZone:
+    type: "object"
+    properties:
+      displayName:
+        type: "string"
+      id:
+        type: "string"
+      dstsavings:
+        type: "integer"
+        format: "int32"
+      rawOffset:
+        type: "integer"
+        format: "int32"
+  MessageSourceResolvable:
+    type: "object"
+    properties:
+      arguments:
+        type: "array"
+        items:
+          type: "object"
+      codes:
+        type: "array"
+        items:
+          type: "string"
+      defaultMessage:
+        type: "string"
+  ExecutionContext:
+    type: "object"
+    properties:
+      dirty:
+        type: "boolean"
+        default: false
+      empty:
+        type: "boolean"
+        default: false
+  StepExecutionInfoResponse:
+    type: "object"
+    properties:
+      id:
+        type: "integer"
+        format: "int64"
+      jobExecutionId:
+        type: "integer"
+        format: "int64"
+      jobName:
+        type: "string"
+      name:
+        type: "string"
+      startDate:
+        type: "string"
+      startTime:
+        type: "string"
+      duration:
+        type: "string"
+      durationMillis:
+        type: "integer"
+        format: "int64"
+      exitCode:
+        type: "string"
+      status:
+        type: "string"
+  JobExecutionInfoResponse:
+    type: "object"
+    properties:
+      id:
+        type: "integer"
+        format: "int64"
+      stepExecutionCount:
+        type: "integer"
+        format: "int32"
+      jobId:
+        type: "integer"
+        format: "int64"
+      jobName:
+        type: "string"
+      startDate:
+        type: "string"
+      startTime:
+        type: "string"
+      duration:
+        type: "string"
+      jobExecutionData:
+        $ref: "#/definitions/JobExecutionData"
+      jobParameters:
+        type: "object"
+        additionalProperties:
+          type: "object"
+      jobParametersString:
+        type: "string"
+      restartable:
+        type: "boolean"
+        default: false
+      abandonable:
+        type: "boolean"
+        default: false
+      stoppable:
+        type: "boolean"
+        default: false
+      timeZone:
+        $ref: "#/definitions/TimeZone"
+  JobInfo:
+    type: "object"
+    properties:
+      name:
+        type: "string"
+      executionCount:
+        type: "integer"
+        format: "int32"
+      launchable:
+        type: "boolean"
+        default: false
+      incrementable:
+        type: "boolean"
+        default: false
+      jobInstanceId:
+        type: "integer"
+        format: "int64"
+  JobExecutionRestartRequest:
+    type: "object"
+    properties:
+      jobName:
+        type: "string"
+      jobInstanceId:
+        type: "integer"
+        format: "int64"
+      operation:
+        type: "string"
+        enum:
+        - "RESTART"
+  Throwable:
+    type: "object"
+    properties:
+      cause:
+        $ref: "#/definitions/Throwable"
+      stackTrace:
+        type: "array"
+        items:
+          $ref: "#/definitions/StackTraceElement"
+      message:
+        type: "string"
+      localizedMessage:
+        type: "string"
+      suppressed:
+        type: "array"
+        items:
+          $ref: "#/definitions/Throwable"
+  JobParameters:
+    type: "object"
+    properties:
+      parameters:
+        type: "object"
+        additionalProperties:
+          $ref: "#/definitions/JobParameter"
+      empty:
+        type: "boolean"
+        default: false
+  CumulativeHistory:
+    type: "object"
+    properties:
+      count:
+        type: "integer"
+        format: "int32"
+      min:
+        type: "number"
+        format: "double"
+      max:
+        type: "number"
+        format: "double"
+      standardDeviation:
+        type: "number"
+        format: "double"
+      mean:
+        type: "number"
+        format: "double"
+  JobInstanceDetailsResponse:
+    type: "object"
+    properties:
+      jobInstance:
+        $ref: "#/definitions/JobInstance"
+      jobExecutionInfoResponseList:
+        type: "array"
+        items:
+          $ref: "#/definitions/JobExecutionInfoResponse"
+  JobParameter:
+    type: "object"
+    properties:
+      identifying:
+        type: "boolean"
+        default: false
+      value:
+        type: "object"
+      type:
+        type: "string"
+        enum:
+        - "STRING"
+        - "DATE"
+        - "LONG"
+        - "DOUBLE"
+  StepExecutionProgressResponse:
+    type: "object"
+    properties:
+      stepExecutionProgress:
+        $ref: "#/definitions/StepExecutionProgress"
+      stepExecutionHistory:
+        $ref: "#/definitions/StepExecutionHistory"
+      stepExecutionInfoResponse:
+        $ref: "#/definitions/StepExecutionInfoResponse"
+  JobDetailsResponse:
+    type: "object"
+    properties:
+      jobInfo:
+        $ref: "#/definitions/JobInfo"
+      jobInstanceDetailsResponseList:
+        type: "array"
+        items:
+          $ref: "#/definitions/JobInstanceDetailsResponse"

http://git-wip-us.apache.org/repos/asf/ambari/blob/23e23afd/ambari-infra/ambari-infra-manager/docs/images/batch-1.png
----------------------------------------------------------------------
diff --git a/ambari-infra/ambari-infra-manager/docs/images/batch-1.png b/ambari-infra/ambari-infra-manager/docs/images/batch-1.png
new file mode 100644
index 0000000..d763852
Binary files /dev/null and b/ambari-infra/ambari-infra-manager/docs/images/batch-1.png differ

http://git-wip-us.apache.org/repos/asf/ambari/blob/23e23afd/ambari-infra/ambari-infra-manager/docs/images/batch-2.png
----------------------------------------------------------------------
diff --git a/ambari-infra/ambari-infra-manager/docs/images/batch-2.png b/ambari-infra/ambari-infra-manager/docs/images/batch-2.png
new file mode 100644
index 0000000..1de3479
Binary files /dev/null and b/ambari-infra/ambari-infra-manager/docs/images/batch-2.png differ

http://git-wip-us.apache.org/repos/asf/ambari/blob/23e23afd/ambari-infra/ambari-infra-manager/docs/images/batch-3.png
----------------------------------------------------------------------
diff --git a/ambari-infra/ambari-infra-manager/docs/images/batch-3.png b/ambari-infra/ambari-infra-manager/docs/images/batch-3.png
new file mode 100644
index 0000000..7f1123c
Binary files /dev/null and b/ambari-infra/ambari-infra-manager/docs/images/batch-3.png differ

http://git-wip-us.apache.org/repos/asf/ambari/blob/23e23afd/ambari-infra/ambari-infra-manager/docs/images/batch-4.png
----------------------------------------------------------------------
diff --git a/ambari-infra/ambari-infra-manager/docs/images/batch-4.png b/ambari-infra/ambari-infra-manager/docs/images/batch-4.png
new file mode 100644
index 0000000..beb610a
Binary files /dev/null and b/ambari-infra/ambari-infra-manager/docs/images/batch-4.png differ

http://git-wip-us.apache.org/repos/asf/ambari/blob/23e23afd/ambari-infra/ambari-infra-manager/src/main/java/org/apache/ambari/infra/common/InfraManagerConstants.java
----------------------------------------------------------------------
diff --git a/ambari-infra/ambari-infra-manager/src/main/java/org/apache/ambari/infra/common/InfraManagerConstants.java b/ambari-infra/ambari-infra-manager/src/main/java/org/apache/ambari/infra/common/InfraManagerConstants.java
index 77f7008..105f20e 100644
--- a/ambari-infra/ambari-infra-manager/src/main/java/org/apache/ambari/infra/common/InfraManagerConstants.java
+++ b/ambari-infra/ambari-infra-manager/src/main/java/org/apache/ambari/infra/common/InfraManagerConstants.java
@@ -25,7 +25,7 @@ public final class InfraManagerConstants {
   public static final String PROTOCOL_SSL = "https";
   public static final String ROOT_CONTEXT = "/";
   public static final String WEB_RESOURCE_FOLDER = "webapp";
-  public static final String DEFAULT_DATA_FOLDER_LOCATION = "/usr/ambari-infra-manager/data";
+  public static final String DEFAULT_DATA_FOLDER_LOCATION = "/opt/ambari-infra-manager/data";
   public static final String DATA_FOLDER_LOCATION_PARAM = "dataFolderLocation";
   public static final Integer SESSION_TIMEOUT = 60 * 30;
 }

http://git-wip-us.apache.org/repos/asf/ambari/blob/23e23afd/ambari-infra/ambari-infra-manager/src/main/java/org/apache/ambari/infra/conf/InfraManagerApiDocConfig.java
----------------------------------------------------------------------
diff --git a/ambari-infra/ambari-infra-manager/src/main/java/org/apache/ambari/infra/conf/InfraManagerApiDocConfig.java b/ambari-infra/ambari-infra-manager/src/main/java/org/apache/ambari/infra/conf/InfraManagerApiDocConfig.java
index 22e2263..4c76742 100644
--- a/ambari-infra/ambari-infra-manager/src/main/java/org/apache/ambari/infra/conf/InfraManagerApiDocConfig.java
+++ b/ambari-infra/ambari-infra-manager/src/main/java/org/apache/ambari/infra/conf/InfraManagerApiDocConfig.java
@@ -21,12 +21,22 @@ package org.apache.ambari.infra.conf;
 import io.swagger.jaxrs.config.BeanConfig;
 import io.swagger.jaxrs.listing.ApiListingResource;
 import io.swagger.jaxrs.listing.SwaggerSerializers;
+import io.swagger.models.Info;
+import io.swagger.models.License;
 import org.springframework.context.annotation.Bean;
 import org.springframework.context.annotation.Configuration;
 
 @Configuration
 public class InfraManagerApiDocConfig {
 
+  private static final String DESCRIPTION = "Manager component for Ambari Infra";
+  private static final String VERSION = "1.0.0";
+  private static final String TITLE = "Infra Manager REST API";
+  private static final String LICENSE = "Apache 2.0";
+  private static final String LICENSE_URL = "http://www.apache.org/licenses/LICENSE-2.0.html";
+  private static final String RESOURCE_PACKAGE = "org.apache.ambari.infra.rest";
+  private static final String BASE_PATH = "/api/v1";
+
   @Bean
   public ApiListingResource apiListingResource() {
     return new ApiListingResource();
@@ -41,14 +51,25 @@ public class InfraManagerApiDocConfig {
   public BeanConfig swaggerConfig() {
     BeanConfig beanConfig = new BeanConfig();
     beanConfig.setSchemes(new String[]{"http", "https"});
-    beanConfig.setBasePath("/api/v1");
-    beanConfig.setTitle("Infra Manager REST API");
-    beanConfig.setDescription("Manager component for Ambari Infra");
-    beanConfig.setLicense("Apache 2.0");
-    beanConfig.setLicenseUrl("http://www.apache.org/licenses/LICENSE-2.0.html");
+    beanConfig.setBasePath(BASE_PATH);
+    beanConfig.setTitle(TITLE);
+    beanConfig.setDescription(DESCRIPTION);
+    beanConfig.setLicense(LICENSE);
+    beanConfig.setLicenseUrl(LICENSE_URL);
     beanConfig.setScan(true);
-    beanConfig.setVersion("1.0.0");
-    beanConfig.setResourcePackage("org.apache.ambari.infra.rest");
+    beanConfig.setVersion(VERSION);
+    beanConfig.setResourcePackage(RESOURCE_PACKAGE);
+
+    License license = new License();
+    license.setName(LICENSE);
+    license.setUrl(LICENSE_URL);
+
+    Info info = new Info();
+    info.setDescription(DESCRIPTION);
+    info.setTitle(TITLE);
+    info.setVersion(VERSION);
+    info.setLicense(license);
+    beanConfig.setInfo(info);
     return beanConfig;
   }
 }

http://git-wip-us.apache.org/repos/asf/ambari/blob/23e23afd/ambari-infra/ambari-infra-manager/src/main/java/org/apache/ambari/infra/conf/batch/InfraManagerBatchConfig.java
----------------------------------------------------------------------
diff --git a/ambari-infra/ambari-infra-manager/src/main/java/org/apache/ambari/infra/conf/batch/InfraManagerBatchConfig.java b/ambari-infra/ambari-infra-manager/src/main/java/org/apache/ambari/infra/conf/batch/InfraManagerBatchConfig.java
index c3d8db6..95f87f5 100644
--- a/ambari-infra/ambari-infra-manager/src/main/java/org/apache/ambari/infra/conf/batch/InfraManagerBatchConfig.java
+++ b/ambari-infra/ambari-infra-manager/src/main/java/org/apache/ambari/infra/conf/batch/InfraManagerBatchConfig.java
@@ -20,7 +20,9 @@ package org.apache.ambari.infra.conf.batch;
 
 import org.apache.ambari.infra.job.dummy.DummyItemProcessor;
 import org.apache.ambari.infra.job.dummy.DummyItemWriter;
+import org.apache.ambari.infra.job.dummy.DummyJobListener;
 import org.apache.ambari.infra.job.dummy.DummyObject;
+import org.apache.ambari.infra.job.dummy.DummyStepListener;
 import org.springframework.batch.admin.service.JdbcSearchableJobExecutionDao;
 import org.springframework.batch.admin.service.JdbcSearchableJobInstanceDao;
 import org.springframework.batch.admin.service.JdbcSearchableStepExecutionDao;
@@ -68,6 +70,7 @@ import org.springframework.jdbc.core.JdbcTemplate;
 import org.springframework.jdbc.datasource.DriverManagerDataSource;
 import org.springframework.jdbc.datasource.init.DataSourceInitializer;
 import org.springframework.jdbc.datasource.init.ResourceDatabasePopulator;
+import org.springframework.scheduling.annotation.EnableAsync;
 import org.springframework.scheduling.annotation.EnableScheduling;
 import org.springframework.transaction.PlatformTransactionManager;
 
@@ -78,6 +81,7 @@ import java.net.MalformedURLException;
 @Configuration
 @EnableBatchProcessing
 @EnableScheduling
+@EnableAsync
 public class InfraManagerBatchConfig {
 
   @Value("classpath:org/springframework/batch/core/schema-drop-sqlite.sql")
@@ -225,13 +229,13 @@ public class InfraManagerBatchConfig {
   protected Step dummyStep(ItemReader<DummyObject> reader,
                        ItemProcessor<DummyObject, String> processor,
                        ItemWriter<String> writer) {
-    return steps.get("dummyStep").<DummyObject, String> chunk(2)
+    return steps.get("dummyStep").listener(new DummyStepListener()).<DummyObject, String> chunk(2)
       .reader(reader).processor(processor).writer(writer).build();
   }
 
   @Bean(name = "dummyJob")
   public Job job(@Qualifier("dummyStep") Step dummyStep) {
-    return jobs.get("dummyJob").start(dummyStep).build();
+    return jobs.get("dummyJob").listener(new DummyJobListener()).start(dummyStep).build();
   }
 
   @Bean

http://git-wip-us.apache.org/repos/asf/ambari/blob/23e23afd/ambari-infra/ambari-infra-manager/src/main/java/org/apache/ambari/infra/job/dummy/DummyItemWriter.java
----------------------------------------------------------------------
diff --git a/ambari-infra/ambari-infra-manager/src/main/java/org/apache/ambari/infra/job/dummy/DummyItemWriter.java b/ambari-infra/ambari-infra-manager/src/main/java/org/apache/ambari/infra/job/dummy/DummyItemWriter.java
index f495795..9a78706 100644
--- a/ambari-infra/ambari-infra-manager/src/main/java/org/apache/ambari/infra/job/dummy/DummyItemWriter.java
+++ b/ambari-infra/ambari-infra-manager/src/main/java/org/apache/ambari/infra/job/dummy/DummyItemWriter.java
@@ -22,8 +22,15 @@ import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 import org.springframework.batch.item.ItemWriter;
 
+import java.io.File;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.util.Date;
 import java.util.List;
 
+import static org.apache.ambari.infra.common.InfraManagerConstants.DATA_FOLDER_LOCATION_PARAM;
+
 public class DummyItemWriter implements ItemWriter<String> {
 
   private static final Logger LOG = LoggerFactory.getLogger(DummyItemWriter.class);
@@ -32,5 +39,11 @@ public class DummyItemWriter implements ItemWriter<String> {
   public void write(List<? extends String> values) throws Exception {
     LOG.info("DummyItem writer called (values: {})... wait 1 seconds", values.toString());
     Thread.sleep(1000);
+    String outputDirectoryLocation = String.format("%s%s%s%s", System.getProperty(DATA_FOLDER_LOCATION_PARAM), File.separator, "dummyOutput-", new Date().getTime());
+    Path pathToDirectory = Paths.get(outputDirectoryLocation);
+    Path pathToFile = Paths.get(String.format("%s%s%s", outputDirectoryLocation, File.separator, "dummyOutput.txt"));
+    Files.createDirectories(pathToDirectory);
+    LOG.info("Write to file: ", pathToFile.getFileName().toAbsolutePath());
+    Files.write(pathToFile, values.toString().getBytes());
   }
 }

http://git-wip-us.apache.org/repos/asf/ambari/blob/23e23afd/ambari-infra/ambari-infra-manager/src/main/java/org/apache/ambari/infra/job/dummy/DummyJobListener.java
----------------------------------------------------------------------
diff --git a/ambari-infra/ambari-infra-manager/src/main/java/org/apache/ambari/infra/job/dummy/DummyJobListener.java b/ambari-infra/ambari-infra-manager/src/main/java/org/apache/ambari/infra/job/dummy/DummyJobListener.java
new file mode 100644
index 0000000..0bbfb55
--- /dev/null
+++ b/ambari-infra/ambari-infra-manager/src/main/java/org/apache/ambari/infra/job/dummy/DummyJobListener.java
@@ -0,0 +1,39 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ * 
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.ambari.infra.job.dummy;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.batch.core.JobExecution;
+import org.springframework.batch.core.JobExecutionListener;
+
+public class DummyJobListener implements JobExecutionListener {
+
+  private static final Logger LOG = LoggerFactory.getLogger(DummyJobListener.class);
+
+  @Override
+  public void beforeJob(JobExecution jobExecution) {
+    LOG.info("Dummy - before job execution");
+  }
+
+  @Override
+  public void afterJob(JobExecution jobExecution) {
+    LOG.info("Dummy - after job execution");
+  }
+}

http://git-wip-us.apache.org/repos/asf/ambari/blob/23e23afd/ambari-infra/ambari-infra-manager/src/main/java/org/apache/ambari/infra/job/dummy/DummyStepListener.java
----------------------------------------------------------------------
diff --git a/ambari-infra/ambari-infra-manager/src/main/java/org/apache/ambari/infra/job/dummy/DummyStepListener.java b/ambari-infra/ambari-infra-manager/src/main/java/org/apache/ambari/infra/job/dummy/DummyStepListener.java
new file mode 100644
index 0000000..548e650
--- /dev/null
+++ b/ambari-infra/ambari-infra-manager/src/main/java/org/apache/ambari/infra/job/dummy/DummyStepListener.java
@@ -0,0 +1,41 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ * 
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.ambari.infra.job.dummy;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.batch.core.ExitStatus;
+import org.springframework.batch.core.StepExecution;
+import org.springframework.batch.core.StepExecutionListener;
+
+public class DummyStepListener implements StepExecutionListener {
+
+  private static final Logger LOG = LoggerFactory.getLogger(DummyStepListener.class);
+
+  @Override
+  public void beforeStep(StepExecution stepExecution) {
+    LOG.info("Dummy step - before step execution");
+  }
+
+  @Override
+  public ExitStatus afterStep(StepExecution stepExecution) {
+    LOG.info("Dummy step - after step execution");
+    return stepExecution.getExitStatus();
+  }
+}

http://git-wip-us.apache.org/repos/asf/ambari/blob/23e23afd/ambari-infra/ambari-infra-manager/src/main/java/org/apache/ambari/infra/rest/JobResource.java
----------------------------------------------------------------------
diff --git a/ambari-infra/ambari-infra-manager/src/main/java/org/apache/ambari/infra/rest/JobResource.java b/ambari-infra/ambari-infra-manager/src/main/java/org/apache/ambari/infra/rest/JobResource.java
index 7023957..0e20b54 100644
--- a/ambari-infra/ambari-infra-manager/src/main/java/org/apache/ambari/infra/rest/JobResource.java
+++ b/ambari-infra/ambari-infra-manager/src/main/java/org/apache/ambari/infra/rest/JobResource.java
@@ -98,7 +98,7 @@ public class JobResource {
 
   @GET
   @Produces({"application/json"})
-  @Path("/info/{jobName}")
+  @Path("{jobName}/info")
   @ApiOperation("Get job details by job name.")
   public JobDetailsResponse getJobDetails(@BeanParam @Valid JobRequest jobRequest) throws NoSuchJobException {
     return jobManager.getJobDetails(jobRequest.getJobName(), jobRequest.getPage(), jobRequest.getSize());