You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@helix.apache.org by gb...@apache.org on 2016/09/17 19:16:25 UTC
helix git commit: helix-ui improvements
Repository: helix
Updated Branches:
refs/heads/master d32968fbd -> 4d9db5f02
helix-ui improvements
Adds zkAliases to helix-ui configuration
`zkAliases` is a map of alias to ZK connection string. If this is provided,
then one can use the alias in place of the zk connection string in
`/dashboard/{zk}` route. In addition, the root `/dashboard` route will allow
the user to choose among the aliases as opposed to pasting in a zk connection
string.
Add ping / health resources
Add tags to instance table
Show controller leader in UI
Add reset resource button to helix-ui
Fix FULL_AUTO bug
Support SEMI_AUTO resource that do not have state map
Project: http://git-wip-us.apache.org/repos/asf/helix/repo
Commit: http://git-wip-us.apache.org/repos/asf/helix/commit/4d9db5f0
Tree: http://git-wip-us.apache.org/repos/asf/helix/tree/4d9db5f0
Diff: http://git-wip-us.apache.org/repos/asf/helix/diff/4d9db5f0
Branch: refs/heads/master
Commit: 4d9db5f02cad4a05ae4f08143e845284e02ee1aa
Parents: d32968f
Author: Greg Brandt <gr...@airbnb.com>
Authored: Thu Jul 14 14:00:43 2016 -0700
Committer: Greg Brandt <br...@gmail.com>
Committed: Sat Sep 17 12:16:11 2016 -0700
----------------------------------------------------------------------
.../org/apache/helix/ui/HelixUIApplication.java | 23 +++++--
.../ui/HelixUIApplicationConfiguration.java | 12 ++++
.../org/apache/helix/ui/api/InstanceSpec.java | 11 +++-
.../apache/helix/ui/api/ResourceStateSpec.java | 53 +++++++++++----
.../helix/ui/resource/DashboardResource.java | 68 ++++++++++++++++----
.../helix/ui/resource/HealthResource.java | 39 +++++++++++
.../apache/helix/ui/resource/PingResource.java | 38 +++++++++++
.../org/apache/helix/ui/util/ClientCache.java | 9 ++-
.../org/apache/helix/ui/util/DataCache.java | 60 +++++++++++++++--
.../org/apache/helix/ui/view/ClusterView.java | 9 ++-
.../org/apache/helix/ui/view/LandingView.java | 11 +++-
helix-ui/src/main/resources/assets/css/app.css | 8 +--
.../main/resources/assets/js/landing-view.js | 10 ++-
.../resources/assets/js/resource-state-table.js | 25 +++++++
.../org/apache/helix/ui/view/cluster-view.ftl | 4 ++
.../helix/ui/view/common/instance-table.ftl | 2 +
.../ui/view/common/resource-state-table.ftl | 13 +++-
.../org/apache/helix/ui/view/landing-view.ftl | 27 +++++++-
18 files changed, 370 insertions(+), 52 deletions(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/helix/blob/4d9db5f0/helix-ui/src/main/java/org/apache/helix/ui/HelixUIApplication.java
----------------------------------------------------------------------
diff --git a/helix-ui/src/main/java/org/apache/helix/ui/HelixUIApplication.java b/helix-ui/src/main/java/org/apache/helix/ui/HelixUIApplication.java
index c6e366e..d431f1d 100644
--- a/helix-ui/src/main/java/org/apache/helix/ui/HelixUIApplication.java
+++ b/helix-ui/src/main/java/org/apache/helix/ui/HelixUIApplication.java
@@ -28,6 +28,8 @@ import io.dropwizard.views.ViewBundle;
import org.apache.helix.ui.health.ClusterConnectionHealthCheck;
import org.apache.helix.ui.resource.AdminResource;
import org.apache.helix.ui.resource.DashboardResource;
+import org.apache.helix.ui.resource.HealthResource;
+import org.apache.helix.ui.resource.PingResource;
import org.apache.helix.ui.resource.VisualizerResource;
import org.apache.helix.ui.task.ClearClientCache;
import org.apache.helix.ui.task.ClearDataCacheTask;
@@ -37,6 +39,10 @@ import org.apache.helix.ui.util.ZkAddressValidator;
import org.eclipse.jetty.util.component.AbstractLifeCycle;
import org.eclipse.jetty.util.component.LifeCycle;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+
public class HelixUIApplication extends Application<HelixUIApplicationConfiguration> {
@Override
public String getName() {
@@ -60,7 +66,7 @@ public class HelixUIApplication extends Application<HelixUIApplicationConfigurat
@Override
public void run(HelixUIApplicationConfiguration config, Environment environment) throws Exception {
final ZkAddressValidator zkAddressValidator = new ZkAddressValidator(config.getZkAddresses());
- final ClientCache clientCache = new ClientCache(zkAddressValidator);
+ final ClientCache clientCache = new ClientCache(zkAddressValidator, config.getZkAliases());
// Close all connections when application stops
environment.lifecycle().addLifeCycleListener(new AbstractLifeCycle.AbstractLifeCycleListener() {
@@ -70,15 +76,24 @@ public class HelixUIApplication extends Application<HelixUIApplicationConfigurat
}
});
- DataCache dataCache = new DataCache(clientCache);
+ // Any zk aliases
+ List<String> zkAliases = new ArrayList<String>();
+ zkAliases.addAll(config.getZkAliases().keySet());
+ Collections.sort(zkAliases);
+ DataCache dataCache = new DataCache(clientCache);
- DashboardResource dashboardResource
- = new DashboardResource(clientCache, dataCache, config.isAdminMode());
+ DashboardResource dashboardResource = new DashboardResource(
+ clientCache,
+ dataCache,
+ config.isAdminMode(),
+ zkAliases);
environment.healthChecks().register("clusterConnection", new ClusterConnectionHealthCheck(clientCache));
environment.jersey().register(dashboardResource);
environment.jersey().register(new VisualizerResource(clientCache, dataCache));
+ environment.jersey().register(new HealthResource());
+ environment.jersey().register(new PingResource());
environment.admin().addTask(new ClearDataCacheTask(dataCache));
environment.admin().addTask(new ClearClientCache(clientCache));
http://git-wip-us.apache.org/repos/asf/helix/blob/4d9db5f0/helix-ui/src/main/java/org/apache/helix/ui/HelixUIApplicationConfiguration.java
----------------------------------------------------------------------
diff --git a/helix-ui/src/main/java/org/apache/helix/ui/HelixUIApplicationConfiguration.java b/helix-ui/src/main/java/org/apache/helix/ui/HelixUIApplicationConfiguration.java
index 6547b47..7a0cd74 100644
--- a/helix-ui/src/main/java/org/apache/helix/ui/HelixUIApplicationConfiguration.java
+++ b/helix-ui/src/main/java/org/apache/helix/ui/HelixUIApplicationConfiguration.java
@@ -24,6 +24,8 @@ import com.google.common.collect.ImmutableMap;
import io.dropwizard.Configuration;
import javax.validation.constraints.NotNull;
+import java.util.Collections;
+import java.util.Map;
import java.util.Set;
public class HelixUIApplicationConfiguration extends Configuration {
@@ -34,6 +36,8 @@ public class HelixUIApplicationConfiguration extends Configuration {
private Set<String> zkAddresses;
+ private Map<String, String> zkAliases = Collections.emptyMap();
+
@JsonProperty("viewRendererConfiguration")
public ImmutableMap<String, ImmutableMap<String, String>> getViewRendererConfiguration() {
return viewRendererConfiguration;
@@ -54,4 +58,12 @@ public class HelixUIApplicationConfiguration extends Configuration {
public Set<String> getZkAddresses() {
return zkAddresses;
}
+
+ public Map<String, String> getZkAliases() {
+ return zkAliases;
+ }
+
+ public void setZkAliases(Map<String, String> zkAliases) {
+ this.zkAliases = zkAliases;
+ }
}
http://git-wip-us.apache.org/repos/asf/helix/blob/4d9db5f0/helix-ui/src/main/java/org/apache/helix/ui/api/InstanceSpec.java
----------------------------------------------------------------------
diff --git a/helix-ui/src/main/java/org/apache/helix/ui/api/InstanceSpec.java b/helix-ui/src/main/java/org/apache/helix/ui/api/InstanceSpec.java
index 6ce008d..a7573de 100644
--- a/helix-ui/src/main/java/org/apache/helix/ui/api/InstanceSpec.java
+++ b/helix-ui/src/main/java/org/apache/helix/ui/api/InstanceSpec.java
@@ -19,17 +19,22 @@ package org.apache.helix.ui.api;
* under the License.
*/
+import java.util.List;
+
public class InstanceSpec implements Comparable<InstanceSpec> {
private final String instanceName;
private final boolean enabled;
private final boolean live;
+ private final List<String> tags;
public InstanceSpec(String instanceName,
boolean enabled,
- boolean live) {
+ boolean live,
+ List<String> tags) {
this.instanceName = instanceName;
this.enabled = enabled;
this.live = live;
+ this.tags = tags;
}
public String getInstanceName() {
@@ -44,6 +49,10 @@ public class InstanceSpec implements Comparable<InstanceSpec> {
return live;
}
+ public List<String> getTags() {
+ return tags;
+ }
+
@Override
public int compareTo(InstanceSpec o) {
return instanceName.compareTo(o.instanceName);
http://git-wip-us.apache.org/repos/asf/helix/blob/4d9db5f0/helix-ui/src/main/java/org/apache/helix/ui/api/ResourceStateSpec.java
----------------------------------------------------------------------
diff --git a/helix-ui/src/main/java/org/apache/helix/ui/api/ResourceStateSpec.java b/helix-ui/src/main/java/org/apache/helix/ui/api/ResourceStateSpec.java
index 64fd48f..f6fbba9 100644
--- a/helix-ui/src/main/java/org/apache/helix/ui/api/ResourceStateSpec.java
+++ b/helix-ui/src/main/java/org/apache/helix/ui/api/ResourceStateSpec.java
@@ -61,27 +61,52 @@ public class ResourceStateSpec {
public List<ResourceStateTableRow> getResourceStateTable() {
List<ResourceStateTableRow> resourceStateTable = new ArrayList<ResourceStateTableRow>();
- Set<String> partitionNames = idealState.getPartitionSet();
- for (String partitionName : partitionNames) {
- Map<String, String> stateMap = idealState.getInstanceStateMap(partitionName);
- if (stateMap != null) {
- for (Map.Entry<String, String> entry : stateMap.entrySet()) {
- String instanceName = entry.getKey();
- String ideal = entry.getValue();
- String external = null;
- if (externalView != null) {
- Map<String, String> externalStateMap = externalView.getStateMap(partitionName);
- if (externalStateMap != null) {
- external = externalStateMap.get(instanceName);
- }
+ if (useExternalView(idealState)) {
+ Set<String> partitionNames = externalView.getPartitionSet();
+ for (String partitionName : partitionNames) {
+ Map<String, String> stateMap = externalView.getStateMap(partitionName);
+ if (stateMap != null) {
+ for (Map.Entry<String, String> entry : stateMap.entrySet()) {
+ String instanceName = entry.getKey();
+ String state = entry.getValue();
+ resourceStateTable.add(new ResourceStateTableRow(resource, partitionName, instanceName, "N/A", state));
}
+ }
+ }
+ } else {
+ // By default, we assume the ideal state has something in it
+ Set<String> partitionNames = idealState.getPartitionSet();
+ for (String partitionName : partitionNames) {
+ Map<String, String> stateMap = idealState.getInstanceStateMap(partitionName);
+ if (stateMap != null) {
+ for (Map.Entry<String, String> entry : stateMap.entrySet()) {
+ String instanceName = entry.getKey();
+ String ideal = entry.getValue();
+
+ String external = null;
+ if (externalView != null) {
+ Map<String, String> externalStateMap = externalView.getStateMap(partitionName);
+ if (externalStateMap != null) {
+ external = externalStateMap.get(instanceName);
+ }
+ }
- resourceStateTable.add(new ResourceStateTableRow(resource, partitionName, instanceName, ideal, external));
+ resourceStateTable.add(new ResourceStateTableRow(resource, partitionName, instanceName, ideal, external));
+ }
}
}
}
return resourceStateTable;
}
+
+ /**
+ * Returns true if there is no legitimate state mapping in the ideal state.
+ */
+ private static boolean useExternalView(IdealState idealState) {
+ return IdealState.RebalanceMode.FULL_AUTO.equals(idealState.getRebalanceMode())
+ || (IdealState.RebalanceMode.SEMI_AUTO.equals(idealState.getRebalanceMode())
+ && idealState.getRecord().getMapFields().isEmpty());
+ }
}
http://git-wip-us.apache.org/repos/asf/helix/blob/4d9db5f0/helix-ui/src/main/java/org/apache/helix/ui/resource/DashboardResource.java
----------------------------------------------------------------------
diff --git a/helix-ui/src/main/java/org/apache/helix/ui/resource/DashboardResource.java b/helix-ui/src/main/java/org/apache/helix/ui/resource/DashboardResource.java
index 0991599..c717441 100644
--- a/helix-ui/src/main/java/org/apache/helix/ui/resource/DashboardResource.java
+++ b/helix-ui/src/main/java/org/apache/helix/ui/resource/DashboardResource.java
@@ -29,6 +29,8 @@ import org.apache.helix.ui.util.DataCache;
import org.apache.helix.ui.view.ClusterView;
import org.apache.helix.ui.view.LandingView;
import org.apache.helix.ui.view.ResourceView;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
import javax.ws.rs.*;
import javax.ws.rs.core.MediaType;
@@ -39,6 +41,7 @@ import java.util.*;
@Path("/")
@Produces(MediaType.TEXT_HTML)
public class DashboardResource {
+ private static final Logger LOG = LoggerFactory.getLogger(DashboardResource.class);
private static final List<String> REBALANCE_MODES = ImmutableList.of(
IdealState.RebalanceMode.SEMI_AUTO.toString(),
IdealState.RebalanceMode.FULL_AUTO.toString(),
@@ -49,13 +52,16 @@ public class DashboardResource {
private final boolean adminMode;
private final ClientCache clientCache;
private final DataCache dataCache;
+ private final List<String> zkAliases;
public DashboardResource(ClientCache clientCache,
DataCache dataCache,
- boolean adminMode) {
+ boolean adminMode,
+ List<String> zkAliases) {
this.clientCache = clientCache;
this.dataCache = dataCache;
this.adminMode = adminMode;
+ this.zkAliases = zkAliases;
}
@GET
@@ -66,7 +72,7 @@ public class DashboardResource {
@GET
@Path("/dashboard")
public LandingView getLandingView() {
- return new LandingView();
+ return new LandingView(zkAliases);
}
@GET
@@ -92,7 +98,18 @@ public class DashboardResource {
// Check it
if (!ZKUtil.isClusterSetup(activeCluster, clusterConnection.getZkClient())) {
- return new ClusterView(adminMode, zkAddress, clusters, false, activeCluster, null, null, null, null, null);
+ return new ClusterView(
+ adminMode,
+ zkAddress,
+ clusters,
+ false,
+ activeCluster,
+ null,
+ null,
+ null,
+ null,
+ null,
+ null);
}
// Resources in the active cluster
@@ -108,17 +125,21 @@ public class DashboardResource {
// Config table
List<ConfigTableRow> configTable = dataCache.getConfigCache().get(clusterSpec);
+ // Controller leader
+ String controllerLeader = dataCache.getControllerLeaderCache().get(clusterSpec);
+
return new ClusterView(
- adminMode,
- zkAddress,
- clusters,
- true,
- activeCluster,
- activeClusterResources,
- instanceSpecs,
- configTable,
- stateModels,
- REBALANCE_MODES);
+ adminMode,
+ zkAddress,
+ clusters,
+ true,
+ activeCluster,
+ activeClusterResources,
+ instanceSpecs,
+ configTable,
+ stateModels,
+ REBALANCE_MODES,
+ controllerLeader);
}
@GET
@@ -188,4 +209,25 @@ public class DashboardResource {
IdealStateSpec.fromIdealState(idealState),
instanceSpecs);
}
+
+ @POST
+ @Path("/dashboard/{zkAddress}/{cluster}/{resource}")
+ public Response resourceAction(
+ @PathParam("zkAddress") String zkAddress,
+ @PathParam("cluster") String cluster,
+ @PathParam("resource") String resource,
+ @QueryParam("action") @DefaultValue("reset") String action) {
+ ClusterConnection clusterConnection = clientCache.get(zkAddress);
+
+ if ("reset".equals(action)) {
+ clusterConnection
+ .getClusterSetup()
+ .getClusterManagementTool()
+ .resetResource(cluster, Collections.singletonList(resource));
+ LOG.info("Reset resource {} in cluster {}", resource, cluster);
+ return Response.ok().build();
+ } else {
+ return Response.status(Response.Status.BAD_REQUEST).entity("Unsupported action " + action).build();
+ }
+ }
}
http://git-wip-us.apache.org/repos/asf/helix/blob/4d9db5f0/helix-ui/src/main/java/org/apache/helix/ui/resource/HealthResource.java
----------------------------------------------------------------------
diff --git a/helix-ui/src/main/java/org/apache/helix/ui/resource/HealthResource.java b/helix-ui/src/main/java/org/apache/helix/ui/resource/HealthResource.java
new file mode 100644
index 0000000..2ea2520
--- /dev/null
+++ b/helix-ui/src/main/java/org/apache/helix/ui/resource/HealthResource.java
@@ -0,0 +1,39 @@
+package org.apache.helix.ui.resource;
+
+/*
+ * 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.
+ */
+
+import io.dropwizard.jersey.caching.CacheControl;
+
+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;
+
+@Path("/health")
+@Produces(MediaType.APPLICATION_JSON)
+public class HealthResource {
+ @GET
+ @CacheControl(mustRevalidate = true, noCache = true, noStore = true)
+ public Response health() {
+ // TODO: placeholder for real health checks
+ return Response.status(Response.Status.OK).entity("IMOK").build();
+ }
+}
http://git-wip-us.apache.org/repos/asf/helix/blob/4d9db5f0/helix-ui/src/main/java/org/apache/helix/ui/resource/PingResource.java
----------------------------------------------------------------------
diff --git a/helix-ui/src/main/java/org/apache/helix/ui/resource/PingResource.java b/helix-ui/src/main/java/org/apache/helix/ui/resource/PingResource.java
new file mode 100644
index 0000000..33c37fe
--- /dev/null
+++ b/helix-ui/src/main/java/org/apache/helix/ui/resource/PingResource.java
@@ -0,0 +1,38 @@
+package org.apache.helix.ui.resource;
+
+/*
+ * 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.
+ */
+
+import io.dropwizard.jersey.caching.CacheControl;
+
+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;
+
+@Path("/ping")
+@Produces(MediaType.APPLICATION_JSON)
+public class PingResource {
+ @GET
+ @CacheControl(mustRevalidate = true, noCache = true, noStore = true)
+ public Response health() {
+ return Response.status(Response.Status.OK).entity("pong").build();
+ }
+}
http://git-wip-us.apache.org/repos/asf/helix/blob/4d9db5f0/helix-ui/src/main/java/org/apache/helix/ui/util/ClientCache.java
----------------------------------------------------------------------
diff --git a/helix-ui/src/main/java/org/apache/helix/ui/util/ClientCache.java b/helix-ui/src/main/java/org/apache/helix/ui/util/ClientCache.java
index f461277..dd02a8c 100644
--- a/helix-ui/src/main/java/org/apache/helix/ui/util/ClientCache.java
+++ b/helix-ui/src/main/java/org/apache/helix/ui/util/ClientCache.java
@@ -41,9 +41,11 @@ public class ClientCache {
private static final int DEFAULT_CONNECTION_TIMEOUT_MILLIS = 5000;
private final ZkAddressValidator zkAddressValidator;
+ private final Map<String, String> zkAliases;
- public ClientCache(ZkAddressValidator zkAddressValidator) {
+ public ClientCache(ZkAddressValidator zkAddressValidator, Map<String, String> zkAliases) {
this.zkAddressValidator = zkAddressValidator;
+ this.zkAliases = zkAliases;
}
// Manages and caches lifecycle of connections to ZK
@@ -77,6 +79,11 @@ public class ClientCache {
});
public ClusterConnection get(String zkAddress) {
+ // Map alias if exists
+ if (zkAliases.containsKey(zkAddress)) {
+ zkAddress = zkAliases.get(zkAddress);
+ }
+
try {
zkAddress = URLDecoder.decode(zkAddress, "UTF-8");
} catch (Exception e) {
http://git-wip-us.apache.org/repos/asf/helix/blob/4d9db5f0/helix-ui/src/main/java/org/apache/helix/ui/util/DataCache.java
----------------------------------------------------------------------
diff --git a/helix-ui/src/main/java/org/apache/helix/ui/util/DataCache.java b/helix-ui/src/main/java/org/apache/helix/ui/util/DataCache.java
index 1441006..9b73a95 100644
--- a/helix-ui/src/main/java/org/apache/helix/ui/util/DataCache.java
+++ b/helix-ui/src/main/java/org/apache/helix/ui/util/DataCache.java
@@ -22,6 +22,8 @@ package org.apache.helix.ui.util;
import com.google.common.cache.CacheBuilder;
import com.google.common.cache.CacheLoader;
import com.google.common.cache.LoadingCache;
+import org.apache.helix.ZNRecord;
+import org.apache.helix.manager.zk.ZKHelixDataAccessor;
import org.apache.helix.manager.zk.ZkClient;
import org.apache.helix.model.HelixConfigScope;
import org.apache.helix.model.InstanceConfig;
@@ -40,6 +42,7 @@ public class DataCache {
private static final TimeUnit CACHE_EXPIRY_UNIT = TimeUnit.SECONDS;
private final LoadingCache<String, List<String>> clusterCache;
+ private final LoadingCache<ClusterSpec, String> controllerLeaderCache;
private final LoadingCache<ClusterSpec, List<String>> resourceCache;
private final LoadingCache<ClusterSpec, List<ConfigTableRow>> configCache;
private final LoadingCache<ResourceSpec, List<ConfigTableRow>> resourceConfigCache;
@@ -58,6 +61,22 @@ public class DataCache {
}
});
+ this.controllerLeaderCache = CacheBuilder.newBuilder()
+ .expireAfterWrite(CACHE_EXPIRY_TIME, CACHE_EXPIRY_UNIT)
+ .build(new CacheLoader<ClusterSpec, String>() {
+ @Override
+ public String load(ClusterSpec clusterSpec) throws Exception {
+ ZkClient zkClient = clientCache
+ .get(clusterSpec.getZkAddress())
+ .getZkClient();
+
+ ZNRecord znRecord = zkClient.readData(
+ String.format("/%s/CONTROLLER/LEADER", clusterSpec.getClusterName()));
+
+ return znRecord.getId();
+ }
+ });
+
this.resourceCache = CacheBuilder.newBuilder()
.expireAfterWrite(CACHE_EXPIRY_TIME, CACHE_EXPIRY_UNIT)
.build(new CacheLoader<ClusterSpec, List<String>>() {
@@ -142,13 +161,16 @@ public class DataCache {
ClusterConnection clusterConnection = clientCache.get(clusterSpec.getZkAddress());
// Instances in the cluster
- List<String> instances =
- clusterConnection.getClusterSetup().getClusterManagementTool().getInstancesInCluster(clusterSpec.getClusterName());
+ List<String> instances = clusterConnection
+ .getClusterSetup()
+ .getClusterManagementTool()
+ .getInstancesInCluster(clusterSpec.getClusterName());
// Live instances in the cluster
// TODO: should be able to use clusterSetup for this, but no method available
- List<String> liveInstances
- = clusterConnection.getZkClient().getChildren(String.format("/%s/LIVEINSTANCES", clusterSpec.getClusterName()));
+ List<String> liveInstances = clusterConnection
+ .getZkClient()
+ .getChildren(String.format("/%s/LIVEINSTANCES", clusterSpec.getClusterName()));
Set<String> liveInstanceSet = new HashSet<String>();
if (liveInstances != null) {
liveInstanceSet.addAll(liveInstances);
@@ -167,14 +189,34 @@ public class DataCache {
}
}
+ // Instance tags
+ Map<String, List<String>> instanceTags = new HashMap<String, List<String>>();
+ if (instances != null) {
+ for (String instance : instances) {
+ InstanceConfig instanceConfig = clusterConnection
+ .getClusterSetup()
+ .getClusterManagementTool()
+ .getInstanceConfig(clusterSpec.getClusterName(), instance);
+
+ List<String> tags = instanceConfig.getTags() == null
+ ? Collections.<String>emptyList()
+ : instanceConfig.getTags();
+
+ Collections.sort(tags);
+
+ instanceTags.put(instance, tags);
+ }
+ }
+
// Rows
List<InstanceSpec> instanceSpecs = new ArrayList<InstanceSpec>();
if (instances != null) {
for (String instance : instances) {
instanceSpecs.add(new InstanceSpec(
- instance,
- enabledInstances.contains(instance),
- liveInstanceSet.contains(instance)));
+ instance,
+ enabledInstances.contains(instance),
+ liveInstanceSet.contains(instance),
+ instanceTags.get(instance)));
}
}
@@ -195,6 +237,10 @@ public class DataCache {
return clusterCache;
}
+ public LoadingCache<ClusterSpec, String> getControllerLeaderCache() {
+ return controllerLeaderCache;
+ }
+
public LoadingCache<ClusterSpec, List<String>> getResourceCache() {
return resourceCache;
}
http://git-wip-us.apache.org/repos/asf/helix/blob/4d9db5f0/helix-ui/src/main/java/org/apache/helix/ui/view/ClusterView.java
----------------------------------------------------------------------
diff --git a/helix-ui/src/main/java/org/apache/helix/ui/view/ClusterView.java b/helix-ui/src/main/java/org/apache/helix/ui/view/ClusterView.java
index 27e1050..4962c74 100644
--- a/helix-ui/src/main/java/org/apache/helix/ui/view/ClusterView.java
+++ b/helix-ui/src/main/java/org/apache/helix/ui/view/ClusterView.java
@@ -38,6 +38,7 @@ public class ClusterView extends View {
private final List<ConfigTableRow> configTable;
private final List<String> stateModels;
private final List<String> rebalanceModes;
+ private final String controllerLeader;
public ClusterView(boolean adminMode,
String zkAddress,
@@ -48,7 +49,8 @@ public class ClusterView extends View {
List<InstanceSpec> instanceSpecs,
List<ConfigTableRow> configTable,
List<String> stateModels,
- List<String> rebalanceModes) {
+ List<String> rebalanceModes,
+ String controllerLeader) {
super("cluster-view.ftl");
this.adminMode = adminMode;
this.zkAddress = zkAddress;
@@ -60,6 +62,7 @@ public class ClusterView extends View {
this.configTable = configTable;
this.stateModels = stateModels;
this.rebalanceModes = rebalanceModes;
+ this.controllerLeader = controllerLeader;
}
public boolean isAdminMode() {
@@ -101,4 +104,8 @@ public class ClusterView extends View {
public List<String> getRebalanceModes() {
return rebalanceModes;
}
+
+ public String getControllerLeader() {
+ return controllerLeader;
+ }
}
http://git-wip-us.apache.org/repos/asf/helix/blob/4d9db5f0/helix-ui/src/main/java/org/apache/helix/ui/view/LandingView.java
----------------------------------------------------------------------
diff --git a/helix-ui/src/main/java/org/apache/helix/ui/view/LandingView.java b/helix-ui/src/main/java/org/apache/helix/ui/view/LandingView.java
index 96d69e0..0826be0 100644
--- a/helix-ui/src/main/java/org/apache/helix/ui/view/LandingView.java
+++ b/helix-ui/src/main/java/org/apache/helix/ui/view/LandingView.java
@@ -21,8 +21,17 @@ package org.apache.helix.ui.view;
import io.dropwizard.views.View;
+import java.util.List;
+
public class LandingView extends View {
- public LandingView() {
+ private final List<String> zkAliases;
+
+ public LandingView(List<String> zkAliases) {
super("landing-view.ftl");
+ this.zkAliases = zkAliases;
+ }
+
+ public List<String> getZkAliases() {
+ return zkAliases;
}
}
http://git-wip-us.apache.org/repos/asf/helix/blob/4d9db5f0/helix-ui/src/main/resources/assets/css/app.css
----------------------------------------------------------------------
diff --git a/helix-ui/src/main/resources/assets/css/app.css b/helix-ui/src/main/resources/assets/css/app.css
index 0a8f623..0223b25 100644
--- a/helix-ui/src/main/resources/assets/css/app.css
+++ b/helix-ui/src/main/resources/assets/css/app.css
@@ -51,10 +51,6 @@
width: 50%;
}
-#landing-area input {
- width: 80%;
-}
-
/* cluster-view.ftl */
#cluster-views-area {
@@ -109,3 +105,7 @@
.instance-down {
background-color: #e5e5e5;
}
+
+#safe-resource-actions {
+ float: right;
+}
http://git-wip-us.apache.org/repos/asf/helix/blob/4d9db5f0/helix-ui/src/main/resources/assets/js/landing-view.js
----------------------------------------------------------------------
diff --git a/helix-ui/src/main/resources/assets/js/landing-view.js b/helix-ui/src/main/resources/assets/js/landing-view.js
index fe46e8d..0847ce1 100644
--- a/helix-ui/src/main/resources/assets/js/landing-view.js
+++ b/helix-ui/src/main/resources/assets/js/landing-view.js
@@ -19,7 +19,11 @@
$(document).ready(function() {
$("#landing-form-button").click(function(event) {
- event.preventDefault()
- window.location = "/dashboard/" + encodeURIComponent($("#zk-address").val())
- })
+ event.preventDefault();
+ if ($("#zk-address").val()) {
+ window.location = "/dashboard/" + encodeURIComponent($("#zk-address").val());
+ } else {
+ window.location = "/dashboard/" + encodeURIComponent($("#zk-alias").val());
+ }
+ });
})
http://git-wip-us.apache.org/repos/asf/helix/blob/4d9db5f0/helix-ui/src/main/resources/assets/js/resource-state-table.js
----------------------------------------------------------------------
diff --git a/helix-ui/src/main/resources/assets/js/resource-state-table.js b/helix-ui/src/main/resources/assets/js/resource-state-table.js
index 6ab112b..2a75b86 100644
--- a/helix-ui/src/main/resources/assets/js/resource-state-table.js
+++ b/helix-ui/src/main/resources/assets/js/resource-state-table.js
@@ -27,6 +27,31 @@ $(document).ready(function() {
}
})
+ $('#reset-resource').click(function(event) {
+ event.preventDefault()
+ var button = $(this);
+
+ var url = '/dashboard'
+ + '/' + encodeURIComponent(button.attr('zk-address'))
+ + '/' + encodeURIComponent(button.attr('cluster'))
+ + '/' + encodeURIComponent(button.attr('resource'))
+ + '?action=reset';
+
+ $.ajax({
+ type: 'POST',
+ url: url,
+ success: function(data, status, jqXHR) {
+ console.log(jqXHR);
+ alert('Successfully reset ' + button.attr('resource'));
+ location.reload();
+ },
+ error: function(jqXHR, status, ex) {
+ console.error(jqXHR);
+ alert(jqXHR.statusText + ": " + jqXHR.responseText);
+ }
+ });
+ });
+
$('#filter-add').click(function(event) {
event.preventDefault()
http://git-wip-us.apache.org/repos/asf/helix/blob/4d9db5f0/helix-ui/src/main/resources/org/apache/helix/ui/view/cluster-view.ftl
----------------------------------------------------------------------
diff --git a/helix-ui/src/main/resources/org/apache/helix/ui/view/cluster-view.ftl b/helix-ui/src/main/resources/org/apache/helix/ui/view/cluster-view.ftl
index 602df96..8a51596 100644
--- a/helix-ui/src/main/resources/org/apache/helix/ui/view/cluster-view.ftl
+++ b/helix-ui/src/main/resources/org/apache/helix/ui/view/cluster-view.ftl
@@ -36,6 +36,10 @@ under the License.
<#if (activeValid)>
<h1>${activeCluster}</h1>
+ <p>
+ <em>Controller leader is ${controllerLeader}</em>
+ </p>
+
<ul id="switcher-tabs" class="uk-subnav uk-subnav-pill" data-uk-switcher="{connect: '#switcher'}">
<li><a href="">Resources</a></li>
<li><a href="">Instances</a></li>
http://git-wip-us.apache.org/repos/asf/helix/blob/4d9db5f0/helix-ui/src/main/resources/org/apache/helix/ui/view/common/instance-table.ftl
----------------------------------------------------------------------
diff --git a/helix-ui/src/main/resources/org/apache/helix/ui/view/common/instance-table.ftl b/helix-ui/src/main/resources/org/apache/helix/ui/view/common/instance-table.ftl
index ac979a0..c2a4b18 100644
--- a/helix-ui/src/main/resources/org/apache/helix/ui/view/common/instance-table.ftl
+++ b/helix-ui/src/main/resources/org/apache/helix/ui/view/common/instance-table.ftl
@@ -29,6 +29,7 @@ under the License.
<th>Instance</th>
<th>Enabled</th>
<th>Live</th>
+ <th>Tags</th>
<#if (adminMode)>
<th></th>
</#if>
@@ -40,6 +41,7 @@ under the License.
<td>${instanceSpec.instanceName}</td>
<td>${instanceSpec.enabled?string("Yes", "No")}</td>
<td>${instanceSpec.live?string("Yes", "No")}</td>
+ <td>${instanceSpec.tags?join(", ")}</td>
<#if (adminMode)>
<td class="table-button">
<div class="uk-button-group">
http://git-wip-us.apache.org/repos/asf/helix/blob/4d9db5f0/helix-ui/src/main/resources/org/apache/helix/ui/view/common/resource-state-table.ftl
----------------------------------------------------------------------
diff --git a/helix-ui/src/main/resources/org/apache/helix/ui/view/common/resource-state-table.ftl b/helix-ui/src/main/resources/org/apache/helix/ui/view/common/resource-state-table.ftl
index fc5db04..7fbe506 100644
--- a/helix-ui/src/main/resources/org/apache/helix/ui/view/common/resource-state-table.ftl
+++ b/helix-ui/src/main/resources/org/apache/helix/ui/view/common/resource-state-table.ftl
@@ -41,7 +41,7 @@ under the License.
</thead>
<tbody>
<#list resourceStateTable as row>
- <tr class="${(row.ideal == row.external)?string("state-ok", "state-warn")} ${(row.external == "ERROR")?string("state-error", "")}">
+ <tr class='${(row.ideal == row.external || row.ideal == "N/A")?string("state-ok", "state-warn")} ${(row.external == "ERROR")?string("state-error", "")}'>
<td>${row.partitionName}</td>
<td>${row.instanceName}</td>
<td>${row.ideal}</td>
@@ -50,4 +50,15 @@ under the License.
</#list>
</tbody>
</table>
+
+ <form class="uk-form" id="safe-resource-actions">
+ <button class="uk-button uk-button-danger"
+ type="button"
+ id="reset-resource"
+ zk-address="${zkAddress}"
+ cluster="${activeCluster}"
+ resource="${activeResource}">
+ Reset
+ </button>
+ </form>
</#if>
http://git-wip-us.apache.org/repos/asf/helix/blob/4d9db5f0/helix-ui/src/main/resources/org/apache/helix/ui/view/landing-view.ftl
----------------------------------------------------------------------
diff --git a/helix-ui/src/main/resources/org/apache/helix/ui/view/landing-view.ftl b/helix-ui/src/main/resources/org/apache/helix/ui/view/landing-view.ftl
index 13f91aa..14309ca 100644
--- a/helix-ui/src/main/resources/org/apache/helix/ui/view/landing-view.ftl
+++ b/helix-ui/src/main/resources/org/apache/helix/ui/view/landing-view.ftl
@@ -28,8 +28,31 @@ under the License.
<div id="landing-area">
<img src="/assets/img/helix-logo.png">
<form id="landing-form" class="uk-form">
- <input id="zk-address" type="text" placeholder="ZooKeeper Address (e.g. localhost:2181)"/>
- <button id="landing-form-button" class="uk-button">Go</button>
+ <div class="uk-form-row">
+ <input id="zk-address"
+ type="text"
+ placeholder="ZooKeeper Address (e.g. localhost:2181)"
+ class="uk-form-width-large"/>
+ </div>
+ <#if zkAliases?has_content>
+ <div class="uk-form-row">
+ OR
+ </div>
+ <div class="uk-form-row">
+ <select id="zk-alias"
+ class="uk-form-width-large">
+ <#list zkAliases as alias>
+ <option value="${alias}">${alias}</option>
+ </#list>
+ </select>
+ </div>
+ </#if>
+ <div class="uk-form-row">
+ <button id="landing-form-button"
+ class="uk-button uk-form-width-large">
+ Go
+ </button>
+ </div>
</form>
</div>