Repository: ambari Updated Branches: refs/heads/trunk 1064ade01 -> 53e3d8dd0
AMBARI-5918. Implement Slider app api to freeze and thaw an application. (onechiporenko, srimanth) Project: http://git-wip-us.apache.org/repos/asf/ambari/repo Commit: http://git-wip-us.apache.org/repos/asf/ambari/commit/53e3d8dd Tree: http://git-wip-us.apache.org/repos/asf/ambari/tree/53e3d8dd Diff: http://git-wip-us.apache.org/repos/asf/ambari/diff/53e3d8dd Branch: refs/heads/trunk Commit: 53e3d8dd018e694a79982179f9f5fabffd275369 Parents: 1064ade Author: Srimanth Gunturi <[email protected]> Authored: Wed May 28 15:18:47 2014 -0700 Committer: Srimanth Gunturi <[email protected]> Committed: Wed May 28 16:37:59 2014 -0700 ---------------------------------------------------------------------- .../view/slider/SliderAppsViewController.java | 9 +- .../slider/SliderAppsViewControllerImpl.java | 63 +++++++++++++ .../view/slider/rest/SliderAppsResource.java | 37 +++++++- .../ui/app/controllers/slider_app_controller.js | 93 +++++++++++++++++++- .../src/main/resources/ui/app/helpers/ajax.js | 32 ++++++- .../src/main/resources/ui/app/initialize.js | 7 ++ .../src/main/resources/ui/app/routes/main.js | 6 +- 7 files changed, 234 insertions(+), 13 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/ambari/blob/53e3d8dd/contrib/views/slider/src/main/java/org/apache/ambari/view/slider/SliderAppsViewController.java ---------------------------------------------------------------------- diff --git a/contrib/views/slider/src/main/java/org/apache/ambari/view/slider/SliderAppsViewController.java b/contrib/views/slider/src/main/java/org/apache/ambari/view/slider/SliderAppsViewController.java index 83b641e..823fea6 100644 --- a/contrib/views/slider/src/main/java/org/apache/ambari/view/slider/SliderAppsViewController.java +++ b/contrib/views/slider/src/main/java/org/apache/ambari/view/slider/SliderAppsViewController.java @@ -74,5 +74,12 @@ public interface SliderAppsViewController { public List<SliderAppType> getSliderAppTypes(Set<String> properties); - public String createSliderApp(JsonObject requestJson) throws IOException, YarnException, InterruptedException; + public String createSliderApp(JsonObject requestJson) throws IOException, + YarnException, InterruptedException; + + public void freezeApp(String appId) throws YarnException, IOException, + InterruptedException; + + public void thawApp(String appId) throws YarnException, IOException, + InterruptedException; } http://git-wip-us.apache.org/repos/asf/ambari/blob/53e3d8dd/contrib/views/slider/src/main/java/org/apache/ambari/view/slider/SliderAppsViewControllerImpl.java ---------------------------------------------------------------------- diff --git a/contrib/views/slider/src/main/java/org/apache/ambari/view/slider/SliderAppsViewControllerImpl.java b/contrib/views/slider/src/main/java/org/apache/ambari/view/slider/SliderAppsViewControllerImpl.java index 679fa3a..4ccc51b 100644 --- a/contrib/views/slider/src/main/java/org/apache/ambari/view/slider/SliderAppsViewControllerImpl.java +++ b/contrib/views/slider/src/main/java/org/apache/ambari/view/slider/SliderAppsViewControllerImpl.java @@ -62,6 +62,8 @@ import org.apache.slider.api.ClusterDescription; import org.apache.slider.client.SliderClient; import org.apache.slider.common.SliderKeys; import org.apache.slider.common.params.ActionCreateArgs; +import org.apache.slider.common.params.ActionFreezeArgs; +import org.apache.slider.common.params.ActionThawArgs; import org.apache.slider.common.tools.SliderFileSystem; import org.apache.slider.core.exceptions.UnknownApplicationInstanceException; import org.apache.slider.core.main.LauncherExitCodes; @@ -486,6 +488,10 @@ public class SliderAppsViewControllerImpl implements SliderAppsViewController { rmSchedulerAddress); yarnConfig.set("fs.defaultFS", hdfsPath); yarnConfig.set("slider.zookeeper.quorum", zkQuorum.toString()); + yarnConfig + .set( + "yarn.application.classpath", + "/etc/hadoop/conf,/usr/lib/hadoop/*,/usr/lib/hadoop/lib/*,/usr/lib/hadoop-hdfs/*,/usr/lib/hadoop-hdfs/lib/*,/usr/lib/hadoop-yarn/*,/usr/lib/hadoop-yarn/lib/*,/usr/lib/hadoop-mapreduce/*,/usr/lib/hadoop-mapreduce/lib/*"); return yarnConfig; } } @@ -802,4 +808,61 @@ public class SliderAppsViewControllerImpl implements SliderAppsViewController { fos.close(); } } + + @Override + public void freezeApp(String appId) throws YarnException, IOException, + InterruptedException { + ClassLoader currentClassLoader = Thread.currentThread() + .getContextClassLoader(); + Thread.currentThread().setContextClassLoader(getClass().getClassLoader()); + try { + Set<String> properties = new HashSet<String>(); + properties.add("id"); + properties.add("name"); + final SliderApp sliderApp = getSliderApp(appId, properties); + if (sliderApp == null) + throw new ApplicationNotFoundException(appId); + + ApplicationId applicationId = UserGroupInformation.getBestUGI(null, + "yarn").doAs(new PrivilegedExceptionAction<ApplicationId>() { + public ApplicationId run() throws IOException, YarnException { + SliderClient sliderClient = getSliderClient(); + ActionFreezeArgs freezeArgs = new ActionFreezeArgs(); + sliderClient.actionFreeze(sliderApp.getName(), freezeArgs); + return sliderClient.applicationId; + } + }); + logger.debug("Slider app has been frozen - " + applicationId.toString()); + } finally { + Thread.currentThread().setContextClassLoader(currentClassLoader); + } + } + + @Override + public void thawApp(String appId) throws YarnException, IOException, + InterruptedException { + ClassLoader currentClassLoader = Thread.currentThread() + .getContextClassLoader(); + Thread.currentThread().setContextClassLoader(getClass().getClassLoader()); + try { + Set<String> properties = new HashSet<String>(); + properties.add("id"); + properties.add("name"); + final SliderApp sliderApp = getSliderApp(appId, properties); + if (sliderApp == null) + throw new ApplicationNotFoundException(appId); + ApplicationId applicationId = UserGroupInformation.getBestUGI(null, + "yarn").doAs(new PrivilegedExceptionAction<ApplicationId>() { + public ApplicationId run() throws IOException, YarnException { + SliderClient sliderClient = getSliderClient(); + ActionThawArgs thawArgs = new ActionThawArgs(); + sliderClient.actionThaw(sliderApp.getName(), thawArgs); + return sliderClient.applicationId; + } + }); + logger.debug("Slider app has been thawed - " + applicationId.toString()); + } finally { + Thread.currentThread().setContextClassLoader(currentClassLoader); + } + } } http://git-wip-us.apache.org/repos/asf/ambari/blob/53e3d8dd/contrib/views/slider/src/main/java/org/apache/ambari/view/slider/rest/SliderAppsResource.java ---------------------------------------------------------------------- diff --git a/contrib/views/slider/src/main/java/org/apache/ambari/view/slider/rest/SliderAppsResource.java b/contrib/views/slider/src/main/java/org/apache/ambari/view/slider/rest/SliderAppsResource.java index 30b9f3b..4459db2 100644 --- a/contrib/views/slider/src/main/java/org/apache/ambari/view/slider/rest/SliderAppsResource.java +++ b/contrib/views/slider/src/main/java/org/apache/ambari/view/slider/rest/SliderAppsResource.java @@ -26,6 +26,7 @@ import javax.ws.rs.Consumes; import javax.ws.rs.DELETE; import javax.ws.rs.GET; import javax.ws.rs.POST; +import javax.ws.rs.PUT; import javax.ws.rs.Path; import javax.ws.rs.PathParam; import javax.ws.rs.Produces; @@ -41,6 +42,7 @@ import org.apache.hadoop.yarn.exceptions.YarnException; import org.apache.log4j.Logger; import com.google.gson.JsonElement; +import com.google.gson.JsonObject; import com.google.gson.JsonParser; import com.google.inject.Inject; @@ -74,8 +76,37 @@ public class SliderAppsResource { sliderAppsViewController.deleteSliderApp(appId); } + @PUT + @Path("{appId}") + @Consumes({ MediaType.TEXT_PLAIN, MediaType.APPLICATION_JSON }) + public Response updateApp(@Context UriInfo uri, String jsonString, + @PathParam("appId") String appId) throws IOException, YarnException, + InterruptedException, URISyntaxException { + if (jsonString != null) { + JsonElement requestContent = new JsonParser().parse(jsonString); + if (requestContent != null && appId != null) { + JsonObject requestJson = requestContent.getAsJsonObject(); + if (requestJson.has("state")) { + String newState = requestJson.get("state").getAsString(); + if ("FROZEN".equals(newState)) + sliderAppsViewController.freezeApp(appId); + else if ("RUNNING".equals(newState)) + sliderAppsViewController.thawApp(appId); + } else if (requestJson.has("components")) { + } + } + String sliderApp = sliderAppsViewController + .createSliderApp(requestContent.getAsJsonObject()); + if (sliderApp != null) + return Response.created(new URI(uri.getAbsolutePath() + sliderApp)) + .build(); + } + logger.warn("No request content sent to create app"); + return Response.status(Response.Status.BAD_REQUEST).build(); + } + @POST - @Consumes({ MediaType.TEXT_PLAIN }) + @Consumes({ MediaType.TEXT_PLAIN, MediaType.APPLICATION_JSON }) public Response createApp(@Context UriInfo uri, String jsonString) throws IOException, YarnException, InterruptedException, URISyntaxException { @@ -84,8 +115,8 @@ public class SliderAppsResource { String sliderApp = sliderAppsViewController .createSliderApp(requestContent.getAsJsonObject()); if (sliderApp != null) - return Response.created( - new URI(uri.getAbsolutePath() + "/" + sliderApp)).build(); + return Response.created(new URI(uri.getAbsolutePath() + sliderApp)) + .build(); } logger.warn("No request content sent to create app"); return Response.status(Response.Status.BAD_REQUEST).build(); http://git-wip-us.apache.org/repos/asf/ambari/blob/53e3d8dd/contrib/views/slider/src/main/resources/ui/app/controllers/slider_app_controller.js ---------------------------------------------------------------------- diff --git a/contrib/views/slider/src/main/resources/ui/app/controllers/slider_app_controller.js b/contrib/views/slider/src/main/resources/ui/app/controllers/slider_app_controller.js index 6a383c5..06af9f3 100644 --- a/contrib/views/slider/src/main/resources/ui/app/controllers/slider_app_controller.js +++ b/contrib/views/slider/src/main/resources/ui/app/controllers/slider_app_controller.js @@ -83,12 +83,99 @@ App.SliderAppController = Ember.ObjectController.extend({ this[currentAction](); }, - thaw: Ember.K, - freeze: Ember.K, - flex: Ember.K, + /** + * Do request to <strong>thaw</strong> current slider's app + * @returns {$.ajax} + * @method freeze + */ + thaw: function() { + var model = this.get('model'); + return App.ajax.send({ + name: 'changeAppState', + sender: this, + data: { + id: model.get('id'), + data: { + id: model.get('id'), + name: model.get('name'), + state: "RUNNING" + } + } + }); + }, /** * Do request to delete current slider's app + * Do request to <strong>freeze</strong> current slider's app + * @returns {$.ajax} + * @method freeze + */ + freeze: function() { + var model = this.get('model'); + return App.ajax.send({ + name: 'changeAppState', + sender: this, + data: { + id: model.get('id'), + data: { + id: model.get('id'), + name: model.get('name'), + state: "FROZEN" + } + } + }); + }, + + /** + * Do request to <strong>flex</strong> current slider's app + * @returns {$.ajax} + * @method flex + */ + flex: function() { + var model = this.get('model'); + return App.ajax.send({ + name: 'flexApp', + sender: this, + data: { + id: model.get('id'), + data: { + id: model.get('id'), + name: model.get('name'), + components: this.mapComponentsForFlexRequest() + } + } + }); + }, + + /** + * Map <code>model.components</code> for Flex request + * Output format: + * <code> + * { + * COMPONENT_NAME_1: { + * instanceCount: 1 + * }, + * COMPONENT_NAME_2: { + * instanceCount: 2 + * }, + * .... + * } + * </code> + * @returns {object} + * @method mapComponentsForFlexRequest + */ + mapComponentsForFlexRequest: function() { + var components = {}; + this.get('model.components').forEach(function(component) { + components[component.get('name')] = { + instanceCount: component.get('defaultNumInstances') + } + }); + return components; + }, + + /** + * Do request to <strong>delete</strong> current slider's app * @return {$.ajax} * @method destroy */ http://git-wip-us.apache.org/repos/asf/ambari/blob/53e3d8dd/contrib/views/slider/src/main/resources/ui/app/helpers/ajax.js ---------------------------------------------------------------------- diff --git a/contrib/views/slider/src/main/resources/ui/app/helpers/ajax.js b/contrib/views/slider/src/main/resources/ui/app/helpers/ajax.js index 05e78db..66b97bd 100644 --- a/contrib/views/slider/src/main/resources/ui/app/helpers/ajax.js +++ b/contrib/views/slider/src/main/resources/ui/app/helpers/ajax.js @@ -56,6 +56,9 @@ var urls = { 'createNewApp': { real: 'apps', mock: '', + headers: { + "Content-Type": "text/plain; charset=utf-8" + }, format: function(data) { return { type: 'POST', @@ -72,6 +75,33 @@ var urls = { method: 'DELETE' } } + }, + + 'changeAppState': { + real: 'apps/{id}', + mock: '', + headers: { + "Content-Type": "text/plain; charset=utf-8" + }, + format: function(data) { + return { + method: 'PUT', + data: JSON.stringify(data.data) + } + } + }, + 'flexApp': { + real: 'apps/{id}', + mock: '', + headers: { + "Content-Type": "text/plain; charset=utf-8" + }, + format: function(data) { + return { + method: 'PUT', + data: JSON.stringify(data.data) + } + } } }; /** @@ -110,7 +140,7 @@ var formatRequest = function (data) { type: this.type || 'GET', dataType: 'json', async: true, - headers: this.headers || {accepts: "application/json; charset=utf-8"} + headers: this.headers || {Accept: "application/json; charset=utf-8"} }; if (App.get('testMode')) { opt.url = formatUrl(this.mock ? this.mock : '', data); http://git-wip-us.apache.org/repos/asf/ambari/blob/53e3d8dd/contrib/views/slider/src/main/resources/ui/app/initialize.js ---------------------------------------------------------------------- diff --git a/contrib/views/slider/src/main/resources/ui/app/initialize.js b/contrib/views/slider/src/main/resources/ui/app/initialize.js index 73f4c6f..cc22ba2 100755 --- a/contrib/views/slider/src/main/resources/ui/app/initialize.js +++ b/contrib/views/slider/src/main/resources/ui/app/initialize.js @@ -102,4 +102,11 @@ folderOrder.forEach(function(folder) { }).forEach(function(module) { require(module); }); +}); + +$.ajaxSetup({ + cache : false, + headers : { + "X-Requested-By" : "X-Requested-By" + } }); \ No newline at end of file http://git-wip-us.apache.org/repos/asf/ambari/blob/53e3d8dd/contrib/views/slider/src/main/resources/ui/app/routes/main.js ---------------------------------------------------------------------- diff --git a/contrib/views/slider/src/main/resources/ui/app/routes/main.js b/contrib/views/slider/src/main/resources/ui/app/routes/main.js index 07babda..f875ffa 100644 --- a/contrib/views/slider/src/main/resources/ui/app/routes/main.js +++ b/contrib/views/slider/src/main/resources/ui/app/routes/main.js @@ -18,10 +18,6 @@ App.IndexRoute = Ember.Route.extend({ - model: function () { - return this.modelFor('sliderApps'); - }, - redirect: function () { this.transitionTo('slider_apps'); } @@ -44,7 +40,7 @@ App.SliderAppsRoute = Ember.Route.extend({ App.SliderAppRoute = Ember.Route.extend({ model: function(params) { - return this.store.all('sliderApp', params.slider_app_id); + return this.store.find('sliderApp', params.slider_app_id); } }); \ No newline at end of file
