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