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.master.procedure; 019 020import java.io.IOException; 021import java.util.Optional; 022import java.util.function.Supplier; 023import java.util.regex.Pattern; 024import org.apache.hadoop.hbase.DoNotRetryIOException; 025import org.apache.hadoop.hbase.NamespaceDescriptor; 026import org.apache.hadoop.hbase.TableName; 027import org.apache.hadoop.hbase.constraint.ConstraintException; 028import org.apache.hadoop.hbase.master.MasterServices; 029import org.apache.hadoop.hbase.procedure2.Procedure; 030import org.apache.hadoop.hbase.procedure2.ProcedureException; 031import org.apache.hadoop.hbase.procedure2.ProcedureExecutor; 032import org.apache.hadoop.hbase.rsgroup.RSGroupInfo; 033import org.apache.hadoop.hbase.security.User; 034import org.apache.hadoop.hbase.util.NonceKey; 035import org.apache.hadoop.security.UserGroupInformation; 036import org.apache.yetus.audience.InterfaceAudience; 037import org.apache.yetus.audience.InterfaceStability; 038 039import org.apache.hadoop.hbase.shaded.protobuf.generated.RPCProtos.UserInformation; 040 041@InterfaceAudience.Private 042@InterfaceStability.Evolving 043public final class MasterProcedureUtil { 044 045 private MasterProcedureUtil() { 046 } 047 048 public static UserInformation toProtoUserInfo(User user) { 049 UserInformation.Builder userInfoPB = UserInformation.newBuilder(); 050 userInfoPB.setEffectiveUser(user.getName()); 051 if (user.getUGI().getRealUser() != null) { 052 userInfoPB.setRealUser(user.getUGI().getRealUser().getUserName()); 053 } 054 return userInfoPB.build(); 055 } 056 057 public static User toUserInfo(UserInformation userInfoProto) { 058 if (userInfoProto.hasEffectiveUser()) { 059 String effectiveUser = userInfoProto.getEffectiveUser(); 060 if (userInfoProto.hasRealUser()) { 061 String realUser = userInfoProto.getRealUser(); 062 UserGroupInformation realUserUgi = UserGroupInformation.createRemoteUser(realUser); 063 return User.create(UserGroupInformation.createProxyUser(effectiveUser, realUserUgi)); 064 } 065 return User.create(UserGroupInformation.createRemoteUser(effectiveUser)); 066 } 067 return null; 068 } 069 070 /** 071 * Helper Runnable used in conjunction with submitProcedure() to deal with submitting procs with 072 * nonce. See submitProcedure() for an example. 073 */ 074 public static abstract class NonceProcedureRunnable { 075 private final MasterServices master; 076 private final NonceKey nonceKey; 077 private Long procId; 078 079 public NonceProcedureRunnable(final MasterServices master, final long nonceGroup, 080 final long nonce) { 081 this.master = master; 082 this.nonceKey = getProcedureExecutor().createNonceKey(nonceGroup, nonce); 083 } 084 085 protected NonceKey getNonceKey() { 086 return nonceKey; 087 } 088 089 protected MasterServices getMaster() { 090 return master; 091 } 092 093 protected ProcedureExecutor<MasterProcedureEnv> getProcedureExecutor() { 094 return master.getMasterProcedureExecutor(); 095 } 096 097 protected long getProcId() { 098 return procId != null ? procId.longValue() : -1; 099 } 100 101 protected long setProcId(final long procId) { 102 this.procId = procId; 103 return procId; 104 } 105 106 protected abstract void run() throws IOException; 107 108 protected abstract String getDescription(); 109 110 protected long submitProcedure(final Procedure<MasterProcedureEnv> proc) { 111 assert procId == null : "submitProcedure() was already called, running procId=" + procId; 112 procId = getProcedureExecutor().submitProcedure(proc, nonceKey); 113 return procId; 114 } 115 } 116 117 /** 118 * Helper used to deal with submitting procs with nonce. Internally the 119 * NonceProcedureRunnable.run() will be called only if no one else registered the nonce. any 120 * Exception thrown by the run() method will be collected/handled and rethrown. <code> 121 * long procId = MasterProcedureUtil.submitProcedure( 122 * new NonceProcedureRunnable(procExec, nonceGroup, nonce) { 123 * {@literal @}Override 124 * public void run() { 125 * cpHost.preOperation(); 126 * submitProcedure(new MyProc()); 127 * cpHost.postOperation(); 128 * } 129 * }); 130 * </code> 131 */ 132 public static long submitProcedure(final NonceProcedureRunnable runnable) throws IOException { 133 final ProcedureExecutor<MasterProcedureEnv> procExec = runnable.getProcedureExecutor(); 134 final long procId = procExec.registerNonce(runnable.getNonceKey()); 135 if (procId >= 0) return procId; // someone already registered the nonce 136 try { 137 runnable.run(); 138 } catch (IOException e) { 139 procExec.setFailureResultForNonce(runnable.getNonceKey(), runnable.getDescription(), 140 procExec.getEnvironment().getRequestUser(), e); 141 throw e; 142 } finally { 143 procExec.unregisterNonceIfProcedureWasNotSubmitted(runnable.getNonceKey()); 144 } 145 return runnable.getProcId(); 146 } 147 148 /** 149 * Pattern used to validate a Procedure WAL file name see 150 * {@link #validateProcedureWALFilename(String)} for description. 151 * @deprecated Since 2.3.0, will be removed in 4.0.0. We do not use this style of procedure wal 152 * file name any more. 153 */ 154 @Deprecated 155 private static final Pattern PATTERN = Pattern.compile(".*pv2-\\d{20}.log"); 156 157 /** 158 * A Procedure WAL file name is of the format: pv-<wal-id>.log where wal-id is 20 digits. 159 * @param filename name of the file to validate 160 * @return <tt>true</tt> if the filename matches a Procedure WAL, <tt>false</tt> otherwise 161 */ 162 public static boolean validateProcedureWALFilename(String filename) { 163 return PATTERN.matcher(filename).matches(); 164 } 165 166 /** 167 * Return the priority for the given table. Now meta table is 3, other system tables are 2, and 168 * user tables are 1. 169 */ 170 public static int getTablePriority(TableName tableName) { 171 if (TableName.isMetaTableName(tableName)) { 172 return 3; 173 } else if (tableName.isSystemTable()) { 174 return 2; 175 } else { 176 return 1; 177 } 178 } 179 180 /** 181 * Return the priority for the given procedure. For now we only have two priorities, 100 for 182 * server carrying meta, and 1 for others. 183 */ 184 public static int getServerPriority(ServerProcedureInterface proc) { 185 return proc.hasMetaTableRegion() ? 100 : 1; 186 } 187 188 /** 189 * This is a version of unwrapRemoteIOException that can do DoNotRetryIOE. We need to throw DNRIOE 190 * to clients if a failed Procedure else they will keep trying. The default 191 * proc.getException().unwrapRemoteException doesn't have access to DNRIOE from the procedure2 192 * module. 193 */ 194 public static IOException unwrapRemoteIOException(Procedure<?> proc) { 195 Exception e = proc.getException().unwrapRemoteException(); 196 // Do not retry ProcedureExceptions! 197 return (e instanceof ProcedureException) 198 ? new DoNotRetryIOException(e) 199 : proc.getException().unwrapRemoteIOException(); 200 } 201 202 /** 203 * Do not allow creating new tables/namespaces which has an empty rs group, expect the default rs 204 * group. Notice that we do not check for online servers, as this is not stable because region 205 * servers can die at any time. 206 */ 207 public static void checkGroupNotEmpty(RSGroupInfo rsGroupInfo, Supplier<String> forWhom) 208 throws ConstraintException { 209 if (rsGroupInfo == null || rsGroupInfo.getName().equals(RSGroupInfo.DEFAULT_GROUP)) { 210 // we do not have a rs group config or we explicitly set the rs group to default, then no need 211 // to check. 212 return; 213 } 214 if (rsGroupInfo.getServers().isEmpty()) { 215 throw new ConstraintException( 216 "No servers in the rsgroup " + rsGroupInfo.getName() + " for " + forWhom.get()); 217 } 218 } 219 220 @FunctionalInterface 221 public interface RSGroupGetter { 222 RSGroupInfo get(String groupName) throws IOException; 223 } 224 225 public static RSGroupInfo checkGroupExists(RSGroupGetter getter, Optional<String> optGroupName, 226 Supplier<String> forWhom) throws IOException { 227 if (optGroupName.isPresent()) { 228 String groupName = optGroupName.get(); 229 RSGroupInfo group = getter.get(groupName); 230 if (group == null) { 231 throw new ConstraintException( 232 "Region server group " + groupName + " for " + forWhom.get() + " does not exit"); 233 } 234 return group; 235 } 236 return null; 237 } 238 239 public static Optional<String> getNamespaceGroup(NamespaceDescriptor namespaceDesc) { 240 return Optional 241 .ofNullable(namespaceDesc.getConfigurationValue(RSGroupInfo.NAMESPACE_DESC_PROP_GROUP)); 242 } 243}