Browse Source

AMBARI-6519. View: Capacity Scheduler view. (jaimin)

Jaimin Jetly 11 years ago
parent
commit
bb0116c3ce
74 changed files with 4818 additions and 0 deletions
  1. 230 0
      contrib/views/capacity-scheduler/pom.xml
  2. 26 0
      contrib/views/capacity-scheduler/src/assembly/assembly.xml
  3. 50 0
      contrib/views/capacity-scheduler/src/main/java/org/apache/ambari/view/capacityscheduler/CapacitySchedulerService.java
  4. 276 0
      contrib/views/capacity-scheduler/src/main/java/org/apache/ambari/view/capacityscheduler/ConfigurationService.java
  5. 70 0
      contrib/views/capacity-scheduler/src/main/java/org/apache/ambari/view/capacityscheduler/HelpService.java
  6. 120 0
      contrib/views/capacity-scheduler/src/main/java/org/apache/ambari/view/capacityscheduler/proxy/Proxy.java
  7. 157 0
      contrib/views/capacity-scheduler/src/main/java/org/apache/ambari/view/capacityscheduler/proxy/RequestBuilder.java
  8. 88 0
      contrib/views/capacity-scheduler/src/main/java/org/apache/ambari/view/capacityscheduler/proxy/ResponseTranslator.java
  9. 46 0
      contrib/views/capacity-scheduler/src/main/java/org/apache/ambari/view/capacityscheduler/utils/MisconfigurationFormattedException.java
  10. 54 0
      contrib/views/capacity-scheduler/src/main/java/org/apache/ambari/view/capacityscheduler/utils/ServiceFormattedException.java
  11. 34 0
      contrib/views/capacity-scheduler/src/main/resources/ui/.gitignore
  12. 513 0
      contrib/views/capacity-scheduler/src/main/resources/ui/app/adapters.js
  13. 19 0
      contrib/views/capacity-scheduler/src/main/resources/ui/app/app.js
  14. 90 0
      contrib/views/capacity-scheduler/src/main/resources/ui/app/assets/data/capacity-scheduler.json
  15. 13 0
      contrib/views/capacity-scheduler/src/main/resources/ui/app/assets/data/cs-tag.json
  16. 7 0
      contrib/views/capacity-scheduler/src/main/resources/ui/app/assets/data/refreshqueues.json
  17. 18 0
      contrib/views/capacity-scheduler/src/main/resources/ui/app/assets/data/resourcemanager.json
  18. 90 0
      contrib/views/capacity-scheduler/src/main/resources/ui/app/assets/data/scheduler-configuration.json
  19. 15 0
      contrib/views/capacity-scheduler/src/main/resources/ui/app/assets/data/view-instance.json
  20. 195 0
      contrib/views/capacity-scheduler/src/main/resources/ui/app/assets/fonts/fontawesome-webfont.svg
  21. 48 0
      contrib/views/capacity-scheduler/src/main/resources/ui/app/assets/index.html
  22. 27 0
      contrib/views/capacity-scheduler/src/main/resources/ui/app/components.js
  23. 42 0
      contrib/views/capacity-scheduler/src/main/resources/ui/app/components/capacityBar.js
  24. 94 0
      contrib/views/capacity-scheduler/src/main/resources/ui/app/components/capacityInput.js
  25. 87 0
      contrib/views/capacity-scheduler/src/main/resources/ui/app/components/confirmDelete.js
  26. 48 0
      contrib/views/capacity-scheduler/src/main/resources/ui/app/components/escapeAcl.js
  27. 61 0
      contrib/views/capacity-scheduler/src/main/resources/ui/app/components/pathInput.js
  28. 42 0
      contrib/views/capacity-scheduler/src/main/resources/ui/app/components/queueListItem.js
  29. 46 0
      contrib/views/capacity-scheduler/src/main/resources/ui/app/components/radioButton.js
  30. 98 0
      contrib/views/capacity-scheduler/src/main/resources/ui/app/components/totalCapacity.js
  31. 44 0
      contrib/views/capacity-scheduler/src/main/resources/ui/app/components/userGroupInput.js
  32. 21 0
      contrib/views/capacity-scheduler/src/main/resources/ui/app/controllers.js
  33. 186 0
      contrib/views/capacity-scheduler/src/main/resources/ui/app/controllers/queue.js
  34. 154 0
      contrib/views/capacity-scheduler/src/main/resources/ui/app/controllers/queues.js
  35. 26 0
      contrib/views/capacity-scheduler/src/main/resources/ui/app/controllers/trace.js
  36. 40 0
      contrib/views/capacity-scheduler/src/main/resources/ui/app/initialize.js
  37. 19 0
      contrib/views/capacity-scheduler/src/main/resources/ui/app/models.js
  38. 64 0
      contrib/views/capacity-scheduler/src/main/resources/ui/app/models/queue.js
  39. 122 0
      contrib/views/capacity-scheduler/src/main/resources/ui/app/router.js
  40. 238 0
      contrib/views/capacity-scheduler/src/main/resources/ui/app/styles/application.less
  41. 36 0
      contrib/views/capacity-scheduler/src/main/resources/ui/app/templates.js
  42. 34 0
      contrib/views/capacity-scheduler/src/main/resources/ui/app/templates/capacityEditForm.hbs
  43. 35 0
      contrib/views/capacity-scheduler/src/main/resources/ui/app/templates/components/capacityBar.hbs
  44. 29 0
      contrib/views/capacity-scheduler/src/main/resources/ui/app/templates/components/pathInput.hbs
  45. 60 0
      contrib/views/capacity-scheduler/src/main/resources/ui/app/templates/components/queueListItem.hbs
  46. 72 0
      contrib/views/capacity-scheduler/src/main/resources/ui/app/templates/components/totalCapacity.hbs
  47. 37 0
      contrib/views/capacity-scheduler/src/main/resources/ui/app/templates/components/userGroupInput.hbs
  48. 35 0
      contrib/views/capacity-scheduler/src/main/resources/ui/app/templates/error.hbs
  49. 19 0
      contrib/views/capacity-scheduler/src/main/resources/ui/app/templates/loading.hbs
  50. 229 0
      contrib/views/capacity-scheduler/src/main/resources/ui/app/templates/queue.hbs
  51. 85 0
      contrib/views/capacity-scheduler/src/main/resources/ui/app/templates/queues.hbs
  52. 20 0
      contrib/views/capacity-scheduler/src/main/resources/ui/app/templates/queues/index.hbs
  53. 59 0
      contrib/views/capacity-scheduler/src/main/resources/ui/app/templates/schedulerPanel.hbs
  54. 21 0
      contrib/views/capacity-scheduler/src/main/resources/ui/app/templates/trace.hbs
  55. 32 0
      contrib/views/capacity-scheduler/src/main/resources/ui/bower.json
  56. 72 0
      contrib/views/capacity-scheduler/src/main/resources/ui/config.coffee
  57. 23 0
      contrib/views/capacity-scheduler/src/main/resources/ui/generators/collection/collection.js.hbs
  58. 9 0
      contrib/views/capacity-scheduler/src/main/resources/ui/generators/collection/generator.json
  59. 23 0
      contrib/views/capacity-scheduler/src/main/resources/ui/generators/component/component.js.hbs
  60. 9 0
      contrib/views/capacity-scheduler/src/main/resources/ui/generators/component/generator.json
  61. 23 0
      contrib/views/capacity-scheduler/src/main/resources/ui/generators/controller/controller.js.hbs
  62. 9 0
      contrib/views/capacity-scheduler/src/main/resources/ui/generators/controller/generator.json
  63. 9 0
      contrib/views/capacity-scheduler/src/main/resources/ui/generators/model/generator.json
  64. 23 0
      contrib/views/capacity-scheduler/src/main/resources/ui/generators/model/model.js.hbs
  65. 9 0
      contrib/views/capacity-scheduler/src/main/resources/ui/generators/route/generator.json
  66. 25 0
      contrib/views/capacity-scheduler/src/main/resources/ui/generators/route/route.js.hbs
  67. 9 0
      contrib/views/capacity-scheduler/src/main/resources/ui/generators/template/generator.json
  68. 18 0
      contrib/views/capacity-scheduler/src/main/resources/ui/generators/template/template.hbs.hbs
  69. 9 0
      contrib/views/capacity-scheduler/src/main/resources/ui/generators/view/generator.json
  70. 23 0
      contrib/views/capacity-scheduler/src/main/resources/ui/generators/view/view.js.hbs
  71. 37 0
      contrib/views/capacity-scheduler/src/main/resources/ui/package.json
  72. 17 0
      contrib/views/capacity-scheduler/src/main/resources/ui/test/spec.coffee
  73. 49 0
      contrib/views/capacity-scheduler/src/main/resources/view.xml
  74. 1 0
      contrib/views/pom.xml

+ 230 - 0
contrib/views/capacity-scheduler/pom.xml

@@ -0,0 +1,230 @@
+<!--
+   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.
+-->
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+     xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+    <modelVersion>4.0.0</modelVersion>
+    <groupId>org.apache.ambari.view</groupId>
+    <artifactId>capacity-scheduler</artifactId>
+    <version>0.0.1-SNAPSHOT</version>
+    <name>Capacity Scheduler</name>
+
+    <parent>
+        <groupId>org.apache.ambari.views</groupId>
+        <artifactId>ambari-contrib-views</artifactId>
+        <version>0.1.0-SNAPSHOT</version>
+    </parent>
+
+    <dependencies>
+        <dependency>
+            <groupId>com.google.inject</groupId>
+            <artifactId>guice</artifactId>
+            <version>4.0-beta</version> <!-- version -->
+        </dependency>
+        <dependency>
+            <groupId>org.glassfish.jersey.containers</groupId>
+            <artifactId>jersey-container-servlet</artifactId>
+            <version>2.6</version>  <!-- version -->
+        </dependency>
+        <dependency>
+            <groupId>org.slf4j</groupId>
+            <artifactId>slf4j-api</artifactId>
+            <version>1.7.5</version>
+        </dependency>
+        <dependency>
+            <groupId>commons-io</groupId>
+            <artifactId>commons-io</artifactId>
+            <version>2.4</version>
+        </dependency>
+        <dependency>
+            <groupId>commons-lang</groupId>
+            <artifactId>commons-lang</artifactId>
+            <version>2.2</version>
+        </dependency>
+        <dependency>
+            <groupId>com.googlecode.json-simple</groupId>
+            <artifactId>json-simple</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>commons-codec</groupId>
+            <artifactId>commons-codec</artifactId>
+            <version>1.9</version>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.ambari</groupId>
+            <artifactId>ambari-views</artifactId>
+            <scope>provided</scope>
+        </dependency>
+    </dependencies>
+
+    <properties>
+      <ui.directory>${basedir}/src/main/resources/ui</ui.directory>
+    </properties>
+
+    <build>
+    <plugins>
+      <plugin>
+        <groupId>com.github.eirslett</groupId>
+        <artifactId>frontend-maven-plugin</artifactId>
+        <version>0.0.14</version>
+        <configuration>
+            <workingDirectory>src/main/resources/ui</workingDirectory>
+        </configuration>
+
+        <executions>
+          <execution>
+            <id>install node and npm</id>
+            <goals>
+                <goal>install-node-and-npm</goal>
+            </goals>
+            <!-- optional: default phase is "generate-resources" -->
+            <phase>generate-resources</phase>
+            <configuration>
+                <nodeVersion>v0.10.26</nodeVersion>
+                <npmVersion>1.4.3</npmVersion>
+            </configuration>
+          </execution>
+          <execution>
+          <id>npm install</id>
+            <goals>
+              <goal>npm</goal>
+            </goals>
+
+            <phase>generate-resources</phase>
+
+            <configuration>
+                <arguments>install --unsafe-perm --registry=http://registry.npmjs.eu</arguments>
+            </configuration>
+          </execution>
+        </executions>
+      </plugin>
+      <plugin>
+        <artifactId>exec-maven-plugin</artifactId>
+        <groupId>org.codehaus.mojo</groupId>
+        <version>1.2.1</version>
+        <executions>
+            <execution>
+            <id>Brunch build</id>
+            <phase>generate-resources</phase>
+            <goals>
+              <goal>exec</goal>
+            </goals>
+            <configuration>
+              <workingDirectory>${basedir}/src/main/resources/ui</workingDirectory>
+              <executable>node/node</executable>
+              <arguments>
+                <argument>node_modules/.bin/brunch</argument>
+                <argument>build</argument>
+                <argument>--production</argument>
+              </arguments>
+            </configuration>
+            </execution>
+        </executions>
+      </plugin>
+      <plugin>
+        <artifactId>maven-dependency-plugin</artifactId>
+        <executions>
+            <execution>
+                <phase>generate-resources</phase>
+                <goals>
+                    <goal>copy-dependencies</goal>
+                </goals>
+                <configuration>
+                    <outputDirectory>${project.build.directory}/lib</outputDirectory>
+                    <includeScope>runtime</includeScope>
+                </configuration>
+            </execution>
+        </executions>
+      </plugin>
+    </plugins>
+    <resources>
+        <resource>
+        <directory>src/main/resources/ui/public</directory>
+        <filtering>false</filtering>
+        </resource>
+
+        <resource>
+        <directory>src/main/resources/</directory>
+        <filtering>false</filtering>
+        <includes>
+            <include>view.xml</include>
+        </includes>
+        </resource>
+        <resource>
+            <targetPath>WEB-INF/lib</targetPath>
+            <filtering>false</filtering>
+            <directory>target/lib</directory>
+        </resource>
+    </resources>
+    <pluginManagement>
+        <plugins>
+        <!--This plugin's configuration is used to store Eclipse m2e settings only. It has no influence on the Maven build itself.-->
+        <plugin>
+            <groupId>org.eclipse.m2e</groupId>
+            <artifactId>lifecycle-mapping</artifactId>
+            <version>1.0.0</version>
+            <configuration>
+            <lifecycleMappingMetadata>
+                <pluginExecutions>
+                <pluginExecution>
+                    <pluginExecutionFilter>
+                    <groupId>
+                        org.codehaus.mojo
+                    </groupId>
+                    <artifactId>
+                        exec-maven-plugin
+                    </artifactId>
+                    <versionRange>
+                        [1.2.1,)
+                    </versionRange>
+                    <goals>
+                        <goal>exec</goal>
+                    </goals>
+                    </pluginExecutionFilter>
+                    <action>
+                    <ignore></ignore>
+                    </action>
+                </pluginExecution>
+                <pluginExecution>
+                    <pluginExecutionFilter>
+                    <groupId>
+                        com.github.eirslett
+                    </groupId>
+                    <artifactId>
+                        frontend-maven-plugin
+                    </artifactId>
+                    <versionRange>
+                        [0.0.14,)
+                    </versionRange>
+                    <goals>
+                        <goal>
+                        install-node-and-npm
+                        </goal>
+                        <goal>npm</goal>
+                    </goals>
+                    </pluginExecutionFilter>
+                    <action>
+                    <ignore></ignore>
+                    </action>
+                </pluginExecution>
+                </pluginExecutions>
+            </lifecycleMappingMetadata>
+            </configuration>
+        </plugin>
+        </plugins>
+    </pluginManagement>
+    </build>
+</project>

+ 26 - 0
contrib/views/capacity-scheduler/src/assembly/assembly.xml

@@ -0,0 +1,26 @@
+<!--
+   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.
+-->
+<assembly
+	xmlns="http://maven.apache.org/plugins/maven-assembly-plugin/assembly/1.1.0"
+	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+	xsi:schemaLocation="http://maven.apache.org/plugins/maven-assembly-plugin/assembly/1.1.0 http://maven.apache.org/xsd/assembly-1.1.0.xsd">
+	<containerDescriptorHandlers>
+		<containerDescriptorHandler>
+			<handlerName>metaInf-services</handlerName>
+		</containerDescriptorHandler>
+	</containerDescriptorHandlers>	
+</assembly>

+ 50 - 0
contrib/views/capacity-scheduler/src/main/java/org/apache/ambari/view/capacityscheduler/CapacitySchedulerService.java

@@ -0,0 +1,50 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.ambari.view.capacityscheduler;
+
+import javax.ws.rs.Path;
+import org.apache.ambari.view.ViewContext;
+import com.google.inject.Inject;
+
+/**
+ * Root capacity-scheduler service
+ */
+public class CapacitySchedulerService {
+
+  @Inject
+  ViewContext context;
+
+  /**
+   * @see org.apache.ambari.view.capacityscheduler.HelpService
+   * @return service
+   */
+  @Path("/help")
+  public HelpService help() {
+    return new HelpService(context);
+  }
+
+  /**
+   * @see org.apache.ambari.view.capacityscheduler.ConfigurationService
+   * @return service
+   */
+  @Path("/configuration")
+  public ConfigurationService configuration() {
+    return new ConfigurationService(context);
+  }
+}

+ 276 - 0
contrib/views/capacity-scheduler/src/main/java/org/apache/ambari/view/capacityscheduler/ConfigurationService.java

