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.rsgroup;
019
020import com.google.protobuf.RpcCallback;
021import com.google.protobuf.RpcController;
022import com.google.protobuf.Service;
023import java.io.IOException;
024import java.util.Collections;
025import java.util.HashSet;
026import java.util.List;
027import java.util.Map;
028import java.util.Optional;
029import java.util.Set;
030import java.util.stream.Collectors;
031import org.apache.hadoop.hbase.CoprocessorEnvironment;
032import org.apache.hadoop.hbase.HBaseIOException;
033import org.apache.hadoop.hbase.HConstants;
034import org.apache.hadoop.hbase.MasterNotRunningException;
035import org.apache.hadoop.hbase.NamespaceDescriptor;
036import org.apache.hadoop.hbase.PleaseHoldException;
037import org.apache.hadoop.hbase.ServerName;
038import org.apache.hadoop.hbase.TableName;
039import org.apache.hadoop.hbase.client.BalanceRequest;
040import org.apache.hadoop.hbase.client.BalanceResponse;
041import org.apache.hadoop.hbase.client.RegionInfo;
042import org.apache.hadoop.hbase.client.SnapshotDescription;
043import org.apache.hadoop.hbase.client.TableDescriptor;
044import org.apache.hadoop.hbase.constraint.ConstraintException;
045import org.apache.hadoop.hbase.coprocessor.CoreCoprocessor;
046import org.apache.hadoop.hbase.coprocessor.HasMasterServices;
047import org.apache.hadoop.hbase.coprocessor.MasterCoprocessor;
048import org.apache.hadoop.hbase.coprocessor.MasterCoprocessorEnvironment;
049import org.apache.hadoop.hbase.coprocessor.MasterObserver;
050import org.apache.hadoop.hbase.coprocessor.ObserverContext;
051import org.apache.hadoop.hbase.ipc.CoprocessorRpcUtils;
052import org.apache.hadoop.hbase.ipc.RpcServer;
053import org.apache.hadoop.hbase.master.MasterServices;
054import org.apache.hadoop.hbase.net.Address;
055import org.apache.hadoop.hbase.protobuf.ProtobufUtil;
056import org.apache.hadoop.hbase.protobuf.generated.HBaseProtos;
057import org.apache.hadoop.hbase.protobuf.generated.RSGroupAdminProtos;
058import org.apache.hadoop.hbase.protobuf.generated.RSGroupAdminProtos.AddRSGroupRequest;
059import org.apache.hadoop.hbase.protobuf.generated.RSGroupAdminProtos.AddRSGroupResponse;
060import org.apache.hadoop.hbase.protobuf.generated.RSGroupAdminProtos.BalanceRSGroupRequest;
061import org.apache.hadoop.hbase.protobuf.generated.RSGroupAdminProtos.BalanceRSGroupResponse;
062import org.apache.hadoop.hbase.protobuf.generated.RSGroupAdminProtos.GetRSGroupInfoOfServerRequest;
063import org.apache.hadoop.hbase.protobuf.generated.RSGroupAdminProtos.GetRSGroupInfoOfServerResponse;
064import org.apache.hadoop.hbase.protobuf.generated.RSGroupAdminProtos.GetRSGroupInfoOfTableRequest;
065import org.apache.hadoop.hbase.protobuf.generated.RSGroupAdminProtos.GetRSGroupInfoOfTableResponse;
066import org.apache.hadoop.hbase.protobuf.generated.RSGroupAdminProtos.GetRSGroupInfoRequest;
067import org.apache.hadoop.hbase.protobuf.generated.RSGroupAdminProtos.GetRSGroupInfoResponse;
068import org.apache.hadoop.hbase.protobuf.generated.RSGroupAdminProtos.ListRSGroupInfosRequest;
069import org.apache.hadoop.hbase.protobuf.generated.RSGroupAdminProtos.ListRSGroupInfosResponse;
070import org.apache.hadoop.hbase.protobuf.generated.RSGroupAdminProtos.MoveServersAndTablesRequest;
071import org.apache.hadoop.hbase.protobuf.generated.RSGroupAdminProtos.MoveServersAndTablesResponse;
072import org.apache.hadoop.hbase.protobuf.generated.RSGroupAdminProtos.MoveServersRequest;
073import org.apache.hadoop.hbase.protobuf.generated.RSGroupAdminProtos.MoveServersResponse;
074import org.apache.hadoop.hbase.protobuf.generated.RSGroupAdminProtos.MoveTablesRequest;
075import org.apache.hadoop.hbase.protobuf.generated.RSGroupAdminProtos.MoveTablesResponse;
076import org.apache.hadoop.hbase.protobuf.generated.RSGroupAdminProtos.RSGroupAdminService;
077import org.apache.hadoop.hbase.protobuf.generated.RSGroupAdminProtos.RemoveRSGroupRequest;
078import org.apache.hadoop.hbase.protobuf.generated.RSGroupAdminProtos.RemoveRSGroupResponse;
079import org.apache.hadoop.hbase.protobuf.generated.RSGroupAdminProtos.RemoveServersRequest;
080import org.apache.hadoop.hbase.protobuf.generated.RSGroupAdminProtos.RemoveServersResponse;
081import org.apache.hadoop.hbase.protobuf.generated.RSGroupAdminProtos.RenameRSGroupRequest;
082import org.apache.hadoop.hbase.protobuf.generated.RSGroupAdminProtos.RenameRSGroupResponse;
083import org.apache.hadoop.hbase.protobuf.generated.RSGroupAdminProtos.UpdateRSGroupConfigRequest;
084import org.apache.hadoop.hbase.protobuf.generated.RSGroupAdminProtos.UpdateRSGroupConfigResponse;
085import org.apache.hadoop.hbase.protobuf.generated.TableProtos;
086import org.apache.hadoop.hbase.security.User;
087import org.apache.hadoop.hbase.security.UserProvider;
088import org.apache.hadoop.hbase.security.access.AccessChecker;
089import org.apache.hadoop.hbase.security.access.Permission.Action;
090import org.apache.yetus.audience.InterfaceAudience;
091import org.slf4j.Logger;
092import org.slf4j.LoggerFactory;
093
094import org.apache.hbase.thirdparty.com.google.common.collect.Maps;
095import org.apache.hbase.thirdparty.com.google.common.collect.Sets;
096
097// TODO: Encapsulate MasterObserver functions into separate subclass.
098@CoreCoprocessor
099@InterfaceAudience.Private
100public class RSGroupAdminEndpoint implements MasterCoprocessor, MasterObserver {
101  private static final Logger LOG = LoggerFactory.getLogger(RSGroupAdminEndpoint.class);
102
103  private MasterServices master = null;
104  // Only instance of RSGroupInfoManager. RSGroup aware load balancers ask for this instance on
105  // their setup.
106  private RSGroupInfoManager groupInfoManager;
107  private RSGroupAdminServer groupAdminServer;
108  private final RSGroupAdminService groupAdminService = new RSGroupAdminServiceImpl();
109  private AccessChecker accessChecker;
110
111  /** Provider for mapping principal names to Users */
112  private UserProvider userProvider;
113
114  @Override
115  public void start(CoprocessorEnvironment env) throws IOException {
116    if (!(env instanceof HasMasterServices)) {
117      throw new IOException("Does not implement HMasterServices");
118    }
119
120    master = ((HasMasterServices) env).getMasterServices();
121    groupInfoManager = RSGroupInfoManagerImpl.getInstance(master);
122    groupAdminServer = new RSGroupAdminServer(master, groupInfoManager);
123    Class<?> clazz =
124      master.getConfiguration().getClass(HConstants.HBASE_MASTER_LOADBALANCER_CLASS, null);
125    if (!RSGroupableBalancer.class.isAssignableFrom(clazz)) {
126      throw new IOException("Configured balancer does not support RegionServer groups.");
127    }
128    accessChecker = ((HasMasterServices) env).getMasterServices().getAccessChecker();
129
130    // set the user-provider.
131    this.userProvider = UserProvider.instantiate(env.getConfiguration());
132  }
133
134  @Override
135  public void stop(CoprocessorEnvironment env) {
136  }
137
138  @Override
139  public Iterable<Service> getServices() {
140    return Collections.singleton(groupAdminService);
141  }
142
143  @Override
144  public Optional<MasterObserver> getMasterObserver() {
145    return Optional.of(this);
146  }
147
148  RSGroupInfoManager getGroupInfoManager() {
149    return groupInfoManager;
150  }
151
152  RSGroupAdminServer getGroupAdminServer() {
153    return groupAdminServer;
154  }
155
156  /**
157   * Implementation of RSGroupAdminService defined in RSGroupAdmin.proto. This class calls
158   * {@link RSGroupAdminServer} for actual work, converts result to protocol buffer response,
159   * handles exceptions if any occurred and then calls the {@code RpcCallback} with the response.
160   */
161  private class RSGroupAdminServiceImpl extends RSGroupAdminProtos.RSGroupAdminService {
162    @Override
163    public void getRSGroupInfo(RpcController controller, GetRSGroupInfoRequest request,
164      RpcCallback<GetRSGroupInfoResponse> done) {
165      GetRSGroupInfoResponse.Builder builder = GetRSGroupInfoResponse.newBuilder();
166      String groupName = request.getRSGroupName();
167      LOG.info(
168        master.getClientIdAuditPrefix() + " initiates rsgroup info retrieval, group=" + groupName);
169      try {
170        checkPermission("getRSGroupInfo");
171        RSGroupInfo rsGroupInfo = groupAdminServer.getRSGroupInfo(groupName);
172        if (rsGroupInfo != null) {
173          builder.setRSGroupInfo(RSGroupProtobufUtil.toProtoGroupInfo(rsGroupInfo));
174        }
175      } catch (IOException e) {
176        CoprocessorRpcUtils.setControllerException(controller, e);
177      }
178      done.run(builder.build());
179    }
180
181    @Override
182    public void getRSGroupInfoOfTable(RpcController controller,
183      GetRSGroupInfoOfTableRequest request, RpcCallback<GetRSGroupInfoOfTableResponse> done) {
184      GetRSGroupInfoOfTableResponse.Builder builder = GetRSGroupInfoOfTableResponse.newBuilder();
185      TableName tableName = ProtobufUtil.toTableName(request.getTableName());
186      LOG.info(
187        master.getClientIdAuditPrefix() + " initiates rsgroup info retrieval, table=" + tableName);
188      try {
189        checkPermission("getRSGroupInfoOfTable");
190        RSGroupInfo RSGroupInfo = groupAdminServer.getRSGroupInfoOfTable(tableName);
191        if (RSGroupInfo != null) {
192          builder.setRSGroupInfo(RSGroupProtobufUtil.toProtoGroupInfo(RSGroupInfo));
193        }
194      } catch (IOException e) {
195        CoprocessorRpcUtils.setControllerException(controller, e);
196      }
197      done.run(builder.build());
198    }
199
200    @Override
201    public void moveServers(RpcController controller, MoveServersRequest request,
202      RpcCallback<MoveServersResponse> done) {
203      MoveServersResponse.Builder builder = MoveServersResponse.newBuilder();
204      Set<Address> hostPorts = Sets.newHashSet();
205      for (HBaseProtos.ServerName el : request.getServersList()) {
206        hostPorts.add(Address.fromParts(el.getHostName(), el.getPort()));
207      }
208      LOG.info(master.getClientIdAuditPrefix() + " move servers " + hostPorts + " to rsgroup "
209        + request.getTargetGroup());
210      try {
211        if (master.getMasterCoprocessorHost() != null) {
212          master.getMasterCoprocessorHost().preMoveServers(hostPorts, request.getTargetGroup());
213        }
214        checkPermission("moveServers");
215        groupAdminServer.moveServers(hostPorts, request.getTargetGroup());
216        if (master.getMasterCoprocessorHost() != null) {
217          master.getMasterCoprocessorHost().postMoveServers(hostPorts, request.getTargetGroup());
218        }
219      } catch (IOException e) {
220        CoprocessorRpcUtils.setControllerException(controller, e);
221      }
222      done.run(builder.build());
223    }
224
225    @Override
226    public void moveTables(RpcController controller, MoveTablesRequest request,
227      RpcCallback<MoveTablesResponse> done) {
228      MoveTablesResponse.Builder builder = MoveTablesResponse.newBuilder();
229      Set<TableName> tables = new HashSet<>(request.getTableNameList().size());
230      for (TableProtos.TableName tableName : request.getTableNameList()) {
231        tables.add(ProtobufUtil.toTableName(tableName));
232      }
233      LOG.info(master.getClientIdAuditPrefix() + " move tables " + tables + " to rsgroup "
234        + request.getTargetGroup());
235      try {
236        if (master.getMasterCoprocessorHost() != null) {
237          master.getMasterCoprocessorHost().preMoveTables(tables, request.getTargetGroup());
238        }
239        checkPermission("moveTables");
240        groupAdminServer.moveTables(tables, request.getTargetGroup());
241        if (master.getMasterCoprocessorHost() != null) {
242          master.getMasterCoprocessorHost().postMoveTables(tables, request.getTargetGroup());
243        }
244      } catch (IOException e) {
245        CoprocessorRpcUtils.setControllerException(controller, e);
246      }
247      done.run(builder.build());
248    }
249
250    @Override
251    public void addRSGroup(RpcController controller, AddRSGroupRequest request,
252      RpcCallback<AddRSGroupResponse> done) {
253      AddRSGroupResponse.Builder builder = AddRSGroupResponse.newBuilder();
254      LOG.info(master.getClientIdAuditPrefix() + " add rsgroup " + request.getRSGroupName());
255      try {
256        if (master.getMasterCoprocessorHost() != null) {
257          master.getMasterCoprocessorHost().preAddRSGroup(request.getRSGroupName());
258        }
259        checkPermission("addRSGroup");
260        groupAdminServer.addRSGroup(request.getRSGroupName());
261        if (master.getMasterCoprocessorHost() != null) {
262          master.getMasterCoprocessorHost().postAddRSGroup(request.getRSGroupName());
263        }
264      } catch (IOException e) {
265        CoprocessorRpcUtils.setControllerException(controller, e);
266      }
267      done.run(builder.build());
268    }
269
270    @Override
271    public void removeRSGroup(RpcController controller, RemoveRSGroupRequest request,
272      RpcCallback<RemoveRSGroupResponse> done) {
273      RemoveRSGroupResponse.Builder builder = RemoveRSGroupResponse.newBuilder();
274      LOG.info(master.getClientIdAuditPrefix() + " remove rsgroup " + request.getRSGroupName());
275      try {
276        if (master.getMasterCoprocessorHost() != null) {
277          master.getMasterCoprocessorHost().preRemoveRSGroup(request.getRSGroupName());
278        }
279        checkPermission("removeRSGroup");
280        groupAdminServer.removeRSGroup(request.getRSGroupName());
281        if (master.getMasterCoprocessorHost() != null) {
282          master.getMasterCoprocessorHost().postRemoveRSGroup(request.getRSGroupName());
283        }
284      } catch (IOException e) {
285        CoprocessorRpcUtils.setControllerException(controller, e);
286      }
287      done.run(builder.build());
288    }
289
290    @Override
291    public void balanceRSGroup(RpcController controller, BalanceRSGroupRequest request,
292      RpcCallback<BalanceRSGroupResponse> done) {
293      BalanceRequest balanceRequest = RSGroupProtobufUtil.toBalanceRequest(request);
294
295      BalanceRSGroupResponse.Builder builder =
296        BalanceRSGroupResponse.newBuilder().setBalanceRan(false);
297
298      LOG.info(
299        master.getClientIdAuditPrefix() + " balance rsgroup, group=" + request.getRSGroupName());
300      try {
301        if (master.getMasterCoprocessorHost() != null) {
302          master.getMasterCoprocessorHost().preBalanceRSGroup(request.getRSGroupName(),
303            balanceRequest);
304        }
305
306        checkPermission("balanceRSGroup");
307        BalanceResponse response =
308          groupAdminServer.balanceRSGroup(request.getRSGroupName(), balanceRequest);
309        RSGroupProtobufUtil.populateBalanceRSGroupResponse(builder, response);
310
311        if (master.getMasterCoprocessorHost() != null) {
312          master.getMasterCoprocessorHost().postBalanceRSGroup(request.getRSGroupName(),
313            balanceRequest, response);
314        }
315      } catch (IOException e) {
316        CoprocessorRpcUtils.setControllerException(controller, e);
317      }
318
319      done.run(builder.build());
320    }
321
322    @Override
323    public void listRSGroupInfos(RpcController controller, ListRSGroupInfosRequest request,
324      RpcCallback<ListRSGroupInfosResponse> done) {
325      ListRSGroupInfosResponse.Builder builder = ListRSGroupInfosResponse.newBuilder();
326      LOG.info(master.getClientIdAuditPrefix() + " list rsgroup");
327      try {
328        checkPermission("listRSGroup");
329        for (RSGroupInfo RSGroupInfo : groupAdminServer.listRSGroups()) {
330          builder.addRSGroupInfo(RSGroupProtobufUtil.toProtoGroupInfo(RSGroupInfo));
331        }
332      } catch (IOException e) {
333        CoprocessorRpcUtils.setControllerException(controller, e);
334      }
335      done.run(builder.build());
336    }
337
338    @Override
339    public void getRSGroupInfoOfServer(RpcController controller,
340      GetRSGroupInfoOfServerRequest request, RpcCallback<GetRSGroupInfoOfServerResponse> done) {
341      GetRSGroupInfoOfServerResponse.Builder builder = GetRSGroupInfoOfServerResponse.newBuilder();
342      Address hp =
343        Address.fromParts(request.getServer().getHostName(), request.getServer().getPort());
344      LOG.info(master.getClientIdAuditPrefix() + " initiates rsgroup info retrieval, server=" + hp);
345      try {
346        checkPermission("getRSGroupInfoOfServer");
347        RSGroupInfo info = groupAdminServer.getRSGroupOfServer(hp);
348        if (info != null) {
349          builder.setRSGroupInfo(RSGroupProtobufUtil.toProtoGroupInfo(info));
350        }
351      } catch (IOException e) {
352        CoprocessorRpcUtils.setControllerException(controller, e);
353      }
354      done.run(builder.build());
355    }
356
357    @Override
358    public void moveServersAndTables(RpcController controller, MoveServersAndTablesRequest request,
359      RpcCallback<MoveServersAndTablesResponse> done) {
360      MoveServersAndTablesResponse.Builder builder = MoveServersAndTablesResponse.newBuilder();
361      Set<Address> hostPorts = Sets.newHashSet();
362      for (HBaseProtos.ServerName el : request.getServersList()) {
363        hostPorts.add(Address.fromParts(el.getHostName(), el.getPort()));
364      }
365      Set<TableName> tables = new HashSet<>(request.getTableNameList().size());
366      for (TableProtos.TableName tableName : request.getTableNameList()) {
367        tables.add(ProtobufUtil.toTableName(tableName));
368      }
369      LOG.info(master.getClientIdAuditPrefix() + " move servers " + hostPorts + " and tables "
370        + tables + " to rsgroup" + request.getTargetGroup());
371      try {
372        if (master.getMasterCoprocessorHost() != null) {
373          master.getMasterCoprocessorHost().preMoveServersAndTables(hostPorts, tables,
374            request.getTargetGroup());
375        }
376        checkPermission("moveServersAndTables");
377        groupAdminServer.moveServersAndTables(hostPorts, tables, request.getTargetGroup());
378        if (master.getMasterCoprocessorHost() != null) {
379          master.getMasterCoprocessorHost().postMoveServersAndTables(hostPorts, tables,
380            request.getTargetGroup());
381        }
382      } catch (IOException e) {
383        CoprocessorRpcUtils.setControllerException(controller, e);
384      }
385      done.run(builder.build());
386    }
387
388    @Override
389    public void removeServers(RpcController controller, RemoveServersRequest request,
390      RpcCallback<RemoveServersResponse> done) {
391      RemoveServersResponse.Builder builder = RemoveServersResponse.newBuilder();
392      Set<Address> servers = Sets.newHashSet();
393      for (HBaseProtos.ServerName el : request.getServersList()) {
394        servers.add(Address.fromParts(el.getHostName(), el.getPort()));
395      }
396      LOG.info(master.getClientIdAuditPrefix() + " remove decommissioned servers from rsgroup: "
397        + servers);
398      try {
399        if (master.getMasterCoprocessorHost() != null) {
400          master.getMasterCoprocessorHost().preRemoveServers(servers);
401        }
402        checkPermission("removeServers");
403        groupAdminServer.removeServers(servers);
404        if (master.getMasterCoprocessorHost() != null) {
405          master.getMasterCoprocessorHost().postRemoveServers(servers);
406        }
407      } catch (IOException e) {
408        CoprocessorRpcUtils.setControllerException(controller, e);
409      }
410      done.run(builder.build());
411    }
412
413    @Override
414    public void renameRSGroup(RpcController controller, RenameRSGroupRequest request,
415      RpcCallback<RenameRSGroupResponse> done) {
416      String oldRSGroup = request.getOldRsgroupName();
417      String newRSGroup = request.getNewRsgroupName();
418      LOG.info("{} rename rsgroup from {} to {}", master.getClientIdAuditPrefix(), oldRSGroup,
419        newRSGroup);
420
421      RenameRSGroupResponse.Builder builder = RenameRSGroupResponse.newBuilder();
422      try {
423        if (master.getMasterCoprocessorHost() != null) {
424          master.getMasterCoprocessorHost().preRenameRSGroup(oldRSGroup, newRSGroup);
425        }
426        checkPermission("renameRSGroup");
427        groupAdminServer.renameRSGroup(oldRSGroup, newRSGroup);
428        if (master.getMasterCoprocessorHost() != null) {
429          master.getMasterCoprocessorHost().postRenameRSGroup(oldRSGroup, newRSGroup);
430        }
431      } catch (IOException e) {
432        CoprocessorRpcUtils.setControllerException(controller, e);
433      }
434      done.run(builder.build());
435    }
436
437    @Override
438    public void updateRSGroupConfig(RpcController controller, UpdateRSGroupConfigRequest request,
439      RpcCallback<UpdateRSGroupConfigResponse> done) {
440      UpdateRSGroupConfigResponse.Builder builder = UpdateRSGroupConfigResponse.newBuilder();
441      String groupName = request.getGroupName();
442      Map<String, String> configuration = Maps.newHashMap();
443      request.getConfigurationList().forEach(p -> configuration.put(p.getName(), p.getValue()));
444      LOG.info("{} update rsgroup {} configuration {}", master.getClientIdAuditPrefix(), groupName,
445        configuration);
446      try {
447        if (master.getMasterCoprocessorHost() != null) {
448          master.getMasterCoprocessorHost().preUpdateRSGroupConfig(groupName, configuration);
449        }
450        groupAdminServer.updateRSGroupConfig(groupName, configuration);
451        if (master.getMasterCoprocessorHost() != null) {
452          master.getMasterCoprocessorHost().postUpdateRSGroupConfig(groupName, configuration);
453        }
454      } catch (IOException e) {
455        CoprocessorRpcUtils.setControllerException(controller, e);
456      }
457      done.run(builder.build());
458    }
459  }
460
461  boolean rsgroupHasServersOnline(TableDescriptor desc) throws IOException {
462    String groupName;
463    try {
464      groupName = master.getClusterSchema().getNamespace(desc.getTableName().getNamespaceAsString())
465        .getConfigurationValue(RSGroupInfo.NAMESPACE_DESC_PROP_GROUP);
466      if (groupName == null) {
467        groupName = RSGroupInfo.DEFAULT_GROUP;
468      }
469    } catch (MasterNotRunningException | PleaseHoldException e) {
470      LOG.info("Master has not initialized yet; temporarily using default RSGroup '"
471        + RSGroupInfo.DEFAULT_GROUP + "' for deploy of system table");
472      groupName = RSGroupInfo.DEFAULT_GROUP;
473    }
474
475    RSGroupInfo rsGroupInfo = groupAdminServer.getRSGroupInfo(groupName);
476    if (rsGroupInfo == null) {
477      throw new ConstraintException(
478        "Default RSGroup (" + groupName + ") for this table's " + "namespace does not exist.");
479    }
480
481    for (ServerName onlineServer : master.getServerManager().createDestinationServersList()) {
482      if (rsGroupInfo.getServers().contains(onlineServer.getAddress())) {
483        return true;
484      }
485    }
486    return false;
487  }
488
489  void assignTableToGroup(TableDescriptor desc) throws IOException {
490    RSGroupInfo rsGroupInfo = determineRSGroupInfoForTable(desc);
491    if (rsGroupInfo == null) {
492      throw new ConstraintException(
493        "Default RSGroup for this table " + desc.getTableName() + " does not exist.");
494    }
495    if (!rsGroupInfo.containsTable(desc.getTableName())) {
496      LOG.debug("Pre-moving table " + desc.getTableName() + " to RSGroup " + rsGroupInfo.getName());
497      groupAdminServer.moveTables(Sets.newHashSet(desc.getTableName()), rsGroupInfo.getName());
498    }
499  }
500
501  /////////////////////////////////////////////////////////////////////////////
502  // MasterObserver overrides
503  /////////////////////////////////////////////////////////////////////////////
504
505  @Override
506  public void preCreateTableAction(final ObserverContext<MasterCoprocessorEnvironment> ctx,
507    final TableDescriptor desc, final RegionInfo[] regions) throws IOException {
508    if (desc.getTableName().isSystemTable()) {
509      return;
510    }
511    moveTableToValidRSGroup(desc);
512  }
513
514  @Override
515  public void preModifyTableAction(ObserverContext<MasterCoprocessorEnvironment> ctx,
516    TableName tableName, TableDescriptor currentDescriptor, TableDescriptor newDescriptor)
517    throws IOException {
518    // If table's rsgroup is changed, it must be valid
519    if (!currentDescriptor.getRegionServerGroup().equals(newDescriptor.getRegionServerGroup())) {
520      RSGroupInfo rsGroupInfo = determineRSGroupInfoForTable(newDescriptor);
521      validateRSGroup(newDescriptor, rsGroupInfo);
522    }
523  }
524
525  @Override
526  public void postCompletedModifyTableAction(ObserverContext<MasterCoprocessorEnvironment> ctx,
527    TableName tableName, TableDescriptor oldDescriptor, TableDescriptor currentDescriptor)
528    throws IOException {
529    // If table's rsgroup is changed, move table into the rsgroup.
530    if (!oldDescriptor.getRegionServerGroup().equals(currentDescriptor.getRegionServerGroup())) {
531      RSGroupInfo rsGroupInfo = determineRSGroupInfoForTable(currentDescriptor);
532      moveTableToRSGroup(currentDescriptor, rsGroupInfo);
533    }
534  }
535
536  // Determine and validate rs group then move table to this valid rs group.
537  private void moveTableToValidRSGroup(TableDescriptor desc) throws IOException {
538    RSGroupInfo rsGroupInfo = determineRSGroupInfoForTable(desc);
539    validateRSGroup(desc, rsGroupInfo);
540    moveTableToRSGroup(desc, rsGroupInfo);
541  }
542
543  private void validateRSGroup(TableDescriptor desc, RSGroupInfo rsGroupInfo) throws IOException {
544    if (rsGroupInfo == null) {
545      throw new ConstraintException(
546        "Default RSGroup for this table " + desc.getTableName() + " does not exist.");
547    }
548    if (!RSGroupUtil.rsGroupHasOnlineServer(master, rsGroupInfo)) {
549      throw new HBaseIOException("No online servers in the rsgroup " + rsGroupInfo.getName()
550        + " which table " + desc.getTableName().getNameAsString() + " belongs to");
551    }
552  }
553
554  private void moveTableToRSGroup(final TableDescriptor desc, RSGroupInfo rsGroupInfo)
555    throws IOException {
556    // In case of modify table, when rs group is not changed, move is not required.
557    if (!rsGroupInfo.containsTable(desc.getTableName())) {
558      synchronized (groupInfoManager) {
559        groupInfoManager.moveTables(Collections.singleton(desc.getTableName()),
560          rsGroupInfo.getName());
561      }
562    }
563  }
564
565  private RSGroupInfo determineRSGroupInfoForTable(final TableDescriptor desc) throws IOException {
566    Optional<String> optGroupNameOfTable = desc.getRegionServerGroup();
567    if (optGroupNameOfTable.isPresent()) {
568      final RSGroupInfo rsGroup = groupInfoManager.getRSGroup(optGroupNameOfTable.get());
569      if (rsGroup == null) {
570        // When rs group is set in table descriptor then it must exist
571        throw new ConstraintException(
572          "Region server group " + optGroupNameOfTable.get() + " does not exist.");
573      } else {
574        return rsGroup;
575      }
576    }
577    return groupInfoManager.determineRSGroupInfoForTable(desc.getTableName());
578  }
579
580  // Remove table from its RSGroup.
581  @Override
582  public void postDeleteTable(ObserverContext<MasterCoprocessorEnvironment> ctx,
583    TableName tableName) throws IOException {
584    try {
585      RSGroupInfo group = groupAdminServer.getRSGroupInfoOfTable(tableName);
586      if (group != null) {
587        LOG.debug(String.format("Removing deleted table '%s' from rsgroup '%s'", tableName,
588          group.getName()));
589        groupAdminServer.moveTables(Sets.newHashSet(tableName), null);
590      }
591    } catch (IOException ex) {
592      LOG.debug("Failed to perform RSGroup information cleanup for table: " + tableName, ex);
593    }
594  }
595
596  @Override
597  public void preCreateNamespace(ObserverContext<MasterCoprocessorEnvironment> ctx,
598    NamespaceDescriptor ns) throws IOException {
599    String group = ns.getConfigurationValue(RSGroupInfo.NAMESPACE_DESC_PROP_GROUP);
600    if (group != null && groupAdminServer.getRSGroupInfo(group) == null) {
601      throw new ConstraintException("Region server group " + group + " does not exist.");
602    }
603  }
604
605  @Override
606  public void preModifyNamespace(ObserverContext<MasterCoprocessorEnvironment> ctx,
607    NamespaceDescriptor ns) throws IOException {
608    preCreateNamespace(ctx, ns);
609  }
610
611  @Override
612  public void preCloneSnapshot(ObserverContext<MasterCoprocessorEnvironment> ctx,
613    SnapshotDescription snapshot, TableDescriptor desc) throws IOException {
614    assignTableToGroup(desc);
615  }
616
617  @Override
618  public void postClearDeadServers(ObserverContext<MasterCoprocessorEnvironment> ctx,
619    List<ServerName> servers, List<ServerName> notClearedServers) throws IOException {
620    Set<Address> clearedServer =
621      servers.stream().filter(server -> !notClearedServers.contains(server))
622        .map(ServerName::getAddress).collect(Collectors.toSet());
623    if (!clearedServer.isEmpty()) {
624      groupAdminServer.removeServers(clearedServer);
625    }
626  }
627
628  public void checkPermission(String request) throws IOException {
629    accessChecker.requirePermission(getActiveUser(), request, null, Action.ADMIN);
630  }
631
632  /**
633   * Returns the active user to which authorization checks should be applied. If we are in the
634   * context of an RPC call, the remote user is used, otherwise the currently logged in user is
635   * used.
636   */
637  private User getActiveUser() throws IOException {
638    // for non-rpc handling, fallback to system user
639    Optional<User> optionalUser = RpcServer.getRequestUser();
640    if (optionalUser.isPresent()) {
641      return optionalUser.get();
642    }
643    return userProvider.getCurrent();
644  }
645}