You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@ambari.apache.org by rl...@apache.org on 2015/05/28 18:11:57 UTC
ambari git commit: AMBARI-11436. CapScheduler Auto-Create and Cluster
association (Erik Bergenholtz via rlevas)
Repository: ambari
Updated Branches:
refs/heads/trunk adbfa0933 -> e2b8a0e92
AMBARI-11436. CapScheduler Auto-Create and Cluster association (Erik Bergenholtz via rlevas)
Project: http://git-wip-us.apache.org/repos/asf/ambari/repo
Commit: http://git-wip-us.apache.org/repos/asf/ambari/commit/e2b8a0e9
Tree: http://git-wip-us.apache.org/repos/asf/ambari/tree/e2b8a0e9
Diff: http://git-wip-us.apache.org/repos/asf/ambari/diff/e2b8a0e9
Branch: refs/heads/trunk
Commit: e2b8a0e92c8e73fd52050bb986238996b7b6cd94
Parents: adbfa09
Author: Erik Bergenholtz <eb...@hortonworks.com>
Authored: Thu May 28 12:11:44 2015 -0400
Committer: Robert Levas <rl...@hortonworks.com>
Committed: Thu May 28 12:11:44 2015 -0400
----------------------------------------------------------------------
contrib/views/capacity-scheduler/pom.xml | 15 +-
contrib/views/capacity-scheduler/readme.md | 10 +-
.../capacityscheduler/ConfigurationService.java | 183 +++++++++++--------
.../view/capacityscheduler/proxy/Proxy.java | 120 ------------
.../capacityscheduler/proxy/RequestBuilder.java | 160 ----------------
.../proxy/ResponseTranslator.java | 100 ----------
.../ui/app/components/dropdownButtons.js | 2 +-
.../main/resources/ui/app/controllers/queue.js | 2 +-
.../main/resources/ui/app/controllers/queues.js | 6 +-
.../src/main/resources/ui/app/serializers.js | 29 ++-
.../resources/ui/app/styles/application.less | 22 ++-
.../app/templates/components/queueContainer.hbs | 2 +-
.../main/resources/ui/app/templates/queues.hbs | 4 +-
.../main/resources/ui/app/templates/refuse.hbs | 10 +-
.../src/main/resources/ui/config.coffee | 2 +-
.../ui/test/integration/serializers_test.js | 124 +++++++++++++
.../src/main/resources/view.xml | 20 +-
contrib/views/pom.xml | 1 +
.../ambari/view/utils/ambari/AmbariApi.java | 132 ++++++++++---
.../ambari/URLStreamProviderBasicAuth.java | 61 +++++--
20 files changed, 481 insertions(+), 524 deletions(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/ambari/blob/e2b8a0e9/contrib/views/capacity-scheduler/pom.xml
----------------------------------------------------------------------
diff --git a/contrib/views/capacity-scheduler/pom.xml b/contrib/views/capacity-scheduler/pom.xml
index 2712953..23e5aee 100644
--- a/contrib/views/capacity-scheduler/pom.xml
+++ b/contrib/views/capacity-scheduler/pom.xml
@@ -19,7 +19,7 @@
<modelVersion>4.0.0</modelVersion>
<groupId>org.apache.ambari.contrib.views</groupId>
<artifactId>capacity-scheduler</artifactId>
- <version>0.3.0-SNAPSHOT</version>
+ <version>0.4.0-SNAPSHOT</version>
<name>Capacity Scheduler</name>
<parent>
@@ -37,12 +37,11 @@
<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>
+ <groupId>org.apache.hadoop</groupId>
+ <artifactId>hadoop-common</artifactId>
+ <version>${hadoop-version}</version>
</dependency>
<dependency>
<groupId>commons-io</groupId>
@@ -68,9 +67,15 @@
<artifactId>ambari-views</artifactId>
<scope>provided</scope>
</dependency>
+ <dependency>
+ <groupId>org.apache.ambari.contrib.views</groupId>
+ <artifactId>ambari-views-utils</artifactId>
+ <version>0.0.1-SNAPSHOT</version>
+ </dependency>
</dependencies>
<properties>
+ <hadoop-version>2.6.0</hadoop-version>
<ambari.dir>${project.parent.parent.parent.basedir}</ambari.dir>
<ui.directory>${basedir}/src/main/resources/ui</ui.directory>
</properties>
http://git-wip-us.apache.org/repos/asf/ambari/blob/e2b8a0e9/contrib/views/capacity-scheduler/readme.md
----------------------------------------------------------------------
diff --git a/contrib/views/capacity-scheduler/readme.md b/contrib/views/capacity-scheduler/readme.md
index e772508..4e5db9d 100644
--- a/contrib/views/capacity-scheduler/readme.md
+++ b/contrib/views/capacity-scheduler/readme.md
@@ -23,7 +23,7 @@ This View provides a UI to manage queues for the YARN Capacity Scheduler.
Requirements
-----
-- Ambari 1.7.0 or later
+- Ambari 2.1.0 or later
- YARN
Build
@@ -35,11 +35,11 @@ The view can be built as a maven project.
The build will produce the view archive.
- target/capacity-scheduler-0.3.0-SNAPSHOT.jar
+ target/capacity-scheduler-???-SNAPSHOT.jar
Place the view archive on the Ambari Server and restart to deploy.
- cp capacity-scheduler-0.3.0-SNAPSHOT /var/lib/ambari-server/resources/views/
+ cp capacity-scheduler-???-SNAPSHOT /var/lib/ambari-server/resources/views/
ambari-server restart
Deploying the View
@@ -49,7 +49,7 @@ Use the [Ambari Vagrant](https://cwiki.apache.org/confluence/display/AMBARI/Quic
Deploy the Capacity Scheduler view into Ambari.
- cp capacity-scheduler-0.3.0-SNAPSHOT /var/lib/ambari-server/resources/views/
+ cp capacity-scheduler-???-SNAPSHOT /var/lib/ambari-server/resources/views/
ambari-server restart
From the Ambari Administration interface, create a view instance.
@@ -65,7 +65,7 @@ From the Ambari Administration interface, create a view instance.
Login to Ambari and browse to the view instance.
- http://c6401.ambari.apache.org:8080/#/main/views/CAPACITY-SCHEDULER/0.3.0/CS_1
+ http://c6401.ambari.apache.org:8080/#/main/views/CAPACITY-SCHEDULER/???/CS_1
Local Development
-----
http://git-wip-us.apache.org/repos/asf/ambari/blob/e2b8a0e9/contrib/views/capacity-scheduler/src/main/java/org/apache/ambari/view/capacityscheduler/ConfigurationService.java
----------------------------------------------------------------------
diff --git a/contrib/views/capacity-scheduler/src/main/java/org/apache/ambari/view/capacityscheduler/ConfigurationService.java b/contrib/views/capacity-scheduler/src/main/java/org/apache/ambari/view/capacityscheduler/ConfigurationService.java
index 8e1b6a6..b192543 100644
--- a/contrib/views/capacity-scheduler/src/main/java/org/apache/ambari/view/capacityscheduler/ConfigurationService.java
+++ b/contrib/views/capacity-scheduler/src/main/java/org/apache/ambari/view/capacityscheduler/ConfigurationService.java
@@ -20,8 +20,9 @@ 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.apache.ambari.view.utils.ambari.AmbariApi;
+import org.apache.commons.io.IOUtils;
import org.json.simple.JSONArray;
import org.json.simple.JSONObject;
import org.json.simple.JSONValue;
@@ -31,22 +32,22 @@ import org.slf4j.LoggerFactory;
import javax.ws.rs.*;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;
+import java.io.InputStream;
+import java.net.ConnectException;
import java.util.HashMap;
-import java.net.URL;
-import java.net.MalformedURLException;
+import java.util.Map;
/**
- * Help service
+ * Configuration service for accessing Ambari REST API for capacity scheduler config.
+ *
*/
public class ConfigurationService {
private static final Logger LOG = LoggerFactory.getLogger(ConfigurationService.class);
- private final Proxy proxy;
- private final String baseUrl;
- private final String serverUrl;
+ private final AmbariApi ambariApi;
private ViewContext context;
- private final String refreshRMRequestData =
+ private static final String REFRESH_RM_REQUEST_DATA =
"{\n" +
" \"RequestInfo\" : {\n" +
" \"command\" : \"REFRESHQUEUES\",\n" +
@@ -59,12 +60,12 @@ public class ConfigurationService {
" \"hosts\" : \"%s\"\n" +
" }]\n" +
"}";
- private final String restartRMRequestData = "{\"RequestInfo\": {\n" +
+ private static final String RESTART_RM_REQUEST_DATA = "{\"RequestInfo\": {\n" +
" \"command\":\"RESTART\",\n" +
" \"context\":\"Restart ResourceManager\",\n" +
" \"operation_level\": {\n" +
" \"level\":\"HOST_COMPONENT\",\n" +
- " \"cluster_name\":\"MyCluster\",\n" +
+ " \"cluster_name\":\"%s\",\n" +
" \"host_name\":\"%s\",\n" +
" \"service_name\":\"YARN\",\n" +
" \"hostcomponent_name\":\"RESOURCEMANAGER\"\n" +
@@ -86,46 +87,27 @@ public class ConfigurationService {
*/
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", "view-capacity-scheduler");
- proxy.setCustomHeaders(customHeaders);
-
- baseUrl = context.getProperties().get("ambari.server.url");
-
- URL url = null;
- try {
- url = new URL(baseUrl);
- } catch (MalformedURLException ex) {
- throw new ServiceFormattedException("Error in ambari.server.url property: " + ex.getMessage(), ex);
- }
-
- String clusterName = url.getFile(); // TODO: make it more robust
- serverUrl = baseUrl.substring(0, baseUrl.length() - clusterName.length());
+ this.ambariApi = new AmbariApi(context);
+ this.ambariApi.setRequestedBy("view-capacity-scheduler");
}
// ================================================================================
// Configuration Reading
// ================================================================================
- private final String versionTagUrl = "%s?fields=Clusters/desired_configs/capacity-scheduler";
- private final String configurationUrl = "%s/configurations?type=capacity-scheduler";
- private final String configurationUrlByTag = "%%s/configurations?type=capacity-scheduler&tag=%s";
- private final String rmHostUrl = "%s/services/YARN/components/RESOURCEMANAGER?fields=host_components/host_name";
+ private static final String VERSION_TAG_URL = "?fields=Clusters/desired_configs/capacity-scheduler";
+ private static final String CONFIGURATION_URL = "configurations?type=capacity-scheduler";
+ private static final String CONFIGURATION_URL_BY_TAG = "configurations?type=capacity-scheduler&tag=%s";
+ private static final String RM_HOST_URL = "services/YARN/components/RESOURCEMANAGER?fields=host_components/host_name";
- private final String rmGetNodeLabelUrl = "http://%s:8088/ws/v1/cluster/get-node-labels";
+ private static final String RM_GET_NODE_LABEL_URL = "http://%s:8088/ws/v1/cluster/get-node-labels";
// ================================================================================
// Privilege Reading
// ================================================================================
- private final String clusterOperatorPrivilegeUrl = "%s?privileges/PrivilegeInfo/permission_name=CLUSTER.OPERATE&privileges/PrivilegeInfo/principal_name=%s";
- private final String ambariAdminPrivilegeUrl = "%s/api/v1/users/%s?Users/admin=true";
+ private static final String CLUSTER_OPERATOR_PRIVILEGE_URL = "?privileges/PrivilegeInfo/permission_name=CLUSTER.OPERATE&privileges/PrivilegeInfo/principal_name=%s";
+ private static final String AMBARI_ADMIN_PRIVILEGE_URL = "/api/v1/users/%s?Users/admin=true";
/**
* Gets capacity scheduler configuration.
@@ -164,7 +146,7 @@ public class ConfigurationService {
try {
validateViewConfiguration();
- JSONObject configurations = proxy.request(baseUrl).get().asJSON();
+ JSONObject configurations = readFromCluster("");
response = Response.ok(configurations).build();
} catch (WebApplicationException ex) {
throw ex;
@@ -188,9 +170,8 @@ public class ConfigurationService {
try {
validateViewConfiguration();
- String url = String.format(configurationUrl, baseUrl);
- JSONObject configurations = proxy.request(url).get().asJSON();
- response = Response.ok(configurations).build();
+ JSONObject responseJSON = readFromCluster(CONFIGURATION_URL);
+ response = Response.ok( responseJSON ).build();
} catch (WebApplicationException ex) {
throw ex;
} catch (Exception ex) {
@@ -234,10 +215,10 @@ public class ConfigurationService {
@Path("/privilege")
public Response getPrivilege() {
Response response = null;
-
+
try {
boolean operator = isOperator();
-
+
response = Response.ok(operator).build();
} catch (WebApplicationException ex) {
throw ex;
@@ -260,9 +241,15 @@ public class ConfigurationService {
Response response;
try {
- String nodeLabels = proxy.request(String.format(rmGetNodeLabelUrl, getRMHost())).get().asString();
+ String url = String.format(RM_GET_NODE_LABEL_URL, getRMHost());
+
+ InputStream rmResponse = context.getURLStreamProvider().readFrom(
+ url, "GET", (String) null, new HashMap<String, String>());
+ String nodeLabels = IOUtils.toString(rmResponse);
response = Response.ok(nodeLabels).build();
+ } catch (ConnectException ex) {
+ throw new ServiceFormattedException("Connection to Resource Manager refused", ex);
} catch (WebApplicationException ex) {
throw ex;
} catch (Exception ex) {
@@ -282,43 +269,85 @@ public class ConfigurationService {
validateViewConfiguration();
// first check if the user is an CLUSTER.OPERATOR
- String url = String.format(clusterOperatorPrivilegeUrl, baseUrl, context.getUsername());
- JSONObject json = proxy.request(url).get().asJSON();
+ String url = String.format(CLUSTER_OPERATOR_PRIVILEGE_URL, context.getUsername());
+ JSONObject json = readFromCluster(url);
if (json == null || json.size() <= 0) {
// user is not a CLUSTER.OPERATOR but might be an AMBARI.ADMIN
- url = String.format(ambariAdminPrivilegeUrl, serverUrl, context.getUsername());
- json = proxy.request(url).get().asJSON();
- if (json == null || json.size() <= 0)
- return false;
+ url = String.format(AMBARI_ADMIN_PRIVILEGE_URL, context.getUsername());
+ String response = ambariApi.readFromAmbari(url, "GET", null, null);
+ if (response == null || response.isEmpty()) {
+ return false;
+ }
+ json = getJsonObject(response);
+ if (json == null || json.size() <= 0) {
+ return false;
+ }
}
return true;
}
-
+
+ private JSONObject readFromCluster(String url) {
+ String response = ambariApi.requestClusterAPI(url);
+ if (response == null || response.isEmpty()) {
+ return null;
+ }
+
+ return getJsonObject(response);
+ }
+
+ private JSONObject getJsonObject(String response) {
+ if (response == null || response.isEmpty()) {
+ return null;
+ }
+ JSONObject jsonObject = (JSONObject) JSONValue.parse(response);
+
+ if (jsonObject.get("status") != null && (Long)jsonObject.get("status") >= 400L) {
+ // Throw exception if HTTP status is not OK
+ String message;
+ if (jsonObject.containsKey("message")) {
+ message = (String) jsonObject.get("message");
+ } else {
+ message = "without message";
+ }
+ throw new ServiceFormattedException("Proxy: Server returned error " + jsonObject.get("status") + " " +
+ message + ". Check Capacity-Scheduler instance properties.");
+ }
+ return jsonObject;
+ }
+
/**
* Validates the view configuration properties.
*
* @throws MisconfigurationFormattedException if one of the required view configuration properties are not set
*/
private void validateViewConfiguration() {
+ // check if we are cluster config'd, if so, just go
+ if (ambariApi.isLocalCluster()) {
+ return;
+ }
+
String hostname = context.getProperties().get("ambari.server.url");
- if (hostname == null)
+ if (hostname == null) {
throw new MisconfigurationFormattedException("ambari.server.url");
+ }
String username = context.getProperties().get("ambari.server.username");
- if (username == null)
+ if (username == null) {
throw new MisconfigurationFormattedException("ambari.server.username");
+ }
String password = context.getProperties().get("ambari.server.password");
- if (password == null)
+ if (password == null) {
throw new MisconfigurationFormattedException("ambari.server.password");
+ }
}
private JSONObject getConfigurationFromAmbari(String versionTag) {
- String urlTemplate = String.format(configurationUrlByTag, versionTag);
- String url = String.format(urlTemplate, baseUrl);
- return proxy.request(url).get().asJSON();
+ String url = String.format(CONFIGURATION_URL_BY_TAG, versionTag);
+ JSONObject responseJSON = readFromCluster(url);
+ return responseJSON;
}
/**
@@ -351,8 +380,8 @@ public class ConfigurationService {
* @return the desired config JSON object
*/
private JSONObject getDesiredConfigs() {
- String url = String.format(versionTagUrl, baseUrl);
- return proxy.request(url).get().asJSON();
+ JSONObject response = readFromCluster(VERSION_TAG_URL);
+ return response;
}
// ================================================================================
@@ -376,9 +405,10 @@ public class ConfigurationService {
return Response.status(401).build();
}
- response = proxy.request(baseUrl).
- setData(request).
- put().asJSON();
+ Map<String, String> headers = new HashMap<String, String>();
+ headers.put("Content-Type", "application/x-www-form-urlencoded");
+ String responseString = ambariApi.requestClusterAPI("", "PUT", request.toJSONString(), headers);
+ response = getJsonObject(responseString);
} catch (WebApplicationException ex) {
throw ex;
@@ -406,10 +436,11 @@ public class ConfigurationService {
}
String rmHost = getRMHost();
- JSONObject data = (JSONObject) JSONValue.parse(String.format(refreshRMRequestData, rmHost));
- proxy.request(baseUrl + "/requests/").
- setData(data).
- post();
+ JSONObject data = getJsonObject(String.format(REFRESH_RM_REQUEST_DATA, rmHost));
+
+ Map<String, String> headers = new HashMap<String, String>();
+ headers.put("Content-Type", "application/x-www-form-urlencoded");
+ ambariApi.requestClusterAPI("requests/", "POST", data.toJSONString(), headers);
} catch (WebApplicationException ex) {
throw ex;
@@ -436,10 +467,12 @@ public class ConfigurationService {
}
String rmHost = getRMHost();
- JSONObject data = (JSONObject) JSONValue.parse(String.format(restartRMRequestData, rmHost, rmHost));
- proxy.request(baseUrl + "/requests/").
- setData(data).
- post();
+ JSONObject data = getJsonObject(String.format(RESTART_RM_REQUEST_DATA,
+ ambariApi.getCluster().getName(), rmHost, rmHost));
+
+ Map<String, String> headers = new HashMap<String, String>();
+ headers.put("Content-Type", "application/x-www-form-urlencoded");
+ ambariApi.requestClusterAPI("requests/", "POST", data.toJSONString(), headers);
} catch (WebApplicationException ex) {
throw ex;
@@ -451,7 +484,9 @@ public class ConfigurationService {
private String getRMHost() {
String rmHost = null;
- JSONObject rmData = proxy.request(String.format(rmHostUrl, baseUrl)).get().asJSON();
+ JSONObject rmData = readFromCluster(RM_HOST_URL);
+ if (rmData == null)
+ throw new ServiceFormattedException("Cannot retrieve ResourceManager host");
JSONArray components = (JSONArray) rmData.get("host_components");
for(Object component : components) {
JSONObject roles = (JSONObject) ((JSONObject) component).get("HostRoles");
@@ -461,8 +496,8 @@ public class ConfigurationService {
}
}
if (rmHost == null)
- throw new ServiceFormattedException("Can't retrieve Resource Manager Host");
+ throw new ServiceFormattedException("Can't find ResourceManager host");
return rmHost;
}
-}
+} // end ConfigurationService
http://git-wip-us.apache.org/repos/asf/ambari/blob/e2b8a0e9/contrib/views/capacity-scheduler/src/main/java/org/apache/ambari/view/capacityscheduler/proxy/Proxy.java
----------------------------------------------------------------------
diff --git a/contrib/views/capacity-scheduler/src/main/java/org/apache/ambari/view/capacityscheduler/proxy/Proxy.java b/contrib/views/capacity-scheduler/src/main/java/org/apache/ambari/view/capacityscheduler/proxy/Proxy.java
deleted file mode 100644
index c026b41..0000000
--- a/contrib/views/capacity-scheduler/src/main/java/org/apache/ambari/view/capacityscheduler/proxy/Proxy.java
+++ /dev/null
@@ -1,120 +0,0 @@
-/**
- * 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;
- }
-}
http://git-wip-us.apache.org/repos/asf/ambari/blob/e2b8a0e9/contrib/views/capacity-scheduler/src/main/java/org/apache/ambari/view/capacityscheduler/proxy/RequestBuilder.java
----------------------------------------------------------------------
diff --git a/contrib/views/capacity-scheduler/src/main/java/org/apache/ambari/view/capacityscheduler/proxy/RequestBuilder.java b/contrib/views/capacity-scheduler/src/main/java/org/apache/ambari/view/capacityscheduler/proxy/RequestBuilder.java
deleted file mode 100644
index 88e19bb..0000000
--- a/contrib/views/capacity-scheduler/src/main/java/org/apache/ambari/view/capacityscheduler/proxy/RequestBuilder.java
+++ /dev/null
@@ -1,160 +0,0 @@
-/**
- * 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.net.UnknownHostException;
-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 (UnknownHostException e) {
- throw new ServiceFormattedException(e.getMessage() + " is unknown host. Check Capacity-Scheduler instance properties.", e);
- } catch (IOException e) {
- throw new ServiceFormattedException(e.toString(), 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;
- }
-}
http://git-wip-us.apache.org/repos/asf/ambari/blob/e2b8a0e9/contrib/views/capacity-scheduler/src/main/java/org/apache/ambari/view/capacityscheduler/proxy/ResponseTranslator.java
----------------------------------------------------------------------
diff --git a/contrib/views/capacity-scheduler/src/main/java/org/apache/ambari/view/capacityscheduler/proxy/ResponseTranslator.java b/contrib/views/capacity-scheduler/src/main/java/org/apache/ambari/view/capacityscheduler/proxy/ResponseTranslator.java
deleted file mode 100644
index 8e3b4a6..0000000
--- a/contrib/views/capacity-scheduler/src/main/java/org/apache/ambari/view/capacityscheduler/proxy/ResponseTranslator.java
+++ /dev/null
@@ -1,100 +0,0 @@
-/**
- * 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();
- JSONObject jsonObject = (JSONObject) JSONValue.parse(jsonString);
- if (jsonObject.get("status") != null && (Long)jsonObject.get("status") >= 400L) {
- // Throw exception if HTTP status is not OK
- String message;
- if (jsonObject.containsKey("message")) {
- message = (String) jsonObject.get("message");
- } else {
- message = "without message";
- }
- throw new ServiceFormattedException("Proxy: Server returned error " + jsonObject.get("status") + " " +
- message + ". Check Capacity-Scheduler instance properties.");
- }
- return jsonObject;
- }
-
- /**
- * InputStream setter
- * @param inputStream InputStream instance
- */
- public void setInputStream(InputStream inputStream) {
- this.inputStream = inputStream;
- }
-}
http://git-wip-us.apache.org/repos/asf/ambari/blob/e2b8a0e9/contrib/views/capacity-scheduler/src/main/resources/ui/app/components/dropdownButtons.js
----------------------------------------------------------------------
diff --git a/contrib/views/capacity-scheduler/src/main/resources/ui/app/components/dropdownButtons.js b/contrib/views/capacity-scheduler/src/main/resources/ui/app/components/dropdownButtons.js
index cc15563..9b0950a 100644
--- a/contrib/views/capacity-scheduler/src/main/resources/ui/app/components/dropdownButtons.js
+++ b/contrib/views/capacity-scheduler/src/main/resources/ui/app/components/dropdownButtons.js
@@ -51,4 +51,4 @@ App.DropdownButtonsComponent = Em.Component.extend(App.ClickElsewhereMixin,{
this.sendAction('action',arg);
}
}
-});
+})
http://git-wip-us.apache.org/repos/asf/ambari/blob/e2b8a0e9/contrib/views/capacity-scheduler/src/main/resources/ui/app/controllers/queue.js
----------------------------------------------------------------------
diff --git a/contrib/views/capacity-scheduler/src/main/resources/ui/app/controllers/queue.js b/contrib/views/capacity-scheduler/src/main/resources/ui/app/controllers/queue.js
index 9828399..019df5f 100644
--- a/contrib/views/capacity-scheduler/src/main/resources/ui/app/controllers/queue.js
+++ b/contrib/views/capacity-scheduler/src/main/resources/ui/app/controllers/queue.js
@@ -209,7 +209,7 @@ App.QueueController = Ember.ObjectController.extend({
if (arguments.length > 1) {
if (!this.get('isFairOP')) {
this.send('rollbackProp','enable_size_based_weight',this.get('content'));
- }
+ };
this.set('content.ordering_policy',val || null);
}
return this.get('content.ordering_policy');
http://git-wip-us.apache.org/repos/asf/ambari/blob/e2b8a0e9/contrib/views/capacity-scheduler/src/main/resources/ui/app/controllers/queues.js
----------------------------------------------------------------------
diff --git a/contrib/views/capacity-scheduler/src/main/resources/ui/app/controllers/queues.js b/contrib/views/capacity-scheduler/src/main/resources/ui/app/controllers/queues.js
index b26697f..7b69c92 100644
--- a/contrib/views/capacity-scheduler/src/main/resources/ui/app/controllers/queues.js
+++ b/contrib/views/capacity-scheduler/src/main/resources/ui/app/controllers/queues.js
@@ -228,7 +228,7 @@ App.QueuesController = Ember.ArrayController.extend({
* check if RM needs restart
* @type {bool}
*/
- needRestart: Em.computed.alias('hasDeletedQueues'),
+ needRestart: Em.computed.and('hasDeletedQueues','isOperator'),
/**
* True if some queue of desired configs was removed.
@@ -244,7 +244,7 @@ App.QueuesController = Ember.ArrayController.extend({
* check if RM needs refresh
* @type {bool}
*/
- needRefresh: cmp.and('needRefreshProps','noNeedRestart'),
+ needRefresh: cmp.and('needRefreshProps','noNeedRestart','isOperator'),
/**
* Inverted needRestart value.
@@ -305,7 +305,7 @@ App.QueuesController = Ember.ArrayController.extend({
* check if can save configs
* @type {bool}
*/
- canNotSave: cmp.any('hasOverCapacity', 'hasUncompetedAddings','hasNotValid','isNotOperator'),
+ canNotSave: cmp.any('hasOverCapacity', 'hasUncompetedAddings','hasNotValid'),
/**
* List of not valid queues.
http://git-wip-us.apache.org/repos/asf/ambari/blob/e2b8a0e9/contrib/views/capacity-scheduler/src/main/resources/ui/app/serializers.js
----------------------------------------------------------------------
diff --git a/contrib/views/capacity-scheduler/src/main/resources/ui/app/serializers.js b/contrib/views/capacity-scheduler/src/main/resources/ui/app/serializers.js
index 4dfb5e5..4b722f9 100644
--- a/contrib/views/capacity-scheduler/src/main/resources/ui/app/serializers.js
+++ b/contrib/views/capacity-scheduler/src/main/resources/ui/app/serializers.js
@@ -248,15 +248,30 @@ App.QueueSerializer = DS.RESTSerializer.extend(App.SerializerMixin,{
return json;
},
serializeHasMany:function (record, json, relationship) {
- var key = relationship.key;
- json[[this.PREFIX, record.get('path'), 'accessible-node-labels'].join('.')] = (record.get('labelsEnabled'))?'':null;
- record.get(key).map(function (l,idx,labels) {
- json[[this.PREFIX, record.get('path'), 'accessible-node-labels'].join('.')] = (record.get('accessAllLabels'))?'*':labels.mapBy('name').join(',');
+ var key = relationship.key,
+ recordLabels = record.get(key),
+ accessible_node_labels_key = [this.PREFIX, record.get('path'), 'accessible-node-labels'].join('.');
+
+ switch (true) {
+ case (record.get('accessAllLabels')):
+ json[accessible_node_labels_key] = '*';
+ break;
+ case (!Em.isEmpty(recordLabels)):
+ json[accessible_node_labels_key] = recordLabels.mapBy('name').join(',');
+ break;
+ case (record.get('labelsEnabled')):
+ json[accessible_node_labels_key] = '';
+ break;
+ default:
+ json[accessible_node_labels_key] = null;
+ }
+
+ recordLabels.forEach(function (l) {
if (!record.get('store.nodeLabels').findBy('name',l.get('name')).notExist) {
- json[[this.PREFIX, record.get('path'), 'accessible-node-labels', l.get('name'), 'capacity'].join('.')] = l.get('capacity');
- json[[this.PREFIX, record.get('path'), 'accessible-node-labels', l.get('name'), 'maximum-capacity'].join('.')] = l.get('maximum_capacity');
+ json[[accessible_node_labels_key, l.get('name'), 'capacity'].join('.')] = l.get('capacity');
+ json[[accessible_node_labels_key, l.get('name'), 'maximum-capacity'].join('.')] = l.get('maximum_capacity');
}
- },this);
+ });
}
});
http://git-wip-us.apache.org/repos/asf/ambari/blob/e2b8a0e9/contrib/views/capacity-scheduler/src/main/resources/ui/app/styles/application.less
----------------------------------------------------------------------
diff --git a/contrib/views/capacity-scheduler/src/main/resources/ui/app/styles/application.less b/contrib/views/capacity-scheduler/src/main/resources/ui/app/styles/application.less
index 82fbc07..23ce2ef 100644
--- a/contrib/views/capacity-scheduler/src/main/resources/ui/app/styles/application.less
+++ b/contrib/views/capacity-scheduler/src/main/resources/ui/app/styles/application.less
@@ -234,7 +234,7 @@
margin-bottom: 20px;
min-height: 20px;
padding: 15px;
- padding-top: 0;
+ padding-top: 0px;
// ----- QUEUE -> HEADING ROW -----
@@ -736,6 +736,24 @@
}
}
+// ----- REFUSE PAGE -----
+
+.refuse-page {
+ .page-body a {
+ &.collapsed i {
+ filter: progid:DXImageTransform.Microsoft.BasicImage(rotation=1);
+ -webkit-transform: rotate(180deg);
+ -moz-transform: rotate(180deg);
+ -ms-transform: rotate(180deg);
+ -o-transform: rotate(180deg);
+ transform: rotate(180deg);
+ }
+ text-decoration: none;
+ font-size: .8em;
+ text-transform: uppercase;
+ }
+}
+
// ----- COMPONENTS -----
@@ -814,7 +832,7 @@
padding: 8px;
border-radius: 4px;
background-color: #fff;
- box-shadow: 0 0 5px;
+ box-shadow: 0px 0 5px;
}
}
http://git-wip-us.apache.org/repos/asf/ambari/blob/e2b8a0e9/contrib/views/capacity-scheduler/src/main/resources/ui/app/templates/components/queueContainer.hbs
----------------------------------------------------------------------
diff --git a/contrib/views/capacity-scheduler/src/main/resources/ui/app/templates/components/queueContainer.hbs b/contrib/views/capacity-scheduler/src/main/resources/ui/app/templates/components/queueContainer.hbs
index cdc9044..19c6cfe 100644
--- a/contrib/views/capacity-scheduler/src/main/resources/ui/app/templates/components/queueContainer.hbs
+++ b/contrib/views/capacity-scheduler/src/main/resources/ui/app/templates/components/queueContainer.hbs
@@ -81,4 +81,4 @@
{{/each}}
</div>
{{/if}}
-</div>
+</div>
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/ambari/blob/e2b8a0e9/contrib/views/capacity-scheduler/src/main/resources/ui/app/templates/queues.hbs
----------------------------------------------------------------------
diff --git a/contrib/views/capacity-scheduler/src/main/resources/ui/app/templates/queues.hbs b/contrib/views/capacity-scheduler/src/main/resources/ui/app/templates/queues.hbs
index b412d7b..f785a62 100644
--- a/contrib/views/capacity-scheduler/src/main/resources/ui/app/templates/queues.hbs
+++ b/contrib/views/capacity-scheduler/src/main/resources/ui/app/templates/queues.hbs
@@ -45,10 +45,10 @@
isNotOperator=isNotOperator
}}
<li >
- <a href="#" {{action 'saveModal' 'refresh' target="view"}} {{bind-attr class=":btn isNotOperator:disabled needRefresh::disabled"}}><i class="fa fa-fw fa-refresh"></i> Save and Refresh Queues</a>
+ <a href="#" {{action 'saveModal' 'refresh' target="view"}} {{bind-attr class=":btn needRefresh::disabled"}}><i class="fa fa-fw fa-refresh"></i> Save and Refresh Queues</a>
</li>
<li>
- <a href="#" {{action 'saveModal' target="view"}} {{bind-attr class=":btn isNotOperator:disabled needSave::disabled"}}><i class="fa fa-fw fa-save"></i> Save Only</a>
+ <a href="#" {{action 'saveModal' target="view"}} {{bind-attr class=":btn needSave::disabled"}}><i class="fa fa-fw fa-save"></i> Save Only</a>
</li>
{{dropdown-buttons action='downloadConfig' layoutName='components/dropdownDownload'}}
</ul>
http://git-wip-us.apache.org/repos/asf/ambari/blob/e2b8a0e9/contrib/views/capacity-scheduler/src/main/resources/ui/app/templates/refuse.hbs
----------------------------------------------------------------------
diff --git a/contrib/views/capacity-scheduler/src/main/resources/ui/app/templates/refuse.hbs b/contrib/views/capacity-scheduler/src/main/resources/ui/app/templates/refuse.hbs
index 8671d7d..ec83d11 100644
--- a/contrib/views/capacity-scheduler/src/main/resources/ui/app/templates/refuse.hbs
+++ b/contrib/views/capacity-scheduler/src/main/resources/ui/app/templates/refuse.hbs
@@ -16,10 +16,16 @@
* limitations under the License.
}}
-<div class="col-md-12">
+<div class="col-md-12 refuse-page">
<div class="page-header">
<div class="pull-right"> {{#link-to 'queues'}} <i class="fa fa-refresh" ></i> Retry connection {{/link-to}} </div>
<h3>Couldn't connect to the cluster</h3>
</div>
- <div>{{content.message}}</div>
+ <div class="page-body">
+ <p>{{content.message}}</p>
+ <a data-toggle="collapse" href="#trace" aria-expanded="false" {{bind-attr class=":collapsed content.trace::hide"}} >
+ trace <i class="fa fa-angle-up"></i>
+ </a>
+ <div class="collapse" id="trace"> <pre> {{content.trace}} </pre> </div>
+ </div>
</div>
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/ambari/blob/e2b8a0e9/contrib/views/capacity-scheduler/src/main/resources/ui/config.coffee
----------------------------------------------------------------------
diff --git a/contrib/views/capacity-scheduler/src/main/resources/ui/config.coffee b/contrib/views/capacity-scheduler/src/main/resources/ui/config.coffee
index 0c86396..5f9bf8b 100644
--- a/contrib/views/capacity-scheduler/src/main/resources/ui/config.coffee
+++ b/contrib/views/capacity-scheduler/src/main/resources/ui/config.coffee
@@ -67,5 +67,5 @@ exports.config =
overrides:
development:
paths:
- public: '/usr/lib/ambari-server/web/views-debug/CAPACITY-SCHEDULER/0.3.0/CS_1/'
+ public: '/usr/lib/ambari-server/web/views-debug/CAPACITY-SCHEDULER/0.4.0/CS_1/'
http://git-wip-us.apache.org/repos/asf/ambari/blob/e2b8a0e9/contrib/views/capacity-scheduler/src/main/resources/ui/test/integration/serializers_test.js
----------------------------------------------------------------------
diff --git a/contrib/views/capacity-scheduler/src/main/resources/ui/test/integration/serializers_test.js b/contrib/views/capacity-scheduler/src/main/resources/ui/test/integration/serializers_test.js
new file mode 100644
index 0000000..9de1ea1
--- /dev/null
+++ b/contrib/views/capacity-scheduler/src/main/resources/ui/test/integration/serializers_test.js
@@ -0,0 +1,124 @@
+/**
+ * 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 run = Ember.run;
+
+var setupStore = function(options) {
+ var env = {};
+ options = options || {};
+
+ var container = env.container = new Ember.Container();
+
+ var adapter = env.adapter = (options.adapter || DS.Adapter);
+ delete options.adapter;
+
+ for (var prop in options) {
+ container.register('model:' + prop, options[prop]);
+ }
+
+ container.register('store:main', App.ApplicationStore.extend({
+ adapter: adapter
+ }));
+
+ container.register('serializer:queue', App.QueueSerializer);
+ container.register('serializer:scheduler', App.SchedulerSerializer);
+ container.register('serializer:label', App.SchedulerSerializer);
+ container.register('serializer:tag', App.TagSerializer);
+
+ container.injection('serializer', 'store', 'store:main');
+
+ env.queueSerializer = container.lookup('serializer:queue');
+ env.schedulerSerializer = container.lookup('serializer:scheduler');
+ env.labelSerializer = container.lookup('serializer:label');
+ env.tagSerializer = container.lookup('serializer:tag');
+
+ env.store = container.lookup('store:main');
+ env.adapter = env.store.get('defaultAdapter');
+
+ return env;
+};
+
+var createStore = function(options) {
+ return setupStore(options).store;
+};
+
+QUnit.module("integration/serializers", {
+ setup: function (options) {
+ store = createStore({queue: App.Queue,label:App.Label,adapter:App.QueueAdapter});
+ },
+ teardown: function() {
+ store = null;
+ }
+});
+
+test('serialize accessible-node-labels property', function () {
+
+ expect(4);
+
+ var queueSerializer = store.serializerFor('queue'),
+ rootQueue,
+ storeLabels,
+ label1,
+ label2,
+ json;
+
+ run(function () {
+ store.set('nodeLabels',[{name:'label1'}, {name:'label2'}]);
+ });
+
+ run(function () {
+ label1 = store.push('label', {'id':'root.label1','name':'label1'});
+ label2 = store.push('label', {'id':'root.label2','name':'label2'});
+ rootQueue = store.push('queue', {
+ 'id':'root',
+ 'path':'root',
+ 'labelsEnabled':true,
+ '_accessAllLabels':true,
+ 'labels':['root.label1', 'root.label2']
+ });
+ });
+
+ json = queueSerializer.serialize(rootQueue);
+
+ equal(json['yarn.scheduler.capacity.root.accessible-node-labels'],'*');
+
+ run(function () {
+ rootQueue.set('_accessAllLabels',false);
+ });
+
+ json = queueSerializer.serialize(rootQueue);
+
+ equal(json['yarn.scheduler.capacity.root.accessible-node-labels'],'label1,label2');
+
+ run(function () {
+ rootQueue.get('labels').clear();
+ });
+
+ json = queueSerializer.serialize(rootQueue);
+
+ equal(json['yarn.scheduler.capacity.root.accessible-node-labels'],'');
+
+ run(function () {
+ rootQueue.set('labelsEnabled',false);
+ });
+
+ json = queueSerializer.serialize(rootQueue);
+
+ equal(json['yarn.scheduler.capacity.root.accessible-node-labels'],undefined);
+
+});
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/ambari/blob/e2b8a0e9/contrib/views/capacity-scheduler/src/main/resources/view.xml
----------------------------------------------------------------------
diff --git a/contrib/views/capacity-scheduler/src/main/resources/view.xml b/contrib/views/capacity-scheduler/src/main/resources/view.xml
index 2974f86..564f353 100644
--- a/contrib/views/capacity-scheduler/src/main/resources/view.xml
+++ b/contrib/views/capacity-scheduler/src/main/resources/view.xml
@@ -17,16 +17,18 @@
<view>
<name>CAPACITY-SCHEDULER</name>
<label>Capacity Scheduler</label>
- <version>0.3.0</version>
- <min-ambari-version>2.0.*</min-ambari-version>
+ <version>0.4.0</version>
+
+ <min-ambari-version>2.1.*</min-ambari-version>
<validator-class>org.apache.ambari.view.capacityscheduler.PropertyValidator</validator-class>
<parameter>
<name>ambari.server.url</name>
- <description>Enter the Ambari REST API cluster resource.</description>
+ <description>Enter the Ambari Server REST API cluster resource.</description>
<label>Ambari Cluster URL</label>
<placeholder>http://ambari.server:8080/api/v1/clusters/MyCluster</placeholder>
+ <cluster-config>fake</cluster-config>
<required>true</required>
</parameter>
<parameter>
@@ -34,12 +36,14 @@
<description>Enter the Cluster Operator username (for example: admin).</description>
<label>Operator Username</label>
<placeholder>admin</placeholder>
+ <cluster-config>fake</cluster-config>
<required>true</required>
</parameter>
<parameter>
<name>ambari.server.password</name>
<description>Enter the Cluster Operator password (for example: password).</description>
<label>Operator Password</label>
+ <cluster-config>fake</cluster-config>
<required>true</required>
<masked>true</masked>
</parameter>
@@ -49,4 +53,14 @@
<service-class>org.apache.ambari.view.capacityscheduler.CapacitySchedulerService</service-class>
</resource>
+ <auto-instance>
+ <name>AUTO_CS_INSTANCE</name>
+ <label>YARN Queue Manager</label>
+ <description>Manage YARN Capacity Scheduler Queues</description>
+ <stack-id>HDP-2.*</stack-id>
+ <services>
+ <service>YARN</service>
+ </services>
+ </auto-instance>
+
</view>
http://git-wip-us.apache.org/repos/asf/ambari/blob/e2b8a0e9/contrib/views/pom.xml
----------------------------------------------------------------------
diff --git a/contrib/views/pom.xml b/contrib/views/pom.xml
index 9fa698b..dc74568 100644
--- a/contrib/views/pom.xml
+++ b/contrib/views/pom.xml
@@ -91,6 +91,7 @@
<exclude>**/assets/stylesheets/**</exclude>
<exclude>**/assets/static/javascripts/**</exclude>
<exclude>**/assets/static/stylesheets/**</exclude>
+ <exclude>storm/src/main/resources/**</exclude>
</excludes>
</configuration>
</plugin>
http://git-wip-us.apache.org/repos/asf/ambari/blob/e2b8a0e9/contrib/views/utils/src/main/java/org/apache/ambari/view/utils/ambari/AmbariApi.java
----------------------------------------------------------------------
diff --git a/contrib/views/utils/src/main/java/org/apache/ambari/view/utils/ambari/AmbariApi.java b/contrib/views/utils/src/main/java/org/apache/ambari/view/utils/ambari/AmbariApi.java
index 88e5f48..e49eea5 100644
--- a/contrib/views/utils/src/main/java/org/apache/ambari/view/utils/ambari/AmbariApi.java
+++ b/contrib/views/utils/src/main/java/org/apache/ambari/view/utils/ambari/AmbariApi.java
@@ -29,8 +29,11 @@ import org.json.simple.JSONValue;
import java.io.IOException;
import java.io.InputStream;
+import java.net.URI;
+import java.net.URISyntaxException;
import java.util.ArrayList;
import java.util.List;
+import java.util.Map;
/**
* Provides API to Ambari. Supports both Local and Remote cluster association.
@@ -44,9 +47,10 @@ public class AmbariApi {
private Cluster cluster;
private ViewContext context;
- private String remoteUrl;
+ private String remoteUrlCluster;
private String remoteUsername;
private String remotePassword;
+ private String requestedBy = "views";
/**
* Constructor for Ambari API based on ViewContext
@@ -55,12 +59,35 @@ public class AmbariApi {
public AmbariApi(ViewContext context) {
this.context = context;
- remoteUrl = context.getProperties().get(AMBARI_SERVER_URL_INSTANCE_PROPERTY);
+ remoteUrlCluster = context.getProperties().get(AMBARI_SERVER_URL_INSTANCE_PROPERTY);
remoteUsername = context.getProperties().get(AMBARI_SERVER_USERNAME_INSTANCE_PROPERTY);
remotePassword = context.getProperties().get(AMBARI_SERVER_PASSWORD_INSTANCE_PROPERTY);
}
/**
+ * X-Requested-By header value
+ * @param requestedBy value of X-Requested-By header
+ */
+ public void setRequestedBy(String requestedBy) {
+ this.requestedBy = requestedBy;
+ }
+
+ private String parseAmbariHostname(String remoteUrlCluster) {
+ String hostname;
+ try {
+ URI uri = new URI(remoteUrlCluster);
+ hostname = String.format("%s://%s", uri.getScheme(), uri.getHost());
+ if (uri.getPort() != -1) {
+ hostname += ":" + uri.getPort();
+ }
+ } catch (URISyntaxException e) {
+ throw new AmbariApiException("RA060 Malformed URI of remote Ambari");
+ }
+
+ return hostname;
+ }
+
+ /**
* Provides ability to get cluster topology
* @param requestComponent name of component
* @return list of hostnames with component
@@ -68,7 +95,7 @@ public class AmbariApi {
*/
public List<String> getHostsWithComponent(String requestComponent) throws AmbariApiException {
String method = "hosts?fields=Hosts/public_host_name,host_components/HostRoles/component_name";
- String response = readFromAmbari(method);
+ String response = requestClusterAPI(method);
List<String> foundHosts = new ArrayList<String>();
@@ -90,32 +117,44 @@ public class AmbariApi {
}
/**
- * Request to Ambari REST API. Supports both local and remote cluster
- * @param method REST API path, e.g. /api/v1/clusters/mycluster?...
+ * Shortcut for GET method
+ * @param path REST API path
+ * @return response
+ * @throws AmbariApiException
+ */
+ public String requestClusterAPI(String path) throws AmbariApiException {
+ return requestClusterAPI(path, "GET", null, null);
+ }
+
+ /**
+ * Request to Ambari REST API for current cluster. Supports both local and remote cluster
+ * @param path REST API path after cluster name e.g. /api/v1/clusters/mycluster/[method]
+ * @param method HTTP method
+ * @param data HTTP data
+ * @param headers HTTP headers
* @return response
* @throws AmbariApiException IO error or not associated with cluster
*/
- public String readFromAmbari(String method) throws AmbariApiException {
+ public String requestClusterAPI(String path, String method, String data, Map<String, String> headers) throws AmbariApiException {
String response;
+ URLStreamProviderBasicAuth urlStreamProvider = getUrlStreamProviderBasicAuth();
try {
- InputStream inputStream;
+ String url;
if (isLocalCluster()) {
- AmbariStreamProvider ambariStreamProvider = context.getAmbariStreamProvider();
- String url = String.format("/api/v1/clusters/%s/%s", getCluster().getName(), method);
- inputStream = ambariStreamProvider.readFrom(url, "GET", (String) null, null, true);
+ url = String.format("/api/v1/clusters/%s/%s", getCluster().getName(), path);
} else if (isRemoteCluster()) {
- URLStreamProvider urlStreamProvider = getUrlStreamProviderBasicAuth();
- String url = String.format("%s/%s", remoteUrl, method);
- inputStream = urlStreamProvider.readFrom(url, "GET", (String) null, null);
+
+ url = String.format("%s/%s", remoteUrlCluster, path);
} else {
throw new NoClusterAssociatedException(
"RA030 View is not associated with any cluster. No way to request Ambari.");
}
+ InputStream inputStream = urlStreamProvider.readFrom(url, method, data, headers);
response = IOUtils.toString(inputStream);
} catch (IOException e) {
throw new AmbariApiException("RA040 I/O error while requesting Ambari", e);
@@ -124,6 +163,43 @@ public class AmbariApi {
}
/**
+ * Request to Ambari REST API. Supports both local and remote cluster
+ * @param path REST API path, e.g. /api/v1/clusters/mycluster/
+ * @param method HTTP method
+ * @param data HTTP data
+ * @param headers HTTP headers
+ * @return response
+ * @throws AmbariApiException IO error or not associated with cluster
+ */
+ public String readFromAmbari(String path, String method, String data, Map<String, String> headers) throws AmbariApiException {
+ String response;
+ URLStreamProviderBasicAuth urlStreamProvider = getUrlStreamProviderBasicAuth();
+
+ try {
+ String url;
+
+ if (isLocalCluster()) {
+ url = path;
+
+ } else if (isRemoteCluster()) {
+ String ambariUrl = parseAmbariHostname(remoteUrlCluster);
+ url = ambariUrl + path;
+
+ } else {
+ throw new NoClusterAssociatedException(
+ "RA060 View is not associated with any cluster. No way to request Ambari.");
+ }
+
+ InputStream inputStream = urlStreamProvider.readFrom(url, method, data, headers);
+ response = IOUtils.toString(inputStream);
+ } catch (IOException e) {
+ throw new AmbariApiException("RA050 I/O error while requesting Ambari", e);
+ }
+ return response;
+ }
+
+
+ /**
* Check if associated with local or remote cluster
* @return true if associated
*/
@@ -170,7 +246,7 @@ public class AmbariApi {
* @return true if associated
*/
public boolean isRemoteCluster() {
- return remoteUrl != null && !remoteUrl.isEmpty();
+ return remoteUrlCluster != null && !remoteUrlCluster.isEmpty();
}
/**
@@ -182,21 +258,31 @@ public class AmbariApi {
return null;
URLStreamProvider urlStreamProviderBasicAuth = getUrlStreamProviderBasicAuth();
- return new RemoteCluster(remoteUrl, urlStreamProviderBasicAuth);
+ return new RemoteCluster(remoteUrlCluster, urlStreamProviderBasicAuth);
}
/**
* Build URLStreamProvider with Basic Authentication for Remote Cluster
* @return URLStreamProvider
*/
- public URLStreamProvider getUrlStreamProviderBasicAuth() {
- if (remoteUsername == null || remoteUsername.isEmpty() ||
- remotePassword == null || remotePassword.isEmpty()) {
- throw new AmbariApiException("RA020 Remote Ambari username and password are not filled");
- }
-
- URLStreamProvider urlStreamProvider = context.getURLStreamProvider();
+ public URLStreamProviderBasicAuth getUrlStreamProviderBasicAuth() {
+ URLStreamProviderBasicAuth urlStreamProviderBasicAuth;
+ if (isRemoteCluster()) {
+ if (remoteUsername == null || remoteUsername.isEmpty() ||
+ remotePassword == null || remotePassword.isEmpty()) {
+ throw new AmbariApiException("RA020 Remote Ambari username and password are not filled");
+ }
- return new URLStreamProviderBasicAuth(urlStreamProvider, remoteUsername, remotePassword);
+ URLStreamProvider urlStreamProvider = context.getURLStreamProvider();
+ urlStreamProviderBasicAuth =
+ new URLStreamProviderBasicAuth(urlStreamProvider, remoteUsername, remotePassword);
+ } else if (isLocalCluster()) {
+ urlStreamProviderBasicAuth = new URLStreamProviderBasicAuth(context.getAmbariStreamProvider());
+ } else {
+ throw new NoClusterAssociatedException(
+ "RA070 Not associated with any cluster. URLStreamProvider is not available");
+ }
+ urlStreamProviderBasicAuth.setRequestedBy(requestedBy);
+ return urlStreamProviderBasicAuth;
}
}
http://git-wip-us.apache.org/repos/asf/ambari/blob/e2b8a0e9/contrib/views/utils/src/main/java/org/apache/ambari/view/utils/ambari/URLStreamProviderBasicAuth.java
----------------------------------------------------------------------
diff --git a/contrib/views/utils/src/main/java/org/apache/ambari/view/utils/ambari/URLStreamProviderBasicAuth.java b/contrib/views/utils/src/main/java/org/apache/ambari/view/utils/ambari/URLStreamProviderBasicAuth.java
index 87a4acb..c9f735a 100644
--- a/contrib/views/utils/src/main/java/org/apache/ambari/view/utils/ambari/URLStreamProviderBasicAuth.java
+++ b/contrib/views/utils/src/main/java/org/apache/ambari/view/utils/ambari/URLStreamProviderBasicAuth.java
@@ -18,24 +18,26 @@
package org.apache.ambari.view.utils.ambari;
+import org.apache.ambari.view.AmbariStreamProvider;
import org.apache.ambari.view.URLStreamProvider;
import org.apache.commons.codec.binary.Base64;
import java.io.IOException;
import java.io.InputStream;
-import java.net.HttpURLConnection;
-import java.net.URL;
import java.util.HashMap;
-import java.util.List;
import java.util.Map;
/**
- * Wrapper for URLStreamProvider that adds authentication header
+ * Wrapper for URLStreamProvider that adds authentication header.
+ * Also supports AmbariStreamProvider. readAs or readAsCurrent should not be used
+ * with AmbariStreamProvider.
*/
public class URLStreamProviderBasicAuth implements URLStreamProvider {
private URLStreamProvider urlStreamProvider;
+ private AmbariStreamProvider ambariStreamProvider;
private String username;
private String password;
+ private String requestedBy = "views";
public URLStreamProviderBasicAuth(URLStreamProvider urlStreamProvider, String username, String password) {
this.urlStreamProvider = urlStreamProvider;
@@ -43,47 +45,78 @@ public class URLStreamProviderBasicAuth implements URLStreamProvider {
this.password = password;
}
+ public URLStreamProviderBasicAuth(AmbariStreamProvider urlStreamProvider) {
+ this.ambariStreamProvider = urlStreamProvider;
+ }
+
+ /**
+ * X-Requested-By header value
+ * @param requestedBy value of X-Requested-By header
+ */
+ public void setRequestedBy(String requestedBy) {
+ this.requestedBy = requestedBy;
+ }
+
@Override
public InputStream readFrom(String url, String method, String data, Map<String, String> headers) throws IOException {
- return urlStreamProvider.readFrom(url, method, data, addAuthHeaders(headers));
+ if (urlStreamProvider != null) {
+ return urlStreamProvider.readFrom(url, method, data, addHeaders(headers));
+ } else {
+ return ambariStreamProvider.readFrom(url, method, data, addHeaders(headers), true);
+ }
}
@Override
public InputStream readFrom(String url, String method, InputStream data, Map<String, String> headers) throws IOException {
- return urlStreamProvider.readFrom(url, method, data, addAuthHeaders(headers));
+ if (urlStreamProvider != null) {
+ return urlStreamProvider.readFrom(url, method, data, addHeaders(headers));
+ } else {
+ return ambariStreamProvider.readFrom(url, method, data, addHeaders(headers), true);
+ }
}
@Override
public InputStream readAs(String url, String method, String data, Map<String, String> headers, String doAs) throws IOException {
- return urlStreamProvider.readAs(url, method, data, addAuthHeaders(headers), doAs);
+ return urlStreamProvider.readAs(url, method, data, addHeaders(headers), doAs);
}
@Override
public InputStream readAs(String url, String method, InputStream data, Map<String, String> headers, String doAs) throws IOException {
- return urlStreamProvider.readAs(url, method, data, addAuthHeaders(headers), doAs);
+ return urlStreamProvider.readAs(url, method, data, addHeaders(headers), doAs);
}
@Override
public InputStream readAsCurrent(String url, String method, String data, Map<String, String> headers) throws IOException {
- return urlStreamProvider.readAsCurrent(url, method, data, addAuthHeaders(headers));
+ return urlStreamProvider.readAsCurrent(url, method, data, addHeaders(headers));
}
@Override
public InputStream readAsCurrent(String url, String method, InputStream data, Map<String, String> headers) throws IOException {
- return urlStreamProvider.readAsCurrent(url, method, data, addAuthHeaders(headers));
+ return urlStreamProvider.readAsCurrent(url, method, data, addHeaders(headers));
}
- private HashMap<String, String> addAuthHeaders(Map<String, String> customHeaders) {
+ private HashMap<String, String> addHeaders(Map<String, String> customHeaders) {
HashMap<String, String> newHeaders = new HashMap<String, String>();
if (customHeaders != null)
newHeaders.putAll(customHeaders);
+ if (urlStreamProvider != null) {
+ // basic auth is not needed for AmbariStreamProvider
+ addBasicAuthHeaders(newHeaders);
+ }
+ addRequestedByHeaders(newHeaders);
+ return newHeaders;
+ }
+
+ private void addRequestedByHeaders(HashMap<String, String> newHeaders) {
+ newHeaders.put("X-Requested-By", requestedBy);
+ }
+
+ private void addBasicAuthHeaders(HashMap<String, String> headers) {
String authString = username + ":" + password;
byte[] authEncBytes = Base64.encodeBase64(authString.getBytes());
String authStringEnc = new String(authEncBytes);
- newHeaders.put("Authorization", "Basic " + authStringEnc);
- newHeaders.put("X-Requested-By", "views");
- return newHeaders;
+ headers.put("Authorization", "Basic " + authStringEnc);
}
}