@@ -0,0 +1,276 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.ambari.view.capacityscheduler;
+
+import org.apache.ambari.view.ViewContext;
+import org.apache.ambari.view.capacityscheduler.utils.MisconfigurationFormattedException;
+import org.apache.ambari.view.capacityscheduler.proxy.Proxy;
+import org.apache.ambari.view.capacityscheduler.utils.ServiceFormattedException;
+import org.json.simple.JSONArray;
+import org.json.simple.JSONObject;
+import org.json.simple.JSONValue;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import javax.ws.rs.*;
+import javax.ws.rs.core.MediaType;
+import javax.ws.rs.core.Response;
+import java.util.HashMap;
+
+/**
+ * Help service
+ */
+public class ConfigurationService {
+
+  private static final Logger LOG = LoggerFactory.getLogger(ConfigurationService.class);
+  private final Proxy proxy;
+  private final String baseUrl;
+
+  private ViewContext context;
+  private final String refreshRMRequestData =
+      "{\n" +
+      "  \"RequestInfo\" : {\n" +
+      "    \"command\" : \"REFRESHQUEUES\",\n" +
+      "    \"context\" : \"Refresh YARN Capacity Scheduler\"\n" +
+      "  },\n" +
+      "  \"Requests/resource_filters\": [{\n" +
+      "    \"service_name\" : \"YARN\",\n" +
+      "    \"component_name\" : \"RESOURCEMANAGER\",\n" +
+      "    \"hosts\" : \"%s\"\n" +
+      "  }]\n" +
+      "}";
+  private final String restartRMRequestData = "{\"RequestInfo\": {\n" +
+      "    \"command\":\"RESTART\",\n" +
+      "    \"context\":\"Restart ResourceManager\",\n" +
+      "    \"operation_level\": {\n" +
+      "        \"level\":\"HOST_COMPONENT\",\n" +
+      "        \"cluster_name\":\"MyCluster\",\n" +
+      "        \"host_name\":\"%s\",\n" +
+      "        \"service_name\":\"YARN\",\n" +
+      "        \"hostcomponent_name\":\"RESOURCEMANAGER\"\n" +
+      "        }\n" +
+      "    },\n" +
+      "    \"Requests/resource_filters\": [\n" +
+      "        {\n" +
+      "            \"service_name\":\"YARN\",\n" +
+      "            \"component_name\":\"RESOURCEMANAGER\",\n" +
+      "            \"hosts\":\"%s\"\n" +
+      "        }\n" +
+      "    ]\n" +
+      "}\n";
+
+  /**
+   * Constructor
+   * @param context View Context instance
+   */
+  public ConfigurationService(ViewContext context) {
+    this.context = context;
+
+    proxy = new Proxy(context.getURLStreamProvider());
+    proxy.setUseAuthorization(true);
+    proxy.setUsername(context.getProperties().get("ambari.server.username"));
+    proxy.setPassword(context.getProperties().get("ambari.server.password"));
+
+    HashMap<String, String> customHeaders = new HashMap<String, String>();
+    customHeaders.put("X-Requested-By", "capacity-scheduler-app");
+    proxy.setCustomHeaders(customHeaders);
+
+    baseUrl = context.getProperties().get("ambari.server.url");
+  }
+
+  // ================================================================================
+  // Configuration Reading
+  // ================================================================================
+
+  private final String versionTagUrl = "%s?fields=Clusters/desired_configs/capacity-scheduler";
+  private final String configurationUrl = "%%s/configurations?type=capacity-scheduler&tag=%s";
+  private final String rmHostUrl = "%s/services/YARN/components/RESOURCEMANAGER?fields=host_components/host_name";
+
+  /**
+   * Get capacity scheduler configuration
+   * @return scheduler configuration
+   */
+  @GET
+  @Produces(MediaType.APPLICATION_JSON)
+  public Response readConfiguration() {
+    Response response = null;
+    try {
+      validateConfig();
+      String versionTag = getVersionTag();
+      JSONObject configurations = getConfigurationFromAmbari(versionTag);
+      response = Response.ok(configurations).build();
+    } catch (WebApplicationException ex) {
+      throw ex;
+    } catch (Exception ex) {
+      throw new ServiceFormattedException(ex.getMessage(), ex);
+    }
+
+    return response;
+  }
+
+  private void validateConfig() {
+    String hostname = context.getProperties().get("ambari.server.url");
+    if (hostname == null)
+      throw new MisconfigurationFormattedException("ambari.server.url");
+
+    String username = context.getProperties().get("ambari.server.username");
+    if (username == null)
+      throw new MisconfigurationFormattedException("ambari.server.username");
+
+    String password = context.getProperties().get("ambari.server.password");
+    if (password == null)
+      throw new MisconfigurationFormattedException("ambari.server.password");
+  }
+
+  private JSONObject getConfigurationFromAmbari(String versionTag) {
+    String urlTemplate = String.format(configurationUrl, versionTag);
+    String url = String.format(urlTemplate, baseUrl);
+    return proxy.request(url).get().asJSON();
+  }
+
+  private String getVersionTag() {
+    JSONObject json = getDesiredConfigs();
+    JSONObject clusters = (JSONObject) json.get("Clusters");
+    JSONObject configs = (JSONObject) clusters.get("desired_configs");
+    JSONObject scheduler = (JSONObject) configs.get("capacity-scheduler");
+    return (String) scheduler.get("tag");
+  }
+
+  private String getClusterName() {
+    JSONObject json = getDesiredConfigs();
+    JSONObject clusters = (JSONObject) json.get("Clusters");
+    return (String) clusters.get("cluster_name");
+  }
+
+  private JSONObject getDesiredConfigs() {
+    String url = String.format(versionTagUrl, baseUrl);
+    return proxy.request(url).get().asJSON();
+  }
+
+  // ================================================================================
+  // Configuration Writing
+  // ================================================================================
+  /**
+   * Set capacity scheduler configuration
+   * @return http response
+   */
+  @PUT
+  @Consumes(MediaType.APPLICATION_JSON)
+  @Produces(MediaType.APPLICATION_JSON)
+  public Response writeConfiguration(JSONObject request) {
+    try {
+      validateConfig();
+
+      proxy.request(baseUrl).
+            setData(makeConfigUpdateData(request)).
+            put();
+
+    } catch (WebApplicationException ex) {
+      throw ex;
+    } catch (Exception ex) {
+      throw new ServiceFormattedException(ex.getMessage(), ex);
+    }
+
+    return readConfiguration();
+  }
+
+  /**
+   * Set capacity scheduler configuration and refresh RM
+   * @return http response
+   */
+  @PUT
+  @Consumes(MediaType.APPLICATION_JSON)
+  @Produces(MediaType.APPLICATION_JSON)
+  @Path("/saveAndRefresh")
+  public Response writeAndRefreshConfiguration(JSONObject request) {
+    try {
+
+      writeConfiguration(request);
+
+      String rmHost = getRMHost();
+      JSONObject data = (JSONObject) JSONValue.parse(String.format(refreshRMRequestData, rmHost));
+      proxy.request(baseUrl + "/requests/").
+          setData(data).
+          post();
+
+    } catch (WebApplicationException ex) {
+      throw ex;
+    } catch (Exception ex) {
+      throw new ServiceFormattedException(ex.getMessage(), ex);
+    }
+    return readConfiguration();
+  }
+
+  /**
+   * Set capacity scheduler configuration and restart RM
+   * @return http response
+   */
+  @PUT
+  @Consumes(MediaType.APPLICATION_JSON)
+  @Produces(MediaType.APPLICATION_JSON)
+  @Path("/saveAndRestart")
+  public Response writeAndRestartConfiguration(JSONObject request) {
+    try {
+
+      writeConfiguration(request);
+
+      String rmHost = getRMHost();
+      JSONObject data = (JSONObject) JSONValue.parse(String.format(restartRMRequestData, rmHost, rmHost));
+      proxy.request(baseUrl + "/requests/").
+          setData(data).
+          post();
+
+    } catch (WebApplicationException ex) {
+      throw ex;
+    } catch (Exception ex) {
+      throw new ServiceFormattedException(ex.getMessage(), ex);
+    }
+    return readConfiguration();
+  }
+
+  private String getRMHost() {
+    String rmHost = null;
+    JSONObject rmData = proxy.request(String.format(rmHostUrl, baseUrl)).get().asJSON();
+    JSONArray components = (JSONArray) rmData.get("host_components");
+    for(Object component : components) {
+      JSONObject roles = (JSONObject) ((JSONObject) component).get("HostRoles");
+      if (roles.get("component_name").equals("RESOURCEMANAGER")) {
+        rmHost = (String) roles.get("host_name");
+        break;
+      }
+    }
+    if (rmHost == null)
+      throw new ServiceFormattedException("Can't retrieve Resource Manager Host");
+    return rmHost;
+  }
+
+  private JSONObject makeConfigUpdateData(JSONObject request) {
+    JSONObject desiredConfigs = (JSONObject) request.clone();
+    desiredConfigs.put("type", "capacity-scheduler");
+    desiredConfigs.put("tag", "version" + String.valueOf(System.currentTimeMillis()));
+
+    JSONObject clusters = new JSONObject();
+    clusters.put("desired_configs", desiredConfigs);
+
+    JSONObject data = new JSONObject();
+    data.put("Clusters", clusters);
+    return data;
+  }
+
+}

+ 70 - 0
contrib/views/capacity-scheduler/src/main/java/org/apache/ambari/view/capacityscheduler/HelpService.java

@@ -0,0 +1,70 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.ambari.view.capacityscheduler;
+
+import org.apache.ambari.view.ViewContext;
+
+import javax.ws.rs.GET;
+import javax.ws.rs.Path;
+import javax.ws.rs.Produces;
+import javax.ws.rs.core.MediaType;
+import javax.ws.rs.core.Response;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * Scheduler configuration service
+ */
+public class HelpService {
+
+  private static final Logger logger = LoggerFactory.getLogger(HelpService.class);
+  private ViewContext context;
+
+  /**
+   * Constructor
+   * @param context View Context instance
+   */
+  public HelpService(ViewContext context) {
+    this.context = context;
+  }
+
+  /**
+   * Version
+   * @return version
+   */
+  @GET
+  @Path("/version")
+  @Produces(MediaType.TEXT_PLAIN)
+  public Response version() {
+    return Response.ok("0.0.1-SNAPSHOT").build();
+  }
+
+  /**
+   * Description
+   * @return description
+   */
+  @GET
+  @Path("/description")
+  @Produces(MediaType.TEXT_PLAIN)
+  public Response description() {
+    return Response.ok("Application to manage YARN Capacity Scheduler").build();
+  }
+
+}

+ 120 - 0
contrib/views/capacity-scheduler/src/main/java/org/apache/ambari/view/capacityscheduler/proxy/Proxy.java

@@ -0,0 +1,120 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.ambari.view.capacityscheduler.proxy;
+
+import org.apache.ambari.view.URLStreamProvider;
+import org.apache.ambari.view.capacityscheduler.utils.ServiceFormattedException;
+import org.apache.commons.codec.binary.Base64;
+import org.apache.commons.io.IOUtils;
+import org.json.simple.JSONObject;
+import org.json.simple.JSONValue;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * Proxy with ability to make authorized request
+ * with simple authorization headers
+ */
+public class Proxy {
+  private static final Logger LOG = LoggerFactory.getLogger(Proxy.class);
+
+  private URLStreamProvider urlStreamProvider;
+  private boolean useAuthorization = false;
+  private String username;
+  private String password;
+  private Map<String, String> customHeaders;
+
+  /**
+   * Constructor
+   * @param urlStreamProvider url stream provider
+   */
+  public Proxy(URLStreamProvider urlStreamProvider) {
+    this.urlStreamProvider = urlStreamProvider;
+  }
+
+  /**
+   * Create RequestBuilder object with
+   * initialized proxy options
+   * @param url url
+   * @return RequestBuilder instance
+   */
+  public RequestBuilder request(String url) {
+    return new RequestBuilder(urlStreamProvider, url).
+                  setHeaders(makeHeaders());
+  }
+
+  private HashMap<String, String> makeHeaders() {
+    HashMap<String, String> headers = new HashMap<String, String>();
+    headers.putAll(customHeaders);
+
+    if (isUseAuthorization()) {
+      String authString = username + ":" + password;
+      byte[] authEncBytes = Base64.encodeBase64(authString.getBytes());
+      String authStringEnc = new String(authEncBytes);
+
+      headers.put("Authorization", "Basic " + authStringEnc);
+    }
+    return headers;
+  }
+
+  public boolean isUseAuthorization() {
+    return useAuthorization;
+  }
+
+  public void setUseAuthorization(boolean useAuthorization) {
+    this.useAuthorization = useAuthorization;
+  }
+
+  public String getUsername() {
+    return username;
+  }
+
+  public void setUsername(String username) {
+    this.username = username;
+  }
+
+  public String getPassword() {
+    return password;
+  }
+
+  public void setPassword(String password) {
+    this.password = password;
+  }
+
+  public URLStreamProvider getUrlStreamProvider() {
+    return urlStreamProvider;
+  }
+
+  public void setUrlStreamProvider(URLStreamProvider urlStreamProvider) {
+    this.urlStreamProvider = urlStreamProvider;
+  }
+
+  public Map<String, String> getCustomHeaders() {
+    return customHeaders;
+  }
+
+  public void setCustomHeaders(Map<String, String> customHeaders) {
+    this.customHeaders = customHeaders;
+  }
+}

+ 157 - 0
contrib/views/capacity-scheduler/src/main/java/org/apache/ambari/view/capacityscheduler/proxy/RequestBuilder.java

@@ -0,0 +1,157 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.ambari.view.capacityscheduler.proxy;
+
+import jersey.repackaged.com.google.common.collect.ImmutableMap;
+import org.apache.ambari.view.URLStreamProvider;
+import org.apache.ambari.view.capacityscheduler.utils.ServiceFormattedException;
+import org.json.simple.JSONObject;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.HashMap;
+
+/**
+ * Request builder with fluent interface
+ */
+public class RequestBuilder {
+  private static final Logger LOG = LoggerFactory.getLogger(RequestBuilder.class);
+
+  //request options
+  private String url = null;
+  private String method = null;
+  private String data = null;
+
+  private HashMap<String, String> headers = null;
+
+  private URLStreamProvider urlStreamProvider;
+
+  /**
+   * Constructor for RequestBuilder
+   * @param urlStreamProvider url stream provider
+   * @param url url
+   */
+  public RequestBuilder(URLStreamProvider urlStreamProvider, String url) {
+    this.urlStreamProvider = urlStreamProvider;
+    this.url = url;
+  }
+
+  /**
+   * Shortcut for making GET request
+   * @return ResponseTranslator object that encapsulates InputStream
+   */
+  public ResponseTranslator get() {
+    setMethod("GET");
+    return doRequest();
+  }
+
+  /**
+   * Shortcut for making PUT request
+   * @return ResponseTranslator object that encapsulates InputStream
+   */
+  public ResponseTranslator put() {
+    setMethod("PUT");
+    return doRequest();
+  }
+
+  /**
+   * Shortcut for making POST request
+   * @return ResponseTranslator object that encapsulates InputStream
+   */
+  public ResponseTranslator post() {
+    setMethod("POST");
+    return doRequest();
+  }
+
+  /**
+   * Shortcut for making DELETE request
+   * @return ResponseTranslator object that encapsulates InputStream
+   */
+  public ResponseTranslator delete() {
+    setMethod("DELETE");
+    return doRequest();
+  }
+
+  /**
+   * Make request
+   * @return ResponseTranslator object that encapsulates InputStream
+   */
+  public ResponseTranslator doRequest() {
+    LOG.debug(String.format("%s Request to %s", method, url));
+    InputStream inputStream = null;
+    try {
+      inputStream = urlStreamProvider.readFrom(url, method, data, headers);
+    } catch (IOException e) {
+      throw new ServiceFormattedException(e.getMessage(), e);
+    }
+    return new ResponseTranslator(inputStream);
+  }
+
+  public String getUrl() {
+    return url;
+  }
+
+  public RequestBuilder setUrl(String url) {
+    this.url = url;
+    return this;
+  }
+
+  public String getMethod() {
+    return method;
+  }
+
+  public RequestBuilder setMethod(String method) {
+    this.method = method;
+    return this;
+  }
+
+  public String getData() {
+    return data;
+  }
+
+  public RequestBuilder setData(String data) {
+    this.data = data;
+    return this;
+  }
+
+  public RequestBuilder setData(JSONObject data) {
+    this.data = data.toString();
+    return this;
+  }
+
+  public HashMap<String, String> getHeaders() {
+    return headers;
+  }
+
+  public RequestBuilder setHeaders(HashMap<String, String> headers) {
+    this.headers = headers;
+    return this;
+  }
+
+  public URLStreamProvider getUrlStreamProvider() {
+    return urlStreamProvider;
+  }
+
+  public RequestBuilder setUrlStreamProvider(URLStreamProvider urlStreamProvider) {
+    this.urlStreamProvider = urlStreamProvider;
+    return this;
+  }
+}

+ 88 - 0
contrib/views/capacity-scheduler/src/main/java/org/apache/ambari/view/capacityscheduler/proxy/ResponseTranslator.java

@@ -0,0 +1,88 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.ambari.view.capacityscheduler.proxy;
+
+import org.apache.ambari.view.capacityscheduler.utils.ServiceFormattedException;
+import org.apache.commons.io.IOUtils;
+import org.json.simple.JSONObject;
+import org.json.simple.JSONValue;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.io.IOException;
+import java.io.InputStream;
+
+/**
+ * Response translator encapsulates InputStream
+ * and able to convert it to different data types
+ */
+public class ResponseTranslator {
+  private static final Logger LOG = LoggerFactory.getLogger(RequestBuilder.class);
+
+  private InputStream inputStream;
+
+  /**
+   * Constructor for ResponseTranslator
+   * @param inputStream InputStream instance
+   */
+  public ResponseTranslator(InputStream inputStream) {
+    this.inputStream = inputStream;
+  }
+
+  /**
+   * Get InputStream of response from server for manual processing
+   * @return input stream of response
+   */
+  public InputStream asInputStream() {
+    return inputStream;
+  }
+
+  /**
+   * Retrieve response as String
+   * @return response as String
+   */
+  public String asString() {
+    String response;
+    try {
+      response = IOUtils.toString(inputStream);
+    } catch (IOException e) {
+      throw new ServiceFormattedException("Can't read from target host", e);
+    }
+    LOG.debug(String.format("Response: %s", response));
+
+    return response;
+  }
+
+  /**
+   * Retrieve response as JSON
+   * @return response as JSON
+   */
+  public JSONObject asJSON() {
+    String jsonString = asString();
+    return (JSONObject) JSONValue.parse(jsonString);
+  }
+
+  /**
+   * InputStream setter
+   * @param inputStream InputStream instance
+   */
+  public void setInputStream(InputStream inputStream) {
+    this.inputStream = inputStream;
+  }
+}

+ 46 - 0
contrib/views/capacity-scheduler/src/main/java/org/apache/ambari/view/capacityscheduler/utils/MisconfigurationFormattedException.java

@@ -0,0 +1,46 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.ambari.view.capacityscheduler.utils;
+
+import org.json.simple.JSONObject;
+
+import javax.ws.rs.WebApplicationException;
+import javax.ws.rs.core.MediaType;
+import javax.ws.rs.core.Response;
+import java.util.HashMap;
+
+/**
+ * Error on some Ambari View property was not set properly
+ */
+public class MisconfigurationFormattedException extends WebApplicationException {
+  private final static int STATUS = 500;
+  private final static String message = "Parameter \"%s\" is set to null";
+
+  public MisconfigurationFormattedException(String name) {
+    super(errorEntity(name));
+  }
+
+  protected static Response errorEntity(String name) {
+    HashMap<String, Object> response = new HashMap<String, Object>();
+    response.put("message", String.format(message, name));
+    response.put("trace", null);
+    response.put("status", STATUS);
+    return Response.status(STATUS).entity(new JSONObject(response)).type(MediaType.APPLICATION_JSON).build();
+  }
+}

