You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@dlab.apache.org by of...@apache.org on 2020/01/27 14:58:34 UTC

[incubator-dlab] branch DLAB-1357 updated: Added create odahu cluster feature

This is an automated email from the ASF dual-hosted git repository.

ofuks pushed a commit to branch DLAB-1357
in repository https://gitbox.apache.org/repos/asf/incubator-dlab.git


The following commit(s) were added to refs/heads/DLAB-1357 by this push:
     new 34fc749  Added create odahu cluster feature
34fc749 is described below

commit 34fc7493ddb2dc0945fae821eeaaabf9820e25a5
Author: Oleh Fuks <ol...@gmail.com>
AuthorDate: Mon Jan 27 16:58:04 2020 +0200

    Added create odahu cluster feature
---
 .../com/epam/dlab/dto/base/odahu/OdahuResult.java  |  41 ++++++++
 .../com/epam/dlab/dto/odahu/OdahuCreateDTO.java    |  38 +++++++
 .../backendapi/ProvisioningServiceApplication.java |   1 +
 .../core/commands/CommandExecutorMockAsync.java    |   2 +-
 .../response/handlers/OdahuCallbackHandler.java    |  80 ++++++++++++++
 .../dlab/backendapi/modules/ProductionModule.java  |   3 +
 .../backendapi/modules/ProvisioningDevModule.java  |   6 +-
 .../dlab/backendapi/resources/OdahuResource.java   |  51 +++++++++
 .../epam/dlab/backendapi/service/OdahuService.java |  27 +++++
 .../backendapi/service/impl/OdahuServiceImpl.java  |  96 +++++++++++++++++
 .../resources/mock_response/gcp/odahu_create.json  |  19 ++++
 .../dlab/backendapi/SelfServiceApplication.java    |   2 +
 .../com/epam/dlab/backendapi/dao/OdahuDAO.java     |  37 +++++++
 .../com/epam/dlab/backendapi/dao/OdahuDAOImpl.java | 117 +++++++++++++++++++++
 .../dlab/backendapi/domain/CreateOdahuDTO.java     |  36 +++++++
 .../com/epam/dlab/backendapi/domain/OdahuDTO.java  |  39 +++++++
 .../epam/dlab/backendapi/domain/ProjectDTO.java    |   2 +
 .../epam/dlab/backendapi/modules/DevModule.java    |   2 +
 .../dlab/backendapi/modules/ProductionModule.java  |   2 +
 .../dlab/backendapi/resources/OdahuResource.java   |  66 ++++++++++++
 .../resources/callback/OdahuCallback.java          |  61 +++++++++++
 .../epam/dlab/backendapi/service/OdahuService.java |  32 ++++++
 .../backendapi/service/impl/OdahuServiceImpl.java  |  97 +++++++++++++++++
 .../epam/dlab/backendapi/util/RequestBuilder.java  |  39 ++++++-
 .../legion-deployment.component.ts                 |   2 +-
 .../legion-list/legion-list.component.ts           |   2 +-
 26 files changed, 891 insertions(+), 9 deletions(-)

