001/**
002 * Licensed to the Apache Software Foundation (ASF) under one
003 * or more contributor license agreements.  See the NOTICE file
004 * distributed with this work for additional information
005 * regarding copyright ownership.  The ASF licenses this file
006 * to you under the Apache License, Version 2.0 (the
007 * "License"); you may not use this file except in compliance
008 * with the License.  You may obtain a copy of the License at
009 *
010 *     http://www.apache.org/licenses/LICENSE-2.0
011 *
012 * Unless required by applicable law or agreed to in writing, software
013 * distributed under the License is distributed on an "AS IS" BASIS,
014 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
015 * See the License for the specific language governing permissions and
016 * limitations under the License.
017 */
018package org.apache.hadoop.hbase.hbtop.screen.top;
019
020import java.util.ArrayList;
021import java.util.Collections;
022import java.util.List;
023import java.util.Objects;
024import java.util.stream.Collectors;
025import org.apache.commons.lang3.time.DateFormatUtils;
026import org.apache.hadoop.hbase.ClusterMetrics;
027import org.apache.hadoop.hbase.client.Admin;
028import org.apache.hadoop.hbase.hbtop.Record;
029import org.apache.hadoop.hbase.hbtop.RecordFilter;
030import org.apache.hadoop.hbase.hbtop.field.Field;
031import org.apache.hadoop.hbase.hbtop.field.FieldInfo;
032import org.apache.hadoop.hbase.hbtop.field.FieldValue;
033import org.apache.hadoop.hbase.hbtop.mode.DrillDownInfo;
034import org.apache.hadoop.hbase.hbtop.mode.Mode;
035import org.apache.yetus.audience.InterfaceAudience;
036import org.slf4j.Logger;
037import org.slf4j.LoggerFactory;
038
039
040/**
041 * The data and business logic for the top screen.
042 */
043@InterfaceAudience.Private
044public class TopScreenModel {
045
046  private static final Logger LOGGER = LoggerFactory.getLogger(TopScreenModel.class);
047
048  private final Admin admin;
049
050  private Mode currentMode;
051  private Field currentSortField;
052  private List<FieldInfo> fieldInfos;
053  private List<Field> fields;
054
055  private Summary summary;
056  private List<Record> records;
057
058  private final List<RecordFilter> filters = new ArrayList<>();
059  private final List<String> filterHistories = new ArrayList<>();
060
061  private boolean ascendingSort;
062
063  public TopScreenModel(Admin admin, Mode initialMode) {
064    this.admin = Objects.requireNonNull(admin);
065    switchMode(Objects.requireNonNull(initialMode), null, false);
066  }
067
068  public void switchMode(Mode nextMode, List<RecordFilter> initialFilters,
069    boolean keepSortFieldAndSortOrderIfPossible) {
070
071    currentMode = nextMode;
072    fieldInfos = Collections.unmodifiableList(new ArrayList<>(currentMode.getFieldInfos()));
073    fields = Collections.unmodifiableList(currentMode.getFieldInfos().stream()
074      .map(FieldInfo::getField).collect(Collectors.toList()));
075
076    if (keepSortFieldAndSortOrderIfPossible) {
077      boolean match = fields.stream().anyMatch(f -> f == currentSortField);
078      if (!match) {
079        currentSortField = nextMode.getDefaultSortField();
080        ascendingSort = false;
081      }
082    } else {
083      currentSortField = nextMode.getDefaultSortField();
084      ascendingSort = false;
085    }
086
087    clearFilters();
088    if (initialFilters != null) {
089      filters.addAll(initialFilters);
090    }
091  }
092
093  public void setSortFieldAndFields(Field sortField, List<Field> fields) {
094    this.currentSortField = sortField;
095    this.fields = Collections.unmodifiableList(new ArrayList<>(fields));
096  }
097
098  /*
099   * HBTop only calls this from a single thread, and if that ever changes, this needs
100   * synchronization
101   */
102  public void refreshMetricsData() {
103    ClusterMetrics clusterMetrics;
104    try {
105      clusterMetrics = admin.getClusterMetrics();
106    } catch (Exception e) {
107      LOGGER.error("Unable to get cluster metrics", e);
108      return;
109    }
110
111    refreshSummary(clusterMetrics);
112    refreshRecords(clusterMetrics);
113  }
114
115  private void refreshSummary(ClusterMetrics clusterMetrics) {
116    String currentTime = DateFormatUtils.ISO_8601_EXTENDED_TIME_FORMAT
117      .format(System.currentTimeMillis());
118    String version = clusterMetrics.getHBaseVersion();
119    String clusterId = clusterMetrics.getClusterId();
120    int liveServers = clusterMetrics.getLiveServerMetrics().size();
121    int deadServers = clusterMetrics.getDeadServerNames().size();
122    int regionCount = clusterMetrics.getRegionCount();
123    int ritCount = clusterMetrics.getRegionStatesInTransition().size();
124    double averageLoad = clusterMetrics.getAverageLoad();
125    long aggregateRequestPerSecond = clusterMetrics.getLiveServerMetrics().entrySet().stream()
126      .mapToLong(e -> e.getValue().getRequestCountPerSecond()).sum();
127
128    summary = new Summary(currentTime, version, clusterId, liveServers + deadServers,
129      liveServers, deadServers, regionCount, ritCount, averageLoad, aggregateRequestPerSecond);
130  }
131
132  private void refreshRecords(ClusterMetrics clusterMetrics) {
133    List<Record> records = currentMode.getRecords(clusterMetrics);
134
135    // Filter and sort
136    records = records.stream()
137      .filter(r -> filters.stream().allMatch(f -> f.execute(r)))
138      .sorted((recordLeft, recordRight) -> {
139        FieldValue left = recordLeft.get(currentSortField);
140        FieldValue right = recordRight.get(currentSortField);
141        return (ascendingSort ? 1 : -1) * left.compareTo(right);
142      }).collect(Collectors.toList());
143
144    this.records = Collections.unmodifiableList(records);
145  }
146
147  public void switchSortOrder() {
148    ascendingSort = !ascendingSort;
149  }
150
151  public boolean addFilter(String filterString, boolean ignoreCase) {
152    RecordFilter filter = RecordFilter.parse(filterString, fields, ignoreCase);
153    if (filter == null) {
154      return false;
155    }
156
157    filters.add(filter);
158    filterHistories.add(filterString);
159    return true;
160  }
161
162  public void clearFilters() {
163    filters.clear();
164  }
165
166  public boolean drillDown(Record selectedRecord) {
167    DrillDownInfo drillDownInfo = currentMode.drillDown(selectedRecord);
168    if (drillDownInfo == null) {
169      return false;
170    }
171    switchMode(drillDownInfo.getNextMode(), drillDownInfo.getInitialFilters(), true);
172    return true;
173  }
174
175  public Mode getCurrentMode() {
176    return currentMode;
177  }
178
179  public Field getCurrentSortField() {
180    return currentSortField;
181  }
182
183  public List<FieldInfo> getFieldInfos() {
184    return fieldInfos;
185  }
186
187  public List<Field> getFields() {
188    return fields;
189  }
190
191  public Summary getSummary() {
192    return summary;
193  }
194
195  public List<Record> getRecords() {
196    return records;
197  }
198
199  public List<RecordFilter> getFilters() {
200    return Collections.unmodifiableList(filters);
201  }
202
203  public List<String> getFilterHistories() {
204    return Collections.unmodifiableList(filterHistories);
205  }
206}