+ 54 - 0
contrib/views/capacity-scheduler/src/main/java/org/apache/ambari/view/capacityscheduler/utils/ServiceFormattedException.java

@@ -0,0 +1,54 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.ambari.view.capacityscheduler.utils;
+
+import org.apache.commons.lang.exception.ExceptionUtils;
+import org.json.simple.JSONObject;
+
+import javax.ws.rs.WebApplicationException;
+import javax.ws.rs.core.MediaType;
+import javax.ws.rs.core.Response;
+import java.util.HashMap;
+
+/**
+ * General server error that provides verbose response
+ * to client with stack trace
+ */
+public class ServiceFormattedException extends WebApplicationException {
+  private final static int STATUS = 500;
+
+  public ServiceFormattedException(String message) {
+    this(message, null);
+  }
+
+  public ServiceFormattedException(String message, Throwable exception) {
+    super(errorEntity(message, exception));
+  }
+
+  protected static Response errorEntity(String message, Throwable e) {
+    HashMap<String, Object> response = new HashMap<String, Object>();
+    response.put("message", message);
+    String trace = null;
+    if (e != null)
+      trace = ExceptionUtils.getStackTrace(e);
+    response.put("trace", trace);
+    response.put("status", STATUS);
+    return Response.status(STATUS).entity(new JSONObject(response)).type(MediaType.APPLICATION_JSON).build();
+  }
+}

+ 34 - 0
contrib/views/capacity-scheduler/src/main/resources/ui/.gitignore

@@ -0,0 +1,34 @@
+# Numerous always-ignore extensions
+*.diff
+*.err
+*.orig
+*.log
+*.rej
+*.swo
+*.swp
+*.vi
+*~
+*.sass-cache
+
+# OS or Editor folders
+.DS_Store
+.cache
+.project
+.settings
+.tmproj
+nbproject
+Thumbs.db
+
+# NPM packages folder.
+node_modules/
+
+bower_components/
+
+node/
+
+# Brunch folder for temporary files.
+tmp/
+
+public/
+
+_generators/

+ 513 - 0
contrib/views/capacity-scheduler/src/main/resources/ui/app/adapters.js

@@ -0,0 +1,513 @@
+/**
+ * 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.
+ */
+
+var App = require('app');
+
+App.QueueAdapter = DS.Adapter.extend({
+  PREFIX: "yarn.scheduler.capacity",
+  queues: [],
+  clusterName: '',
+  tag: '',
+  createRecord: function(store, type, record) {
+    var data = this.serialize(record, { includeId: true });
+    return new Ember.RSVP.Promise(function(resolve, reject) {
+      return store.filter('queue',function (q) {
+        return q.id === record.id;
+      }).then(function (queues) {
+        var message;
+        if (record.get('name.length')==0) {
+          message = "Field can not be empty";
+        } else if (queues.get('length') > 1) {
+          message = "Queue already exists";
+        };
+        if (message) {
+          var error = new DS.InvalidError({path:[message]});
+          store.recordWasInvalid(record, error.errors);
+          return;
+        };
+
+        Ember.run(record, resolve, {'queue':data})
+
+      });
+    });
+  },
+
+  saveMark:'',
+
+  updateRecord:function (store,type,record) {
+    var adapter = this,
+        saveMark = this.get('saveMark'),
+        uri = _getCapacitySchedulerUri(adapter.clusterName, adapter.tag),
+        serializer = store.serializerFor('queue'),
+        props = serializer.serializeConfig(record);
+
+    if (saveMark) {
+      uri = [uri,saveMark].join('/');
+      this.set('saveMark','');
+    };
+
+    return new Ember.RSVP.Promise(function(resolve, reject) {
+      adapter.ajax(uri,'PUT',{data:props}).then(function(data) {
+        Ember.run(null, resolve, data);
+      }, function(jqXHR) {
+        jqXHR.then = null;
+        Ember.run(null, reject, jqXHR);
+      });
+    });
+  },
+  
+  /**
+   * Finds queue by id.
+   *
+   */
+  findQueue: function(id) {
+    var result = $.grep(App.Adapter.queues, function(e){ return e.get('id') == id; });
+    return result[0];
+  },
+
+  /**
+   * Finds queue by id in store.
+   *
+   */
+  find: function(store, type, id) {
+    return store.findAll('queue').then(function (queues) {
+      return {"queue":queues.findBy('id',id).toJSON({includeId:true})};
+    });
+  },
+
+  /**
+   * Finds all queues.
+   *
+   */
+  findAll: function(store, type) {
+    var adapter = this;
+    var uri = _getCapacitySchedulerUri(adapter.clusterName, adapter.tag);
+    return new Ember.RSVP.Promise(function(resolve, reject) {
+      adapter.ajax(uri).then(function(data) {
+        Ember.run(null, resolve, data);
+      }, function(jqXHR) {
+        jqXHR.then = null;
+        Ember.run(null, reject, jqXHR);
+      });
+    });
+
+  },
+     
+  /**
+   * Finds the cluster name.
+   *
+   */
+  findClusterName: function() {
+    var uri = _getClusterNameUri();
+        
+    return this.ajax(uri).then(function(data) {
+      var clusterName = data.ViewInstanceInfo.properties.clusterName;
+      console.log("found cluster name: "+clusterName);
+      App.Adapter.clusterName = clusterName;
+      return clusterName;
+    });
+  },
+
+  /**
+   * Finds the capacity scheduler tag.
+   */
+  findTag: function(clusterName) {
+    var uri = _getTagVersionUri(clusterName);
+    
+    return this.ajax(uri).then(function(data) {
+      var tag = data.Clusters.desired_configs["capacity-scheduler"].tag;
+      console.log("found tag version: "+tag);
+      App.Adapter.tag = tag;
+      return  tag;
+    })
+  },
+  
+  ajax: function(url, type, hash) {
+    var adapter = this;
+
+    return new Ember.RSVP.Promise(function(resolve, reject) {
+      hash = adapter.ajaxOptions(url, type, hash);
+
+      hash.success = function(json) {
+        Ember.run(null, resolve, json);
+      };
+
+      hash.error = function(jqXHR, textStatus, errorThrown) {
+        Ember.run(null, reject, adapter.ajaxError(jqXHR));
+      };
+
+      Ember.$.ajax(hash);
+    }, "DS: RestAdapter#ajax " + type + " to " + url);
+  },
+
+  ajaxOptions: function(url, type, hash) {
+    hash = hash || {};
+    hash.url = url;
+    hash.type = type;
+    hash.dataType = 'json';
+    hash.context = this;
+
+    if (hash.data && type !== 'GET') {
+      hash.contentType = 'application/json; charset=utf-8';
+      hash.data = JSON.stringify(hash.data);
+    }
+    hash.beforeSend = function (xhr) {
+      xhr.setRequestHeader('X-Requested-By', 'ambari');
+    };
+    return hash;
+  },
+
+  ajaxError: function(jqXHR) {
+    if (jqXHR && typeof jqXHR === 'object') {
+      jqXHR.then = null;
+    }
+
+    return jqXHR;
+  },
+});
+
+App.SchedulerAdapter = App.QueueAdapter.extend({
+  find: function(store, type, id) {
+    return store.findAll('scheduler').then(function (scheduler) {
+      return {"scheduler":scheduler.findBy('id',id).toJSON({includeId:true})};
+    });
+  },
+});
+
+App.ApplicationStore = DS.Store.extend({
+
+  adapter: App.QueueAdapter,
+
+  markForRefresh:function (mark) {
+    this.set('defaultAdapter.saveMark','saveAndRefresh');
+  },
+
+  markForRestart:function (mark) {
+    this.set('defaultAdapter.saveMark','saveAndRestart');
+  },
+
+  flushPendingSave: function() {
+    var pending = this._pendingSave.slice(),
+        newPending = [[]],scheduler;
+
+    if (pending.length == 1) {
+      this._super();
+      return;
+    };
+    
+    pending.forEach(function (tuple) {
+      var record = tuple[0], resolver = tuple[1],
+          operation;
+      newPending[0].push(record)
+      newPending[1] = resolver;
+    });
+
+    this._pendingSave = [newPending];
+    this._super();
+  },
+  didSaveRecord: function(record, data) {
+    if (Em.isArray(record)) {
+      for (var i = 0; i < record.length; i++) {
+        this._super(record[i],data.findBy('id',record[i].id));
+      };
+    } else {
+      this._super(record, data);
+    };
+  },
+  recordWasError: function(record) {
+    if (Em.isArray(record)) {
+      for (var i = 0; i < record.length; i++) {
+        record[i].adapterDidError();
+      };
+    } else {
+      record.adapterDidError();
+    }
+  },
+});
+
+App.ApplicationSerializer = DS.RESTSerializer.extend({
+
+  PREFIX:"yarn.scheduler.capacity",
+
+  serializeConfig:function (records) {
+    var config = {},
+        prefix = this.PREFIX;
+    records.forEach(function (record) {
+      if (record.id == 'scheduler') {
+        config[prefix + ".maximum-am-resource-percent"] = record.get('maximum_am_resource_percent');
+        config[prefix + ".maximum-applications"] = record.get('maximum_applications');
+        config[prefix + ".node-locality-delay"] = record.get('node_locality_delay');
+      } else {
+        config[prefix + "." + record.get('path') + ".unfunded.capacity"] = record.get('unfunded_capacity');
+        config[prefix + "." + record.get('path') + ".acl_administer_queue"] = record.get('acl_administer_queue');
+        config[prefix + "." + record.get('path') + ".acl_administer_jobs"] = record.get('acl_administer_jobs');
+        config[prefix + "." + record.get('path') + ".acl_submit_applications"] = record.get('acl_submit_applications');
+        config[prefix + "." + record.get('path') + ".minimum-user-limit-percent"] = record.get('minimum_user_limit_percent');
+        config[prefix + "." + record.get('path') + ".maximum-capacity"] = record.get('maximum_capacity');
+        config[prefix + "." + record.get('path') + ".user-limit-factor"] = record.get('user_limit_factor');
+        config[prefix + "." + record.get('path') + ".state"] = record.get('state');
+        config[prefix + "." + record.get('path') + ".capacity"] = record.get('capacity');
+        config[prefix + "." + record.get('path') + ".queues"] = record.get('queueNames')||'';
+      };
+    });
+
+    for (var i in config) {
+      if (config[i] === null || config[i] === undefined) {
+        delete config[i];
+      }
+    }
+
+    return {properties : config};
+
+  },
+  normalizeConfig:function (store, payload) {
+    var queues = [];
+    var props = payload.items[0].properties;
+
+    var scheduler = [{
+      id:'scheduler',
+      maximum_am_resource_percent:props[this.PREFIX + ".maximum-am-resource-percent"],
+      maximum_applications:props[this.PREFIX + ".maximum-applications"],
+      node_locality_delay:props[this.PREFIX + ".node-locality-delay"]
+    }];
+    queues = _recurseQueues(null, "root", 0, props, queues, store);
+
+    return {'queue':queues,'scheduler':scheduler};
+  },
+
+  extractFindAll: function(store, type, payload){
+    var queues = [];
+    var props = payload.items[0].properties;
+
+    var scheduler = {
+      id:'scheduler',
+      maximum_am_resource_percent:props[this.PREFIX + ".maximum-am-resource-percent"],
+      maximum_applications:props[this.PREFIX + ".maximum-applications"],
+      node_locality_delay:props[this.PREFIX + ".node-locality-delay"]
+    };
+    queues = _recurseQueues(null, "root", 0, props, queues, store);
+
+    var config = this.normalizeConfig(store, payload);
+    
+    return this.extractArray(store, type, config);
+  },
+
+  extractUpdateRecord:function (store, type, payload, id, requestType) {
+    var queues = [];
+    var props = payload.items[0].properties;
+
+    var scheduler = {
+      id:'scheduler',
+      maximum_am_resource_percent:props[this.PREFIX + ".maximum-am-resource-percent"],
+      maximum_applications:props[this.PREFIX + ".maximum-applications"],
+      node_locality_delay:props[this.PREFIX + ".node-locality-delay"]
+    };
+    queues = _recurseQueues(null, "root", 0, props, queues, store);
+    
+    var config = this.normalizeConfig(store, payload);
+
+    return this.extractConfig(store, App.Queue, {'queue':queues,'scheduler':[scheduler]});
+  },
+
+  extractConfig: function(store, primaryType, payload) {
+      payload = this.normalizePayload(payload);
+
+      var primaryTypeName = primaryType.typeKey,
+          primaryArray = [],
+          scheduler,queues;
+
+      for (var prop in payload) {
+        var typeKey = prop,
+            forcedSecondary = false;
+
+        if (prop.charAt(0) === '_') {
+          forcedSecondary = true;
+          typeKey = prop.substr(1);
+        }
+
+        var typeName = this.typeForRoot(typeKey),
+            type = store.modelFor(typeName),
+            typeSerializer = store.serializerFor(type),
+            isPrimary = (!forcedSecondary && (type.typeKey === primaryTypeName));
+
+        var normalizedArray = Ember.ArrayPolyfills.map.call(payload[prop], function(hash) {
+          return typeSerializer.normalize(type, hash, prop);
+        }, this);
+
+        if (typeKey == App.Scheduler.typeKey) {
+          scheduler = normalizedArray;
+        } else {
+          queues = normalizedArray;
+        }
+      }
+
+      return scheduler.concat(queues);
+    },
+
+  extractQueue: function(data, props) {
+    var q = { name: data.name, parentPath: data.parentPath, depth: data.depth };
+    var prefix = this.PREFIX;
+
+    if (q.parentPath == null || q.parentPath.length == 0){
+        q.path = q.name;
+      } else {
+        q.path = q.parentPath + '.' + q.name;
+      }
+      q.id = q.path.dasherize();
+
+    q.unfunded_capacity = props[prefix + "." + q.path + ".unfunded.capacity"];
+    q.acl_administer_queue = props[prefix + "." + q.path + ".acl_administer_queue"];
+    q.acl_administer_jobs = props[prefix + "." + q.path + ".acl_administer_jobs"];
+    q.acl_submit_applications = props[prefix + "." + q.path + ".acl_submit_applications"];
+    q.minimum_user_limit_percent = props[prefix + "." + q.path + ".minimum-user-limit-percent"];
+    q.maximum_capacity = props[prefix + "." + q.path + ".maximum-capacity"];
+    q.user_limit_factor = props[prefix + "." + q.path + ".user-limit-factor"];
+    q.state = props[prefix + "." + q.path + ".state"];
+
+    q.capacity = props[prefix + "." + q.path + ".capacity"];
+    q.queueNames = props[prefix + "." + q.path + ".queues"];
+
+    return q;
+  }
+});
+
+/**
+ * Recursively builds the list of queues.
+ *
+ */
+function _recurseQueues(parentQueue, queueName, depth, props, queues, store) {
+  var serializer = store.serializerFor('queue');
+  var prefix = serializer.PREFIX;
+  var parentPath = '';
+  if (parentQueue != null) {
+    parentPath = parentQueue.path;
+    prefix += ".";
+  }
+
+  var queue = serializer.extractQueue({ name: queueName, parentPath: parentPath, depth: depth}, props);
+  queues.push(queue);
+
+  var queueProp = prefix + parentPath + "." + queueName + ".queues";
+  if (props[queueProp]) {
+    var qs = props[queueProp].split(',');
+    for (var i=0; i < qs.length; i++) {
+      queues = _recurseQueues(queue, qs[i], depth+1, props, queues, store)
+    }
+  }
+  
+  return queues;
+}
+
+/**
+ * Gets the cluster name URI based on test mode
+ *
+ * @return  cluster name URI
+ */
+function _getCapacitySchedulerUri() {
+  if (App.testMode)
+    return  "/data/scheduler-configuration.json"
+
+  var parts = window.location.pathname.match(/\/[^\/]*/g);
+  var view = parts[1];
+  var version = '/versions' + parts[2];
+  var instance = parts[3];
+  if (parts.length == 4) { // version is not present
+    instance = parts[2];
+    version = '';
+  }
+  return '/api/v1/views' + view + version + '/instances' + instance+'/resources/scheduler/configuration';
+}
+
+/**
+ * Gets the cluster name URI based on test mode
+ *
+ * @return  cluster name URI
+ */
+function _getClusterNameUri() {
+  if (App.testMode)
+    return  "/data/view-instance.json"
+
+  var parts = window.location.pathname.match(/\/[^\/]*/g);
+  var view = parts[1];
+  var version = '/versions' + parts[2];
+  var instance = parts[3];
+  if (parts.length == 4) { // version is not present
+    instance = parts[2];
+    version = '';
+  }
+  return '/api/v1/views' + view + version + '/instances' + instance;
+}
+
+/**
+ * Gets the capacity scheduler URI based on test mode.
+ *
+ * @param   clusterName     the cluster name
+ * @param   tagVersion      the tag version
+ * @return  the capacity scheduler URI
+ */
+function _getCapacitySchedulerUri_old(clusterName, tagVersion) {
+  console.log("_getCapacitySchedulerUri.clusterName = "+clusterName);
+  console.log("_getCapacitySchedulerUri.tagVersion = "+tagVersion);
+  if (App.testMode)
+    return  "/data/capacity-scheduler.json"
+    
+  return "/api/v1/clusters/" + clusterName + "/configurations?type=capacity-scheduler&tag="+tagVersion;
+}
+
+/**
+ * Gets the tag version URI based on test mode.
+ *
+ * @param   clusterName     the cluster name
+ * @return  the capacity scheduler tag URI
+ */
+function _getTagVersionUri(clusterName) {
+  if (App.testMode)
+    return  "/data/cs-tag.json"
+    
+  return "/api/v1/clusters/" + clusterName + "?fields=Clusters/desired_configs/capacity-scheduler";
+}
+
+/**
+ * Setup claster name and version tag for adapter
+ */
+
+/*App.deferReadiness();
+
+(function () {
+
+  var clusterNameUri = _getClusterNameUri();
+    
+  return Em.$.ajax({url: clusterNameUri, dataType: "json"}).then(function(data) {
+    var clusterName = data.ViewInstanceInfo.properties.clusterName;
+    console.log("found cluster name: "+clusterName);
+
+    App.QueueAdapter.reopen({clusterName:clusterName});
+
+    var tagVersionUri = _getTagVersionUri(clusterName);
+  
+    return Em.$.ajax({url: tagVersionUri, dataType: "json"}).then(function(data) {
+      var tag = data.Clusters.desired_configs["capacity-scheduler"].tag;
+      console.log("found tag version: "+tag);
+
+      App.QueueAdapter.reopen({tag:tag});
+
+      App.advanceReadiness();
+
+    });
+  });
+})();*/

