Fix ComponentRemoteAnsibleExecutorTest 65/90965/2
authorEliezio Oliveira <eliezio.oliveira@est.tech>
Thu, 13 Jun 2019 15:04:38 +0000 (16:04 +0100)
committerEliezio Oliveira <eliezio.oliveira@est.tech>
Mon, 8 Jul 2019 10:27:20 +0000 (11:27 +0100)
Change-Id: I0031e354c61ba84fb280f9b50e93c8bdc772627b
Issue-ID: CCSDK-1452
Signed-off-by: Eliezio Oliveira <eliezio.oliveira@est.tech>
ms/blueprintsprocessor/functions/ansible-awx-executor/pom.xml
ms/blueprintsprocessor/functions/ansible-awx-executor/src/main/kotlin/org/onap/ccsdk/cds/blueprintsprocessor/functions/ansible/executor/ComponentRemoteAnsibleExecutor.kt
ms/blueprintsprocessor/functions/ansible-awx-executor/src/test/kotlin/org/onap/ccsdk/cds/blueprintsprocessor/functions/ansible/executor/ComponentRemoteAnsibleExecutorTest.kt
ms/blueprintsprocessor/functions/ansible-awx-executor/src/test/resources/application-test.properties [new file with mode: 0644]
ms/blueprintsprocessor/functions/ansible-awx-executor/src/test/resources/payload/requests/remote-ansible-request-full.json [new file with mode: 0644]

index 8eafc4a..828ff2e 100644 (file)
             <artifactId>httpclient</artifactId>
             <version>${apache.httpcomponents.client.version}</version>
         </dependency>
-        <dependency>
-            <groupId>org.springframework.boot</groupId>
-            <artifactId>spring-boot-starter-web</artifactId>
-            <scope>test</scope>
-        </dependency>
-        <dependency>
-            <groupId>org.springframework.boot</groupId>
-            <artifactId>spring-boot-starter-security</artifactId>
-            <scope>test</scope>
-        </dependency>
     </dependencies>
 
 </project>
index f145d96..63998dd 100644 (file)
@@ -47,17 +47,20 @@ import org.springframework.stereotype.Component
  */
 @Component("component-remote-ansible-executor")
 @Scope(value = ConfigurableBeanFactory.SCOPE_PROTOTYPE)
