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