+ 19 - 0
contrib/views/capacity-scheduler/src/main/resources/ui/app/app.js

@@ -0,0 +1,19 @@
+/**
+ * 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.
+ */
+
+module.exports = Em.Application.create();

+ 90 - 0
contrib/views/capacity-scheduler/src/main/resources/ui/app/assets/data/capacity-scheduler.json

@@ -0,0 +1,90 @@
+{
+  "href" : "http://c6403.ambari.apache.org:8080/api/v1/clusters/MyCluster/configurations?type=capacity-scheduler&tag=version1400218672484",
+  "items" : [
+    {
+      "href" : "http://c6403.ambari.apache.org:8080/api/v1/clusters/MyCluster/configurations?type=capacity-scheduler&tag=version1400218672484",
+      "tag" : "version1400218672484",
+      "type" : "capacity-scheduler",
+      "Config" : {
+        "cluster_name" : "MyCluster"
+      },
+      "properties" : {
+        "yarn.scheduler.capacity.maximum-am-resource-percent" : "0.2",
+        "yarn.scheduler.capacity.maximum-applications" : "10000",
+        "yarn.scheduler.capacity.node-locality-delay" : "40",
+        "yarn.scheduler.capacity.root.Engineering.Development.acl_administer_jobs" : "*",
+        "yarn.scheduler.capacity.root.Engineering.Development.acl_administer_queue" : "*",
+        "yarn.scheduler.capacity.root.Engineering.Development.acl_submit_applications" : "*",
+        "yarn.scheduler.capacity.root.Engineering.Development.capacity" : "20",
+        "yarn.scheduler.capacity.root.Engineering.Development.maximum-capacity" : "100",
+        "yarn.scheduler.capacity.root.Engineering.Development.state" : "RUNNING",
+        "yarn.scheduler.capacity.root.Engineering.Development.user-limit-factor" : "1",
+        "yarn.scheduler.capacity.root.Engineering.QE.acl_administer_jobs" : "*",
+        "yarn.scheduler.capacity.root.Engineering.QE.acl_administer_queue" : " ",
+        "yarn.scheduler.capacity.root.Engineering.QE.acl_submit_applications" : "*",
+        "yarn.scheduler.capacity.root.Engineering.QE.capacity" : "80",
+        "yarn.scheduler.capacity.root.Engineering.QE.maximum-capacity" : "90",
+        "yarn.scheduler.capacity.root.Engineering.QE.state" : "RUNNING",
+        "yarn.scheduler.capacity.root.Engineering.QE.user-limit-factor" : "1",
+        "yarn.scheduler.capacity.root.Engineering.acl_administer_jobs" : "*",
+        "yarn.scheduler.capacity.root.Engineering.acl_administer_queue" : "user1,user2 group2",
+        "yarn.scheduler.capacity.root.Engineering.acl_submit_applications" : "*",
+        "yarn.scheduler.capacity.root.Engineering.capacity" : "60",
+        "yarn.scheduler.capacity.root.Engineering.maximum-capacity" : "100",
+        "yarn.scheduler.capacity.root.Engineering.queues" : "Development,QE",
+        "yarn.scheduler.capacity.root.Engineering.state" : "RUNNING",
+        "yarn.scheduler.capacity.root.Engineering.user-limit-factor" : "1",
+        "yarn.scheduler.capacity.root.Marketing.Advertising.acl_administer_jobs" : "*",
+        "yarn.scheduler.capacity.root.Marketing.Advertising.acl_administer_queue" : " ",
+        "yarn.scheduler.capacity.root.Marketing.Advertising.acl_submit_applications" : "*",
+        "yarn.scheduler.capacity.root.Marketing.Advertising.capacity" : "30",
+        "yarn.scheduler.capacity.root.Marketing.Advertising.maximum-capacity" : "40",
+        "yarn.scheduler.capacity.root.Marketing.Advertising.state" : "STOPPED",
+        "yarn.scheduler.capacity.root.Marketing.Advertising.user-limit-factor" : "1",
+        "yarn.scheduler.capacity.root.Marketing.Sales.acl_administer_jobs" : "*",
+        "yarn.scheduler.capacity.root.Marketing.Sales.acl_administer_queue" : " group5",
+        "yarn.scheduler.capacity.root.Marketing.Sales.acl_submit_applications" : "*",
+        "yarn.scheduler.capacity.root.Marketing.Sales.capacity" : "70",
+        "yarn.scheduler.capacity.root.Marketing.Sales.maximum-capacity" : "80",
+        "yarn.scheduler.capacity.root.Marketing.Sales.minimum-user-limit-percent" : "20",
+        "yarn.scheduler.capacity.root.Marketing.Sales.state" : "RUNNING",
+        "yarn.scheduler.capacity.root.Marketing.Sales.user-limit-factor" : "1",
+        "yarn.scheduler.capacity.root.Marketing.acl_administer_jobs" : "*",
+        "yarn.scheduler.capacity.root.Marketing.acl_submit_applications" : "*",
+        "yarn.scheduler.capacity.root.Marketing.acl_administer_queue" : " group5",
+        "yarn.scheduler.capacity.root.Marketing.capacity" : "10",
+        "yarn.scheduler.capacity.root.Marketing.maximum-capacity" : "40",
+        "yarn.scheduler.capacity.root.Marketing.queues" : "Sales,Advertising",
+        "yarn.scheduler.capacity.root.Marketing.state" : "RUNNING",
+        "yarn.scheduler.capacity.root.Marketing.user-limit-factor" : "1",
+        "yarn.scheduler.capacity.root.Support.Services.acl_administer_jobs" : "*",
+        "yarn.scheduler.capacity.root.Support.Services.acl_administer_queue" : "*",
+        "yarn.scheduler.capacity.root.Support.Services.acl_submit_applications" : "*",
+        "yarn.scheduler.capacity.root.Support.Services.capacity" : "80",
+        "yarn.scheduler.capacity.root.Support.Services.maximum-capacity" : "100",
+        "yarn.scheduler.capacity.root.Support.Services.minimum-user-limit-percent" : "20",
+        "yarn.scheduler.capacity.root.Support.Services.state" : "RUNNING",
+        "yarn.scheduler.capacity.root.Support.Services.user-limit-factor" : "1",
+        "yarn.scheduler.capacity.root.Support.Training.acl_administer_jobs" : "*",
+        "yarn.scheduler.capacity.root.Support.Training.acl_administer_queue" : "user2",
+        "yarn.scheduler.capacity.root.Support.Training.acl_submit_applications" : "*",
+        "yarn.scheduler.capacity.root.Support.Training.capacity" : "20",
+        "yarn.scheduler.capacity.root.Support.Training.maximum-capacity" : "60",
+        "yarn.scheduler.capacity.root.Support.Training.state" : "RUNNING",
+        "yarn.scheduler.capacity.root.Support.Training.user-limit-factor" : "1",
+        "yarn.scheduler.capacity.root.Support.acl_administer_jobs" : "*",
+        "yarn.scheduler.capacity.root.Support.acl_administer_queue" : " ",
+        "yarn.scheduler.capacity.root.Support.acl_submit_applications" : "*",
+        "yarn.scheduler.capacity.root.Support.capacity" : "30",
+        "yarn.scheduler.capacity.root.Support.maximum-capacity" : "100",
+        "yarn.scheduler.capacity.root.Support.queues" : "Training,Services",
+        "yarn.scheduler.capacity.root.Support.state" : "RUNNING",
+        "yarn.scheduler.capacity.root.Support.user-limit-factor" : "1",
+        "yarn.scheduler.capacity.root.acl_administer_queue" : "*",
+        "yarn.scheduler.capacity.root.capacity" : "100",
+        "yarn.scheduler.capacity.root.queues" : "Support,Marketing,Engineering",
+        "yarn.scheduler.capacity.root.unfunded.capacity" : "50"
+      }
+    }
+  ]
+}

+ 13 - 0
contrib/views/capacity-scheduler/src/main/resources/ui/app/assets/data/cs-tag.json

@@ -0,0 +1,13 @@
+{
+  "href" : "http://c6401.ambari.apache.org:8080/api/v1/clusters/MyCluster?fields=Clusters/desired_configs/capacity-scheduler",
+  "Clusters" : {
+    "cluster_name" : "MyCluster",
+    "version" : "HDP-2.1",
+    "desired_configs" : {
+      "capacity-scheduler" : {
+        "user" : "admin",
+        "tag" : "version1"
+      }
+    }
+  }
+}

+ 7 - 0
contrib/views/capacity-scheduler/src/main/resources/ui/app/assets/data/refreshqueues.json

@@ -0,0 +1,7 @@
+{
+  "href" : "http://c6403.ambari.apache.org:8080/api/v1/clusters/MyCluster/requests/29",
+  "Requests" : {
+    "id" : 29,
+    "status" : "InProgress"
+  }
+}

+ 18 - 0
contrib/views/capacity-scheduler/src/main/resources/ui/app/assets/data/resourcemanager.json

@@ -0,0 +1,18 @@
+{
+  "href" : "http://c6401.ambari.apache.org:8080/api/v1/clusters/MyCluster/services/YARN/components/RESOURCEMANAGER?fields=host_components",
+  "ServiceComponentInfo" : {
+    "cluster_name" : "MyCluster",
+    "component_name" : "RESOURCEMANAGER",
+    "service_name" : "YARN"
+  },
+  "host_components" : [
+    {
+      "href" : "http://c6401.ambari.apache.org:8080/api/v1/clusters/MyCluster/hosts/c6401.ambari.apache.org/host_components/RESOURCEMANAGER",
+      "HostRoles" : {
+        "cluster_name" : "MyCluster",
+        "component_name" : "RESOURCEMANAGER",
+        "host_name" : "c6401.ambari.apache.org"
+      }
+    }
+  ]
+}

+ 90 - 0
contrib/views/capacity-scheduler/src/main/resources/ui/app/assets/data/scheduler-configuration.json

@@ -0,0 +1,90 @@
+{
+  "href" : "http://c6403.ambari.apache.org:8080/api/v1/clusters/MyCluster/configurations?type=capacity-scheduler&tag=version1400218672484",
+  "items" : [
+    {
+      "href" : "http://c6403.ambari.apache.org:8080/api/v1/clusters/MyCluster/configurations?type=capacity-scheduler&tag=version1400218672484",
+      "tag" : "version1400218672484",
+      "type" : "capacity-scheduler",
+      "Config" : {
+        "cluster_name" : "MyCluster"
+      },
+      "properties" : {
+        "yarn.scheduler.capacity.maximum-am-resource-percent" : "0.2",
+        "yarn.scheduler.capacity.maximum-applications" : "10000",
+        "yarn.scheduler.capacity.node-locality-delay" : "40",
+        "yarn.scheduler.capacity.root.Engineering.Development.acl_administer_jobs" : "*",
+        "yarn.scheduler.capacity.root.Engineering.Development.acl_administer_queue" : "*",
+        "yarn.scheduler.capacity.root.Engineering.Development.acl_submit_applications" : "*",
+        "yarn.scheduler.capacity.root.Engineering.Development.capacity" : "20",
+        "yarn.scheduler.capacity.root.Engineering.Development.maximum-capacity" : "100",
+        "yarn.scheduler.capacity.root.Engineering.Development.state" : "RUNNING",
+        "yarn.scheduler.capacity.root.Engineering.Development.user-limit-factor" : "1",
+        "yarn.scheduler.capacity.root.Engineering.QE.acl_administer_jobs" : "*",
+        "yarn.scheduler.capacity.root.Engineering.QE.acl_administer_queue" : " ",
+        "yarn.scheduler.capacity.root.Engineering.QE.acl_submit_applications" : "*",
+        "yarn.scheduler.capacity.root.Engineering.QE.capacity" : "80",
+        "yarn.scheduler.capacity.root.Engineering.QE.maximum-capacity" : "90",
+        "yarn.scheduler.capacity.root.Engineering.QE.state" : "RUNNING",
+        "yarn.scheduler.capacity.root.Engineering.QE.user-limit-factor" : "1",
+        "yarn.scheduler.capacity.root.Engineering.acl_administer_jobs" : "*",
+        "yarn.scheduler.capacity.root.Engineering.acl_administer_queue" : "user1,user2 group2",
+        "yarn.scheduler.capacity.root.Engineering.acl_submit_applications" : "*",
+        "yarn.scheduler.capacity.root.Engineering.capacity" : "60",
+        "yarn.scheduler.capacity.root.Engineering.maximum-capacity" : "100",
+        "yarn.scheduler.capacity.root.Engineering.queues" : "Development,QE",
+        "yarn.scheduler.capacity.root.Engineering.state" : "RUNNING",
+        "yarn.scheduler.capacity.root.Engineering.user-limit-factor" : "1",
+        "yarn.scheduler.capacity.root.Marketing.Advertising.acl_administer_jobs" : "*",
+        "yarn.scheduler.capacity.root.Marketing.Advertising.acl_administer_queue" : " ",
+        "yarn.scheduler.capacity.root.Marketing.Advertising.acl_submit_applications" : "*",
+        "yarn.scheduler.capacity.root.Marketing.Advertising.capacity" : "30",
+        "yarn.scheduler.capacity.root.Marketing.Advertising.maximum-capacity" : "40",
+        "yarn.scheduler.capacity.root.Marketing.Advertising.state" : "STOPPED",
+        "yarn.scheduler.capacity.root.Marketing.Advertising.user-limit-factor" : "1",
+        "yarn.scheduler.capacity.root.Marketing.Sales.acl_administer_jobs" : "*",
+        "yarn.scheduler.capacity.root.Marketing.Sales.acl_administer_queue" : " group5",
+        "yarn.scheduler.capacity.root.Marketing.Sales.acl_submit_applications" : "*",
+        "yarn.scheduler.capacity.root.Marketing.Sales.capacity" : "70",
+        "yarn.scheduler.capacity.root.Marketing.Sales.maximum-capacity" : "80",
+        "yarn.scheduler.capacity.root.Marketing.Sales.minimum-user-limit-percent" : "20",
+        "yarn.scheduler.capacity.root.Marketing.Sales.state" : "RUNNING",
+        "yarn.scheduler.capacity.root.Marketing.Sales.user-limit-factor" : "1",
+        "yarn.scheduler.capacity.root.Marketing.acl_administer_jobs" : "*",
+        "yarn.scheduler.capacity.root.Marketing.acl_submit_applications" : "*",
+        "yarn.scheduler.capacity.root.Marketing.acl_administer_queue" : " group5",
+        "yarn.scheduler.capacity.root.Marketing.capacity" : "10",
+        "yarn.scheduler.capacity.root.Marketing.maximum-capacity" : "40",
+        "yarn.scheduler.capacity.root.Marketing.queues" : "Sales,Advertising",
+        "yarn.scheduler.capacity.root.Marketing.state" : "RUNNING",
+        "yarn.scheduler.capacity.root.Marketing.user-limit-factor" : "1",
+        "yarn.scheduler.capacity.root.Support.Services.acl_administer_jobs" : "*",
+        "yarn.scheduler.capacity.root.Support.Services.acl_administer_queue" : "*",
+        "yarn.scheduler.capacity.root.Support.Services.acl_submit_applications" : "*",
+        "yarn.scheduler.capacity.root.Support.Services.capacity" : "80",
+        "yarn.scheduler.capacity.root.Support.Services.maximum-capacity" : "100",
+        "yarn.scheduler.capacity.root.Support.Services.minimum-user-limit-percent" : "20",
+        "yarn.scheduler.capacity.root.Support.Services.state" : "RUNNING",
+        "yarn.scheduler.capacity.root.Support.Services.user-limit-factor" : "1",
+        "yarn.scheduler.capacity.root.Support.Training.acl_administer_jobs" : "*",
+        "yarn.scheduler.capacity.root.Support.Training.acl_administer_queue" : "user2",
+        "yarn.scheduler.capacity.root.Support.Training.acl_submit_applications" : "*",
+        "yarn.scheduler.capacity.root.Support.Training.capacity" : "20",
+        "yarn.scheduler.capacity.root.Support.Training.maximum-capacity" : "60",
+        "yarn.scheduler.capacity.root.Support.Training.state" : "RUNNING",
+        "yarn.scheduler.capacity.root.Support.Training.user-limit-factor" : "1",
+        "yarn.scheduler.capacity.root.Support.acl_administer_jobs" : "*",
+        "yarn.scheduler.capacity.root.Support.acl_administer_queue" : " ",
+        "yarn.scheduler.capacity.root.Support.acl_submit_applications" : "*",
+        "yarn.scheduler.capacity.root.Support.capacity" : "30",
+        "yarn.scheduler.capacity.root.Support.maximum-capacity" : "100",
+        "yarn.scheduler.capacity.root.Support.queues" : "Training,Services",
+        "yarn.scheduler.capacity.root.Support.state" : "RUNNING",
+        "yarn.scheduler.capacity.root.Support.user-limit-factor" : "1",
+        "yarn.scheduler.capacity.root.acl_administer_queue" : "*",
+        "yarn.scheduler.capacity.root.capacity" : "100",
+        "yarn.scheduler.capacity.root.queues" : "Support,Marketing,Engineering",
+        "yarn.scheduler.capacity.root.unfunded.capacity" : "50"
+      }
+    }
+  ]
+}

+ 15 - 0
contrib/views/capacity-scheduler/src/main/resources/ui/app/assets/data/view-instance.json

@@ -0,0 +1,15 @@
+{
+  "href" : "http://c6401.ambari.apache.org:8080/api/v1/views/CS/versions/0.1.0/instances/CS_1",
+  "ViewInstanceInfo" : {
+    "context_path" : "/views/CS/0.1.0/CS_1",
+    "instance_name" : "CS_1",
+    "version" : "0.1.0",
+    "view_name" : "CS",
+    "instance_data" : { },
+    "properties" : {
+      "clusterName" : "MyCluster"
+    }
+  },
+  "resources" : [
+  ]
+}