-open class ComponentRemoteAnsibleExecutor(private val blueprintRestLibPropertyService: BluePrintRestLibPropertyService)
+open class ComponentRemoteAnsibleExecutor(private val blueprintRestLibPropertyService: BluePrintRestLibPropertyService,
+                                          private val mapper: ObjectMapper)
     : AbstractComponentFunction() {
 
-    private val log = LoggerFactory.getLogger(ComponentRemoteAnsibleExecutor::class.java)!!
-
     // HTTP related constants
     private val HTTP_SUCCESS = 200..202
     private val GET = HttpMethod.GET.name
     private val POST = HttpMethod.POST.name
 
+    var checkDelay: Long = 1_000
+
     companion object {
+        private val log = LoggerFactory.getLogger(ComponentRemoteAnsibleExecutor::class.java)
+
         // input fields names accepted by this executor
         const val INPUT_ENDPOINT_SELECTOR = "endpoint-selector"
         const val INPUT_JOB_TEMPLATE_NAME = "job-template-name"
@@ -71,8 +74,6 @@ open class ComponentRemoteAnsibleExecutor(private val blueprintRestLibPropertySe
         const val ATTRIBUTE_EXEC_CMD_STATUS = "ansible-command-status"
         const val ATTRIBUTE_EXEC_CMD_LOG = "ansible-command-logs"
         const val ATTRIBUTE_EXEC_CMD_STATUS_ERROR = "error"
-
-        const val CHECKDELAY: Long = 10000
     }
 
     override suspend fun processNB(executionRequest: ExecutionServiceInput) {
@@ -131,8 +132,6 @@ open class ComponentRemoteAnsibleExecutor(private val blueprintRestLibPropertySe
      * Finds the job template ID based on the job template name provided in the request
      */
     private fun lookupJobTemplateIDByName(awxClient : BlueprintWebClientService, job_template_name: String?): String {
-        val mapper = ObjectMapper()
-
         val encodedJTName = URI(null,null,
                 "/api/v2/job_templates/${job_template_name}/",
                 null,null).rawPath
@@ -150,12 +149,11 @@ open class ComponentRemoteAnsibleExecutor(private val blueprintRestLibPropertySe
      * The status and output attributes are populated in the process.
      */
     private fun runJobTemplateOnAWX(awxClient : BlueprintWebClientService, job_template_name: String?, jtId: String) {
-        val mapper = ObjectMapper()
-
         setNodeOutputProperties( "preparing".asJsonPrimitive(), "".asJsonPrimitive())
 
         // Get Job Template requirements
         var response = awxClient.exchangeResource(GET, "/api/v2/job_templates/${jtId}/launch/","")
+        // FIXME: handle non-successful SC
         val jtLaunchReqs: JsonNode = mapper.readTree(response.body)
         var payload = prepareLaunchPayload(awxClient, jtLaunchReqs)
         log.info("Running job with $payload, for requestId $processId.")
@@ -182,7 +180,7 @@ open class ComponentRemoteAnsibleExecutor(private val blueprintRestLibPropertySe
                 val jobLaunched: JsonNode = mapper.readTree(response.body)
                 jobStatus = jobLaunched.at("/status").asText()
                 jobEndTime = jobLaunched.at("/finished").asText()
-                Thread.sleep(CHECKDELAY)
+                Thread.sleep(checkDelay)
             }
 
             log.info("Execution of job template $job_template_name in job #$jobId finished with status ($jobStatus) for requestId $processId")
@@ -255,8 +253,6 @@ open class ComponentRemoteAnsibleExecutor(private val blueprintRestLibPropertySe
         val encoded = URLEncoder.encode(inventoryProp)
         val response = awxClient.exchangeResource(GET,"/api/v2/inventories/?name=$encoded","")
         if (response.status in HTTP_SUCCESS) {
-            val mapper = ObjectMapper()
-
             // Extract the inventory ID from response
             val invDetails = mapper.readTree(response.body)
             val nbInvFound = invDetails.at("/count").asInt()
index b7f1ed5..b602902 100644 (file)
@@ -21,223 +21,1362 @@ import com.fasterxml.jackson.databind.ObjectMapper
 import io.mockk.every
 import io.mockk.mockk
 import kotlinx.coroutines.runBlocking
-import org.junit.Ignore
+import org.junit.Assert.assertEquals
+import org.junit.Assert.assertTrue
 import org.junit.Test
 import org.junit.runner.RunWith
-import org.onap.ccsdk.cds.blueprintsprocessor.core.BluePrintProperties
-import org.onap.ccsdk.cds.blueprintsprocessor.core.BlueprintPropertyConfiguration
 import org.onap.ccsdk.cds.blueprintsprocessor.core.api.data.ExecutionServiceInput
 import org.onap.ccsdk.cds.blueprintsprocessor.core.api.data.StepData
-import org.onap.ccsdk.cds.blueprintsprocessor.rest.BluePrintRestLibConfiguration
 import org.onap.ccsdk.cds.blueprintsprocessor.rest.service.BluePrintRestLibPropertyService
+import org.onap.ccsdk.cds.blueprintsprocessor.rest.service.BlueprintWebClientService
+import org.onap.ccsdk.cds.blueprintsprocessor.rest.service.BlueprintWebClientService.WebClientResponse
 import org.onap.ccsdk.cds.controllerblueprints.core.BluePrintConstants
-import org.onap.ccsdk.cds.controllerblueprints.core.asJsonPrimitive
 import org.onap.ccsdk.cds.controllerblueprints.core.putJsonElement
-import org.onap.ccsdk.cds.controllerblueprints.core.service.DefaultBluePrintRuntimeService
+import org.onap.ccsdk.cds.controllerblueprints.core.service.BluePrintRuntimeService
 import org.onap.ccsdk.cds.controllerblueprints.core.utils.BluePrintMetadataUtils
 import org.onap.ccsdk.cds.controllerblueprints.core.utils.JacksonUtils
-import org.slf4j.LoggerFactory
-import org.springframework.beans.factory.annotation.Autowired
-import org.springframework.boot.autoconfigure.EnableAutoConfiguration
-import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration
-import org.springframework.boot.test.context.SpringBootTest
-import org.springframework.test.context.ContextConfiguration
 import org.springframework.test.context.TestPropertySource
 import org.springframework.test.context.junit4.SpringRunner
 
 @RunWith(SpringRunner::class)
-@EnableAutoConfiguration(exclude = [DataSourceAutoConfiguration::class])
-@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.DEFINED_PORT)
-@ContextConfiguration(classes = [BluePrintRestLibConfiguration::class,
-    BlueprintPropertyConfiguration::class,
-    BluePrintProperties::class,
-    BluePrintProperties::class])
-@TestPropertySource(properties =
-[
-    "server.port=8443",
-    "server.ssl.enabled=true",
-    "server.ssl.key-store=classpath:keystore.p12",
-    "server.ssl.key-store-password=changeit",
-    "server.ssl.keyStoreType=PKCS12",
-    "server.ssl.keyAlias=tomcat",
-    "blueprintsprocessor.restclient.awx.type=token-auth",
-    "blueprintsprocessor.restclient.awx.url=http://142.44.184.236",
-    "blueprintsprocessor.restclient.awx.token=Bearer J9gEtMDqf7P4YsJ74fioY9VAhLDIs1",
-    "blueprintsprocessor.restclient.future.keyStoreInstance=PKCS12",
-    "blueprintsprocessor.restclient.future.sslTrust=src/test/resources/keystore.p12",
-    "blueprintsprocessor.restclient.future.sslTrustPassword=changeit"
-])
+@TestPropertySource(locations = ["classpath:application-test.properties"])
+@Suppress("SameParameterValue")
 class ComponentRemoteAnsibleExecutorTest {
 
-    @Autowired
-    lateinit var bluePrintRestLibPropertyService: BluePrintRestLibPropertyService
+    private val webClientService = mockk<BlueprintWebClientService>()
 
-    @Transient
-    private val log = LoggerFactory.getLogger(ComponentRemoteAnsibleExecutorTest::class.java)
+    companion object {
+        private const val jtId = 9
+        private const val jobId = 223
+
+        private val mapper = ObjectMapper()
+
+        // IMPORTANT: must match the corresponding properties blueprintsprocessor.restclient.awx.* on
+        // "application-test.properties"
+        private const val endpointSelector = """{
+            "type": "token-auth",
+            "url": "http://142.44.184.236",
+            "token": "Bearer J9gEtMDqf7P4YsJ7444fioY9VAhLDIs1"
+            }"""
+    }
 
     @Test
-    @Ignore
     fun testComponentRemoteAnsibleExecutor() {
-        runBlocking {
 
-            val awxRemoteExecutor = ComponentRemoteAnsibleExecutor(bluePrintRestLibPropertyService)
+        every {
+            webClientService.exchangeResource("GET", "/api/v2/job_templates/hello_world_job_template/", "")
+        } returns WebClientResponse(200, getJobTemplates(jtId))
+        every {
+            webClientService.exchangeResource("GET", "/api/v2/job_templates/$jtId/launch/", "")
+        } returns WebClientResponse(200, getJobTemplateLaunch(jtId))
+        every {
+            webClientService.exchangeResource("GET", "/api/v2/inventories/?name=Demo+Inventory", "")
+        } returns WebClientResponse(200, getInventory())
+        every {
+            webClientService.exchangeResource("POST", "/api/v2/job_templates/$jtId/launch/",
+                    """{"inventory":1,"extra_vars":{"site_id":"3 - Belmont","tor_group":"vEPC"}}""")
+        } returns WebClientResponse(201, newJobTemplateLaunch(jtId, jobId))
+        every {
+            webClientService.exchangeResource("GET", "/api/v2/jobs/$jobId/", "")
+        } returnsMany listOf(
+                WebClientResponse(200, getJobStatus1(jtId, jobId)),
+                WebClientResponse(200, getJobStatus2(jtId, jobId)),
+                WebClientResponse(200, getJobStatus3(jtId, jobId)),
+                WebClientResponse(200, getJobStatus4(jtId, jobId))
+        )
+        every {
+            webClientService.exchangeResource("GET", "/api/v2/jobs/$jobId/stdout/?format=txt", "",
+                    mapOf("Content-Type" to "text/plain ;utf-8"))
+        } returns WebClientResponse(200, getReport())
+        val selector = mapper.readTree(endpointSelector)
+        val bluePrintRestLibPropertyService = mockk<BluePrintRestLibPropertyService>()
+        every { bluePrintRestLibPropertyService.blueprintWebClientService(selector) } returns webClientService
+        val awxRemoteExecutor = ComponentRemoteAnsibleExecutor(bluePrintRestLibPropertyService, mapper)
+        awxRemoteExecutor.checkDelay = 1
 
-            val executionServiceInput = JacksonUtils.readValueFromClassPathFile(
-                    "payload/requests/sample-remote-ansible-request.json",
-                    ExecutionServiceInput::class.java)!!
+        val executionServiceInput = JacksonUtils.readValueFromClassPathFile(
+                "payload/requests/sample-remote-ansible-request.json",
+                ExecutionServiceInput::class.java)!!
 
-            log.info("Request Inputs : " + executionServiceInput.payload)
+        val bluePrintRuntimeService = createBlueprintRuntimeService(awxRemoteExecutor, executionServiceInput)
 
-            val bluePrintRuntimeService = BluePrintMetadataUtils.getBluePrintRuntime("123456-1000",
-                    "./../../../../components/model-catalog/blueprint-model/test-blueprint/remote_ansible")
-            awxRemoteExecutor.bluePrintRuntimeService = bluePrintRuntimeService
+        // when
+        runBlocking {
+            awxRemoteExecutor.applyNB(executionServiceInput)
+        }
+
+        // then
+        assertTrue(bluePrintRuntimeService.getBluePrintError().errors.isEmpty())
+    }
 
-            val workflowName = executionServiceInput.actionIdentifiers.actionName
+    @Test
+    fun `handle unknown inventory`() {
 
-            // Assign Workflow inputs
-            val input = executionServiceInput.payload.get("$workflowName-request")
-            bluePrintRuntimeService.assignWorkflowInputs(workflowName, input)
+        every {
+            webClientService.exchangeResource("GET", "/api/v2/job_templates/hello_world_job_template/", "")
+        } returns WebClientResponse(200, getJobTemplates(jtId))
+        every {
+            webClientService.exchangeResource("GET", "/api/v2/job_templates/$jtId/launch/", "")
+        } returns WebClientResponse(200, getJobTemplateLaunch(jtId))
+        every {
+            webClientService.exchangeResource("GET", "/api/v2/inventories/?name=Demo+Inventory", "")
+        } returns WebClientResponse(404, "")
+        val selector = mapper.readTree(endpointSelector)
+        val bluePrintRestLibPropertyService = mockk<BluePrintRestLibPropertyService>()
+        every { bluePrintRestLibPropertyService.blueprintWebClientService(selector) } returns webClientService
+        val awxRemoteExecutor = ComponentRemoteAnsibleExecutor(bluePrintRestLibPropertyService, mapper)
+        awxRemoteExecutor.checkDelay = 1
 
-            val stepMetaData: MutableMap<String, JsonNode> = hashMapOf()
-            stepMetaData.putJsonElement(BluePrintConstants.PROPERTY_CURRENT_NODE_TEMPLATE, "execute-remote-ansible")
-            stepMetaData.putJsonElement(BluePrintConstants.PROPERTY_CURRENT_INTERFACE, "ComponentRemoteAnsibleExecutor")
-            stepMetaData.putJsonElement(BluePrintConstants.PROPERTY_CURRENT_OPERATION, "process")
+        val executionServiceInput = JacksonUtils.readValueFromClassPathFile(
+                "payload/requests/remote-ansible-request-full.json",
+                ExecutionServiceInput::class.java)!!
 
-            val stepInputData = StepData().apply {
-                name = "execute-remote-ansible"
-                properties = stepMetaData
-            }
-            executionServiceInput.stepData = stepInputData
+        val bluePrintRuntimeService = createBlueprintRuntimeService(awxRemoteExecutor, executionServiceInput)
 
+        // when
+        runBlocking {
             awxRemoteExecutor.applyNB(executionServiceInput)
         }
+
+        // then
+        val errors = bluePrintRuntimeService.getBluePrintError().errors
+        assertEquals(1, errors.size)
     }
 
-    /**
-     * Test cases for Ansible executor to work with the process NB of remote
-     * executor.
-     */
     @Test
-    @Ignore
-    fun testComponentRemoteAnsibleExecutorProcessNB() {
+    fun `handle failure on job submission`() {
+
+        every {
+            webClientService.exchangeResource("GET", "/api/v2/job_templates/hello_world_job_template/", "")
+        } returns WebClientResponse(200, getJobTemplates(jtId))
+        every {
+            webClientService.exchangeResource("GET", "/api/v2/job_templates/$jtId/launch/", "")
+        } returns WebClientResponse(200, getJobTemplateLaunch(jtId))
+        every {
+            webClientService.exchangeResource("GET", "/api/v2/inventories/?name=Demo+Inventory", "")
+        } returns WebClientResponse(200, getInventory())
+        every {
+            webClientService.exchangeResource("POST", "/api/v2/job_templates/$jtId/launch/",
+                    """{"limit":"123","tags":"some-tag","skip_tags":"some-skip-tag","inventory":1,"extra_vars":{"site_id":"3 - Belmont","tor_group":"vEPC"}}""")
+        } returns WebClientResponse(500, "")
+        val selector = mapper.readTree(endpointSelector)
+        val bluePrintRestLibPropertyService = mockk<BluePrintRestLibPropertyService>()
+        every { bluePrintRestLibPropertyService.blueprintWebClientService(selector) } returns webClientService
+        val awxRemoteExecutor = ComponentRemoteAnsibleExecutor(bluePrintRestLibPropertyService, mapper)
+        awxRemoteExecutor.checkDelay = 1
+
+        val executionServiceInput = JacksonUtils.readValueFromClassPathFile(
+                "payload/requests/remote-ansible-request-full.json",
+                ExecutionServiceInput::class.java)!!
+
+        val bluePrintRuntimeService = createBlueprintRuntimeService(awxRemoteExecutor, executionServiceInput)
+
+        // when
         runBlocking {
-            //            val remoteScriptExecutionService = MockRemoteScriptExecutionService(bluePrintRestLibPropertyService)
-            val componentRemoteAnsibleExecutor = ComponentRemoteAnsibleExecutor(bluePrintRestLibPropertyService)
-            val bluePrintRuntime = mockk<DefaultBluePrintRuntimeService>("123456-1000")
-            val input = getMockedOutput(bluePrintRuntime)
-            componentRemoteAnsibleExecutor.bluePrintRuntimeService = bluePrintRuntime
-            componentRemoteAnsibleExecutor.applyNB(input)
+            awxRemoteExecutor.applyNB(executionServiceInput)
         }
+
+        // then
+        val errors = bluePrintRuntimeService.getBluePrintError().errors
+        assertEquals(1, errors.size)
     }
 
-    /**
-     * Mocked input information for remote Ansible executor.
-     */
-    fun getMockedOutput(svc: DefaultBluePrintRuntimeService):
-            ExecutionServiceInput {
-        val stepMetaData: MutableMap<String, JsonNode> = hashMapOf()
+    private fun createBlueprintRuntimeService(awxRemoteExecutor: ComponentRemoteAnsibleExecutor, executionServiceInput: ExecutionServiceInput): BluePrintRuntimeService<MutableMap<String, JsonNode>> {
+        val bluePrintRuntimeService = BluePrintMetadataUtils.getBluePrintRuntime("123456-1000",
+                "./../../../../components/model-catalog/blueprint-model/test-blueprint/remote_ansible")
+        awxRemoteExecutor.bluePrintRuntimeService = bluePrintRuntimeService
+
+        val workflowName = executionServiceInput.actionIdentifiers.actionName
 
+        // Assign Workflow inputs
+        val input = executionServiceInput.payload.get("$workflowName-request")
+        bluePrintRuntimeService.assignWorkflowInputs(workflowName, input)
+
+        val stepMetaData: MutableMap<String, JsonNode> = hashMapOf()
         stepMetaData.putJsonElement(BluePrintConstants.PROPERTY_CURRENT_NODE_TEMPLATE, "execute-remote-ansible")
         stepMetaData.putJsonElement(BluePrintConstants.PROPERTY_CURRENT_INTERFACE, "ComponentRemoteAnsibleExecutor")
         stepMetaData.putJsonElement(BluePrintConstants.PROPERTY_CURRENT_OPERATION, "process")
 
-        val mapper = ObjectMapper()
-        val rootNode = mapper.createObjectNode()
-        rootNode.put("ip-address", "0.0.0.0")
-        rootNode.put("type", "rest")
-
-        val operationalInputs: MutableMap<String, JsonNode> = hashMapOf()
-        operationalInputs.putJsonElement(BluePrintConstants.PROPERTY_CURRENT_NODE_TEMPLATE, "execute-remote-ansible")
-        operationalInputs.putJsonElement(BluePrintConstants.PROPERTY_CURRENT_INTERFACE, "ComponentRemoteAnsibleExecutor")
-        operationalInputs.putJsonElement(BluePrintConstants.PROPERTY_CURRENT_OPERATION, "process")
-        operationalInputs.putJsonElement("endpoint-selector", "aai")
-//        operationalInputs.putJsonElement("dynamic-properties", rootNode)
-//        operationalInputs.putJsonElement("command", "./run.sh")
-        operationalInputs.putJsonElement("job-template-name", "CDS_job_template2")
-
-        every {
-            svc.resolveNodeTemplateInterfaceOperationInputs(
-                    "execute-remote-ansible",
-                    "ComponentRemoteAnsibleExecutor", "process")
-        } returns operationalInputs
-
         val stepInputData = StepData().apply {
             name = "execute-remote-ansible"
             properties = stepMetaData
         }
-
-        val executionServiceInput = JacksonUtils
-                .readValueFromClassPathFile(
-                        "payload/requests/sample-remote-ansible-request.json",
-                        ExecutionServiceInput::class.java)!!
         executionServiceInput.stepData = stepInputData
+        return bluePrintRuntimeService
+    }
 
-        val operationOutputs = hashMapOf<String, JsonNode>()
-        every {
-            svc.resolveNodeTemplateInterfaceOperationOutputs(
-                    "execute-remote-ansible",
-                    "ComponentRemoteAnsibleExecutor", "process")
-        } returns operationOutputs
-        val bluePrintRuntimeService = BluePrintMetadataUtils
-                .getBluePrintRuntime("123456-1000",
-                        "./../../../../components/model-" +
-                                "catalog/blueprint-model/test-blueprint/" +
-                                "remote_ansible")
-//        every {
-//            svc.resolveNodeTemplateArtifactDefinition("execute-remote-ansible", "component-script")
-//        } returns bluePrintRuntimeService.resolveNodeTemplateArtifactDefinition("execute-remote-ansible",
-//                                                                                "component-script")
-        every {
-            svc.setNodeTemplateAttributeValue(
-                    "execute-remote-ansible",
-                    "execute-command-status",
-                    "successful".asJsonPrimitive())
-        } returns Unit
+    private fun getJobTemplates(jtId: Int) = """{
+      "id": $jtId,
+      "type": "job_template",
+      "url": "/api/v2/job_templates/$jtId/",
+      "related": {
+        "named_url": "/api/v2/job_templates/hello_world_job_template/",
+        "created_by": "/api/v2/users/1/",
+        "modified_by": "/api/v2/users/1/",
+        "labels": "/api/v2/job_templates/$jtId/labels/",
+        "inventory": "/api/v2/inventories/1/",
+        "project": "/api/v2/projects/8/",
+        "extra_credentials": "/api/v2/job_templates/$jtId/extra_credentials/",
+        "credentials": "/api/v2/job_templates/$jtId/credentials/",
+        "last_job": "/api/v2/jobs/222/",
+        "jobs": "/api/v2/job_templates/$jtId/jobs/",
+        "schedules": "/api/v2/job_templates/$jtId/schedules/",
+        "activity_stream": "/api/v2/job_templates/$jtId/activity_stream/",
+        "launch": "/api/v2/job_templates/$jtId/launch/",
+        "notification_templates_any": "/api/v2/job_templates/$jtId/notification_templates_any/",
+        "notification_templates_success": "/api/v2/job_templates/$jtId/notification_templates_success/",
+        "notification_templates_error": "/api/v2/job_templates/$jtId/notification_templates_error/",
+        "access_list": "/api/v2/job_templates/$jtId/access_list/",
+        "survey_spec": "/api/v2/job_templates/$jtId/survey_spec/",
+        "object_roles": "/api/v2/job_templates/$jtId/object_roles/",
+        "instance_groups": "/api/v2/job_templates/$jtId/instance_groups/",
+        "slice_workflow_jobs": "/api/v2/job_templates/$jtId/slice_workflow_jobs/",
+        "copy": "/api/v2/job_templates/$jtId/copy/"
+      },
+      "summary_fields": {
+        "inventory": {
+          "id": 1,
+          "name": "Demo Inventory",
+          "description": "",
+          "has_active_failures": false,
+          "total_hosts": 1,
+          "hosts_with_active_failures": 0,
+          "total_groups": 0,
+          "groups_with_active_failures": 0,
+          "has_inventory_sources": false,
+          "total_inventory_sources": 0,
+          "inventory_sources_with_failures": 0,
+          "organization_id": 1,
+          "kind": ""
+        },
+        "project": {
+          "id": 8,
+          "name": "cds_playbooks",
+          "description": "CDS - cds_playbooks Project",
+          "status": "ok",
+          "scm_type": ""
+        },
+        "last_job": {
+          "id": 222,
+          "name": "hello_world_job_template",
+          "description": "hello_world Runner Job Template",
+          "finished": "2019-06-12T11:20:27.892787Z",
+          "status": "successful",
+          "failed": false
+        },
+        "last_update": {
+          "id": 222,
+          "name": "hello_world_job_template",
+          "description": "hello_world Runner Job Template",
+          "status": "successful",
+          "failed": false
+        },
+        "created_by": {
+          "id": 1,
+          "username": "admin",
+          "first_name": "",
+          "last_name": ""
+        },
+        "modified_by": {
+          "id": 1,
+          "username": "admin",
+          "first_name": "",
+          "last_name": ""
+        },
+        "object_roles": {
+          "admin_role": {
+            "description": "Can manage all aspects of the job template",
+            "name": "Admin",
+            "id": 51
+          },
+          "execute_role": {
+            "description": "May run the job template",
+            "name": "Execute",
+            "id": 52
+          },
+          "read_role": {
+            "description": "May view settings for the job template",
+            "name": "Read",
+            "id": 53
+          }
+        },
+        "user_capabilities": {
+          "edit": true,
+          "delete": true,
+          "start": true,
+          "schedule": true,
+          "copy": true
+        },
+        "labels": {
+          "count": 0,
+          "results": []
+        },
+        "survey": {
+          "title": "",
+          "description": ""
+        },
+        "recent_jobs": [
+          {
+            "id": 222,
+            "status": "successful",
+            "finished": "2019-06-12T11:20:27.892787Z",
+            "type": "job"
+          },
+          {
+            "id": 65,
+            "status": "successful",
+            "finished": "2019-06-03T18:27:19.114796Z",
+            "type": "job"
+          },
+          {
+            "id": 64,
+            "status": "successful",
+            "finished": "2019-06-03T18:26:53.606618Z",
+            "type": "job"
+          },
+          {
+            "id": 63,
+            "status": "successful",
+            "finished": "2019-06-03T18:24:36.072943Z",
+            "type": "job"
+          },
+          {
+            "id": 62,
+            "status": "successful",
+            "finished": "2019-06-03T18:17:50.616528Z",
+            "type": "job"
+          },
+          {
+            "id": 61,
+            "status": "successful",
+            "finished": "2019-06-03T18:04:42.995611Z",
+            "type": "job"
+          },
+          {
+            "id": 60,
+            "status": "successful",
+            "finished": "2019-06-03T17:47:13.983951Z",
+            "type": "job"
+          },
+          {
+            "id": 50,
+            "status": "successful",
+            "finished": "2019-05-30T15:47:55.700161Z",
+            "type": "job"
+          },
+          {
+            "id": 49,
+            "status": "successful",
+            "finished": "2019-05-29T14:46:51.615926Z",
+            "type": "job"
+          },
+          {
+            "id": 47,
+            "status": "successful",
+            "finished": "2019-05-27T20:23:58.656709Z",
+            "type": "job"
+          }
+        ],
+        "extra_credentials": [],
+        "credentials": []
+      },
+      "created": "2019-05-21T19:28:05.953730Z",
+      "modified": "2019-05-21T20:06:55.728697Z",
+      "name": "hello_world_job_template",
+      "description": "hello_world Runner Job Template",
+      "job_type": "run",
+      "inventory": 1,
+      "project": 8,
+      "playbook": "hello_world.yml",
+      "forks": 0,
+      "limit": "",
+      "verbosity": 0,
+      "extra_vars": "",
+      "job_tags": "",
+      "force_handlers": false,
+      "skip_tags": "",
+      "start_at_task": "",
+      "timeout": 0,
+      "use_fact_cache": false,
+      "last_job_run": "2019-06-12T11:20:27.892787Z",
+      "last_job_failed": false,
+      "next_job_run": null,
+      "status": "successful",
+      "host_config_key": "",
+      "ask_diff_mode_on_launch": false,
+      "ask_variables_on_launch": true,
+      "ask_limit_on_launch": true,
+      "ask_tags_on_launch": true,
+      "ask_skip_tags_on_launch": true,
+      "ask_job_type_on_launch": false,
+      "ask_verbosity_on_launch": false,
+      "ask_inventory_on_launch": true,
+      "ask_credential_on_launch": true,
+      "survey_enabled": true,
+      "become_enabled": false,
+      "diff_mode": false,
+      "allow_simultaneous": false,
+      "custom_virtualenv": null,
+      "job_slice_count": 1,
+      "credential": null,
+      "vault_credential": null
+    }"""
 
-        every {
-            svc.setNodeTemplateAttributeValue(
-                    "execute-remote-ansible",
-                    "execute-command-logs", "N/A".asJsonPrimitive())
-        } returns Unit
+    private fun getJobTemplateLaunch(jtId: Int) = """{
+      "can_start_without_user_input": false,
+      "passwords_needed_to_start": [],
+      "ask_variables_on_launch": true,
+      "ask_tags_on_launch": true,
+      "ask_diff_mode_on_launch": false,
+      "ask_skip_tags_on_launch": true,
+      "ask_job_type_on_launch": false,
+      "ask_limit_on_launch": true,
+      "ask_verbosity_on_launch": false,
+      "ask_inventory_on_launch": true,
+      "ask_credential_on_launch": true,
+      "survey_enabled": true,
+      "variables_needed_to_start": [
+        "tor_group",
+        "site_id"
+      ],
+      "credential_needed_to_start": false,
+      "inventory_needed_to_start": false,
+      "job_template_data": {
+        "name": "hello_world_job_template",
+        "id": $jtId,
+        "description": "hello_world Runner Job Template"
+      },
+      "defaults": {
+        "extra_vars": "",
+        "diff_mode": false,
+        "limit": "",
+        "job_tags": "",
+        "skip_tags": "",
+        "job_type": "run",
+        "verbosity": 0,
+        "inventory": {
+          "name": "Demo Inventory",
+          "id": 1
+        }
+      }
+    }"""
 
-        every {
-            svc.setNodeTemplateAttributeValue(
-                    "execute-remote-ansible",
-                    "execute-command-logs",
-                    "processed successfully".asJsonPrimitive())
-        } returns Unit
+    private fun getInventory() = """{
+          "count": 1,
+          "next": null,
+          "previous": null,
+          "results": [
+            {
+              "id": 1,
+              "type": "inventory",
+              "url": "/api/v2/inventories/1/",
+              "related": {
+                "created_by": "/api/v2/users/1/",
+                "modified_by": "/api/v2/users/1/",
+                "hosts": "/api/v2/inventories/1/hosts/",
+                "groups": "/api/v2/inventories/1/groups/",
+                "root_groups": "/api/v2/inventories/1/root_groups/",
+                "variable_data": "/api/v2/inventories/1/variable_data/",
+                "script": "/api/v2/inventories/1/script/",
+                "tree": "/api/v2/inventories/1/tree/",
+                "inventory_sources": "/api/v2/inventories/1/inventory_sources/",
+                "update_inventory_sources": "/api/v2/inventories/1/update_inventory_sources/",
+                "activity_stream": "/api/v2/inventories/1/activity_stream/",
+                "job_templates": "/api/v2/inventories/1/job_templates/",
+                "ad_hoc_commands": "/api/v2/inventories/1/ad_hoc_commands/",
+                "access_list": "/api/v2/inventories/1/access_list/",
+                "object_roles": "/api/v2/inventories/1/object_roles/",
+                "instance_groups": "/api/v2/inventories/1/instance_groups/",
+                "copy": "/api/v2/inventories/1/copy/",
+                "organization": "/api/v2/organizations/1/"
+              },
+              "summary_fields": {
+                "organization": {
+                  "id": 1,
+                  "name": "Default",
+                  "description": ""
+                },
+                "created_by": {
+                  "id": 1,
+                  "username": "admin",
+                  "first_name": "",
+                  "last_name": ""
+                },
+                "modified_by": {
+                  "id": 1,
+                  "username": "admin",
+                  "first_name": "",
+                  "last_name": ""
+                },
+                "object_roles": {
+                  "admin_role": {
+                    "description": "Can manage all aspects of the inventory",
+                    "name": "Admin",
+                    "id": 21
+                  },
+                  "update_role": {
+                    "description": "May update project or inventory or group using the configured source update system",
+                    "name": "Update",
+                    "id": 22
+                  },
+                  "adhoc_role": {
+                    "description": "May run ad hoc commands on an inventory",
+                    "name": "Ad Hoc",
+                    "id": 23
+                  },
+                  "use_role": {
+                    "description": "Can use the inventory in a job template",
+                    "name": "Use",
+                    "id": 24
+                  },
+                  "read_role": {
+                    "description": "May view settings for the inventory",
+                    "name": "Read",
+                    "id": 25
+                  }
+                },
+                "user_capabilities": {
+                  "edit": true,
+                  "delete": true,
+                  "copy": true,
+                  "adhoc": true
+                }
+              },
+              "created": "2019-05-21T15:45:31.954359Z",
+              "modified": "2019-05-21T15:45:31.954378Z",
+              "name": "Demo Inventory",
+              "description": "",
+              "organization": 1,
+              "kind": "",
+              "host_filter": null,
+              "variables": "",
+              "has_active_failures": false,
+              "total_hosts": 1,
+              "hosts_with_active_failures": 0,
+              "total_groups": 0,
+              "groups_with_active_failures": 0,
+              "has_inventory_sources": false,
+              "total_inventory_sources": 0,
+              "inventory_sources_with_failures": 0,
+              "insights_credential": null,
+              "pending_deletion": false
+            }
+          ]
+        }"""
 
-        every {
-            svc.bluePrintContext()
-        } returns bluePrintRuntimeService.bluePrintContext()
-        return executionServiceInput
-    }
+    private fun newJobTemplateLaunch(jtId: Int, jobId: Int) = """{
+      "job": $jobId,
+      "ignored_fields": {},
+      "id": $jobId,
+      "type": "job",
+      "url": "/api/v2/jobs/$jobId/",
+      "related": {
+        "created_by": "/api/v2/users/1/",
+        "modified_by": "/api/v2/users/1/",
+        "labels": "/api/v2/jobs/$jobId/labels/",
+        "inventory": "/api/v2/inventories/1/",
+        "project": "/api/v2/projects/8/",
+        "extra_credentials": "/api/v2/jobs/$jobId/extra_credentials/",
+        "credentials": "/api/v2/jobs/$jobId/credentials/",
+        "unified_job_template": "/api/v2/job_templates/$jtId/",
+        "stdout": "/api/v2/jobs/$jobId/stdout/",
+        "job_events": "/api/v2/jobs/$jobId/job_events/",
+        "job_host_summaries": "/api/v2/jobs/$jobId/job_host_summaries/",
+        "activity_stream": "/api/v2/jobs/$jobId/activity_stream/",
+        "notifications": "/api/v2/jobs/$jobId/notifications/",
+        "job_template": "/api/v2/job_templates/$jtId/",
+        "cancel": "/api/v2/jobs/$jobId/cancel/",
+        "create_schedule": "/api/v2/jobs/$jobId/create_schedule/",
+        "relaunch": "/api/v2/jobs/$jobId/relaunch/"
+      },
+      "summary_fields": {
+        "inventory": {
+          "id": 1,
+          "name": "Demo Inventory",
+          "description": "",
+          "has_active_failures": false,
+          "total_hosts": 1,
+          "hosts_with_active_failures": 0,
+          "total_groups": 0,
+          "groups_with_active_failures": 0,
+          "has_inventory_sources": false,
+          "total_inventory_sources": 0,
+          "inventory_sources_with_failures": 0,
+          "organization_id": 1,
+          "kind": ""
+        },
+        "project": {
+          "id": 8,
+          "name": "cds_playbooks",
+          "description": "CDS - cds_playbooks Project",
+          "status": "ok",
+          "scm_type": ""
+        },
+        "job_template": {
+          "id": $jtId,
+          "name": "hello_world_job_template",
+          "description": "hello_world Runner Job Template"
+        },
+        "unified_job_template": {
+          "id": $jtId,
+          "name": "hello_world_job_template",
+          "description": "hello_world Runner Job Template",
+          "unified_job_type": "job"
+        },
+        "created_by": {
+          "id": 1,
+          "username": "admin",
+          "first_name": "",
+          "last_name": ""
+        },
+        "modified_by": {
+          "id": 1,
+          "username": "admin",
+          "first_name": "",
+          "last_name": ""
+        },
+        "user_capabilities": {
+          "delete": true,
+          "start": true
+        },
+        "labels": {
+          "count": 0,
+          "results": []
+        },
+        "extra_credentials": [],
+        "credentials": []
+      },
+      "created": "2019-06-12T11:21:26.891986Z",
+      "modified": "2019-06-12T11:21:27.016410Z",
+      "name": "hello_world_job_template",
+      "description": "hello_world Runner Job Template",
+      "job_type": "run",
+      "inventory": 1,
+      "project": 8,
+      "playbook": "hello_world.yml",
+      "forks": 0,
+      "limit": "",
+      "verbosity": 0,
+      "extra_vars": "{\"tor_group\": \"vEPC\", \"site_id\": \"3 - Belmont\"}",
+      "job_tags": "",
+      "force_handlers": false,
+      "skip_tags": "",
+      "start_at_task": "",
+      "timeout": 0,
+      "use_fact_cache": false,
+      "unified_job_template": $jtId,
+      "launch_type": "manual",
+      "status": "pending",
+      "failed": false,
+      "started": null,
+      "finished": null,
+      "elapsed": 0,
+      "job_args": "",
+      "job_cwd": "",
+      "job_env": {},
+      "job_explanation": "",
+      "execution_node": "",
+      "controller_node": "",
+      "result_traceback": "",
+      "event_processing_finished": false,
+      "job_template": $jtId,
+      "passwords_needed_to_start": [],
+      "ask_diff_mode_on_launch": false,
+      "ask_variables_on_launch": true,
+      "ask_limit_on_launch": true,
+      "ask_tags_on_launch": true,
+      "ask_skip_tags_on_launch": true,
+      "ask_job_type_on_launch": false,
+      "ask_verbosity_on_launch": false,
+      "ask_inventory_on_launch": true,
+      "ask_credential_on_launch": true,
+      "allow_simultaneous": false,
+      "artifacts": {},
+      "scm_revision": "",
+      "instance_group": null,
+      "diff_mode": false,
+      "job_slice_number": 0,
+      "job_slice_count": 1,
+      "credential": null,
+      "vault_credential": null
+    }"""
+
+    private fun getJobStatus1(jtId: Int, jobId: Int) = """{
+      "id": $jobId,
+      "type": "job",
+      "url": "/api/v2/jobs/$jobId/",
+      "related": {
+        "created_by": "/api/v2/users/1/",
+        "labels": "/api/v2/jobs/$jobId/labels/",
+        "inventory": "/api/v2/inventories/1/",
+        "project": "/api/v2/projects/8/",
+        "extra_credentials": "/api/v2/jobs/$jobId/extra_credentials/",
+        "credentials": "/api/v2/jobs/$jobId/credentials/",
+        "unified_job_template": "/api/v2/job_templates/$jtId/",
+        "stdout": "/api/v2/jobs/$jobId/stdout/",
+        "job_events": "/api/v2/jobs/$jobId/job_events/",
+        "job_host_summaries": "/api/v2/jobs/$jobId/job_host_summaries/",
+        "activity_stream": "/api/v2/jobs/$jobId/activity_stream/",
+        "notifications": "/api/v2/jobs/$jobId/notifications/",
+        "job_template": "/api/v2/job_templates/$jtId/",
+        "cancel": "/api/v2/jobs/$jobId/cancel/",
+        "create_schedule": "/api/v2/jobs/$jobId/create_schedule/",
+        "relaunch": "/api/v2/jobs/$jobId/relaunch/"
+      },
+      "summary_fields": {
+        "inventory": {
+          "id": 1,
+          "name": "Demo Inventory",
+          "description": "",
+          "has_active_failures": false,
+          "total_hosts": 1,
+          "hosts_with_active_failures": 0,
+          "total_groups": 0,
+          "groups_with_active_failures": 0,
+          "has_inventory_sources": false,
+          "total_inventory_sources": 0,
+          "inventory_sources_with_failures": 0,
+          "organization_id": 1,
+          "kind": ""
+        },
+        "project": {
+          "id": 8,
+          "name": "cds_playbooks",
+          "description": "CDS - cds_playbooks Project",
+          "status": "ok",
+          "scm_type": ""
+        },
+        "job_template": {
+          "id": $jtId,
+          "name": "hello_world_job_template",
+          "description": "hello_world Runner Job Template"
+        },
+        "unified_job_template": {
+          "id": $jtId,
+          "name": "hello_world_job_template",
+          "description": "hello_world Runner Job Template",
+          "unified_job_type": "job"
+        },
+        "instance_group": {
+          "name": "tower",
+          "id": 1
+        },
+        "created_by": {
+          "id": 1,
+          "username": "admin",
+          "first_name": "",
+          "last_name": ""
+        },
+        "user_capabilities": {
+          "delete": true,
+          "start": true
+        },
+        "labels": {
+          "count": 0,
+          "results": []
+        },
+        "extra_credentials": [],
+        "credentials": []
+      },
+      "created": "2019-06-12T11:21:26.891986Z",
+      "modified": "2019-06-12T11:21:27.355185Z",
+      "name": "hello_world_job_template",
+      "description": "hello_world Runner Job Template",
+      "job_type": "run",
+      "inventory": 1,
+      "project": 8,
+      "playbook": "hello_world.yml",
+      "forks": 0,
+      "limit": "",
+      "verbosity": 0,
+      "extra_vars": "{\"tor_group\": \"vEPC\", \"site_id\": \"3 - Belmont\"}",
+      "job_tags": "",
+      "force_handlers": false,
+      "skip_tags": "",
+      "start_at_task": "",
+      "timeout": 0,
+      "use_fact_cache": false,
+      "unified_job_template": $jtId,
+      "launch_type": "manual",
+      "status": "waiting",
+      "failed": false,
+      "started": null,
+      "finished": null,
+      "elapsed": 0,
+      "job_args": "",
+      "job_cwd": "",
+      "job_env": {},
+      "job_explanation": "",
+      "execution_node": "awx",
+      "controller_node": "",
+      "result_traceback": "",
+      "event_processing_finished": false,
+      "job_template": $jtId,
+      "passwords_needed_to_start": [],
+      "ask_diff_mode_on_launch": false,
+      "ask_variables_on_launch": true,
+      "ask_limit_on_launch": true,
+      "ask_tags_on_launch": true,
+      "ask_skip_tags_on_launch": true,
+      "ask_job_type_on_launch": false,
+      "ask_verbosity_on_launch": false,
+      "ask_inventory_on_launch": true,
+      "ask_credential_on_launch": true,
+      "allow_simultaneous": false,
+      "artifacts": {},
+      "scm_revision": "",
+      "instance_group": 1,
+      "diff_mode": false,
+      "job_slice_number": 0,
+      "job_slice_count": 1,
+      "host_status_counts": {},
+      "playbook_counts": {
+        "play_count": 0,
+        "task_count": 0
+      },
+      "custom_virtualenv": null,
+      "credential": null,
+      "vault_credential": null
+    }"""
+
+    private fun getJobStatus2(jtId: Int, jobId: Int) = """{
+      "id": $jobId,
+      "type": "job",
+      "url": "/api/v2/jobs/$jobId/",
+      "related": {
+        "created_by": "/api/v2/users/1/",
+        "labels": "/api/v2/jobs/$jobId/labels/",
+        "inventory": "/api/v2/inventories/1/",
+        "project": "/api/v2/projects/8/",
+        "extra_credentials": "/api/v2/jobs/$jobId/extra_credentials/",
+        "credentials": "/api/v2/jobs/$jobId/credentials/",
+        "unified_job_template": "/api/v2/job_templates/$jtId/",
+        "stdout": "/api/v2/jobs/$jobId/stdout/",
+        "job_events": "/api/v2/jobs/$jobId/job_events/",
+        "job_host_summaries": "/api/v2/jobs/$jobId/job_host_summaries/",
+        "activity_stream": "/api/v2/jobs/$jobId/activity_stream/",
+        "notifications": "/api/v2/jobs/$jobId/notifications/",
+        "job_template": "/api/v2/job_templates/$jtId/",
+        "cancel": "/api/v2/jobs/$jobId/cancel/",
+        "create_schedule": "/api/v2/jobs/$jobId/create_schedule/",
+        "relaunch": "/api/v2/jobs/$jobId/relaunch/"
+      },
+      "summary_fields": {
+        "inventory": {
+          "id": 1,
+          "name": "Demo Inventory",
+          "description": "",
+          "has_active_failures": false,
+          "total_hosts": 1,
+          "hosts_with_active_failures": 0,
+          "total_groups": 0,
+          "groups_with_active_failures": 0,
+          "has_inventory_sources": false,
+          "total_inventory_sources": 0,
+          "inventory_sources_with_failures": 0,
+          "organization_id": 1,
+          "kind": ""
+        },
+        "project": {
+          "id": 8,
+          "name": "cds_playbooks",
+          "description": "CDS - cds_playbooks Project",
+          "status": "ok",
+          "scm_type": ""
+        },
+        "job_template": {
+          "id": $jtId,
+          "name": "hello_world_job_template",
+          "description": "hello_world Runner Job Template"
+        },
+        "unified_job_template": {
+          "id": $jtId,
+          "name": "hello_world_job_template",
+          "description": "hello_world Runner Job Template",
+          "unified_job_type": "job"
+        },
+        "instance_group": {
+          "name": "tower",
+          "id": 1
+        },
+        "created_by": {
+          "id": 1,
+          "username": "admin",
+          "first_name": "",
+          "last_name": ""
+        },
+        "user_capabilities": {
+          "delete": true,
+          "start": true
+        },
+        "labels": {
+          "count": 0,
+          "results": []
+        },
+        "extra_credentials": [],
+        "credentials": []
+      },
+      "created": "2019-06-12T11:21:26.891986Z",
+      "modified": "2019-06-12T11:21:27.355185Z",
+      "name": "hello_world_job_template",
+      "description": "hello_world Runner Job Template",
+      "job_type": "run",
+      "inventory": 1,
+      "project": 8,
+      "playbook": "hello_world.yml",
+      "forks": 0,
+      "limit": "",
+      "verbosity": 0,
+      "extra_vars": "{\"tor_group\": \"vEPC\", \"site_id\": \"3 - Belmont\"}",
+      "job_tags": "",
+      "force_handlers": false,
+      "skip_tags": "",
+      "start_at_task": "",
+      "timeout": 0,
+      "use_fact_cache": false,
+      "unified_job_template": $jtId,
+      "launch_type": "manual",
+      "status": "running",
+      "failed": false,
+      "started": "2019-06-12T11:21:27.510766Z",
+      "finished": null,
+      "elapsed": 10.862184,
+      "job_args": "[\"ansible-playbook\", \"-u\", \"root\", \"-i\", \"/tmp/awx_223_ft8hu4p4/tmptmtwllu4\", \"-e\", \"@/tmp/awx_223_ft8hu4p4/env/extravars\", \"hello_world.yml\"]",
+      "job_cwd": "/var/lib/awx/projects/cds_playbooks_folder",
+      "job_env": {
+        "HOSTNAME": "awx",
+        "LC_ALL": "en_US.UTF-8",
+        "VIRTUAL_ENV": "/var/lib/awx/venv/ansible",
+        "PATH": "/var/lib/awx/venv/ansible/bin:/var/lib/awx/venv/awx/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin",
+        "SUPERVISOR_GROUP_NAME": "tower-processes",
+        "PWD": "/var/lib/awx",
+        "LANG": "en_US.UTF-8",
+        "PS1": "(awx) ",
+        "SUPERVISOR_ENABLED": "1",
+        "HOME": "/var/lib/awx",
+        "SHLVL": "2",
+        "LANGUAGE": "en_US.UTF-8",
+        "LC_CTYPE": "en_US.UTF-8",
+        "SUPERVISOR_PROCESS_NAME": "dispatcher",
+        "SUPERVISOR_SERVER_URL": "unix:///tmp/supervisor.sock",
+        "DJANGO_SETTINGS_MODULE": "awx.settings.production",
+        "DJANGO_LIVE_TEST_SERVER_ADDRESS": "localhost:9013-9199",
+        "TZ": "UTC",
+        "ANSIBLE_FORCE_COLOR": "True",
+        "ANSIBLE_HOST_KEY_CHECKING": "False",
+        "ANSIBLE_INVENTORY_UNPARSED_FAILED": "True",
+        "ANSIBLE_PARAMIKO_RECORD_HOST_KEYS": "False",
+        "ANSIBLE_VENV_PATH": "/var/lib/awx/venv/ansible",
+        "AWX_PRIVATE_DATA_DIR": "/tmp/awx_223_ft8hu4p4",
+        "PYTHONPATH": "/var/lib/awx/venv/ansible/lib/python2.7/site-packages:/var/lib/awx/venv/awx/lib64/python3.6/site-packages/awx/lib:",
+        "JOB_ID": "$jobId",
+        "INVENTORY_ID": "1",
+        "PROJECT_REVISION": "",
+        "ANSIBLE_RETRY_FILES_ENABLED": "False",
+        "MAX_EVENT_RES": "700000",
+        "ANSIBLE_CALLBACK_PLUGINS": "/var/lib/awx/venv/awx/lib64/python3.6/site-packages/awx/plugins/callback",
+        "AWX_HOST": "https://towerhost",
+        "ANSIBLE_SSH_CONTROL_PATH_DIR": "/tmp/awx_223_ft8hu4p4/cp",
+        "ANSIBLE_STDOUT_CALLBACK": "awx_display",
+        "AWX_ISOLATED_DATA_DIR": "/tmp/awx_223_ft8hu4p4/artifacts/$jobId"
+      },
+      "job_explanation": "",
+      "execution_node": "awx",
+      "controller_node": "",
+      "result_traceback": "",
+      "event_processing_finished": false,
+      "job_template": $jtId,
+      "passwords_needed_to_start": [],
+      "ask_diff_mode_on_launch": false,
+      "ask_variables_on_launch": true,
+      "ask_limit_on_launch": true,
+      "ask_tags_on_launch": true,
+      "ask_skip_tags_on_launch": true,
+      "ask_job_type_on_launch": false,
+      "ask_verbosity_on_launch": false,
+      "ask_inventory_on_launch": true,
+      "ask_credential_on_launch": true,
+      "allow_simultaneous": false,
+      "artifacts": {},
+      "scm_revision": "",
+      "instance_group": 1,
+      "diff_mode": false,
+      "job_slice_number": 0,
+      "job_slice_count": 1,
+      "host_status_counts": {},
+      "playbook_counts": {
+        "play_count": 1,
+        "task_count": 1
+      },
+      "custom_virtualenv": "/var/lib/awx/venv/ansible",
+      "credential": null,
+      "vault_credential": null
+    }"""
+
+    private fun getJobStatus3(jtId: Int, jobId: Int) = """{
+      "id": $jobId,
+      "type": "job",
+      "url": "/api/v2/jobs/$jobId/",
+      "related": {
+        "created_by": "/api/v2/users/1/",
+        "labels": "/api/v2/jobs/$jobId/labels/",
+        "inventory": "/api/v2/inventories/1/",
+        "project": "/api/v2/projects/8/",
+        "extra_credentials": "/api/v2/jobs/$jobId/extra_credentials/",
+        "credentials": "/api/v2/jobs/$jobId/credentials/",
+        "unified_job_template": "/api/v2/job_templates/$jtId/",
+        "stdout": "/api/v2/jobs/$jobId/stdout/",
+        "job_events": "/api/v2/jobs/$jobId/job_events/",
+        "job_host_summaries": "/api/v2/jobs/$jobId/job_host_summaries/",
+        "activity_stream": "/api/v2/jobs/$jobId/activity_stream/",
+        "notifications": "/api/v2/jobs/$jobId/notifications/",
+        "job_template": "/api/v2/job_templates/$jtId/",
+        "cancel": "/api/v2/jobs/$jobId/cancel/",
+        "create_schedule": "/api/v2/jobs/$jobId/create_schedule/",
+        "relaunch": "/api/v2/jobs/$jobId/relaunch/"
+      },
+      "summary_fields": {
+        "inventory": {
+          "id": 1,
+          "name": "Demo Inventory",
+          "description": "",
+          "has_active_failures": false,
+          "total_hosts": 1,
+          "hosts_with_active_failures": 0,
+          "total_groups": 0,
+          "groups_with_active_failures": 0,
+          "has_inventory_sources": false,
+          "total_inventory_sources": 0,
+          "inventory_sources_with_failures": 0,
+          "organization_id": 1,
+          "kind": ""
+        },
+        "project": {
+          "id": 8,
+          "name": "cds_playbooks",
+          "description": "CDS - cds_playbooks Project",
+          "status": "ok",
+          "scm_type": ""
+        },
+        "job_template": {
+          "id": $jtId,
+          "name": "hello_world_job_template",
+          "description": "hello_world Runner Job Template"
+        },
+        "unified_job_template": {
+          "id": $jtId,
+          "name": "hello_world_job_template",
+          "description": "hello_world Runner Job Template",
+          "unified_job_type": "job"
+        },
+        "instance_group": {
+          "name": "tower",
+          "id": 1
+        },
+        "created_by": {
+          "id": 1,
+          "username": "admin",
+          "first_name": "",
+          "last_name": ""
+        },
+        "user_capabilities": {
+          "delete": true,
+          "start": true
+        },
+        "labels": {
+          "count": 0,
+          "results": []
+        },
+        "extra_credentials": [],
+        "credentials": []
+      },
+      "created": "2019-06-12T11:21:26.891986Z",
+      "modified": "2019-06-12T11:21:27.355185Z",
+      "name": "hello_world_job_template",
+      "description": "hello_world Runner Job Template",
+      "job_type": "run",
+      "inventory": 1,
+      "project": 8,
+      "playbook": "hello_world.yml",
+      "forks": 0,
+      "limit": "",
+      "verbosity": 0,
+      "extra_vars": "{\"tor_group\": \"vEPC\", \"site_id\": \"3 - Belmont\"}",
+      "job_tags": "",
+      "force_handlers": false,
+      "skip_tags": "",
+      "start_at_task": "",
+      "timeout": 0,
+      "use_fact_cache": false,
+      "unified_job_template": $jtId,
+      "launch_type": "manual",
+      "status": "running",
+      "failed": false,
+      "started": "2019-06-12T11:21:27.510766Z",
+      "finished": null,
+      "elapsed": 21.297881,
+      "job_args": "[\"ansible-playbook\", \"-u\", \"root\", \"-i\", \"/tmp/awx_223_ft8hu4p4/tmptmtwllu4\", \"-e\", \"@/tmp/awx_223_ft8hu4p4/env/extravars\", \"hello_world.yml\"]",
+      "job_cwd": "/var/lib/awx/projects/cds_playbooks_folder",
+      "job_env": {
+        "HOSTNAME": "awx",
+        "LC_ALL": "en_US.UTF-8",
+        "VIRTUAL_ENV": "/var/lib/awx/venv/ansible",
+        "PATH": "/var/lib/awx/venv/ansible/bin:/var/lib/awx/venv/awx/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin",
+        "SUPERVISOR_GROUP_NAME": "tower-processes",
+        "PWD": "/var/lib/awx",
+        "LANG": "en_US.UTF-8",
+        "PS1": "(awx) ",
+        "SUPERVISOR_ENABLED": "1",
+        "HOME": "/var/lib/awx",
+        "SHLVL": "2",
+        "LANGUAGE": "en_US.UTF-8",
+        "LC_CTYPE": "en_US.UTF-8",
+        "SUPERVISOR_PROCESS_NAME": "dispatcher",
+        "SUPERVISOR_SERVER_URL": "unix:///tmp/supervisor.sock",
+        "DJANGO_SETTINGS_MODULE": "awx.settings.production",
+        "DJANGO_LIVE_TEST_SERVER_ADDRESS": "localhost:9013-9199",
+        "TZ": "UTC",
+        "ANSIBLE_FORCE_COLOR": "True",
+        "ANSIBLE_HOST_KEY_CHECKING": "False",
+        "ANSIBLE_INVENTORY_UNPARSED_FAILED": "True",
+        "ANSIBLE_PARAMIKO_RECORD_HOST_KEYS": "False",
+        "ANSIBLE_VENV_PATH": "/var/lib/awx/venv/ansible",
+        "AWX_PRIVATE_DATA_DIR": "/tmp/awx_223_ft8hu4p4",
+        "PYTHONPATH": "/var/lib/awx/venv/ansible/lib/python2.7/site-packages:/var/lib/awx/venv/awx/lib64/python3.6/site-packages/awx/lib:",
+        "JOB_ID": "$jobId",
+        "INVENTORY_ID": "1",
+        "PROJECT_REVISION": "",
+        "ANSIBLE_RETRY_FILES_ENABLED": "False",
+        "MAX_EVENT_RES": "700000",
+        "ANSIBLE_CALLBACK_PLUGINS": "/var/lib/awx/venv/awx/lib64/python3.6/site-packages/awx/plugins/callback",
+        "AWX_HOST": "https://towerhost",
+        "ANSIBLE_SSH_CONTROL_PATH_DIR": "/tmp/awx_223_ft8hu4p4/cp",
+        "ANSIBLE_STDOUT_CALLBACK": "awx_display",
+        "AWX_ISOLATED_DATA_DIR": "/tmp/awx_223_ft8hu4p4/artifacts/$jobId"
+      },
+      "job_explanation": "",
+      "execution_node": "awx",
+      "controller_node": "",
+      "result_traceback": "",
+      "event_processing_finished": false,
+      "job_template": $jtId,
+      "passwords_needed_to_start": [],
+      "ask_diff_mode_on_launch": false,
+      "ask_variables_on_launch": true,
+      "ask_limit_on_launch": true,
+      "ask_tags_on_launch": true,
+      "ask_skip_tags_on_launch": true,
+      "ask_job_type_on_launch": false,
+      "ask_verbosity_on_launch": false,
+      "ask_inventory_on_launch": true,
+      "ask_credential_on_launch": true,
+      "allow_simultaneous": false,
+      "artifacts": {},
+      "scm_revision": "",
+      "instance_group": 1,
+      "diff_mode": false,
+      "job_slice_number": 0,
+      "job_slice_count": 1,
+      "host_status_counts": {},
+      "playbook_counts": {
+        "play_count": 1,
+        "task_count": 2
+      },
+      "custom_virtualenv": "/var/lib/awx/venv/ansible",
+      "credential": null,
+      "vault_credential": null
+    } """
+
+    private fun getJobStatus4(jtId: Int, jobId: Int) = """{
+      "id": $jobId,
+      "type": "job",
+      "url": "/api/v2/jobs/$jobId/",
+      "related": {
+        "created_by": "/api/v2/users/1/",
+        "labels": "/api/v2/jobs/$jobId/labels/",
+        "inventory": "/api/v2/inventories/1/",
+        "project": "/api/v2/projects/8/",
+        "extra_credentials": "/api/v2/jobs/$jobId/extra_credentials/",
+        "credentials": "/api/v2/jobs/$jobId/credentials/",
+        "unified_job_template": "/api/v2/job_templates/$jtId/",
+        "stdout": "/api/v2/jobs/$jobId/stdout/",
+        "job_events": "/api/v2/jobs/$jobId/job_events/",
+        "job_host_summaries": "/api/v2/jobs/$jobId/job_host_summaries/",
+        "activity_stream": "/api/v2/jobs/$jobId/activity_stream/",
+        "notifications": "/api/v2/jobs/$jobId/notifications/",
+        "job_template": "/api/v2/job_templates/$jtId/",
+        "cancel": "/api/v2/jobs/$jobId/cancel/",
+        "create_schedule": "/api/v2/jobs/$jobId/create_schedule/",
+        "relaunch": "/api/v2/jobs/$jobId/relaunch/"
+      },
+      "summary_fields": {
+        "inventory": {
+          "id": 1,
+          "name": "Demo Inventory",
+          "description": "",
+          "has_active_failures": false,
+          "total_hosts": 1,
+          "hosts_with_active_failures": 0,
+          "total_groups": 0,
+          "groups_with_active_failures": 0,
+          "has_inventory_sources": false,
+          "total_inventory_sources": 0,
+          "inventory_sources_with_failures": 0,
+          "organization_id": 1,
+          "kind": ""
+        },
+        "project": {
+          "id": 8,
+          "name": "cds_playbooks",
+          "description": "CDS - cds_playbooks Project",
+          "status": "ok",
+          "scm_type": ""
+        },
+        "job_template": {
+          "id": $jtId,
+          "name": "hello_world_job_template",
+          "description": "hello_world Runner Job Template"
+        },
+        "unified_job_template": {
+          "id": $jtId,
+          "name": "hello_world_job_template",
+          "description": "hello_world Runner Job Template",
+          "unified_job_type": "job"
+        },
+        "instance_group": {
+          "name": "tower",
+          "id": 1
+        },
+        "created_by": {
+          "id": 1,
+          "username": "admin",
+          "first_name": "",
+          "last_name": ""
+        },
+        "user_capabilities": {
+          "delete": true,
+          "start": true
+        },
+        "labels": {
+          "count": 0,
+          "results": []
+        },
+        "extra_credentials": [],
+        "credentials": []
+      },
+      "created": "2019-06-12T11:21:26.891986Z",
+      "modified": "2019-06-12T11:21:27.355185Z",
+      "name": "hello_world_job_template",
+      "description": "hello_world Runner Job Template",
+      "job_type": "run",
+      "inventory": 1,
+      "project": 8,
+      "playbook": "hello_world.yml",
+      "forks": 0,
+      "limit": "",
+      "verbosity": 0,
+      "extra_vars": "{\"tor_group\": \"vEPC\", \"site_id\": \"3 - Belmont\"}",
+      "job_tags": "",
+      "force_handlers": false,
+      "skip_tags": "",
+      "start_at_task": "",
+      "timeout": 0,
+      "use_fact_cache": false,
+      "unified_job_template": $jtId,
+      "launch_type": "manual",
+      "status": "successful",
+      "failed": false,
+      "started": "2019-06-12T11:21:27.510766Z",
+      "finished": "2019-06-12T11:21:48.993385Z",
+      "elapsed": 21.483,
+      "job_args": "[\"ansible-playbook\", \"-u\", \"root\", \"-i\", \"/tmp/awx_223_ft8hu4p4/tmptmtwllu4\", \"-e\", \"@/tmp/awx_223_ft8hu4p4/env/extravars\", \"hello_world.yml\"]",
+      "job_cwd": "/var/lib/awx/projects/cds_playbooks_folder",
+      "job_env": {
+        "HOSTNAME": "awx",
+        "LC_ALL": "en_US.UTF-8",
+        "VIRTUAL_ENV": "/var/lib/awx/venv/ansible",
+        "PATH": "/var/lib/awx/venv/ansible/bin:/var/lib/awx/venv/awx/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin",
+        "SUPERVISOR_GROUP_NAME": "tower-processes",
+        "PWD": "/var/lib/awx",
+        "LANG": "en_US.UTF-8",
+        "PS1": "(awx) ",
+        "SUPERVISOR_ENABLED": "1",
+        "HOME": "/var/lib/awx",
+        "SHLVL": "2",
+        "LANGUAGE": "en_US.UTF-8",
+        "LC_CTYPE": "en_US.UTF-8",
+        "SUPERVISOR_PROCESS_NAME": "dispatcher",
+        "SUPERVISOR_SERVER_URL": "unix:///tmp/supervisor.sock",
+        "DJANGO_SETTINGS_MODULE": "awx.settings.production",
+        "DJANGO_LIVE_TEST_SERVER_ADDRESS": "localhost:9013-9199",
+        "TZ": "UTC",
+        "ANSIBLE_FORCE_COLOR": "True",
+        "ANSIBLE_HOST_KEY_CHECKING": "False",
+        "ANSIBLE_INVENTORY_UNPARSED_FAILED": "True",
+        "ANSIBLE_PARAMIKO_RECORD_HOST_KEYS": "False",
+        "ANSIBLE_VENV_PATH": "/var/lib/awx/venv/ansible",
+        "AWX_PRIVATE_DATA_DIR": "/tmp/awx_223_ft8hu4p4",
+        "PYTHONPATH": "/var/lib/awx/venv/ansible/lib/python2.7/site-packages:/var/lib/awx/venv/awx/lib64/python3.6/site-packages/awx/lib:",
+        "JOB_ID": "$jobId",
+        "INVENTORY_ID": "1",
+        "PROJECT_REVISION": "",
+        "ANSIBLE_RETRY_FILES_ENABLED": "False",
+        "MAX_EVENT_RES": "700000",
+        "ANSIBLE_CALLBACK_PLUGINS": "/var/lib/awx/venv/awx/lib64/python3.6/site-packages/awx/plugins/callback",
+        "AWX_HOST": "https://towerhost",
+        "ANSIBLE_SSH_CONTROL_PATH_DIR": "/tmp/awx_223_ft8hu4p4/cp",
+        "ANSIBLE_STDOUT_CALLBACK": "awx_display",
+        "AWX_ISOLATED_DATA_DIR": "/tmp/awx_223_ft8hu4p4/artifacts/$jobId"
+      },
+      "job_explanation": "",
+      "execution_node": "awx",
+      "controller_node": "",
+      "result_traceback": "",
+      "event_processing_finished": true,
+      "job_template": $jtId,
+      "passwords_needed_to_start": [],
+      "ask_diff_mode_on_launch": false,
+      "ask_variables_on_launch": true,
+      "ask_limit_on_launch": true,
+      "ask_tags_on_launch": true,
+      "ask_skip_tags_on_launch": true,
+      "ask_job_type_on_launch": false,
+      "ask_verbosity_on_launch": false,
+      "ask_inventory_on_launch": true,
+      "ask_credential_on_launch": true,
+      "allow_simultaneous": false,
+      "artifacts": {},
+      "scm_revision": "",
+      "instance_group": 1,
+      "diff_mode": false,
+      "job_slice_number": 0,
+      "job_slice_count": 1,
+      "host_status_counts": {
+        "ok": 1
+      },
+      "playbook_counts": {
+        "play_count": 1,
+        "task_count": 2
+      },
+      "custom_virtualenv": "/var/lib/awx/venv/ansible",
+      "credential": null,
+      "vault_credential": null
+    }"""
+
+    private fun getReport() = """
+
+PLAY [Hello World Sample] ******************************************************
+
+TASK [Gathering Facts] *********************************************************
+ok: [localhost]
+
+TASK [Hello Message] ***********************************************************
+ok: [localhost] => {
+    "msg": "Hello World!"
 }
 
-//class MockRemoteScriptExecutionService : RemoteScriptExecutionService {
-//    override suspend fun init(selector: String) {
-//    }
-//
-//    override suspend fun prepareEnv(prepareEnvInput: PrepareRemoteEnvInput): RemoteScriptExecutionOutput {
-//        assertEquals(prepareEnvInput.requestId, "123456-1000", "failed to match request id")
-//        assertNotNull(prepareEnvInput.packages, "failed to get packages")
-//
-//        val remoteScriptExecutionOutput = mockk<RemoteScriptExecutionOutput>()
-//        every { remoteScriptExecutionOutput.response } returns "prepared successfully"
-//        every { remoteScriptExecutionOutput.status } returns StatusType.SUCCESS
-//        return remoteScriptExecutionOutput
-//    }
-//
-//    override suspend fun executeCommand(remoteExecutionInput: RemoteScriptExecutionInput): RemoteScriptExecutionOutput {
-//        assertEquals(remoteExecutionInput.requestId, "123456-1000", "failed to match request id")
-//
-//        val remoteScriptExecutionOutput = mockk<RemoteScriptExecutionOutput>()
-//        every { remoteScriptExecutionOutput.response } returns "processed successfully"
-//        every { remoteScriptExecutionOutput.status } returns StatusType.SUCCESS
-//        return remoteScriptExecutionOutput
-//    }
-//
-//    override suspend fun close() {
-//
-//    }
-//}
+PLAY RECAP *********************************************************************
+localhost                  : ok=2    changed=0    unreachable=0    failed=0
+
+"""
+}
diff --git a/ms/blueprintsprocessor/functions/ansible-awx-executor/src/test/resources/application-test.properties b/ms/blueprintsprocessor/functions/ansible-awx-executor/src/test/resources/application-test.properties
new file mode 100644 (file)
index 0000000..527eb8a
--- /dev/null
@@ -0,0 +1,35 @@
+#
+# Copyright © 2017-2018 AT&T Intellectual Property.
+#
+# Modifications Copyright © 2019 IBM, Bell Canada.
+#
+# Licensed 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.
+#
+blueprintsprocessor.db.primary.url=jdbc:h2:mem:testdb;DB_CLOSE_DELAY=-1
+blueprintsprocessor.db.primary.username=sa
+blueprintsprocessor.db.primary.password=
+blueprintsprocessor.db.primary.driverClassName=org.h2.Driver
+blueprintsprocessor.db.primary.hibernateHbm2ddlAuto=create-drop
+blueprintsprocessor.db.primary.hibernateDDLAuto=update
+blueprintsprocessor.db.primary.hibernateNamingStrategy=org.hibernate.cfg.ImprovedNamingStrategy
+blueprintsprocessor.db.primary.hibernateDialect=org.hibernate.dialect.H2Dialect
+# Controller Blueprints Core Configuration
+blueprintsprocessor.blueprintDeployPath=./target/blueprints/deploy
+blueprintsprocessor.blueprintArchivePath=./target/blueprints/archive
+
+# Python executor
+blueprints.processor.functions.python.executor.executionPath=./../../../../components/scripts/python/ccsdk_blueprints
+blueprints.processor.functions.python.executor.modulePaths=./../../../../components/scripts/python/ccsdk_blueprints
+
+# Executor Options
+blueprintprocessor.netconfExecutor.enabled=true
\ No newline at end of file
diff --git a/ms/blueprintsprocessor/functions/ansible-awx-executor/src/test/resources/payload/requests/remote-ansible-request-full.json b/ms/blueprintsprocessor/functions/ansible-awx-executor/src/test/resources/payload/requests/remote-ansible-request-full.json
new file mode 100644 (file)
index 0000000..11bb90b
--- /dev/null
@@ -0,0 +1,32 @@
+{
+  "actionIdentifiers": {
+    "blueprintName": "remote_ansible",
+    "blueprintVersion": "1.0.0",
+    "actionName": "execute-remote-ansible",
+    "mode": "sync"
+  },
+  "commonHeader": {
+    "flags": {
+      "force": true,
+      "ttl": 3600
+    },
+    "originatorId": "mock",
+    "requestId": "123456-2000",
+    "subRequestId": "sub-123456-1000",
+    "timestamp": "2012-04-23T18:25:43.511Z"
+  },
+  "payload": {
+    "execute-remote-ansible-request": {
+      "endpoint-selector": "awx",
+      "job-template-name": "hello_world_job_template",
+      "inventory": "Demo Inventory",
+      "limit": "123",
+      "tags": "some-tag",
+      "skip-tags": "some-skip-tag",
+      "extra-vars": {
+        "site_id": "3 - Belmont",
+        "tor_group": "vEPC"
+      }
+    }
+  }
+}