diff --git a/services/dlab-model/src/main/java/com/epam/dlab/dto/base/odahu/OdahuResult.java b/services/dlab-model/src/main/java/com/epam/dlab/dto/base/odahu/OdahuResult.java
new file mode 100644
index 0000000..2991392
--- /dev/null
+++ b/services/dlab-model/src/main/java/com/epam/dlab/dto/base/odahu/OdahuResult.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 com.epam.dlab.dto.base.odahu;
+
+import com.epam.dlab.dto.ResourceURL;
+import com.epam.dlab.dto.StatusBaseDTO;
+import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
+import com.fasterxml.jackson.annotation.JsonProperty;
+import lombok.Builder;
+import lombok.Data;
+
+import java.util.List;
+
+@Data
+@JsonIgnoreProperties(ignoreUnknown = true)
+public class OdahuResult extends StatusBaseDTO<OdahuResult> {
+    private String name;
+    @JsonProperty("project_name")
+    private String projectName;
+    @JsonProperty("endpoint_name")
+    private String endpointName;
+    @JsonProperty("odahu_urls")
+    private List<ResourceURL> resourceUrls;
+}
diff --git a/services/dlab-model/src/main/java/com/epam/dlab/dto/odahu/OdahuCreateDTO.java b/services/dlab-model/src/main/java/com/epam/dlab/dto/odahu/OdahuCreateDTO.java
new file mode 100644
index 0000000..9fae29a
--- /dev/null
+++ b/services/dlab-model/src/main/java/com/epam/dlab/dto/odahu/OdahuCreateDTO.java
@@ -0,0 +1,38 @@
+/*
+ * 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 com.epam.dlab.dto.odahu;
+
+import com.epam.dlab.dto.ResourceBaseDTO;
+import com.fasterxml.jackson.annotation.JsonProperty;
+import lombok.Builder;
+import lombok.Data;
+
+@Data
+@Builder
+public class OdahuCreateDTO extends ResourceBaseDTO<OdahuCreateDTO> {
+    @JsonProperty("odahu_cluster_name")
+    private final String name;
+    @JsonProperty("project_name")
+    private final String project;
+    @JsonProperty("endpoint_name")
+    private final String endpoint;
+    @JsonProperty("ssh_key")
+    private final String key;
+}
diff --git a/services/provisioning-service/src/main/java/com/epam/dlab/backendapi/ProvisioningServiceApplication.java b/services/provisioning-service/src/main/java/com/epam/dlab/backendapi/ProvisioningServiceApplication.java
index 5cc0d2d..93b8e24 100644
--- a/services/provisioning-service/src/main/java/com/epam/dlab/backendapi/ProvisioningServiceApplication.java
+++ b/services/provisioning-service/src/main/java/com/epam/dlab/backendapi/ProvisioningServiceApplication.java
@@ -146,6 +146,7 @@ public class ProvisioningServiceApplication extends Application<ProvisioningServ
 		jersey.register(injector.getInstance(KeyResource.class));
 		jersey.register(injector.getInstance(CallbackHandlerResource.class));
 		jersey.register(injector.getInstance(ProjectResource.class));
+		jersey.register(injector.getInstance(OdahuResource.class));
 		jersey.register(new ProvisioningHealthCheckResource(environment.healthChecks()));
 
 	}
diff --git a/services/provisioning-service/src/main/java/com/epam/dlab/backendapi/core/commands/CommandExecutorMockAsync.java b/services/provisioning-service/src/main/java/com/epam/dlab/backendapi/core/commands/CommandExecutorMockAsync.java
index 7277961..dae3b78 100644
--- a/services/provisioning-service/src/main/java/com/epam/dlab/backendapi/core/commands/CommandExecutorMockAsync.java
+++ b/services/provisioning-service/src/main/java/com/epam/dlab/backendapi/core/commands/CommandExecutorMockAsync.java
@@ -294,7 +294,7 @@ public class CommandExecutorMockAsync implements Supplier<Boolean> {
 	private void action(String user, DockerAction action) {
 		String resourceType = parser.getResourceType();
 
-		String prefixFileName = (Lists.newArrayList("project", "edge", "dataengine", "dataengine-service")
+		String prefixFileName = (Lists.newArrayList("project", "edge", "odahu", "dataengine", "dataengine-service")
 				.contains(resourceType) ? resourceType : "notebook") + "_";
 		String templateFileName = "mock_response/" + cloudProvider.getName() + '/' + prefixFileName +
 				action.toString() + JSON_FILE_ENDING;
diff --git a/services/provisioning-service/src/main/java/com/epam/dlab/backendapi/core/response/handlers/OdahuCallbackHandler.java b/services/provisioning-service/src/main/java/com/epam/dlab/backendapi/core/response/handlers/OdahuCallbackHandler.java
new file mode 100644
index 0000000..d9027b1
--- /dev/null
+++ b/services/provisioning-service/src/main/java/com/epam/dlab/backendapi/core/response/handlers/OdahuCallbackHandler.java
@@ -0,0 +1,80 @@
+/*
+ * 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 com.epam.dlab.backendapi.core.response.handlers;
+
+import com.epam.dlab.backendapi.core.commands.DockerAction;
+import com.epam.dlab.dto.ResourceURL;
+import com.epam.dlab.dto.base.odahu.OdahuResult;
+import com.epam.dlab.rest.client.RESTService;
+import com.fasterxml.jackson.core.type.TypeReference;
+import com.fasterxml.jackson.databind.JsonNode;
+import lombok.extern.slf4j.Slf4j;
+
+import java.io.IOException;
+import java.util.List;
+
+@Slf4j
+public class OdahuCallbackHandler extends ResourceCallbackHandler<OdahuResult> {
+
+    private static final String ODAHU_URLS_FIELD = "odahu_urls";
+    private final String callbackUri;
+    private final String name;
+    private final String projectName;
+    private final String endpointName;
+
+    public OdahuCallbackHandler(RESTService selfService, String user, String uuid, DockerAction action,
+                                String callbackUri, String name, String projectName, String endpointName) {
+        super(selfService, user, uuid, action);
+        this.callbackUri = callbackUri;
+        this.name = name;
+        this.projectName = projectName;
+        this.endpointName = endpointName;
+    }
+
+    @Override
+    protected String getCallbackURI() {
+        return callbackUri;
+    }
+
+    @Override
+    protected OdahuResult parseOutResponse(JsonNode resultNode, OdahuResult result) {
+        result.setName(name);
+        result.setProjectName(projectName);
+        result.setEndpointName(endpointName);
+
+        if (resultNode == null) {
+            return result;
+        }
+        final JsonNode nodeUrl = resultNode.get(ODAHU_URLS_FIELD);
+        List<ResourceURL> urls;
+        if (nodeUrl != null) {
+            try {
+                urls = mapper.readValue(nodeUrl.toString(), new TypeReference<List<ResourceURL>>() {
+                });
+                result.setResourceUrls(urls);
+            } catch (IOException e) {
+                log.warn("Cannot parse field {} for UUID {} in JSON",
+                        RESPONSE_NODE + "." + RESULT_NODE + "." + ODAHU_URLS_FIELD, getUUID(), e);
+            }
+        }
+
+        return result;
+    }
+}
diff --git a/services/provisioning-service/src/main/java/com/epam/dlab/backendapi/modules/ProductionModule.java b/services/provisioning-service/src/main/java/com/epam/dlab/backendapi/modules/ProductionModule.java
index 40744fa..f510ec8 100644
--- a/services/provisioning-service/src/main/java/com/epam/dlab/backendapi/modules/ProductionModule.java
+++ b/services/provisioning-service/src/main/java/com/epam/dlab/backendapi/modules/ProductionModule.java
@@ -28,9 +28,11 @@ import com.epam.dlab.backendapi.core.commands.ICommandExecutor;
 import com.epam.dlab.backendapi.core.response.handlers.dao.CallbackHandlerDao;
 import com.epam.dlab.backendapi.core.response.handlers.dao.FileSystemCallbackHandlerDao;
 import com.epam.dlab.backendapi.service.CheckInactivityService;
+import com.epam.dlab.backendapi.service.OdahuService;
 import com.epam.dlab.backendapi.service.ProjectService;
 import com.epam.dlab.backendapi.service.RestoreCallbackHandlerService;
 import com.epam.dlab.backendapi.service.impl.CheckInactivityServiceImpl;
+import com.epam.dlab.backendapi.service.impl.OdahuServiceImpl;
 import com.epam.dlab.backendapi.service.impl.ProjectServiceImpl;
 import com.epam.dlab.backendapi.service.impl.RestoreCallbackHandlerServiceImpl;
 import com.epam.dlab.constants.ServiceConsts;
@@ -73,5 +75,6 @@ public class ProductionModule extends ModuleBase<ProvisioningServiceApplicationC
 		bind(RestoreCallbackHandlerService.class).to(RestoreCallbackHandlerServiceImpl.class);
 		bind(CheckInactivityService.class).to(CheckInactivityServiceImpl.class);
 		bind(ProjectService.class).to(ProjectServiceImpl.class);
+		bind(OdahuService.class).to(OdahuServiceImpl.class);
 	}
 }
diff --git a/services/provisioning-service/src/main/java/com/epam/dlab/backendapi/modules/ProvisioningDevModule.java b/services/provisioning-service/src/main/java/com/epam/dlab/backendapi/modules/ProvisioningDevModule.java
index 73d333f..8329c71 100644
--- a/services/provisioning-service/src/main/java/com/epam/dlab/backendapi/modules/ProvisioningDevModule.java
+++ b/services/provisioning-service/src/main/java/com/epam/dlab/backendapi/modules/ProvisioningDevModule.java
@@ -30,11 +30,12 @@ import com.epam.dlab.backendapi.core.commands.CommandExecutorMock;
 import com.epam.dlab.backendapi.core.commands.ICommandExecutor;
 import com.epam.dlab.backendapi.core.response.handlers.dao.CallbackHandlerDao;
 import com.epam.dlab.backendapi.core.response.handlers.dao.FileSystemCallbackHandlerDao;
-import com.epam.dlab.backendapi.service.ProjectService;
-import com.epam.dlab.backendapi.service.RestoreCallbackHandlerService;
 import com.epam.dlab.backendapi.service.CheckInactivityService;
+import com.epam.dlab.backendapi.service.OdahuService;
+import com.epam.dlab.backendapi.service.ProjectService;
 import com.epam.dlab.backendapi.service.RestoreCallbackHandlerService;
 import com.epam.dlab.backendapi.service.impl.CheckInactivityServiceImpl;
+import com.epam.dlab.backendapi.service.impl.OdahuServiceImpl;
 import com.epam.dlab.backendapi.service.impl.ProjectServiceImpl;
 import com.epam.dlab.backendapi.service.impl.RestoreCallbackHandlerServiceImpl;
 import com.epam.dlab.constants.ServiceConsts;
@@ -80,6 +81,7 @@ public class ProvisioningDevModule extends ModuleBase<ProvisioningServiceApplica
 		bind(RestoreCallbackHandlerService.class).to(RestoreCallbackHandlerServiceImpl.class);
 		bind(CheckInactivityService.class).to(CheckInactivityServiceImpl.class);
 		bind(ProjectService.class).to(ProjectServiceImpl.class);
+		bind(OdahuService.class).to(OdahuServiceImpl.class);
 	}
 
 	/**
diff --git a/services/provisioning-service/src/main/java/com/epam/dlab/backendapi/resources/OdahuResource.java b/services/provisioning-service/src/main/java/com/epam/dlab/backendapi/resources/OdahuResource.java
new file mode 100644
index 0000000..8e701c3
--- /dev/null
+++ b/services/provisioning-service/src/main/java/com/epam/dlab/backendapi/resources/OdahuResource.java
@@ -0,0 +1,51 @@
+/*
+ * 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 com.epam.dlab.backendapi.resources;
+
+import com.epam.dlab.auth.UserInfo;
+import com.epam.dlab.backendapi.service.OdahuService;
+import com.epam.dlab.dto.odahu.OdahuCreateDTO;
+import com.google.inject.Inject;
+import io.dropwizard.auth.Auth;
+
+import javax.ws.rs.Consumes;
+import javax.ws.rs.POST;
+import javax.ws.rs.Path;
+import javax.ws.rs.Produces;
+import javax.ws.rs.core.MediaType;
+import javax.ws.rs.core.Response;
+
+@Path("infrastructure/odahu")
+public class OdahuResource {
+
+    private final OdahuService odahuService;
+
+    @Inject
+    public OdahuResource(OdahuService odahuService) {
+        this.odahuService = odahuService;
+    }
+
+    @POST
+    @Consumes(MediaType.APPLICATION_JSON)
+    @Produces(MediaType.APPLICATION_JSON)
+    public Response createProject(@Auth UserInfo userInfo, OdahuCreateDTO dto) {
+        return Response.ok(odahuService.create(userInfo, dto)).build();
+    }
+}
diff --git a/services/provisioning-service/src/main/java/com/epam/dlab/backendapi/service/OdahuService.java b/services/provisioning-service/src/main/java/com/epam/dlab/backendapi/service/OdahuService.java
new file mode 100644
index 0000000..f74d17c
--- /dev/null
+++ b/services/provisioning-service/src/main/java/com/epam/dlab/backendapi/service/OdahuService.java
@@ -0,0 +1,27 @@
+/*
+ * 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 com.epam.dlab.backendapi.service;
+
+import com.epam.dlab.auth.UserInfo;
+import com.epam.dlab.dto.odahu.OdahuCreateDTO;
+
+public interface OdahuService {
+    String create(UserInfo userInfo, OdahuCreateDTO odahuCreateDTO);
+}
diff --git a/services/provisioning-service/src/main/java/com/epam/dlab/backendapi/service/impl/OdahuServiceImpl.java b/services/provisioning-service/src/main/java/com/epam/dlab/backendapi/service/impl/OdahuServiceImpl.java
new file mode 100644
index 0000000..5b58e77
--- /dev/null
+++ b/services/provisioning-service/src/main/java/com/epam/dlab/backendapi/service/impl/OdahuServiceImpl.java
@@ -0,0 +1,96 @@
+/*
+ * 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 com.epam.dlab.backendapi.service.impl;
+
+import com.epam.dlab.auth.UserInfo;
+import com.epam.dlab.backendapi.ProvisioningServiceApplicationConfiguration;
+import com.epam.dlab.backendapi.core.commands.CommandBuilder;
+import com.epam.dlab.backendapi.core.commands.DockerAction;
+import com.epam.dlab.backendapi.core.commands.DockerCommands;
+import com.epam.dlab.backendapi.core.commands.ICommandExecutor;
+import com.epam.dlab.backendapi.core.commands.RunDockerCommand;
+import com.epam.dlab.backendapi.core.response.folderlistener.FolderListenerExecutor;
+import com.epam.dlab.backendapi.core.response.handlers.OdahuCallbackHandler;
+import com.epam.dlab.backendapi.service.OdahuService;
+import com.epam.dlab.dto.ResourceBaseDTO;
+import com.epam.dlab.dto.odahu.OdahuCreateDTO;
+import com.epam.dlab.rest.client.RESTService;
+import com.fasterxml.jackson.core.JsonProcessingException;
+import com.google.inject.Inject;
+
+public class OdahuServiceImpl implements OdahuService {
+
+    private static final String CALLBACK_URI = "/api/odahu/status";
+    private static final String ODAHU_RESOURCE_TYPE = "odahu";
+    private static final String ODAHU_IMAGE = "docker.dlab-odahu";
+
+    private final ProvisioningServiceApplicationConfiguration configuration;
+    private final FolderListenerExecutor folderListenerExecutor;
+    private final CommandBuilder commandBuilder;
+    private final ICommandExecutor commandExecutor;
+    private final RESTService selfService;
+
+    @Inject
+    public OdahuServiceImpl(ProvisioningServiceApplicationConfiguration configuration,
+                            FolderListenerExecutor folderListenerExecutor, CommandBuilder commandBuilder,
+                            ICommandExecutor commandExecutor, RESTService selfService) {
+        this.configuration = configuration;
+        this.folderListenerExecutor = folderListenerExecutor;
+        this.commandBuilder = commandBuilder;
+        this.commandExecutor = commandExecutor;
+        this.selfService = selfService;
+    }
+
+    @Override
+    public String create(UserInfo userInfo, OdahuCreateDTO dto) {
+        return executeDocker(userInfo, dto, DockerAction.CREATE, ODAHU_RESOURCE_TYPE, ODAHU_IMAGE, dto.getName(),
+                dto.getProject(), dto.getEndpoint());
+    }
+
+
+    private String executeDocker(UserInfo userInfo, ResourceBaseDTO dto, DockerAction action, String resourceType,
+                                 String image, String name, String project, String endpoint) {
+        String uuid = DockerCommands.generateUUID();
+
+        folderListenerExecutor.start(configuration.getKeyLoaderDirectory(),
+                configuration.getKeyLoaderPollTimeout(),
+                new OdahuCallbackHandler(selfService, userInfo.getName(), uuid, action, CALLBACK_URI, name, project, endpoint));
+
+        RunDockerCommand runDockerCommand = new RunDockerCommand()
+                .withInteractive()
+                .withName(String.join("_", userInfo.getSimpleName(), name, resourceType, action.toString(),
+                        Long.toString(System.currentTimeMillis())))
+                .withVolumeForRootKeys(configuration.getKeyDirectory())
+                .withVolumeForResponse(configuration.getKeyLoaderDirectory())
+                .withVolumeForLog(configuration.getDockerLogDirectory(), resourceType)
+                .withResource(resourceType)
+                .withRequestId(uuid)
+                .withConfKeyName(configuration.getAdminKey())
+                .withImage(image)
+                .withAction(action);
+
+        try {
+            commandExecutor.executeAsync(userInfo.getName(), uuid, commandBuilder.buildCommand(runDockerCommand, dto));
+        } catch (JsonProcessingException e) {
+            e.printStackTrace();
+        }
+        return uuid;
+    }
+}
diff --git a/services/provisioning-service/src/main/resources/mock_response/gcp/odahu_create.json b/services/provisioning-service/src/main/resources/mock_response/gcp/odahu_create.json
new file mode 100644
index 0000000..c0bc7f5
--- /dev/null
+++ b/services/provisioning-service/src/main/resources/mock_response/gcp/odahu_create.json
@@ -0,0 +1,19 @@
+{
+  "status": "ok",
+  "response": {
+    "result": {
+      "odahu_urls": [
+        {
+          "url": "http://10.10.16.2:8888/",
+          "description": "API Gateway"
+        },
+        {
+          "url": "http://10.10.16.2:8085/",
+          "description": "Documentation"
+        }
+      ]
+    },
+    "log": "/var/log/dlab/odahu/odahu_${EDGE_USER_NAME}_${REQUEST_ID}.log"
+  },
+  "request_id": "${REQUEST_ID}"
+}
diff --git a/services/self-service/src/main/java/com/epam/dlab/backendapi/SelfServiceApplication.java b/services/self-service/src/main/java/com/epam/dlab/backendapi/SelfServiceApplication.java
index da6a5c5..4ec039d 100644
--- a/services/self-service/src/main/java/com/epam/dlab/backendapi/SelfServiceApplication.java
+++ b/services/self-service/src/main/java/com/epam/dlab/backendapi/SelfServiceApplication.java
@@ -165,6 +165,8 @@ public class SelfServiceApplication extends Application<SelfServiceApplicationCo
 		jersey.register(injector.getInstance(EndpointResource.class));
 		jersey.register(injector.getInstance(ProjectResource.class));
 		jersey.register(injector.getInstance(ProjectCallback.class));
+		jersey.register(injector.getInstance(OdahuResource.class));
+		jersey.register(injector.getInstance(OdahuCallback.class));
 	}
 
 	private void disableGzipHandlerForGuacamoleServlet(Server server) {
diff --git a/services/self-service/src/main/java/com/epam/dlab/backendapi/dao/OdahuDAO.java b/services/self-service/src/main/java/com/epam/dlab/backendapi/dao/OdahuDAO.java
new file mode 100644
index 0000000..aeaa698
--- /dev/null
+++ b/services/self-service/src/main/java/com/epam/dlab/backendapi/dao/OdahuDAO.java
@@ -0,0 +1,37 @@
+/*
+ * 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 com.epam.dlab.backendapi.dao;
+
+import com.epam.dlab.backendapi.domain.OdahuDTO;
+import com.epam.dlab.dto.ResourceURL;
+import com.epam.dlab.dto.UserInstanceStatus;
+
+import java.util.List;
+import java.util.Optional;
+
+public interface OdahuDAO {
+    Optional<OdahuDTO> getByProjectEndpoint(String project, String endpoint);
+
+    List<OdahuDTO> findOdahuClusters();
+
+    boolean create(OdahuDTO odahuDTO);
+
+    void updateStatus(String name, String project, String endpoint, List<ResourceURL> urls, UserInstanceStatus status);
+}
diff --git a/services/self-service/src/main/java/com/epam/dlab/backendapi/dao/OdahuDAOImpl.java b/services/self-service/src/main/java/com/epam/dlab/backendapi/dao/OdahuDAOImpl.java
new file mode 100644
index 0000000..6a7f6a7
--- /dev/null
+++ b/services/self-service/src/main/java/com/epam/dlab/backendapi/dao/OdahuDAOImpl.java
@@ -0,0 +1,117 @@
+/*
+ * 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 com.epam.dlab.backendapi.dao;
+
+import com.epam.dlab.backendapi.domain.OdahuDTO;
+import com.epam.dlab.backendapi.domain.ProjectDTO;
+import com.epam.dlab.dto.ResourceURL;
+import com.epam.dlab.dto.UserInstanceStatus;
+import com.mongodb.BasicDBObject;
+import com.mongodb.client.result.UpdateResult;
+import org.bson.Document;
+import org.bson.conversions.Bson;
+
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Optional;
+
+import static com.mongodb.client.model.Filters.and;
+import static com.mongodb.client.model.Filters.eq;
+import static com.mongodb.client.model.Projections.elemMatch;
+import static com.mongodb.client.model.Projections.excludeId;
+import static com.mongodb.client.model.Projections.fields;
+import static com.mongodb.client.model.Projections.include;
+import static com.mongodb.client.model.Updates.push;
+import static java.util.stream.Collectors.toList;
+
+public class OdahuDAOImpl extends BaseDAO implements OdahuDAO {
+
+    private static final String PROJECTS_COLLECTION = "Projects";
+    private static final String ENDPOINTS = "endpoints";
+    private static final String ODAHU_FIELD = "odahu";
+    private static final String NAME_FIELD = "name";
+    private static final String ENDPOINT_FIELD = "endpoint";
+    private static final String PROJECT_FIELD = "project";
+    private static final String STATUS_FIELD = "status";
+    private static final String URLS_FIELD = "urls";
+    private static final String COMPUTATIONAL_URL_DESC = "description";
+    private static final String COMPUTATIONAL_URL_URL = "url";
+
+    @Override
+    public Optional<OdahuDTO> getByProjectEndpoint(String project, String endpoint) {
+        Optional<ProjectDTO> one = findOne(PROJECTS_COLLECTION, odahuProjectEndpointCondition(project, endpoint),
+                fields(include(ODAHU_FIELD), excludeId()),
+                ProjectDTO.class);
+
+        return one.map(ProjectDTO::getOdahu)
+                .filter(odahu -> !odahu.isEmpty())
+                .map(odahu -> odahu.get(0));
+    }
+
+    @Override
+    public List<OdahuDTO> findOdahuClusters() {
+        List<ProjectDTO> projectDTOS = find(PROJECTS_COLLECTION, ProjectDTO.class);
+        return projectDTOS.stream()
+                .map(ProjectDTO::getOdahu)
+                .flatMap(List::stream)
+                .collect(toList());
+    }
+
+    @Override
+    public boolean create(OdahuDTO odahuDTO) {
+        UpdateResult updateResult = updateOne(PROJECTS_COLLECTION, projectEndpointCondition(odahuDTO.getProject(),
+                odahuDTO.getEndpoint()),
+                push(ODAHU_FIELD, convertToBson(odahuDTO)));
+        return updateResult.getModifiedCount() > 0;
+    }
+
+    @Override
+    public void updateStatus(String name, String project, String endpoint, List<ResourceURL> urls,
+                             UserInstanceStatus status) {
+        BasicDBObject dbObject = new BasicDBObject();
+        dbObject.put(ODAHU_FIELD + ".$." + STATUS_FIELD, status.name());
+        dbObject.put(ODAHU_FIELD + ".$." + URLS_FIELD, getResourceUrlData(urls));
+        updateOne(PROJECTS_COLLECTION, and(elemMatch(ODAHU_FIELD, eq(NAME_FIELD, name)),
+                odahuProjectEndpointCondition(project, endpoint)), new Document(SET, dbObject));
+    }
+
+    private Bson odahuProjectEndpointCondition(String projectName, String endpointName) {
+        return and(eq(NAME_FIELD, projectName), and(elemMatch(ODAHU_FIELD, and(eq(ENDPOINT_FIELD, endpointName),
+                eq(PROJECT_FIELD, projectName)))));
+    }
+
+    private Bson projectEndpointCondition(String projectName, String endpointName) {
+        return and(eq(NAME_FIELD, projectName), and(elemMatch(ENDPOINTS, eq(NAME_FIELD, endpointName))));
+    }
+
+    private List<Map<String, String>> getResourceUrlData(List<ResourceURL> urls) {
+        return urls.stream()
+                .map(this::toUrlDocument)
+                .collect(toList());
+    }
+
+    private LinkedHashMap<String, String> toUrlDocument(ResourceURL url) {
+        LinkedHashMap<String, String> map = new LinkedHashMap<>();
+        map.put(COMPUTATIONAL_URL_URL, url.getUrl());
+        map.put(COMPUTATIONAL_URL_DESC, url.getDescription());
+        return map;
+    }
+}
diff --git a/services/self-service/src/main/java/com/epam/dlab/backendapi/domain/CreateOdahuDTO.java b/services/self-service/src/main/java/com/epam/dlab/backendapi/domain/CreateOdahuDTO.java
new file mode 100644
index 0000000..4d5d1ed
--- /dev/null
+++ b/services/self-service/src/main/java/com/epam/dlab/backendapi/domain/CreateOdahuDTO.java
@@ -0,0 +1,36 @@
+/*
+ * 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 com.epam.dlab.backendapi.domain;
+
+import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
+import lombok.Data;
+
+import javax.validation.constraints.NotNull;
+
+@Data
+@JsonIgnoreProperties(ignoreUnknown = true)
+public class CreateOdahuDTO {
+    @NotNull
+    private final String name;
+    @NotNull
+    private final String project;
+    @NotNull
+    private final String endpoint;
+}
diff --git a/services/self-service/src/main/java/com/epam/dlab/backendapi/domain/OdahuDTO.java b/services/self-service/src/main/java/com/epam/dlab/backendapi/domain/OdahuDTO.java
new file mode 100644
index 0000000..7747b69
--- /dev/null
+++ b/services/self-service/src/main/java/com/epam/dlab/backendapi/domain/OdahuDTO.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 com.epam.dlab.backendapi.domain;
+
+import com.epam.dlab.dto.ResourceURL;
+import com.epam.dlab.dto.UserInstanceStatus;
+import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
+import lombok.Data;
+
+import java.util.ArrayList;
+import java.util.List;
+
+
+@Data
+@JsonIgnoreProperties(ignoreUnknown = true)
+public class OdahuDTO {
+    private final String name;
+    private final String project;
+    private final String endpoint;
+    private final List<ResourceURL> urls = new ArrayList<>();
+    private final UserInstanceStatus status;
+}
diff --git a/services/self-service/src/main/java/com/epam/dlab/backendapi/domain/ProjectDTO.java b/services/self-service/src/main/java/com/epam/dlab/backendapi/domain/ProjectDTO.java
index eb53fc0..d3533cc 100644
--- a/services/self-service/src/main/java/com/epam/dlab/backendapi/domain/ProjectDTO.java
+++ b/services/self-service/src/main/java/com/epam/dlab/backendapi/domain/ProjectDTO.java
@@ -8,6 +8,7 @@ import lombok.Data;
 
 import javax.validation.constraints.NotNull;
 import javax.validation.constraints.Pattern;
+import java.util.ArrayList;
 import java.util.List;
 import java.util.Set;
 
@@ -28,6 +29,7 @@ public class ProjectDTO {
 	private final Integer budget;
 	private final List<ProjectEndpointDTO> endpoints;
 	private final boolean sharedImageEnabled;
+	private final List<OdahuDTO> odahu = new ArrayList<>();
 
 
 	public enum Status {
diff --git a/services/self-service/src/main/java/com/epam/dlab/backendapi/modules/DevModule.java b/services/self-service/src/main/java/com/epam/dlab/backendapi/modules/DevModule.java
index 3594826..c260052 100644
--- a/services/self-service/src/main/java/com/epam/dlab/backendapi/modules/DevModule.java
+++ b/services/self-service/src/main/java/com/epam/dlab/backendapi/modules/DevModule.java
@@ -108,6 +108,8 @@ public class DevModule extends ModuleBase<SelfServiceApplicationConfiguration> i
 		bind(EndpointDAO.class).to(EndpointDAOImpl.class);
 		bind(ProjectService.class).to(ProjectServiceImpl.class);
 		bind(ProjectDAO.class).to(ProjectDAOImpl.class);
+		bind(OdahuDAO.class).to(OdahuDAOImpl.class);
+		bind(OdahuService.class).to(OdahuServiceImpl.class);
 	}
 
 	private void configureCors(Environment environment) {
diff --git a/services/self-service/src/main/java/com/epam/dlab/backendapi/modules/ProductionModule.java b/services/self-service/src/main/java/com/epam/dlab/backendapi/modules/ProductionModule.java
index f66487a..766fec7 100644
--- a/services/self-service/src/main/java/com/epam/dlab/backendapi/modules/ProductionModule.java
+++ b/services/self-service/src/main/java/com/epam/dlab/backendapi/modules/ProductionModule.java
@@ -101,5 +101,7 @@ public class ProductionModule extends ModuleBase<SelfServiceApplicationConfigura
 		bind(SecurityService.class).to(SecurityServiceImpl.class);
 		bind(KeycloakService.class).to(KeycloakServiceImpl.class);
 		bind(Client.class).toInstance(httpClient);
+		bind(OdahuDAO.class).to(OdahuDAOImpl.class);
+		bind(OdahuService.class).to(OdahuServiceImpl.class);
 	}
 }
diff --git a/services/self-service/src/main/java/com/epam/dlab/backendapi/resources/OdahuResource.java b/services/self-service/src/main/java/com/epam/dlab/backendapi/resources/OdahuResource.java
new file mode 100644
index 0000000..ce5d829
--- /dev/null
+++ b/services/self-service/src/main/java/com/epam/dlab/backendapi/resources/OdahuResource.java
@@ -0,0 +1,66 @@
+/*
+ * 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 com.epam.dlab.backendapi.resources;
+
+import com.epam.dlab.auth.UserInfo;
+import com.epam.dlab.backendapi.domain.CreateOdahuDTO;
+import com.epam.dlab.backendapi.service.OdahuService;
+import com.google.inject.Inject;
+import io.dropwizard.auth.Auth;
+import io.swagger.v3.oas.annotations.Parameter;
+
+import javax.validation.Valid;
+import javax.ws.rs.Consumes;
+import javax.ws.rs.GET;
+import javax.ws.rs.POST;
+import javax.ws.rs.Path;
+import javax.ws.rs.Produces;
+import javax.ws.rs.core.Context;
+import javax.ws.rs.core.MediaType;
+import javax.ws.rs.core.Response;
+import javax.ws.rs.core.UriInfo;
+import java.net.URI;
+
+@Path("odahu")
+@Consumes(MediaType.APPLICATION_JSON)
+public class OdahuResource {
+
+    private final OdahuService odahuService;
+
+    @Inject
+    public OdahuResource(OdahuService odahuService) {
+        this.odahuService = odahuService;
+    }
+
+    @POST
+    public Response createOdahuCluster(@Parameter(hidden = true) @Auth UserInfo userInfo,
+                                       @Parameter(hidden = true) @Context UriInfo uriInfo,
+                                       @Valid CreateOdahuDTO createOdahuDTO) {
+        odahuService.create(createOdahuDTO.getProject(), createOdahuDTO, userInfo);
+        final URI uri = uriInfo.getRequestUriBuilder().path(createOdahuDTO.getName()).build();
+        return Response.created(uri).build();
+    }
+
+    @GET
+    @Produces(MediaType.APPLICATION_JSON)
+    public Response getOdahuClusters(@Parameter(hidden = true) @Auth UserInfo userInfo) {
+        return Response.ok(odahuService.findOdahu()).build();
+    }
+}
diff --git a/services/self-service/src/main/java/com/epam/dlab/backendapi/resources/callback/OdahuCallback.java b/services/self-service/src/main/java/com/epam/dlab/backendapi/resources/callback/OdahuCallback.java
new file mode 100644
index 0000000..9881889
--- /dev/null
+++ b/services/self-service/src/main/java/com/epam/dlab/backendapi/resources/callback/OdahuCallback.java
@@ -0,0 +1,61 @@
+/*
+ * 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 com.epam.dlab.backendapi.resources.callback;
+
+import com.epam.dlab.backendapi.dao.OdahuDAO;
+import com.epam.dlab.backendapi.domain.RequestId;
+import com.epam.dlab.dto.UserInstanceStatus;
+import com.epam.dlab.dto.base.odahu.OdahuResult;
+import com.epam.dlab.exceptions.DlabException;
+import com.google.inject.Inject;
+
+import javax.ws.rs.Consumes;
+import javax.ws.rs.POST;
+import javax.ws.rs.Path;
+import javax.ws.rs.core.MediaType;
+import javax.ws.rs.core.Response;
+import java.util.Optional;
+
+@Path("odahu/status")
+@Consumes(MediaType.APPLICATION_JSON)
+public class OdahuCallback {
+
+    private final OdahuDAO odahuDAO;
+    private final RequestId requestId;
+
+    @Inject
+    public OdahuCallback(OdahuDAO odahuDAO, RequestId requestId) {
+        this.odahuDAO = odahuDAO;
+        this.requestId = requestId;
+    }
+
+    @POST
+    public Response updateOdahuStatus(OdahuResult odahuResult) {
+        requestId.checkAndRemove(odahuResult.getRequestId());
+        final UserInstanceStatus status = UserInstanceStatus.of(odahuResult.getStatus());
+        Optional.ofNullable(status)
+                .orElseThrow(() -> new DlabException(String.format("Cannot convert %s to UserInstanceStatus", status)));
+
+        odahuDAO.updateStatus(odahuResult.getName(), odahuResult.getProjectName(), odahuResult.getEndpointName(),
+                odahuResult.getResourceUrls(), status);
+
+        return Response.ok().build();
+    }
+}
diff --git a/services/self-service/src/main/java/com/epam/dlab/backendapi/service/OdahuService.java b/services/self-service/src/main/java/com/epam/dlab/backendapi/service/OdahuService.java
new file mode 100644
index 0000000..550f55a
--- /dev/null
+++ b/services/self-service/src/main/java/com/epam/dlab/backendapi/service/OdahuService.java
@@ -0,0 +1,32 @@
+/*
+ * 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 com.epam.dlab.backendapi.service;
+
+import com.epam.dlab.auth.UserInfo;
+import com.epam.dlab.backendapi.domain.CreateOdahuDTO;
+import com.epam.dlab.backendapi.domain.OdahuDTO;
+
+import java.util.List;
+
+public interface OdahuService {
+    List<OdahuDTO> findOdahu();
+
+    void create(String project, CreateOdahuDTO createOdahuDTO, UserInfo userInfo);
+}
diff --git a/services/self-service/src/main/java/com/epam/dlab/backendapi/service/impl/OdahuServiceImpl.java b/services/self-service/src/main/java/com/epam/dlab/backendapi/service/impl/OdahuServiceImpl.java
new file mode 100644
index 0000000..1a01f5c
--- /dev/null
+++ b/services/self-service/src/main/java/com/epam/dlab/backendapi/service/impl/OdahuServiceImpl.java
@@ -0,0 +1,97 @@
+/*
+ * 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 com.epam.dlab.backendapi.service.impl;
+
+import com.epam.dlab.auth.UserInfo;
+import com.epam.dlab.backendapi.annotation.BudgetLimited;
+import com.epam.dlab.backendapi.annotation.Project;
+import com.epam.dlab.backendapi.dao.OdahuDAO;
+import com.epam.dlab.backendapi.domain.CreateOdahuDTO;
+import com.epam.dlab.backendapi.domain.OdahuDTO;
+import com.epam.dlab.backendapi.domain.ProjectDTO;
+import com.epam.dlab.backendapi.domain.RequestId;
+import com.epam.dlab.backendapi.service.EndpointService;
+import com.epam.dlab.backendapi.service.OdahuService;
+import com.epam.dlab.backendapi.service.ProjectService;
+import com.epam.dlab.backendapi.util.RequestBuilder;
+import com.epam.dlab.constants.ServiceConsts;
+import com.epam.dlab.dto.UserInstanceStatus;
+import com.epam.dlab.exceptions.ResourceConflictException;
+import com.epam.dlab.rest.client.RESTService;
+import com.google.inject.Inject;
+import com.google.inject.name.Named;
+
+import java.util.List;
+import java.util.Optional;
+
+public class OdahuServiceImpl implements OdahuService {
+
+    private static final String CREATE_ODAHU_API = "infrastructure/odahu";
+
+    private final ProjectService projectService;
+    private final EndpointService endpointService;
+    private final OdahuDAO odahuDAO;
+    private final RESTService provisioningService;
+    private final RequestBuilder requestBuilder;
+    private final RequestId requestId;
+
+    @Inject
+    public OdahuServiceImpl(ProjectService projectService, EndpointService endpointService, OdahuDAO odahuDAO,
+                            @Named(ServiceConsts.PROVISIONING_SERVICE_NAME) RESTService provisioningService,
+                            RequestBuilder requestBuilder, RequestId requestId) {
+        this.projectService = projectService;
+        this.endpointService = endpointService;
+        this.odahuDAO = odahuDAO;
+        this.provisioningService = provisioningService;
+        this.requestBuilder = requestBuilder;
+        this.requestId = requestId;
+    }
+
+
+    @Override
+    public List<OdahuDTO> findOdahu() {
+        return odahuDAO.findOdahuClusters();
+    }
+
+    @BudgetLimited
+    @Override
+    public void create(@Project String project, CreateOdahuDTO createOdahuDTO, UserInfo userInfo) {
+        Optional<OdahuDTO> odahuDTO = odahuDAO.getByProjectEndpoint(createOdahuDTO.getProject(), createOdahuDTO.getEndpoint());
+        if (odahuDTO.isPresent()) {
+            throw new ResourceConflictException(String.format("Odahu cluster already exist in system for project %s " +
+                    "and endpoint %s", createOdahuDTO.getProject(), createOdahuDTO.getEndpoint()));
+        }
+        ProjectDTO projectDTO = projectService.get(project);
+        boolean isAdded = odahuDAO.create(new OdahuDTO(createOdahuDTO.getName(), createOdahuDTO.getProject(),
+                createOdahuDTO.getEndpoint(), UserInstanceStatus.CREATING));
+
+        if (isAdded) {
+            createOnCloud(userInfo, createOdahuDTO, projectDTO);
+        }
+    }
+
+    private void createOnCloud(UserInfo user, CreateOdahuDTO createOdahuDTO, ProjectDTO projectDTO) {
+        String uuid =
+                provisioningService.post(endpointService.get(createOdahuDTO.getEndpoint()).getUrl() + CREATE_ODAHU_API,
+                        user.getAccessToken(),
+                        requestBuilder.newOdahuCreate(user, createOdahuDTO, projectDTO), String.class);
+        requestId.put(user.getName(), uuid);
+    }
+}
diff --git a/services/self-service/src/main/java/com/epam/dlab/backendapi/util/RequestBuilder.java b/services/self-service/src/main/java/com/epam/dlab/backendapi/util/RequestBuilder.java
index d4f690e..c13aafc 100644
--- a/services/self-service/src/main/java/com/epam/dlab/backendapi/util/RequestBuilder.java
+++ b/services/self-service/src/main/java/com/epam/dlab/backendapi/util/RequestBuilder.java
@@ -22,6 +22,7 @@ package com.epam.dlab.backendapi.util;
 import com.epam.dlab.auth.UserInfo;
 import com.epam.dlab.backendapi.conf.SelfServiceApplicationConfiguration;
 import com.epam.dlab.backendapi.dao.SettingsDAO;
+import com.epam.dlab.backendapi.domain.CreateOdahuDTO;
 import com.epam.dlab.backendapi.domain.ExploratoryLibCache;
 import com.epam.dlab.backendapi.domain.ProjectDTO;
 import com.epam.dlab.backendapi.resources.dto.BackupFormDTO;
@@ -30,7 +31,11 @@ import com.epam.dlab.backendapi.resources.dto.SparkStandaloneClusterCreateForm;
 import com.epam.dlab.backendapi.resources.dto.aws.AwsComputationalCreateForm;
 import com.epam.dlab.backendapi.resources.dto.gcp.GcpComputationalCreateForm;
 import com.epam.dlab.cloud.CloudProvider;
-import com.epam.dlab.dto.*;
+import com.epam.dlab.dto.LibListComputationalDTO;
+import com.epam.dlab.dto.ResourceBaseDTO;
+import com.epam.dlab.dto.ResourceSysBaseDTO;
+import com.epam.dlab.dto.UserEnvironmentResources;
+import com.epam.dlab.dto.UserInstanceDTO;
 import com.epam.dlab.dto.aws.AwsCloudSettings;
 import com.epam.dlab.dto.aws.computational.AwsComputationalTerminateDTO;
 import com.epam.dlab.dto.aws.computational.ClusterConfig;
@@ -51,8 +56,21 @@ import com.epam.dlab.dto.base.CloudSettings;
 import com.epam.dlab.dto.base.DataEngineType;
 import com.epam.dlab.dto.base.computational.ComputationalBase;
 import com.epam.dlab.dto.base.keyload.UploadFile;
-import com.epam.dlab.dto.computational.*;
-import com.epam.dlab.dto.exploratory.*;
+import com.epam.dlab.dto.computational.ComputationalCheckInactivityDTO;
+import com.epam.dlab.dto.computational.ComputationalClusterConfigDTO;
+import com.epam.dlab.dto.computational.ComputationalStartDTO;
+import com.epam.dlab.dto.computational.ComputationalStopDTO;
+import com.epam.dlab.dto.computational.ComputationalTerminateDTO;
+import com.epam.dlab.dto.computational.UserComputationalResource;
+import com.epam.dlab.dto.exploratory.ExploratoryActionDTO;
+import com.epam.dlab.dto.exploratory.ExploratoryCheckInactivityAction;
+import com.epam.dlab.dto.exploratory.ExploratoryCreateDTO;
+import com.epam.dlab.dto.exploratory.ExploratoryGitCredsDTO;
+import com.epam.dlab.dto.exploratory.ExploratoryGitCredsUpdateDTO;
+import com.epam.dlab.dto.exploratory.ExploratoryImageDTO;
+import com.epam.dlab.dto.exploratory.ExploratoryReconfigureSparkClusterActionDTO;
+import com.epam.dlab.dto.exploratory.LibInstallDTO;
+import com.epam.dlab.dto.exploratory.LibraryInstallDTO;
 import com.epam.dlab.dto.gcp.GcpCloudSettings;
 import com.epam.dlab.dto.gcp.computational.ComputationalCreateGcp;
 import com.epam.dlab.dto.gcp.computational.GcpComputationalTerminateDTO;
@@ -60,6 +78,7 @@ import com.epam.dlab.dto.gcp.computational.SparkComputationalCreateGcp;
 import com.epam.dlab.dto.gcp.edge.EdgeCreateGcp;
 import com.epam.dlab.dto.gcp.exploratory.ExploratoryCreateGcp;
 import com.epam.dlab.dto.gcp.keyload.UploadFileGcp;
+import com.epam.dlab.dto.odahu.OdahuCreateDTO;
 import com.epam.dlab.dto.project.ProjectActionDTO;
 import com.epam.dlab.dto.project.ProjectCreateDTO;
 import com.epam.dlab.dto.reuploadkey.ReuploadKeyDTO;
@@ -74,7 +93,9 @@ import java.util.List;
 import java.util.Map;
 import java.util.UUID;
 
-import static com.epam.dlab.cloud.CloudProvider.*;
+import static com.epam.dlab.cloud.CloudProvider.AWS;
+import static com.epam.dlab.cloud.CloudProvider.AZURE;
+import static com.epam.dlab.cloud.CloudProvider.GCP;
 
 @Singleton
 public class RequestBuilder {
@@ -641,6 +662,16 @@ public class RequestBuilder {
 				.withCloudSettings(cloudSettings(userInfo));
 	}
 
+	public OdahuCreateDTO newOdahuCreate(UserInfo user, CreateOdahuDTO createOdahuDTO, ProjectDTO projectDTO) {
+		return OdahuCreateDTO.builder()
+				.name(createOdahuDTO.getName())
+				.project(projectDTO.getName())
+				.endpoint(createOdahuDTO.getEndpoint())
+				.key(projectDTO.getKey().replace("\n", ""))
+				.build()
+				.withCloudSettings(cloudSettings(user));
+	}
+
 	private CloudProvider cloudProvider() {
 		return configuration.getCloudProvider();
 	}
diff --git a/services/self-service/src/main/resources/webapp/src/app/administration/legion-deployment/legion-deployment.component.ts b/services/self-service/src/main/resources/webapp/src/app/administration/legion-deployment/legion-deployment.component.ts
index c1b7a35..91472f0 100644
--- a/services/self-service/src/main/resources/webapp/src/app/administration/legion-deployment/legion-deployment.component.ts
+++ b/services/self-service/src/main/resources/webapp/src/app/administration/legion-deployment/legion-deployment.component.ts
@@ -49,7 +49,7 @@ export class LegionDeploymentComponent implements OnInit {
       .subscribe((result: any) => this.healthStatus = result);
   }
 
-  private refreshGrid(): void {
+  public refreshGrid(): void {
     this.legionDeploymentDataService.updateClasters();
   }
 }
diff --git a/services/self-service/src/main/resources/webapp/src/app/administration/legion-deployment/legion-list/legion-list.component.ts b/services/self-service/src/main/resources/webapp/src/app/administration/legion-deployment/legion-list/legion-list.component.ts
index bea63e7..c6ede82 100644
--- a/services/self-service/src/main/resources/webapp/src/app/administration/legion-deployment/legion-list/legion-list.component.ts
+++ b/services/self-service/src/main/resources/webapp/src/app/administration/legion-deployment/legion-list/legion-list.component.ts
@@ -11,7 +11,7 @@ import { MatTableDataSource } from '@angular/material/table';
 export class LegionListComponent implements OnInit {
   private legionClustersList: any[];
   private subscriptions: Subscription = new Subscription();
-  private dataSource: MatTableDataSource<unknown>;
+  public dataSource: MatTableDataSource<unknown>;
   displayedColumns: string[] = ['name', 'endpoint-url', 'legion-name', 'legion-status', "actions"];
 
   constructor(


---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@dlab.apache.org
For additional commands, e-mail: commits-help@dlab.apache.org