File diff suppressed because it is too large
+ 195 - 0
contrib/views/capacity-scheduler/src/main/resources/ui/app/assets/fonts/fontawesome-webfont.svg


+ 48 - 0
contrib/views/capacity-scheduler/src/main/resources/ui/app/assets/index.html

@@ -0,0 +1,48 @@
+<!--
+   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.
+-->
+
+<!DOCTYPE html>
+<html lang="en">
+<head>
+  <meta charset="utf-8">
+  <meta http-equiv="X-UA-Compatible" content="IE=edge">
+  <meta name="viewport" content="width=device-width, initial-scale=1.0">
+  <title>Capacity Scheduler</title>
+  <link rel="stylesheet" href="stylesheets/vendor.css">
+  <link rel="stylesheet" href="stylesheets/app.css">
+  <script src="javascripts/vendor.js"></script>
+  <script src="javascripts/app.js"></script>
+  <script>
+      $(document).ready(function() {
+          require('initialize');
+      });
+  </script>
+</head>
+<body>
+  <script type="text/x-handlebars">
+  <div class="wrap">
+    <div class="container-fluid">
+      <div class="row">
+        {{outlet}}
+      </div>
+    </div>
+  </div>
+  </script>
+
+
+  </body>
+</html>

+ 27 - 0
contrib/views/capacity-scheduler/src/main/resources/ui/app/components.js

@@ -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.
+ */
+
+require('components/capacityBar');
+require('components/capacityInput');
+require('components/totalCapacity');
+require('components/queueListItem');
+require('components/pathInput');
+require('components/radioButton');
+require('components/userGroupInput');
+require('components/escapeAcl');
+require('components/confirmDelete');

+ 42 - 0
contrib/views/capacity-scheduler/src/main/resources/ui/app/components/capacityBar.js

@@ -0,0 +1,42 @@
+/**
+ * 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.
+ */
+
+var App = require('app');
+
+App.CapacityBarComponent = Em.Component.extend({
+  layoutName:'components/capacityBar',
+  classNames:['capacity-bar'],
+  mark:function () {
+    if (this.$().parent().hasClass('active')) {
+      Ember.run.next(this, 'addInClass');
+    };
+  }.on('didInsertElement'),
+  addInClass:function () {
+    this.$().children().addClass('in');
+  },
+  capacityWidth: function() {
+    return  (Number(this.get('capacityValue'))<=100)?('width: '+this.get('capacityValue')+'%'):'width: 100%';
+  }.property('capacityValue'),
+  maxCapacityWidth: function() {
+    var val = Number(this.get('maxCapacityValue')) - Number(this.get('capacityValue'));
+    return  (val<=100)?('width: '+val+'%'):'width: 100%';
+  }.property('maxCapacityValue','capacityValue'),
+  showMaxCapacity:function () {
+    return this.get('maxCapacityValue') > this.get('capacityValue');
+  }.property('maxCapacityValue','capacityValue')
+});

+ 94 - 0
contrib/views/capacity-scheduler/src/main/resources/ui/app/components/capacityInput.js

@@ -0,0 +1,94 @@
+/**
+ * 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.
+ */
+
+var App = require('app');
+
+
+App.FocusInputComponent = Ember.TextField.extend({
+  becomeFocused: function() {
+    this.$().focus().val(this.value);
+  }.on('didInsertElement'),
+  cancel:function () {
+    this.get('targetObject').send(this.get('revert'),'cancel');
+  }
+});
+
+App.IntInputComponent = Ember.TextField.extend({
+  classNames:['form-control'],
+  maxVal:null,
+  intVal:function () {
+    var val = parseFloat(this.get('value'))||0;
+    var maxVal = this.get('maxVal');
+    val = (maxVal && maxVal < val)?maxVal:val;
+    this.set('value', val);
+  }.on('change'),
+  checkNumber:function () {
+    var val = this.get('value'),
+        num = Number(val),
+        str = String(val);
+    if (typeof val !== "number" && !isNaN(num) && str == num.toString()) {
+      this.set('value', Number(val));
+    };
+  }.observes('value')
+});
+
+App.CapacityInputComponent = App.IntInputComponent.extend({
+  
+  totalCapacity:null,
+  queue:null,
+
+  keyDown: function(evt) {
+    var newChar, val = this.get('value')||0;
+    val = val.toString();
+    
+    if ((evt.keyCode > 64 && evt.keyCode < 91) || 
+      (evt.keyCode > 185 && evt.keyCode < 193) || 
+      (evt.keyCode > 218 && evt.keyCode < 223)) {
+      return false;
+    };
+
+    if (evt.keyCode > 95 && evt.keyCode < 106) {
+      newChar = (evt.keyCode - 96).toString();
+    } else {
+      newChar = String.fromCharCode(evt.keyCode);
+    }
+
+    if (newChar.match(/[0-9]/)) {
+      val = val.substring(0, evt.target.selectionStart) + newChar + val.substring(evt.target.selectionEnd);
+    };
+
+    return parseFloat(val)<=100;
+  },
+});
+
+App.MaxCapacityInputComponent = App.CapacityInputComponent.extend({
+  isInvalid:false,
+  invalid:function (c,o) {
+    var queue = this.get('queue'),
+        max_capacity = Number(queue.get('maximum_capacity')),
+        capacity = Number(queue.get('capacity'));
+    if (o == 'queue.capacity' && max_capacity < capacity) {
+      return queue.set('maximum_capacity',capacity);
+    };
+    if (max_capacity < capacity && queue.get('isDirty')) {
+      queue.get('errors').add('maximum_capacity', 'Maximum capacity must be greater then capacity');
+    } else {
+      queue.get('errors').remove('maximum_capacity');
+    }
+  }.observes('queue.maximum_capacity','queue.capacity')
+});

+ 87 - 0
contrib/views/capacity-scheduler/src/main/resources/ui/app/components/confirmDelete.js

@@ -0,0 +1,87 @@
+/**
+ * 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.
+ */
+
+var App = require('app');
+
+var bound;
+
+bound = function(fnName) {
+  return Ember.computed(fnName,function() {
+    return this.get(fnName).bind(this);
+  });
+};
+
+App.ClickElsewhereMixin = Ember.Mixin.create({
+  onClickElsewhere: Ember.K,
+  clickHandler: bound("elsewhereHandler"),
+  elsewhereHandler: function(e) {
+    var $target, element, thisIsElement;
+    element = this.get("element");
+    $target = $(e.target);
+    thisIsElement = $target.closest(element).length === 1;
+    if (!thisIsElement) {
+      return this.onClickElsewhere(event);
+    }
+  },
+  didInsertElement: function() {
+    this._super.apply(this, arguments);
+    return $(window).on("click", this.get("clickHandler"));
+  },
+  willDestroyElement: function() {
+    $(window).off("click", this.get("clickHandler"));
+    return this._super.apply(this, arguments);
+  }
+});
+
+App.ComfirmDeleteComponent = Em.Component.extend(App.ClickElsewhereMixin,{
+  confirm:false,
+  onClickElsewhere:function () {
+    this.set('confirm',false);
+  },
+  actions: {
+    delete: function() {
+      if (this.get('confirm')) {
+        this.toggleProperty('confirm');
+        this.sendAction('action', this.get('param'));
+      } else {
+        this.toggleProperty('confirm');
+      };
+    }
+  },
+  tagName:'a',
+  tooltip:function () {
+    var element = this.$();
+    if (this.get('confirm')) {
+      element.tooltip({
+        placement:'left',
+        title:'Click again to confirm'
+      }).tooltip('show');
+    } else {
+      element.tooltip('destroy')
+    };
+  }.observes('confirm'),
+  click:function (e) {
+    this.send('delete');
+  },
+  didChange:function () {
+    this.set('confirm',false);
+  }.observes('param'),
+
+  classNames:['pull-right rm-queue'],
+  layout:Em.Handlebars.compile('<span class="fa-stack"> <i class="fa fa-times fa-stack-2x"></i> {{#if confirm}} <i class="fa fa-check fa-stack-1x fa-stack-bottom green"></i> {{/if}} </span> ')
+});

+ 48 - 0
contrib/views/capacity-scheduler/src/main/resources/ui/app/components/escapeAcl.js

@@ -0,0 +1,48 @@
+/**
+ * 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.
+ */
+
+
+Ember.Handlebars.helper('escapeACL', function(value) {
+  var output = '';
+  
+  value = value || '';
+
+  if (value.trim() == '') {
+    output = '<span class="label label-danger"> <i class="fa fa-ban fa-fw"></i>  Nobody </span> ';
+  } else if (value.trim() == '*') {
+    output = '<label class="label label-success"> <i class="fa fa-asterisk fa-fw"></i> Anyone</label>';
+  } else {
+    var ug = value.split(' ');
+    var users = ug[0].split(',')||[]; 
+    var groups = (ug.length == 2)?ug[1].split(',')||[]:[];
+
+    output += ' <span class="users"> '
+
+    users.forEach(function (user) {
+      output += (user)?'<span class="label label-primary"><i class="fa fa-user fa-fw"></i> '+ user +'</span> ':'';
+    });
+
+    output += ' </span> <span class="groups"> '
+
+    groups.forEach(function (group) {
+      output += (group)?'<span class="label label-primary"><i class="fa fa-users fa-fw"></i> '+ group +'</span> ':'';
+    });
+    output += ' </span> '
+  }
+  return new Ember.Handlebars.SafeString(output);
+});

+ 61 - 0
contrib/views/capacity-scheduler/src/main/resources/ui/app/components/pathInput.js

@@ -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.
+ */
+
+var App = require('app');
+
+App.PathInputComponent = Em.Component.extend({
+  layoutName:'components/pathInput',
+  actions:{
+    add:function () {
+      var path = this.get('path'),
+          basedir = path.substr(0,path.lastIndexOf('.'));
+
+      if (this.get('pathMap').contains(path)) {
+        this.sendAction('action',path);
+        this.set('activeFlag',false);
+      } else if (this.get('pathMap').contains(basedir)) {
+        this.sendAction('action',basedir,path.substr(path.lastIndexOf('.')+1));
+        this.set('activeFlag',false);
+      }
+    },
+    cancel:function () {
+      this.set('activeFlag',false);
+    }
+  }, 
+  queues:[],
+  activeFlag:false,
+  pathMap:Em.computed.mapBy('queues','path'),
+  path:'',
+  inputFieldView: Em.TextField.extend({
+    classNames:['form-control newQPath'],
+    action:'add',
+    pathSource:[],
+    placeholder:"Enter queue path...",
+    typeaheadInit:function () {
+      $(this.get('element')).typeahead({
+          source: this.get('pathSource'),
+          matcher: function (item) {
+            return ~item.toLowerCase().indexOf(this.query.toLowerCase())
+          },
+          minLength:0,
+          items:100,
+          scrollHeight:5
+      }).focus();
+    }.observes('pathSource').on('didInsertElement'),
+  })
+});

+ 42 - 0
contrib/views/capacity-scheduler/src/main/resources/ui/app/components/queueListItem.js

@@ -0,0 +1,42 @@
+/**
+ * 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.
+ */
+
+var App = require('app');
+
+App.RecurceQueuesComponent = Ember.View.extend({
+  templateName: "components/queueListItem",
+  depth:0,
+  parent:'',
+  leaf:function () {
+    return this.get('controller.arrangedContent')
+      .filterBy('depth',this.get('depth'))
+      .filterBy('parentPath',this.get('parent'));
+  }.property('depth','parent','controller.content.length','controller.content.@each.name'),
+  childDepth:function () {
+    return this.get('leaf.firstObject.depth')+1;
+  }.property('depth'),
+  didInsertElement:function () {
+    Ember.run.scheduleOnce('afterRender',null, this.setFirstAndLast, this);
+  },
+  setFirstAndLast:function (item) {
+    var items = item.$().parents('.queue-list').find('.list-group-item');
+    items.first().addClass('first');
+    items.last().addClass('last');
+  }
+
+});

+ 46 - 0
contrib/views/capacity-scheduler/src/main/resources/ui/app/components/radioButton.js

@@ -0,0 +1,46 @@
+/**
+ * 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.
+ */
+
+var App = require('app');
+
+App.RadioButtonInputComponent = Ember.View.extend({
+    tagName : "input",
+    type : "radio",
+    attributeBindings : [ "type", "value", "checked:checked" ],
+    click : function() {
+      this.set("selection", this.get('value'))
+    }
+});
+
+App.RadioButtonComponent = Em.Component.extend({
+  tagName:'label',
+  classNames:['btn btn-default'],
+  classNameBindings:['isActive:active'],
+
+  //arguments
+  selection:null,
+  label:null,
+  value:null, 
+  click : function() {
+    this.set("selection", this.get('value'))
+  },
+  isActive : function() {
+    return this.get("value") == this.get("selection");   
+  }.property("selection"),
+  layout:Em.Handlebars.compile('{{label}} {{radio-button-input selection=selection value=value checked=isActive}}')
+});

+ 98 - 0
contrib/views/capacity-scheduler/src/main/resources/ui/app/components/totalCapacity.js

@@ -0,0 +1,98 @@
+/**
+ * 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.
+ */
+
+var App = require('app');
+
+App.TotalCapacityComponent = Ember.Component.extend({
+  layoutName:'components/totalCapacity',
+
+  actions:{
+    toggleEdit:function () {
+      this.toggleProperty('isEdit');
+    },
+    addQueue:function (path) {
+      this.sendAction('addQueue',path);
+    },
+    createQueue:function (queue) {
+      this.sendAction('createQueue',queue);
+    },
+    deleteQueue:function (queue) {
+      this.sendAction('deleteQueue',queue);
+    },
+  },
+
+  /**
+   * passed params
+   */
+  currentQueue:null,
+  allQueues:[],
+  allQueuesArranged:[],
+
+  isEdit:false,
+
+  disableEdit:function () {
+    this.set('isEdit',false);
+  }.observes('allQueues'),
+
+  currentPrPath:Em.computed.alias('currentQueue.parentPath'),
+
+  leafQueuesCapacity: Ember.computed.map('leafQueues.@each.capacity', function (queue) {
+    return Number(queue.get('capacity')); 
+  }),
+
+  totalCapacity: Ember.computed.sum('leafQueuesCapacity'),
+
+  leafQueues:function () {
+    return this.allQueuesArranged.filterBy('parentPath',this.get('currentPrPath')).filterBy('isNew',false);
+  }.property('allQueuesArranged.length','currentPrPath'),
+
+  newLeafQueues:function () {
+    return this.allQueues.filterBy('parentPath',this.get('currentPrPath')).filterBy('isNew',true);
+  }.property('allQueues.length','currentPrPath'),
+  
+  parentQueue:function () {
+    return this.allQueues.findBy('path',this.get('currentPrPath'));
+  }.property('allQueues','currentPrPath'),
+
+  currentInLeaf:function (argument) {
+    var leaf = this.get('leafQueues');
+    leaf.setEach('isCurrent',false);
+    if(!this.get('currentQueue.currentState.stateName').match(/root.deleted/)) {
+      this.get('currentQueue').set('isCurrent',true);
+    }
+  }.observes('leafQueues','currentQueue').on('init'),
+
+  newQueueNameField: Em.TextField.extend({
+    queue:null,
+    classNames:['form-control'],
+    classNameBindings:['isValid::input-error'],
+    isValid:Em.computed.bool('queue.isValid')
+  })
+});
+
+App.CapacityEditFormView = Em.View.extend({
+  mark:function () {
+    this.addObserver('controller.target.isEdit',this,'slide');
+    if (!this.get('controller.target.isEdit')) {
+      this.$('.capacity-edit-form').hide();
+    };
+  }.on('didInsertElement'),
+  slide:function () {
+    this.$('.capacity-edit-form').slideToggle(100);
+  }
+});

+ 44 - 0
contrib/views/capacity-scheduler/src/main/resources/ui/app/components/userGroupInput.js

@@ -0,0 +1,44 @@
+/**
+ * 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.
+ */
+
+var App = require('app');
+
+App.UserGroupInputComponent = Em.Component.extend({
+  layoutName:'components/userGroupInput',
+
+  users:null,
+  groups:null,
+
+  splitter:function () {
+    if (this.ug == '*') return;
+    var ug = this.ug || '';
+    var spl = ug.split(' ');
+    this.setProperties({
+      users:spl[0],
+      groups:spl[1]
+    });
+  }.observes('ug').on('init'),
+
+  setter:function () {
+    this.set('ug',[this.get('users'),this.get('groups')].join(' '));
+  }.observes('users','groups'),
+
+  noSpace:function (e) {
+    return (e.keyCode==32)?false:true;
+  },
+});

+ 21 - 0
contrib/views/capacity-scheduler/src/main/resources/ui/app/controllers.js

@@ -0,0 +1,21 @@
+/**
+ * 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.
+ */
+
+require('controllers/queue');
+require('controllers/queues');
+require('controllers/trace');

+ 186 - 0
contrib/views/capacity-scheduler/src/main/resources/ui/app/controllers/queue.js

@@ -0,0 +1,186 @@
+/**
+ * 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.
+ */
+
+var App = require('app');
+
+var _runState = 'RUNNING';
+var _stopState = 'STOPPED';
+
+App.QueueController = Ember.ObjectController.extend({
+  needs:['queues'],
+  actions:{
+    setState:function (state) {
+      var state = (state === "running")?_runState:_stopState;
+      this.content.set('state',state);
+    },
+    createQ:function (record) {
+      this.get('controllers.queues').send('createQ',this.get('content'));
+    },
+    addQ:function (path) {
+      this.get('controllers.queues').send('addQ',path);
+    },
+    delQ:function (record) {
+      this.get('controllers.queues').send('delQ',this.get('content'));
+    },
+    renameQ:function (opt) {
+      if (opt == 'ask') {
+        this.content.addObserver('name',this,this.setQueuePath);
+        this.toggleProperty('isRenaming');
+        return;
+      };
+      if (opt == 'cancel') {
+        this.send('rollbackProp','name');
+        this.send('rollbackProp','id');
+        this.send('rollbackProp','path');
+        this.content.removeObserver('name',this,this.setQueuePath);
+        this.toggleProperty('isRenaming');
+        return;
+      };
+      if (opt) {
+        var self = this;
+        this.store.filter('queue',function (q) {
+          return q.id === self.content.id;
+        }).then(function(queues){
+          if (queues.get('length') > 1) {
+            return self.content.get('errors').add('path', 'Queue already exists');
+          };
+          self.toggleProperty('isRenaming');
+          self.content.removeObserver('name',self,self.setQueuePath);
+          self.transitionToRoute('queue',self.content.id);
+        })
+      }
+
+    },
+    toggleEditRA:function () {
+      this.toggleProperty('isEditRA');
+    },
+    toggleEditACL:function () {
+      this.toggleProperty('isEditACL');
+    },
+    rollbackProp:function(prop){
+      attributes = this.content.changedAttributes();
+      if (attributes.hasOwnProperty(prop)) {
+        this.content.set(prop,attributes[prop][0]);
+      }
+    }
+  },
+  setQueuePath:function (queue,o) {
+    var name = queue.get(o);
+    var parentPath = queue.get('parentPath');
+
+    queue.setProperties({
+      name:name.replace(/\s|\./g, ''),
+      path:parentPath+'.'+name,
+      id:(parentPath+'.'+name).dasherize(),
+    });
+
+    if (name == '') {
+      queue.get('errors').add('path', 'This field is required');
+    }
+  },
+
+  isRenaming:false,
+
+  unsetRenaming:function () {
+    this.set('isRenaming',false);
+  }.observes('content'),
+
+  isRoot:Ember.computed.equal('content.id', 'root'),
+  isRunning:Ember.computed.equal('content.state', _runState),
+  allQueues:Em.computed.alias('controllers.queues.content'),
+  allQueuesArranged:Em.computed.alias('controllers.queues.arrangedContent'),
+
+  isEditRA:false,
+  isEditACL:false,
+
+  acl_administer_queue:null,
+  aaq_anyone:Ember.computed.equal('acl_administer_queue', '*'),
+  aaq_dirty:function () {
+    var attributes = this.content.changedAttributes();
+    return attributes.hasOwnProperty('acl_administer_queue');
+  }.property('content.acl_administer_queue'),
+
+  acl_administer_jobs:null,
+  aaj_anyone:Ember.computed.equal('acl_administer_jobs', '*'),
+  aaj_dirty:function () {
+    var attributes = this.content.changedAttributes();
+    return attributes.hasOwnProperty('acl_administer_jobs');
+  }.property('content.acl_administer_jobs'),
+
+  acl_submit_applications:null,
+  asa_anyone:Ember.computed.equal('acl_submit_applications', '*'),
+  asa_dirty:function () {
+    var attributes = this.content.changedAttributes();
+    return attributes.hasOwnProperty('acl_submit_applications');
+  }.property('content.acl_submit_applications'),
+
+  aclSerializer:function (c,o) {
+    var acl = o.substr(o.indexOf('.')+1);
+    var aclProp = c.get(o);
+    var aclVal;
+    switch(aclProp) {
+      case '*': 
+        aclVal = '*'; 
+        break;
+      
+      default:
+        aclVal = 'custom'; 
+    }
+    c.set(acl,aclVal);
+  }.observes('content.acl_administer_queue','content.acl_administer_jobs','content.acl_submit_applications'),
+
+  aclDeserializer:function (c,o) {
+    var aclVal = c.get(o);
+    var aclProp;
+    switch(aclVal) {
+      case '*': 
+        aclProp = '*'; 
+        this.set('content.'+o,aclProp);
+        break;
+      
+      default:
+        if (this.get('content.'+o)=='*') {
+          this.set('content.'+o,' ');
+        }
+    }
+  }.observes('acl_administer_queue','acl_administer_jobs','acl_submit_applications'),
+
+  capacityControl:function () {
+    var leafQueues = this.get('leafQueues');
+    var total = 0;
+
+    leafQueues.forEach(function (queue) {
+      total+=Number(queue.get('capacity'));
+    });
+    leafQueues.setEach('overCapacity',total>100)
+  }.observes('content.capacity','leafQueues.@each.capacity'),
+  leafQueues:function () {
+    return this.get('allQueues').filterBy('parentPath',this.get('content.parentPath'));
+  }.property('allQueues.length','content.parentPath'),
+
+  queueNamesControl:function (c,o) {
+    var leaf = c.get('leafQueues');
+    var parent = c.get('allQueues').filterBy('path',c.get('content.parentPath')).get('firstObject');
+    if (parent) parent.set('queueNames',leaf.mapBy('name').join());
+  }.observes('allQueues.length','allQueues.@each.name'),
+
+  pathErrors:Ember.computed.mapBy('content.errors.path','message')
+  
+});
+
+App.ErrorController = Ember.ObjectController.extend();

+ 154 - 0
contrib/views/capacity-scheduler/src/main/resources/ui/app/controllers/queues.js

@@ -0,0 +1,154 @@
+/**
+ * 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.
+ */
+
+var App = require('app');
+
+var cmp = Ember.computed;
+
+App.QueuesController = Ember.ArrayController.extend({
+  sortProperties: ['name'],
+  sortAscending: true,
+  actions:{
+    goToQueue:function (queue) {
+      this.transitionToRoute('queue',queue)
+    },
+    askPath:function () {
+      this.set('isWaitingPath',true);
+    },
+    addQ:function (parentPath,name) {
+      if (!parentPath || this.get('hasNewQueue')) {
+        return;
+      };
+      name = name || '';
+      var newQueue = this.store.createRecord('queue',{
+        name:name,
+        parentPath: parentPath,
+        depth: parentPath.split('.').length,
+        isNewQueue:true
+      });
+      this.set('newQueue',newQueue);
+      this.send('goToQueue',newQueue);
+    },
+    createQ:function (record) {
+      record.save().then(Em.run.bind(this,this.set,'newQueue',null));
+    },
+    delQ:function (record) {
+      if (record.get('isNew')) {
+        this.set('newQueue',null);
+      };
+      if (!record.get('isNewQueue')) {
+        this.set('hasDeletedQueues',true);
+      };
+      if (record.isCurrent) {
+        this.transitionToRoute('queue',record.get('parentPath'));
+      };
+      this.store.deleteRecord(record);
+    },
+    saveConfig:function (mark) {
+      if (mark == 'restart') {
+        this.get('store').markForRestart();
+      } else if (mark == 'refresh') {
+        this.get('store').markForRefresh();
+      };
+      Em.RSVP.Promise.all([this.get('scheduler').save(),this.get('model').save()])
+        .catch(Em.run.bind(this,this.saveError));
+    },
+    toggleEditScheduler:function () {
+      this.toggleProperty('isEditScheduler');
+    },
+    clearAlert:function () {
+      this.set('alertMessage',null);
+    }
+  },
+
+  alertMessage:null,
+  saveError:function (error) {
+    var response = JSON.parse(error.responseText);
+    this.set('alertMessage',response);
+  },
+
+  isEditScheduler:false,
+
+  isWaitingPath:false,
+  /**
+   * check if RM needs refresh
+   * @type {bool}
+   */
+  needRefresh: cmp.any('hasChanges', 'hasNewQueues','dirtyScheduler'),
+
+  /**
+   * props for 'needRefresh'
+   */
+  dirtyQueues: cmp.filterBy('content', 'isDirty', true),
+  dirtyScheduler: cmp.bool('scheduler.isDirty'),
+  newQueues: cmp.filterBy('content', 'isNewQueue', true),
+  hasChanges: cmp.notEmpty('dirtyQueues.[]'),
+  hasNewQueues: cmp.notEmpty('newQueues.[]'),
+  
+  /**
+   * check if RM needs restart 
+   * @type {bool}
+   */
+  needRestart: cmp.any('hasDeletedQueues', 'hasRenamedQueues'),
+  
+  /**
+   * props for 'needRestart'
+   */
+  hasDeletedQueues:false,
+  hasRenamedQueues: cmp.notEmpty('renamedQueues.[]'),
+  renamedQueues:function () {
+    return this.content.filter(function(queue){
+      var attr = queue.changedAttributes();
+      return attr.hasOwnProperty('name') && !queue.get('isNewQueue');
+    });
+  }.property('content.@each.name'),
+
+  /**
+   * check if can save configs
+   * @type {bool}
+   */
+  canNotSave: cmp.any('hasOverCapacity', 'hasUncompetedAddings','hasNotValid'),
+
+  /**
+   * props for canNotSave
+   */
+  notValid:cmp.filterBy('content','isValid',false),
+  overCapacityQ:cmp.filterBy('content','overCapacity',true),
+  uncompetedAddings:cmp.filterBy('content', 'isNew', true),
+  hasNotValid:cmp.notEmpty('notValid.[]'),
+  hasOverCapacity:cmp.notEmpty('overCapacityQ.[]'),
+  hasUncompetedAddings:cmp.notEmpty('uncompetedAddings.[]'),
+
+  newQueue:null,
+  hasNewQueue: cmp.bool('newQueue'),
+  trackNewQueue:function () {
+    var newQueue = this.get('newQueue');
+    if (Em.isEmpty(newQueue)){
+      return;
+    };
+    var name = newQueue.get('name');
+    var parentPath = newQueue.get('parentPath');
+
+    this.get('newQueue').setProperties({
+      name:name.replace(/\s/g, ''),
+      path:parentPath+'.'+name,
+      id:(parentPath+'.'+name).dasherize(),
+    });
+
+  }.observes('newQueue.name')
+});

+ 26 - 0
contrib/views/capacity-scheduler/src/main/resources/ui/app/controllers/trace.js

@@ -0,0 +1,26 @@
+/**
+ * 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.
+ */
+
+var App = require('app');
+
+App.TraceController = Ember.ObjectController.extend({
+  needs:['queues'],
+  trace:function () {
+    return this.get('controllers.queues.alertMessage.trace')||'No trace';
+  }.property('controllers.queues.alertMessage')
+});

+ 40 - 0
contrib/views/capacity-scheduler/src/main/resources/ui/app/initialize.js

@@ -0,0 +1,40 @@
+/**
+ * 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.
+ */
+
+window.App = require('app');
+
+// Set this value to true to run in test mode with local data
+App.testMode = false;
+
+// adapters
+require('adapters');
+
+//components
+require('components');
+
+//controllers
+require('controllers');
+
+// templates
+require('templates');
+
+// models
+require('models');
+
+// routes
+require('router');

+ 19 - 0
contrib/views/capacity-scheduler/src/main/resources/ui/app/models.js

@@ -0,0 +1,19 @@
+/**
+ * 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.
+ */
+
+require('models/queue');

+ 64 - 0
contrib/views/capacity-scheduler/src/main/resources/ui/app/models/queue.js

@@ -0,0 +1,64 @@
+/**
+ * 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.
+ */
+
+var App = require('app');
+
+App.Scheduler = DS.Model.extend({
+  maximum_am_resource_percent: DS.attr('number', { defaultValue: 0 }),
+  maximum_applications: DS.attr('number', { defaultValue: 0 }),
+  node_locality_delay: DS.attr('number', { defaultValue: 0 })
+});
+
+/**
+ * Represents the queue.
+ *
+ */
+App.Queue = DS.Model.extend({
+  name: DS.attr('string'),
+  parentPath: DS.attr('string'),
+  depth: DS.attr('number'),
+  path: DS.attr('string'),
+
+  // cs props
+  
+  // queue props
+  state: DS.attr('string', { defaultValue: 'RUNNING' }),
+
+  capacity: DS.attr('number', { defaultValue: 0 }),
+  maximum_capacity: DS.attr('number', { defaultValue: 0 }),
+  unfunded_capacity: DS.attr('number', { defaultValue: 0 }),
+  
+  acl_administer_queue: DS.attr('string', { defaultValue: '*' }),
+  acl_administer_jobs: DS.attr('string', { defaultValue: '*' }),
+  acl_submit_applications: DS.attr('string', { defaultValue: '*' }),
+  
+  minimum_user_limit_percent: DS.attr('number', { defaultValue: 0 }),
+  user_limit_factor: DS.attr('number', { defaultValue: 0 }),
+  
+  queueNames: DS.attr('string'),
+  queueNamesArray:function () {
+    return (this.get('queueNames.length')>0)?this.get('queueNames').split(','):[];
+  }.property('queueNames'),
+
+  
+
+  overCapacity:false,
+
+  //new queue flag
+  isNewQueue:DS.attr('boolean', {defaultValue: false})
+});

+ 122 - 0
contrib/views/capacity-scheduler/src/main/resources/ui/app/router.js

@@ -0,0 +1,122 @@
+/**
+ * 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.
+ */
+
+var App = require('app');
+
+App.Router.map(function() {
+  this.resource('queues', { path: '/queues' }, function() {
+    this.resource('queue', { path: '/:queue_id' });
+    this.resource('trace', { path: '/log' });
+  });
+});
+
+
+/**
+ * The queues route.
+ *
+ * /queues
+ */
+App.TraceRoute = Ember.Route.extend({
+  model: function() {
+    return this.controllerFor('queues').get('alertMessage');
+  }
+});
+
+/**
+ * The queues route.
+ *
+ * /queues
+ */
+App.QueuesRoute = Ember.Route.extend({
+  actions:{
+  },
+  model: function() {
+    return this.store.find('queue');
+  },
+  setupController:function (c,model) {
+    c.set('model',model);
+    this.store.find('scheduler','scheduler').then(function (s) {
+      c.set('scheduler',s);
+    });
+  }
+});
+
+/**
+ * The queue route.
+ *
+ * /queues/:id
+ */
+App.QueueRoute = Ember.Route.extend({
+  model: function(params,tr) {
+    var queues = this.modelFor('queues') || this.store.find('queue'),
+        filterQueues = function (queues) {
+          return queues.filterBy('id',params.queue_id).get('firstObject');
+        };
+
+    return (queues instanceof DS.PromiseArray)?queues.then(filterQueues):filterQueues(queues);
+  },
+  afterModel:function (model) {
+    if (!model) {
+      this.transitionTo('queues');
+    }
+  },
+  
+  actions: {
+    refreshQueue: function() {
+    var x = "{ \"RequestInfo\" : { \"command\" : \"REFRESHQUEUES\", \"context\" : \"Refresh YARN Capacity Scheduler\" }, "
+    x = x + "\"Requests/resource_filters\":[{\"service_name\":\"YARN\",\"component_name\":\"RESOURCEMANAGER\",\"hosts\":\"c6403.ambari.apache.org\"}]}";
+    
+      App.Adapter.ajaxPost("/api/v1/clusters/MyCluster/requests", x);
+    },
+    willTransition:function (tr) {
+      if (this.get('controller.isRenaming')) {
+        tr.abort();
+      };
+    }
+  }
+
+});
+
+/**
+ * Routes index to /queues path
+ *
+ */
+App.IndexRoute = Ember.Route.extend({
+  beforeModel: function() {
+    this.transitionTo('queues');
+  }
+});
+
+/**
+ * Loading spinner page.
+ *
+ */
+App.LoadingRoute = Ember.Route.extend();
+
+/**
+ * Error page.
+ *
+ */
+App.ErrorRoute = Ember.Route.extend({
+  setupController:function (controller,model) {
+    var response = JSON.parse(model.responseText);
+    controller.set('model',response);
+  }
+});
+
+

+ 238 - 0
contrib/views/capacity-scheduler/src/main/resources/ui/app/styles/application.less

@@ -0,0 +1,238 @@
+/**
+ * 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.
+ */
+
+.wrap {
+  padding: 15px;
+}
+
+.btn {
+  padding: 6px 10px;
+  transition: background-color .08s linear 0s;
+}
+
+.input-error  {
+  color: #a94442;
+  border-color: #a94442;
+  -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075);
+          box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075);
+}
+
+.input-error:focus {
+  border-color: #843534;
+  -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 6px #ce8483;
+          box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 6px #ce8483;
+}
+
+.gray {
+  color: #999;
+}
+
+.green {
+  color: #5cb85c;
+}
+
+.blue {
+  color: #428bca;
+}
+
+.red {
+  color: #d9534f;
+}
+
+.add-queue {
+  margin-bottom: 5px;
+}
+
+.rm-queue {
+  cursor: pointer;
+  .gray;
+  transition: color .1s linear;
+  &:hover {
+    .red;
+  } 
+}
+
+.fa-stack-bottom {
+  text-align: right;
+  bottom: 0;
+  line-height: 1em;
+  //opacity: 0.9;
+  text-shadow: 0px -1px 3px #111;
+}
+
+.queue-list{
+  .badge {
+    color: #999;
+    background-color: #fff;
+    margin-left: 5px;
+  }
+    .progress {
+      height: 3px;
+      margin: 0;
+    }
+    .spacer {
+      height: 25px;
+    }
+    .spacer.col-md-0 {
+      height: 0;
+    }
+    .capacity-bar {
+      display: inline-block;
+      float: right;
+      margin-top: 8px;
+      width: 20%;
+    }
+  .list-group-item {
+    cursor: pointer;
+    transition: background-color .1s linear 0s;
+  }
+  .list-group-item.last {
+    border-bottom-left-radius: 4px;
+    border-bottom-right-radius: 4px;
+  }
+  .list-group-item.first {
+    border-top-left-radius: 4px;
+    border-top-right-radius: 4px;
+  }
+}
+
+.path-input {
+  .btn-cancel {
+    border-radius: 0;
+  }
+}
+
+.user-group-input.has-feedback  {
+  .form-control-feedback {
+    top: -2px;
+    right: 15px;
+    opacity: 0.4;
+  }
+}
+
+.btn-group-save {
+  table-layout: auto;
+  .btn-group {
+    width: 100%;
+    .btn-save {
+      border-top-left-radius: 4px !important;
+      border-bottom-left-radius: 4px !important;
+    }
+  }
+}
+
+.form-acl {
+  .users .label,.groups .label {
+    border-radius: 0;
+  }
+  .users .label:first-child,.groups .label:first-child {
+    border-top-left-radius: .25em;
+    border-bottom-left-radius: .25em;
+  }
+  .users .label:last-child,.groups .label:last-child {
+    border-top-right-radius: .25em;
+    border-bottom-right-radius: .25em;
+  }
+  .label {
+    vertical-align: text-top;
+  }
+}
+
+.panel-capacity {
+  .new-queue {
+    .input-row {
+      margin-bottom: 5px;
+    } 
+  }
+  .progress{
+    background-color: #ddd;
+    .marks {
+      width: 100%;
+      top: 20px;
+      position: absolute;
+      label {
+        width: 25%;
+        display: block;
+        text-align: center;
+        position: absolute;
+        margin-left: -12.5%;
+        div {
+          position: absolute;
+          left: 50%;
+          top: -5px;
+          height: 20px;
+          border-left: solid 1px #fff; 
+          -webkit-box-shadow:0px 0px 2px 1px rgba(0, 0, 0, .15);
+          box-shadow:0px 0px 2px 1px rgba(0, 0, 0, .15);
+        }
+      }
+    }
+  }
+  .panel-title {
+    position: relative;
+    a {
+      position: absolute;
+      right: 0;
+      color: #428bca;
+    }
+  }
+  .panel-body.total{
+    .progress {
+      margin: 0;
+    }
+  }
+  .panel-body.queues {
+    border-top: 1px solid #dddddd;
+    background-color: #f5f5f5;
+  }
+  .queue-capacity{
+    border-bottom: 1px solid #dddddd;
+    padding-bottom: 8px;
+    padding-top: 8px;
+    &.active {
+      .progress {
+        -webkit-box-shadow: 0px 0px 0px #888888;
+        -moz-shadow: 0px 0px 0px #888888;
+        box-shadow: 0px 0px 0px #888888;
+        transition: box-shadow .25s linear 0s;
+      }
+        .progress.in {
+          -webkit-box-shadow: 3px 5px 5px #888888;
+          -moz-shadow: 3px 5px 5px #888888;
+          box-shadow: 3px 5px 5px #888888;
+        }
+    }
+    .form-inline .form-group {
+      display: inline-block;
+      margin-bottom: 0;
+      vertical-align: top;
+    }
+    .help-block {
+      display: inline;
+    }
+    /*input {
+      width: auto;
+    }*/
+  }
+}
+
+.queue-heading-row {
+  h3 {
+    margin:10px;
+  }
+}

+ 36 - 0
contrib/views/capacity-scheduler/src/main/resources/ui/app/templates.js

@@ -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.
+ */
+
+// load templates
+
+require('templates/error');
+require('templates/trace');
+require('templates/loading');
+require('templates/queue');
+require('templates/queues');
+require('templates/error');
+require('templates/queues/index');
+
+require('templates/capacityEditForm');
+require('templates/schedulerPanel');
+
+require('templates/components/capacityBar');
+require('templates/components/totalCapacity');
+require('templates/components/queueListItem');
+require('templates/components/pathInput');
+require('templates/components/userGroupInput');

+ 34 - 0
contrib/views/capacity-scheduler/src/main/resources/ui/app/templates/capacityEditForm.hbs

@@ -0,0 +1,34 @@
+{{!
+* 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.
+}}
+
+<form {{bind-attr class=":form-inline :capacity-edit-form " }} role="form">
+  <div {{bind-attr class=":form-group" }}>
+    <label  class="control-label">Capacity: </label>
+    {{capacity-input class='input-sm' value=this.capacity totalCapacity=view.totalCapacity queue=this maxVal=100}}
+    
+  </div>
+  <div {{bind-attr class=":form-group this.isValid::has-error" }}>
+    <label class="control-label">Maximum capacity: </label>
+    {{max-capacity-input class='input-sm' value=this.maximum_capacity totalCapacity=view.totalCapacity queue=this maxVal=100}}
+    {{#each this.errors.maximum_capacity}}
+      <span class="help-block">
+        {{message}}
+      </span>
+    {{/each}}
+  </div>
+</form>

+ 35 - 0
contrib/views/capacity-scheduler/src/main/resources/ui/app/templates/components/capacityBar.hbs

@@ -0,0 +1,35 @@
+{{!
+* 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.
+}}
+
+<div class="progress progress-labelled" data-label-max="100" data-label-step="25" style="position: relative;">
+    <div {{bind-attr style="capacityWidth"  class=":progress-bar warn:progress-bar-danger:progress-bar-success"}}>
+      <span>
+        {{capacityValue}}%
+      </span>
+    </div>
+      <div class="progress-bar progress-bar-warning" {{bind-attr style="maxCapacityWidth"}}>
+      </div>
+    
+    <div class="marks">
+      <label style="left: 25%;"><div></div></label>
+      <label style="left: 50%;"><div></div></label>
+      <label style="left: 75%;"><div></div></label>
+    </div>
+
+</div>
+

+ 29 - 0
contrib/views/capacity-scheduler/src/main/resources/ui/app/templates/components/pathInput.hbs

@@ -0,0 +1,29 @@
+{{!
+* 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.
+}}
+
+<div class="input-group path-input">
+  {{view inputFieldView pathSource=pathMap value=path}}
+  <span class="input-group-btn">
+    <button {{action cancel}} class="btn btn-danger" type="button">
+      <i class="fa fa-times"></i>
+    </button>
+    <button {{action add}} class="btn btn-success" type="button">
+      <i class="fa fa-check"></i>
+    </button>
+  </span>
+</div>

+ 60 - 0
contrib/views/capacity-scheduler/src/main/resources/ui/app/templates/components/queueListItem.hbs

@@ -0,0 +1,60 @@
+{{!
+* 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.
+}}
+
+{{#each view.leaf}}
+  {{#link-to "queue" this classNameBindings=":list-group-item overCapacity:list-group-item-danger isNew:list-group-item-info" }}
+    <div class="col-md-offset-{{unbound this.depth}} col-sm-offset-{{unbound this.depth}} col-xs-offset-{{unbound this.depth}}">
+      
+    {{name}} ({{capacity}}%)
+
+    <span class="badge pull-right">
+      {{#if overCapacity}}
+        <i class="fa fa-fw fa-lg red fa-warning"></i>
+      {{/if}}
+      {{#if isNewQueue }}
+        {{#if isSaving}}
+        <i class="fa fa-fw fa-lg gray fa-spinner fa-spin"></i>
+        {{else}}
+        <i class="fa fa-fw fa-lg blue fa-plus"></i> 
+        {{/if}}
+      {{else}}
+        {{#if isError}}
+          {{#if isSaving}}
+            <i class="fa fa-fw fa-lg gray fa-spinner fa-spin"></i>
+          {{else}}
+            <span> queue was not saved </span> <i class="fa fa-fw fa-lg red fa-warning"></i>
+          {{/if}}
+        {{else}}    
+          {{#if isDirty}}
+            {{#if isSaving}}
+              <i class="fa fa-fw fa-lg gray fa-spinner fa-spin"></i>
+            {{else}}
+              <i class="fa fa-fw fa-lg blue fa-pencil"></i>
+            {{/if}}
+          {{else}}
+            <i class="fa fa-fw fa-lg green fa-check"></i> 
+          {{/if}}
+        {{/if}}
+      {{/if}}
+    </span>
+    
+    </div>
+  {{/link-to}}
+
+  {{recurce-queues depth=view.childDepth parent=this.path}}
+{{/each}}

+ 72 - 0
contrib/views/capacity-scheduler/src/main/resources/ui/app/templates/components/totalCapacity.hbs

@@ -0,0 +1,72 @@
+{{!
+* 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.
+}}
+
+<div class="panel panel-default panel-capacity">
+  <div class="panel-heading">
+  <div class="panel-title">
+    <strong>Capacity</strong>
+    <a href="#" {{action 'toggleEdit'}} class="text-right"> 
+      {{#if view.isEdit}}
+        <i class="fa fa-edit">Hide edit</i> 
+      {{else}}
+        <i class="fa fa-edit">Show edit</i> 
+      {{/if}}
+    </a>
+  </div>
+  </div>
+  <div class="panel-body total">
+    TOTAL
+    {{capacity-bar capacityValue=totalCapacity maxCapacityValue=totalCapacity warn=leafQueues.firstObject.overCapacity}}
+  </div>
+  <div class="panel-body queues">
+    {{#each leafQueues}}
+      <div {{bind-attr class=":queue-capacity this.isCurrent:active"}}>
+        <p>
+          {{this.name}} {{#if isCurrent}} <span class="label label-default">Current</span> {{/if}}
+        </p>
+        {{capacity-bar capacityValue=this.capacity maxCapacityValue=this.maximum_capacity warn=this.overCapacity}}
+        {{render "capacityEditForm" this}}
+      </div>
+    {{/each}}
+    {{#each newLeafQueues}}
+      <div {{bind-attr class=":queue-capacity :new-queue this.isCurrent:active"}} >
+        <div class="input-row row" >
+          <div class="col-md-5">
+            <div class="input-group">
+              {{view view.newQueueNameField value=this.name queue=this placeholder="Enter queue name..."}}
+              <span class="input-group-btn">
+                <button {{action "deleteQueue" this}} {{bind-attr class=":btn :btn-danger"}} type="button">Cancel</button>
+                <button {{action "createQueue" this}} {{bind-attr class=":btn :btn-default this.isValid::disabled"}} type="button">Create</button>
+              </span>
+            </div>
+            {{#each this.errors.path}}
+              <p class="help-block red">{{this.message}}</p>
+            {{/each}}
+          </div>
+        </div>
+        {{capacity-bar capacityValue=this.capacity maxCapacityValue=this.maximum_capacity warn=this.overCapacity}}
+        {{render "capacityEditForm" this}}
+      </div>
+    {{/each}}
+    {{#if currentQueue.parentPath}}
+    <div class="queue-capacity" >
+      <button {{action "addQueue" currentQueue.parentPath}} {{bind-attr class=":btn :btn-default :btn-block hasNew:disabled"}}><i class="fa fa-plus"></i>  Add queue</button>
+    </div>
+    {{/if}}
+  </div>
+</div>

+ 37 - 0
contrib/views/capacity-scheduler/src/main/resources/ui/app/templates/components/userGroupInput.hbs

@@ -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.
+}}
+
+<div class="user-group-input form-group has-feedback "> 
+  <div class="col-sm-4 control-label">
+    Users:
+  </div> 
+  <div class="col-sm-8"> 
+    {{input keyDown=noSpace disabledBinding="disabled" class="form-control input-sm" value=users placeholder="Comma-separated list of users"}}
+    <i class="fa fa-user form-control-feedback"></i>
+  </div> 
+</div> 
+<div class="form-group has-feedback user-group-input">
+  <div class="col-sm-4 control-label">
+    Groups:
+  </div> 
+  <div class="col-sm-8"> 
+    {{input keyDown=noSpace disabledBinding="disabled" class="form-control input-sm" value=groups placeholder="Comma-separated list of groups"}} 
+    <i class="fa fa-users form-control-feedback"></i>
+  </div> 
+</div>
+

+ 35 - 0
contrib/views/capacity-scheduler/src/main/resources/ui/app/templates/error.hbs

@@ -0,0 +1,35 @@
+{{!
+* 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.
+}}
+
+<div class="panel panel-danger">
+  <div class="panel-heading">
+    <div class="text-center">
+      <strong>{{content.status}}</strong>  {{content.message}}
+      {{#if content.trace}}
+        <a data-toggle="collapse" class="btn btn-danger btn-xs" href="#collapseOne">Trace <span class="caret"></span></a>
+      {{/if}}
+    </div>
+  </div>
+  <div id="collapseOne" class="panel-collapse collapse">
+    <div class="panel-body">
+      <pre>
+        {{content.trace}}
+      </pre>
+    </div>
+  </div>
+</div>

+ 19 - 0
contrib/views/capacity-scheduler/src/main/resources/ui/app/templates/loading.hbs

@@ -0,0 +1,19 @@
+{{!
+   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.
+}}
+
+<div class="spinner spinner-bg"></div>

+ 229 - 0
contrib/views/capacity-scheduler/src/main/resources/ui/app/templates/queue.hbs

@@ -0,0 +1,229 @@
+{{!
+* 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.
+}}
+
+<div class="well">
+  
+  <div class="row queue-heading-row">
+      <div class="col-md-12">
+          {{#if content.isNew}}
+            <div class="input-group">
+              {{focus-input value=content.name class="form-control" classBinding="content.isValid::input-error" action="createQ" revert="delQ" placeholder="Enter queue name..."}}
+              <span class="input-group-btn">
+                <button {{action "delQ" content target="controllers.queues"}} {{bind-attr class=":btn :btn-danger"}} type="button">Cancel</button>
+                <button {{action "createQ" content target="controllers.queues"}} {{bind-attr class=":btn :btn-default content.isValid::disabled"}} type="button">Create</button>
+              </span>
+            </div>
+            {{else}}
+              {{#unless isRenaming}}
+                <h3>
+                  {{content.name}}
+                  {{#unless isRoot}}
+                  <small>
+                    <a href="#" {{action "renameQ" 'ask'}} class="">
+                      <i class="fa fa-edit"></i>
+                    </a>
+                  </small>
+                  <small>
+                    {{comfirm-delete action="delQ" param=content}}
+                  </small>
+                  {{/unless}}
+                </h3>
+              {{else}}
+              <div class="input-group">
+                {{focus-input value=content.name class="form-control" action="renameQ" revert="renameQ" classBinding="content.isValid::input-error" placeholder="Enter queue name..."}}
+                <span class="input-group-btn">
+                  <button {{action "renameQ" 'cancel'}} {{bind-attr class=":btn :btn-danger"}} type="button">Cancel</button>
+                  <button {{action "renameQ" 'rename'}} {{bind-attr class=":btn :btn-default content.isValid::disabled"}} type="button">Rename</button>
+                </span>
+              </div>
+              {{/unless}}
+          {{/if}}
+            {{#each pathErrors}}
+                <p class="help-block red">{{this}}</p>
+            {{/each}}
+          <h4 ><small>{{content.path}}</small>
+            <div class="btn-group btn-group-xs" data-toggle="buttons" >
+              <label  {{action 'setState' 'running'}} {{bind-attr class=":btn isRunning:btn-success:btn-default isRunning:active" }}>
+                <input type="radio" name="options"> Running
+              </label>
+              <label  {{action 'setState' 'stopped'}} {{bind-attr class=":btn isRunning:btn-default:btn-danger isRunning::active" }}>
+                <input type="radio" name="options"> Stopped
+              </label>
+            </div>
+          </h4>
+      </div>
+  </div>
+  <div class="row queue-capacity-row">
+    <div class="col-md-12">
+      {{total-capacity 
+        currentQueue=content 
+        allQueues=allQueues 
+        allQueuesArranged=allQueuesArranged 
+        addQueue="addQ" 
+        createQueue="createQ" 
+        deleteQueue="delQ" 
+        hasNew=controllers.queues.hasNewQueue
+      }}
+    </div>
+  </div>
+  <div class="row queue-acl-row">
+    <div class="col-md-6">
+      <div class="panel panel-default panel-capacity">
+        <div class="panel-heading">
+          <div class="panel-title">
+            Access control
+            <a href="#" {{action 'toggleEditACL'}} class="text-right"> 
+              {{#if isEditACL}}
+                <small><i class="fa fa-edit">  Hide edit</i> </small>
+              {{else}}
+                <small><i class="fa fa-edit">Show edit</i> </small>
+              {{/if}}
+            </a>
+          </div>
+        </div>
+        <div class="panel-body">
+          <form class="form-horizontal form-acl" role="form">
+          {{#if isEditACL}}
+
+            <div class="form-group row">
+              <label class="col-lg-6 col-md-6 col-sm-4 col-xs-5 control-label">Administer Queue ACL</label>
+              <div class="col-lg-6 col-md-6 col-sm-8 col-xs-7">
+                <div class="btn-group btn-group-xs" data-toggle="buttons">
+                  {{radio-button label="Anyone" selectionBinding="acl_administer_queue" value="*"}}
+                  {{radio-button label="Custom" selectionBinding="acl_administer_queue" value="custom"}}
+                </div>
+                {{#if aaq_dirty}}
+                  <a {{action 'rollbackProp' 'acl_administer_queue'}} href="#"><i class="fa fa-undo"></i></a>
+                {{/if}}
+              </div>
+            </div>
+            {{#unless aaq_anyone}}
+              {{user-group-input ug=content.acl_administer_queue disabled=aaq_anyone}}
+            {{/unless}}
+
+            <div class="form-group row">
+              <label class="col-lg-6 col-md-6 col-sm-4 col-xs-5 control-label">Administer Jobs ACL</label>
+              <div class="col-lg-6 col-md-6 col-sm-8 col-xs-7">
+                <div class="btn-group btn-group-xs" data-toggle="buttons">
+                  {{radio-button label="Anyone" selectionBinding="acl_administer_jobs" value="*"}}
+                  {{radio-button label="Custom" selectionBinding="acl_administer_jobs" value="custom"}}
+                </div>
+                {{#if aaj_dirty}}
+                  <a {{action 'rollbackProp' 'acl_administer_jobs'}} href="#"><i class="fa fa-undo"></i></a>
+                {{/if}}
+              </div>
+            </div>
+            {{#unless aaj_anyone}}
+              {{user-group-input ug=content.acl_administer_jobs disabled=aaj_anyone}}
+            {{/unless}}
+
+
+            <div class="form-group row">
+              <label class="col-lg-6 col-md-6 col-sm-4 col-xs-5 control-label">Submit Applications ACL</label>
+              <div class="col-lg-6 col-md-6 col-sm-8 col-xs-7">
+                <div class="btn-group btn-group-xs" data-toggle="buttons">
+                  {{radio-button label="Anyone" selectionBinding="acl_submit_applications" value="*"}}
+                  {{radio-button label="Custom" selectionBinding="acl_submit_applications" value="custom"}}
+                </div>
+                {{#if asa_dirty}}
+                  <a {{action 'rollbackProp' 'acl_submit_applications'}} href="#"><i class="fa fa-undo"></i></a>
+                {{/if}}
+              </div>
+            </div>
+            {{#unless asa_anyone}}
+              {{user-group-input ug=content.acl_submit_applications disabled=asa_anyone}}
+            {{/unless}}
+            
+          {{else}}
+            <div class="form-group">
+              <label class="col-lg-6 col-md-6 col-sm-6 col-xs-6 control-label">Administer Queue ACL</label>
+              <div class="col-lg-6 col-md-6 col-sm-6 col-xs-6">
+               <p class="form-control-static">
+                {{escapeACL content.acl_administer_queue}}
+               </p> 
+              </div>
+            </div>
+
+            <div class="form-group">
+              <label class="col-lg-6 col-md-6 col-sm-6 col-xs-6 control-label">Administer Jobs ACL</label>
+              <div class="col-lg-6 col-md-6 col-sm-6 col-xs-6">
+               <p class="form-control-static">
+                {{escapeACL content.acl_administer_jobs}}
+               </p> 
+              </div>
+            </div>
+
+            <div class="form-group">
+              <label class="col-lg-6 col-md-6 col-sm-6 col-xs-6 control-label">Submit Applications ACL</label>
+              <div class="col-lg-6 col-md-6 col-sm-6 col-xs-6">
+               <p class="form-control-static">
+                {{escapeACL content.acl_submit_applications}}
+               </p> 
+              </div>
+            </div>
+          {{/if}}
+          </form>
+        </div>
+      </div>
+    </div>
+
+    <div class="col-md-6">
+      <div class="panel panel-default panel-capacity">
+        <div class="panel-heading">
+          <div class="panel-title">
+            Resource Allocation
+            <a href="#" {{action 'toggleEditRA'}} class="text-right"> 
+              {{#if isEditRA}}
+                <small><i class="fa fa-edit">  Hide edit</i> </small>
+              {{else}}
+                <small><i class="fa fa-edit">Show edit</i> </small>
+              {{/if}}
+            </a>
+          </div>
+        </div>
+        <div class="panel-body">
+          <form class="form-horizontal" role="form">
+            <div class="form-group">
+              <label class="col-lg-6 col-md-6 col-sm-6 col-xs-6 control-label">User Limit Factor</label>
+              <div class="col-lg-6 col-md-6 col-sm-6 col-xs-6">
+              {{#if isEditRA}}
+                {{int-input value=content.user_limit_factor class="input-sm"}}
+              {{else}}
+                <p class="form-control-static">{{content.user_limit_factor}}</p>
+              {{/if}}
+              </div>
+            </div>
+            <div class="form-group">
+              <label class="col-lg-6 col-md-6 col-sm-6 col-xs-6 control-label">Minimum User Limit</label>
+              <div class="col-lg-6 col-md-6 col-sm-6 col-xs-6">
+                {{#if isEditRA}}  
+                  <div class="input-group">
+                    {{int-input value=content.minimum_user_limit_percent class="input-sm" maxVal=100}}
+                    <span class="input-group-addon">%</span>
+                  </div>
+                {{else}}
+                  <p class="form-control-static">{{content.minimum_user_limit_percent}} %</p>
+                {{/if}}
+              </div>
+            </div>
+          </form>
+        </div>
+      </div>
+    </div>
+  </div>
+</div>

+ 85 - 0
contrib/views/capacity-scheduler/src/main/resources/ui/app/templates/queues.hbs

@@ -0,0 +1,85 @@
+{{!
+* 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.
+}}
+
+<div class="col-md-4">
+  {{!-- BUTTONS --}}
+  <div class="row">
+      {{#if isWaitingPath}}
+        <div class="add-queue col-md-12">
+          {{path-input queues=content action="addQ" activeFlag=isWaitingPath}}
+        </div>
+      {{else}}
+        <div {{bind-attr class=":add-queue needRefresh:col-md-5:col-md-6 :col-sm-6" }} >
+          <button {{action askPath}} {{bind-attr class=":btn :btn-default :btn-block hasNewQueue:disabled"}} ><i class="fa fa-plus"></i> Add queue</button>
+        </div>
+        <div  {{bind-attr class=":add-queue needRefresh:col-md-7:col-md-6 :col-sm-6" }} >
+          <div class="btn-group btn-group-justified btn-group-save">
+            <div class="btn-group">
+            {{#if needRestart}}
+              <button {{action saveConfig 'restart'}} {{bind-attr class=":btn :btn-save :btn-success canNotSave:disabled"}} ><i class="fa fa-fw fa fa-cogs"></i> Save and restart</button>
+              {{else}}
+              {{#if needRefresh}}
+              <button {{action saveConfig 'refresh'}} {{bind-attr class=":btn :btn-save :btn-success canNotSave:disabled"}} ><i class="fa fa-fw fa-refresh"></i> Save and refresh</button>
+              {{else}}
+              <button {{action saveConfig}} {{bind-attr class=":btn :btn-save :btn-success canNotSave:disabled"}} ><i class="fa fa-save"></i> Save</button>
+              {{/if}}
+            {{/if}}
+            </div>
+            <div class="btn-group">
+                <button type="button" {{bind-attr class=":btn :btn-success canNotSave:disabled :dropdown-toggle"}} data-toggle="dropdown">
+                  <span class="caret"></span>
+                </button>
+                <ul class="dropdown-menu pull-right" role="menu">
+                  <li><a href="#" {{action saveConfig 'restart'}}><i class="fa fa-fw fa fa-cogs"></i> Save and restart ResourceManager</a></li>
+                  <li><a href="#" {{action saveConfig 'refresh'}}><i class="fa fa-fw fa-refresh"></i> Save and refresh Queues</a></li>
+                  <li><a href="#" {{action saveConfig}}><i class="fa fa-fw fa-save"></i> Save only</a></li>
+                </ul>
+            </div>
+          </div>
+        </div>
+      {{/if}}
+  </div>
+  {{!-- QUEUE LIST --}}
+  <div class="list-group queue-list">
+    {{recurce-queues depth=0}}
+  </div>
+  {{!-- SHEDULER --}}
+  <div class="hidden-sm hidden-xs">
+    {{partial "schedulerPanel"}}
+  </div>
+  {{!-- ALERT --}}
+  {{#if alertMessage}}
+  <div class="alert alert-danger">
+    <button {{action 'clearAlert'}}type="button" class="close" aria-hidden="true">&times;</button>
+    <strong> {{alertMessage.status}} </strong> {{alertMessage.message}}
+    <br>
+    {{#link-to 'trace' class="alert-link"}}Trace{{/link-to}}
+  </div>
+  {{/if}}
+</div>
+
+{{!-- QUEUE --}}
+<div class="col-md-8">
+  {{outlet}}
+</div>
+
+{{!-- SHEDULER --}}
+<div class="col-md-12 hidden-lg hidden-md">
+  {{partial "schedulerPanel"}}
+</div>
+

+ 20 - 0
contrib/views/capacity-scheduler/src/main/resources/ui/app/templates/queues/index.hbs

@@ -0,0 +1,20 @@
+{{!
+* 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.
+}}
+
+
+<div class="alert alert-info">Click on a queue to the left for details.</div>

+ 59 - 0
contrib/views/capacity-scheduler/src/main/resources/ui/app/templates/schedulerPanel.hbs

@@ -0,0 +1,59 @@
+{{!
+* 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.
+}}
+
+<div class="panel panel-default panel-capacity">
+  <div class="panel-heading">
+    <div class="panel-title">
+      Scheduler
+      <a href="#" {{action 'toggleEditScheduler'}} class="text-right"> 
+        {{#if isEditScheduler}}
+          <small><i class="fa fa-edit"> Hide edit</i> </small>
+        {{else}}
+          <small><i class="fa fa-edit"> Show edit</i> </small>
+        {{/if}}
+      </a>
+    </div>
+  </div>
+  <div class="panel-body">
+    <form class="form-horizontal" role="form">
+      <div class="form-group">
+        <label class="col-xs-6 col-lg-6 col-md-7 col-sm-6 control-label">Maximum apps</label>
+        <div class="col-xs-6 col-lg-6 col-md-5 col-sm-6">
+        {{#if isEditScheduler}}
+          {{int-input value=scheduler.maximum_applications class="input-sm"}}
+        {{else}}
+          <p class="form-control-static">{{scheduler.maximum_applications}}</p>
+        {{/if}}
+        </div>
+      </div>
+      <div class="form-group">
+        <label class="col-xs-6 col-lg-6 col-md-7 col-sm-6 control-label">Minimum AM resourse</label>
+        <div class="col-xs-6 col-lg-6 col-md-5 col-sm-6">
+        {{#if isEditScheduler}}
+          <div class="input-group">
+            {{int-input value=scheduler.maximum_am_resource_percent class="input-sm" maxVal=100}}
+            <span class="input-group-addon">%</span>
+          </div>
+        {{else}}
+          <p class="form-control-static">{{scheduler.maximum_am_resource_percent}} %</p>
+        {{/if}}
+        </div>
+      </div>
+    </form>
+  </div>
+</div>

+ 21 - 0
contrib/views/capacity-scheduler/src/main/resources/ui/app/templates/trace.hbs

@@ -0,0 +1,21 @@
+{{!
+* 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.
+}}
+
+<pre>
+  {{trace}}
+</pre>

+ 32 - 0
contrib/views/capacity-scheduler/src/main/resources/ui/bower.json

@@ -0,0 +1,32 @@
+{
+  "name": "capacity-scheduler-web",
+  "version": "0.0.1",
+  "main": "public/app.js",
+  "dependencies": {
+    "ember": "1.6.0-beta.1",
+    "ember-data": "1.0.0-beta.8",
+    "jquery": "1.9.0",
+    "bootstrap": "3.1.x",
+    "moment": "~2.5.1",
+    "ember-i18n": "~1.6.0",
+    "font-awesome": "~4.1",
+    "bootstrap3-typeahead": "~3.0.3"
+  },
+  "overrides": {
+    "jquery": {
+      "main": "jquery.js"
+    },
+    "ember-uploader": {
+      "main": "dist/ember-uploader.js"
+    },
+    "cldr": {
+      "main": "plurals.js"
+    },
+    "ember-i18n": {
+      "scripts": []
+    },
+    "font-awesome": {
+      "main": "css/font-awesome.css"
+    }
+  }
+}

+ 72 - 0
contrib/views/capacity-scheduler/src/main/resources/ui/config.coffee

@@ -0,0 +1,72 @@
+#  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.
+
+
+exports.config = 
+
+  files: 
+    
+    javascripts: 
+      defaultExtension: 'js'
+      joinTo: 
+        'javascripts/app.js': /^app/
+        'javascripts/vendor.js': /^bower_components|vendor/
+      order:
+        before: [
+          'bower_components/jquery/jquery.js',
+          'bower_components/handlebars/handlebars.js',
+          'bower_components/ember/ember.js',
+          'bower_components/ember-data/ember-data.js'
+          'bower_components/ember-i18n/lib/i18n.js'
+          ]
+
+    stylesheets:
+      defaultExtension: 'css'
+      joinTo:
+        'stylesheets/app.css': /^app/
+        'stylesheets/vendor.css': /^bower_components|vendor/
+      order:
+        before: [
+          'bower_components/bootstrap/dist/css/bootstrap.css',
+          'bower_components/bootstrap/dist/css/bootstrap-theme.css',
+          'bower_components/font-awesome/font-awesome.css'
+        ]
+
+    templates:
+      precompile: true
+      root: 'templates'
+      defaultExtension: 'hbs'
+      joinTo: 'javascripts/app.js' : /^app/
+      paths:
+        jquery: 'bower_components/jquery/jquery.js'
+        handlebars: 'bower_components/handlebars/handlebars.js'
+        ember: 'bower_components/ember/ember.js'
+
+  modules:
+    addSourceURLs: true
+
+  paths:
+    public: '/usr/lib/ambari-server/web/views-debug/CAPACITY-SCHEDULER/0.1.0/CS_1/'
+
+  overrides:
+    production:
+        paths:
+          public: 'public'
+
+  server:
+    port: 3333
+    base: '/'
+    run: no

+ 23 - 0
contrib/views/capacity-scheduler/src/main/resources/ui/generators/collection/collection.js.hbs

@@ -0,0 +1,23 @@
+/**
+ * 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.
+ */
+
+var App = require('app');
+
+App.{{#camelize}}{{pluralName}}{{/camelize}}Controller = Em.ArrayController.extend({
+    content: [] 
+});

+ 9 - 0
contrib/views/capacity-scheduler/src/main/resources/ui/generators/collection/generator.json

@@ -0,0 +1,9 @@
+{
+  "files": [
+    {
+      "from": "collection.js.hbs",
+      "to": "app/controllers/{{pluralName}}.js"
+    }
+  ],
+  "dependencies": []
+}

+ 23 - 0
contrib/views/capacity-scheduler/src/main/resources/ui/generators/component/component.js.hbs

@@ -0,0 +1,23 @@
+/**
+ * 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.
+ */
+
+var App = require('app');
+
+App.{{#camelize}}{{name}}{{/camelize}}Component = Em.Component.extend({
+    
+});

+ 9 - 0
contrib/views/capacity-scheduler/src/main/resources/ui/generators/component/generator.json

@@ -0,0 +1,9 @@
+{
+  "files": [
+    {
+      "from": "component.js.hbs",
+      "to": "app/components/{{name}}.js"
+    }
+  ],
+  "dependencies": []
+}

+ 23 - 0
contrib/views/capacity-scheduler/src/main/resources/ui/generators/controller/controller.js.hbs

@@ -0,0 +1,23 @@
+/**
+ * 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.
+ */
+
+var App = require('app');
+
+App.{{#camelize}}{{name}}{{/camelize}}Controller = Em.ObjectController.extend({
+    content: null
+});

+ 9 - 0
contrib/views/capacity-scheduler/src/main/resources/ui/generators/controller/generator.json

@@ -0,0 +1,9 @@
+{
+  "files": [
+    {
+      "from": "controller.js.hbs",
+      "to": "app/controllers/{{name}}.js"
+    }
+  ],
+  "dependencies": []
+}

+ 9 - 0
contrib/views/capacity-scheduler/src/main/resources/ui/generators/model/generator.json

@@ -0,0 +1,9 @@
+{
+  "files": [
+    {
+      "from": "model.js.hbs",
+      "to": "app/models/{{name}}.js"
+    }
+  ],
+  "dependencies": []
+}

+ 23 - 0
contrib/views/capacity-scheduler/src/main/resources/ui/generators/model/model.js.hbs

@@ -0,0 +1,23 @@
+/**
+ * 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.
+ */
+
+var App = require('app');
+
+App.{{#camelize}}{{name}}{{/camelize}} = DS.Model.extend({
+    
+});

+ 9 - 0
contrib/views/capacity-scheduler/src/main/resources/ui/generators/route/generator.json

@@ -0,0 +1,9 @@
+{
+  "files": [
+    {
+      "from": "route.js.hbs",
+      "to": "app/routes/{{name}}.js"
+    }
+  ],
+  "dependencies": []
+}

+ 25 - 0
contrib/views/capacity-scheduler/src/main/resources/ui/generators/route/route.js.hbs

@@ -0,0 +1,25 @@
+/**
+ * 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.
+ */
+
+var App = require('app');
+
+App.{{#camelize}}{{name}}{{/camelize}}Route = Em.Route.extend({
+    setupController: function (controller) {
+
+    }
+});

+ 9 - 0
contrib/views/capacity-scheduler/src/main/resources/ui/generators/template/generator.json

@@ -0,0 +1,9 @@
+{
+  "files": [
+    {
+      "from": "template.hbs.hbs",
+      "to": "app/templates/{{name}}.hbs"
+    }
+  ],
+  "dependencies": []
+}

+ 18 - 0
contrib/views/capacity-scheduler/src/main/resources/ui/generators/template/template.hbs.hbs

@@ -0,0 +1,18 @@
+{{!
+   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.
+}}
+

+ 9 - 0
contrib/views/capacity-scheduler/src/main/resources/ui/generators/view/generator.json

@@ -0,0 +1,9 @@
+{
+  "files": [
+    {
+      "from": "view.js.hbs",
+      "to": "app/views/{{name}}.js"
+    }
+  ],
+  "dependencies": []
+}

+ 23 - 0
contrib/views/capacity-scheduler/src/main/resources/ui/generators/view/view.js.hbs

@@ -0,0 +1,23 @@
+/**
+ * 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.
+ */
+
+var App = require('app');
+
+App.{{#camelize}}{{name}}{{/camelize}}View = Em.View.extend({
+    templateName: '{{name}}'
+});

+ 37 - 0
contrib/views/capacity-scheduler/src/main/resources/ui/package.json

@@ -0,0 +1,37 @@
+{
+  "name": "CapacityScheduler",
+  "description": "Package description",
+  "version": "0.0.1",
+  "homepage": "",
+  "repository": {
+    "type": "git",
+    "url": ""
+  },
+  "engines": {
+    "node": "~0.6.10 || 0.8 || 0.9"
+  },
+  "scripts": {
+    "start": "brunch watch --server",
+    "preinstall": "chmod +x node/npm/bin/node-gyp-bin/node-gyp",
+    "postinstall" : "bash node/with_new_path.sh node node_modules/.bin/bower --allow-root install"
+  },
+  "dependencies": {
+    "javascript-brunch": "^1.7.1",
+    "css-brunch": "^1.7.0",
+    "uglify-js-brunch": "^1.7.7",
+    "clean-css-brunch": "^1.7.1",
+    "bower": "^1.2.8",
+    "brunch": "^1.7.13",
+    "scaffolt": "^0.4.3",
+    "ember-precompiler-brunch": "^1.5.1",
+    "less-brunch": "^1.7.2"
+  },
+  "devDependencies": {},
+  "ignore": [
+    "**/.*",
+    "node_modules",
+    "bower_components",
+    "test",
+    "tests"
+  ]
+}

+ 17 - 0
contrib/views/capacity-scheduler/src/main/resources/ui/test/spec.coffee

@@ -0,0 +1,17 @@
+# 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.
+
+# Write your [mocha](http://visionmedia.github.com/mocha/) specs here.

+ 49 - 0
contrib/views/capacity-scheduler/src/main/resources/view.xml

@@ -0,0 +1,49 @@
+<!--
+   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.
+-->
+<view>
+    <name>CAPACITY-SCHEDULER</name>
+    <label>Capacity Scheduler</label>
+    <version>0.1.0</version>
+
+    <parameter>
+        <name>ambari.server.url</name>
+        <description>Target Ambari URL</description>
+        <required>true</required>
+    </parameter>
+
+    <parameter>
+        <name>ambari.server.username</name>
+        <description>Target Ambari username</description>
+        <required>true</required>
+    </parameter>
+
+    <parameter>
+        <name>ambari.server.password</name>
+        <description>Target Ambari password</description>
+        <required>true</required>
+        <!-- <hidden>true</hidden> -->
+    </parameter>
+
+    <instance>
+        <name>Scheduler</name>
+    </instance>
+
+    <resource>
+        <name>scheduler</name>
+        <service-class>org.apache.ambari.view.capacityscheduler.CapacitySchedulerService</service-class>
+    </resource>
+</view>

+ 1 - 0
contrib/views/pom.xml

@@ -26,6 +26,7 @@
     <module>files</module>
     <module>pig</module>
     <module>slider</module>
+    <module>capacity-scheduler</module>
   </modules>
   <build>
     <pluginManagement>

Some files were not shown because too many files changed in this diff