| /** |
| * Licensed to the Apache Software Foundation (ASF) under one or more |
| * contributor license agreements. See the NOTICE file distributed with this |
| * work for additional information regarding copyright ownership. The ASF |
| * licenses this file to you under the Apache License, Version 2.0 (the |
| * "License"); you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * <p> |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * <p> |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT |
| * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the |
| * License for the specific language governing permissions and limitations under |
| * the License. |
| */ |
| |
| package org.apache.hadoop.ozone; |
| |
| import com.google.common.base.Joiner; |
| import java.io.BufferedInputStream; |
| import java.io.BufferedOutputStream; |
| import java.io.File; |
| import java.io.FileInputStream; |
| import java.io.FileOutputStream; |
| import java.io.IOException; |
| import java.net.InetSocketAddress; |
| import java.nio.charset.StandardCharsets; |
| import java.nio.file.Path; |
| import java.nio.file.Paths; |
| import java.security.MessageDigest; |
| import java.security.NoSuchAlgorithmException; |
| import java.security.SecureRandom; |
| import java.util.Collection; |
| import java.util.Collections; |
| import java.util.Optional; |
| import java.util.zip.GZIPOutputStream; |
| |
| import com.google.common.base.Strings; |
| import org.apache.commons.compress.archivers.tar.TarArchiveEntry; |
| import org.apache.commons.compress.archivers.tar.TarArchiveOutputStream; |
| import org.apache.commons.compress.utils.IOUtils; |
| import org.apache.commons.lang3.StringUtils; |
| import org.apache.hadoop.conf.Configuration; |
| import org.apache.hadoop.hdds.scm.HddsServerUtil; |
| import org.apache.hadoop.hdds.server.ServerUtils; |
| import org.apache.hadoop.net.NetUtils; |
| import org.apache.hadoop.ozone.om.OMConfigKeys; |
| import org.apache.hadoop.ozone.protocol.proto.OzoneManagerProtocolProtos; |
| |
| import static org.apache.hadoop.hdds.HddsUtils.getHostNameFromConfigKeys; |
| import static org.apache.hadoop.hdds.HddsUtils.getPortNumberFromConfigKeys; |
| import static org.apache.hadoop.ozone.om.OMConfigKeys.OZONE_OM_ADDRESS_KEY; |
| import static org.apache.hadoop.ozone.om.OMConfigKeys.OZONE_OM_BIND_HOST_DEFAULT; |
| import static org.apache.hadoop.ozone.om.OMConfigKeys.OZONE_OM_HTTPS_ADDRESS_KEY; |
| import static org.apache.hadoop.ozone.om.OMConfigKeys.OZONE_OM_HTTPS_BIND_HOST_KEY; |
| import static org.apache.hadoop.ozone.om.OMConfigKeys.OZONE_OM_HTTPS_BIND_PORT_DEFAULT; |
| import static org.apache.hadoop.ozone.om.OMConfigKeys.OZONE_OM_HTTP_ADDRESS_KEY; |
| import static org.apache.hadoop.ozone.om.OMConfigKeys.OZONE_OM_HTTP_BIND_HOST_KEY; |
| import static org.apache.hadoop.ozone.om.OMConfigKeys.OZONE_OM_HTTP_BIND_PORT_DEFAULT; |
| import static org.apache.hadoop.ozone.om.OMConfigKeys.OZONE_OM_NODES_KEY; |
| import static org.apache.hadoop.ozone.om.OMConfigKeys.OZONE_OM_PORT_DEFAULT; |
| |
| import org.slf4j.Logger; |
| import org.slf4j.LoggerFactory; |
| |
| /** |
| * Stateless helper functions for the server and client side of OM |
| * communication. |
| */ |
| public final class OmUtils { |
| public static final Logger LOG = LoggerFactory.getLogger(OmUtils.class); |
| private static final SecureRandom SRAND = new SecureRandom(); |
| private static byte[] randomBytes = new byte[32]; |
| |
| private OmUtils() { |
| } |
| |
| /** |
| * Retrieve the socket address that is used by OM. |
| * @param conf |
| * @return Target InetSocketAddress for the SCM service endpoint. |
| */ |
| public static InetSocketAddress getOmAddress(Configuration conf) { |
| return NetUtils.createSocketAddr(getOmRpcAddress(conf)); |
| } |
| |
| /** |
| * Retrieve the socket address that is used by OM. |
| * @param conf |
| * @return Target InetSocketAddress for the SCM service endpoint. |
| */ |
| public static String getOmRpcAddress(Configuration conf) { |
| final Optional<String> host = getHostNameFromConfigKeys(conf, |
| OZONE_OM_ADDRESS_KEY); |
| |
| return host.orElse(OZONE_OM_BIND_HOST_DEFAULT) + ":" + |
| getOmRpcPort(conf); |
| } |
| |
| /** |
| * Retrieve the socket address that is used by OM as specified by the confKey. |
| * Return null if the specified conf key is not set. |
| * @param conf configuration |
| * @param confKey configuration key to lookup address from |
| * @return Target InetSocketAddress for the OM RPC server. |
| */ |
| public static String getOmRpcAddress(Configuration conf, String confKey) { |
| final Optional<String> host = getHostNameFromConfigKeys(conf, confKey); |
| |
| if (host.isPresent()) { |
| return host.get() + ":" + getOmRpcPort(conf, confKey); |
| } else { |
| // The specified confKey is not set |
| return null; |
| } |
| } |
| |
| /** |
| * Retrieve the socket address that should be used by clients to connect |
| * to OM. |
| * @param conf |
| * @return Target InetSocketAddress for the OM service endpoint. |
| */ |
| public static InetSocketAddress getOmAddressForClients( |
| Configuration conf) { |
| final Optional<String> host = getHostNameFromConfigKeys(conf, |
| OZONE_OM_ADDRESS_KEY); |
| |
| if (!host.isPresent()) { |
| throw new IllegalArgumentException( |
| OZONE_OM_ADDRESS_KEY + " must be defined. See" + |
| " https://wiki.apache.org/hadoop/Ozone#Configuration for" + |
| " details on configuring Ozone."); |
| } |
| |
| return NetUtils.createSocketAddr( |
| host.get() + ":" + getOmRpcPort(conf)); |
| } |
| |
| public static int getOmRpcPort(Configuration conf) { |
| // If no port number is specified then we'll just try the defaultBindPort. |
| final Optional<Integer> port = getPortNumberFromConfigKeys(conf, |
| OZONE_OM_ADDRESS_KEY); |
| return port.orElse(OZONE_OM_PORT_DEFAULT); |
| } |
| |
| /** |
| * Retrieve the port that is used by OM as specified by the confKey. |
| * Return default port if port is not specified in the confKey. |
| * @param conf configuration |
| * @param confKey configuration key to lookup address from |
| * @return Port on which OM RPC server will listen on |
| */ |
| public static int getOmRpcPort(Configuration conf, String confKey) { |
| // If no port number is specified then we'll just try the defaultBindPort. |
| final Optional<Integer> port = getPortNumberFromConfigKeys(conf, confKey); |
| return port.orElse(OZONE_OM_PORT_DEFAULT); |
| } |
| |
| public static int getOmRestPort(Configuration conf) { |
| // If no port number is specified then we'll just try the default |
| // HTTP BindPort. |
| final Optional<Integer> port = |
| getPortNumberFromConfigKeys(conf, OZONE_OM_HTTP_ADDRESS_KEY); |
| return port.orElse(OZONE_OM_HTTP_BIND_PORT_DEFAULT); |
| } |
| |
| /** |
| * Get the location where OM should store its metadata directories. |
| * Fall back to OZONE_METADATA_DIRS if not defined. |
| * |
| * @param conf - Config |
| * @return File path, after creating all the required Directories. |
| */ |
| public static File getOmDbDir(Configuration conf) { |
| return ServerUtils.getDBPath(conf, OMConfigKeys.OZONE_OM_DB_DIRS); |
| } |
| |
| /** |
| * Checks if the OM request is read only or not. |
| * @param omRequest OMRequest proto |
| * @return True if its readOnly, false otherwise. |
| */ |
| public static boolean isReadOnly( |
| OzoneManagerProtocolProtos.OMRequest omRequest) { |
| OzoneManagerProtocolProtos.Type cmdType = omRequest.getCmdType(); |
| switch (cmdType) { |
| case CheckVolumeAccess: |
| case InfoVolume: |
| case ListVolume: |
| case InfoBucket: |
| case ListBuckets: |
| case LookupKey: |
| case ListKeys: |
| case InfoS3Bucket: |
| case ListS3Buckets: |
| case ServiceList: |
| case ListMultiPartUploadParts: |
| case GetFileStatus: |
| case LookupFile: |
| case ListStatus: |
| case GetAcl: |
| return true; |
| case CreateVolume: |
| case SetVolumeProperty: |
| case DeleteVolume: |
| case CreateBucket: |
| case SetBucketProperty: |
| case DeleteBucket: |
| case CreateKey: |
| case RenameKey: |
| case DeleteKey: |
| case CommitKey: |
| case AllocateBlock: |
| case CreateS3Bucket: |
| case DeleteS3Bucket: |
| case InitiateMultiPartUpload: |
| case CommitMultiPartUpload: |
| case CompleteMultiPartUpload: |
| case AbortMultiPartUpload: |
| case GetS3Secret: |
| case GetDelegationToken: |
| case RenewDelegationToken: |
| case CancelDelegationToken: |
| case CreateDirectory: |
| case CreateFile: |
| case RemoveAcl: |
| case SetAcl: |
| case AddAcl: |
| case PurgeKeys: |
| return false; |
| default: |
| LOG.error("CmdType {} is not categorized as readOnly or not.", cmdType); |
| return false; |
| } |
| } |
| |
| public static byte[] getMD5Digest(String input) throws IOException { |
| try { |
| MessageDigest md = MessageDigest.getInstance(OzoneConsts.MD5_HASH); |
| return md.digest(input.getBytes(StandardCharsets.UTF_8)); |
| } catch (NoSuchAlgorithmException ex) { |
| throw new IOException("Error creating an instance of MD5 digest.\n" + |
| "This could possibly indicate a faulty JRE"); |
| } |
| } |
| |
| public static byte[] getSHADigest() throws IOException { |
| try { |
| SRAND.nextBytes(randomBytes); |
| MessageDigest sha = MessageDigest.getInstance(OzoneConsts.FILE_HASH); |
| return sha.digest(randomBytes); |
| } catch (NoSuchAlgorithmException ex) { |
| throw new IOException("Error creating an instance of SHA-256 digest.\n" + |
| "This could possibly indicate a faulty JRE"); |
| } |
| } |
| |
| /** |
| * Add non empty and non null suffix to a key. |
| */ |
| private static String addSuffix(String key, String suffix) { |
| if (suffix == null || suffix.isEmpty()) { |
| return key; |
| } |
| assert !suffix.startsWith(".") : |
| "suffix '" + suffix + "' should not already have '.' prepended."; |
| return key + "." + suffix; |
| } |
| |
| /** |
| * Concatenate list of suffix strings '.' separated. |
| */ |
| private static String concatSuffixes(String... suffixes) { |
| if (suffixes == null) { |
| return null; |
| } |
| return Joiner.on(".").skipNulls().join(suffixes); |
| } |
| |
| /** |
| * Return configuration key of format key.suffix1.suffix2...suffixN. |
| */ |
| public static String addKeySuffixes(String key, String... suffixes) { |
| String keySuffix = concatSuffixes(suffixes); |
| return addSuffix(key, keySuffix); |
| } |
| |
| /** |
| * Match input address to local address. |
| * Return true if it matches, false otherwsie. |
| */ |
| public static boolean isAddressLocal(InetSocketAddress addr) { |
| return NetUtils.isLocalAddress(addr.getAddress()); |
| } |
| |
| /** |
| * Get a collection of all omNodeIds for the given omServiceId. |
| */ |
| public static Collection<String> getOMNodeIds(Configuration conf, |
| String omServiceId) { |
| String key = addSuffix(OZONE_OM_NODES_KEY, omServiceId); |
| return conf.getTrimmedStringCollection(key); |
| } |
| |
| /** |
| * @return <code>coll</code> if it is non-null and non-empty. Otherwise, |
| * returns a list with a single null value. |
| */ |
| public static Collection<String> emptyAsSingletonNull(Collection<String> |
| coll) { |
| if (coll == null || coll.isEmpty()) { |
| return Collections.singletonList(null); |
| } else { |
| return coll; |
| } |
| } |
| |
| /** |
| * Given a source directory, create a tar.gz file from it. |
| * |
| * @param sourcePath the path to the directory to be archived. |
| * @return tar.gz file |
| * @throws IOException |
| */ |
| public static File createTarFile(Path sourcePath) throws IOException { |
| TarArchiveOutputStream tarOs = null; |
| try { |
| String sourceDir = sourcePath.toString(); |
| String fileName = sourceDir.concat(".tar.gz"); |
| FileOutputStream fileOutputStream = new FileOutputStream(fileName); |
| GZIPOutputStream gzipOutputStream = |
| new GZIPOutputStream(new BufferedOutputStream(fileOutputStream)); |
| tarOs = new TarArchiveOutputStream(gzipOutputStream); |
| File folder = new File(sourceDir); |
| File[] filesInDir = folder.listFiles(); |
| if (filesInDir != null) { |
| for (File file : filesInDir) { |
| addFilesToArchive(file.getName(), file, tarOs); |
| } |
| } |
| return new File(fileName); |
| } finally { |
| try { |
| org.apache.hadoop.io.IOUtils.closeStream(tarOs); |
| } catch (Exception e) { |
| LOG.error("Exception encountered when closing " + |
| "TAR file output stream: " + e); |
| } |
| } |
| } |
| |
| private static void addFilesToArchive(String source, File file, |
| TarArchiveOutputStream |
| tarFileOutputStream) |
| throws IOException { |
| tarFileOutputStream.putArchiveEntry(new TarArchiveEntry(file, source)); |
| if (file.isFile()) { |
| FileInputStream fileInputStream = new FileInputStream(file); |
| BufferedInputStream bufferedInputStream = |
| new BufferedInputStream(fileInputStream); |
| IOUtils.copy(bufferedInputStream, tarFileOutputStream); |
| tarFileOutputStream.closeArchiveEntry(); |
| fileInputStream.close(); |
| } else if (file.isDirectory()) { |
| tarFileOutputStream.closeArchiveEntry(); |
| File[] filesInDir = file.listFiles(); |
| if (filesInDir != null) { |
| for (File cFile : filesInDir) { |
| addFilesToArchive(cFile.getAbsolutePath(), cFile, |
| tarFileOutputStream); |
| } |
| } |
| } |
| } |
| |
| /** |
| * If a OM conf is only set with key suffixed with OM Node ID, return the |
| * set value. |
| * @return null if base conf key is set, otherwise the value set for |
| * key suffixed with Node ID. |
| */ |
| public static String getConfSuffixedWithOMNodeId(Configuration conf, |
| String confKey, String omServiceID, String omNodeId) { |
| String confValue = conf.getTrimmed(confKey); |
| if (StringUtils.isNotEmpty(confValue)) { |
| return null; |
| } |
| String suffixedConfKey = OmUtils.addKeySuffixes( |
| confKey, omServiceID, omNodeId); |
| confValue = conf.getTrimmed(suffixedConfKey); |
| if (StringUtils.isNotEmpty(confValue)) { |
| return confValue; |
| } |
| return null; |
| } |
| |
| /** |
| * Returns the http address of peer OM node. |
| * @param conf Configuration |
| * @param omNodeId peer OM node ID |
| * @param omNodeHostAddr peer OM node host address |
| * @return http address of peer OM node in the format <hostName>:<port> |
| */ |
| public static String getHttpAddressForOMPeerNode(Configuration conf, |
| String omServiceId, String omNodeId, String omNodeHostAddr) { |
| final Optional<String> bindHost = getHostNameFromConfigKeys(conf, |
| addKeySuffixes(OZONE_OM_HTTP_BIND_HOST_KEY, omServiceId, omNodeId)); |
| |
| final Optional<Integer> addressPort = getPortNumberFromConfigKeys(conf, |
| addKeySuffixes(OZONE_OM_HTTP_ADDRESS_KEY, omServiceId, omNodeId)); |
| |
| final Optional<String> addressHost = getHostNameFromConfigKeys(conf, |
| addKeySuffixes(OZONE_OM_HTTP_ADDRESS_KEY, omServiceId, omNodeId)); |
| |
| String hostName = bindHost.orElse(addressHost.orElse(omNodeHostAddr)); |
| |
| return hostName + ":" + addressPort.orElse(OZONE_OM_HTTP_BIND_PORT_DEFAULT); |
| } |
| |
| /** |
| * Returns the https address of peer OM node. |
| * @param conf Configuration |
| * @param omNodeId peer OM node ID |
| * @param omNodeHostAddr peer OM node host address |
| * @return https address of peer OM node in the format <hostName>:<port> |
| */ |
| public static String getHttpsAddressForOMPeerNode(Configuration conf, |
| String omServiceId, String omNodeId, String omNodeHostAddr) { |
| final Optional<String> bindHost = getHostNameFromConfigKeys(conf, |
| addKeySuffixes(OZONE_OM_HTTPS_BIND_HOST_KEY, omServiceId, omNodeId)); |
| |
| final Optional<Integer> addressPort = getPortNumberFromConfigKeys(conf, |
| addKeySuffixes(OZONE_OM_HTTPS_ADDRESS_KEY, omServiceId, omNodeId)); |
| |
| final Optional<String> addressHost = getHostNameFromConfigKeys(conf, |
| addKeySuffixes(OZONE_OM_HTTPS_ADDRESS_KEY, omServiceId, omNodeId)); |
| |
| String hostName = bindHost.orElse(addressHost.orElse(omNodeHostAddr)); |
| |
| return hostName + ":" + |
| addressPort.orElse(OZONE_OM_HTTPS_BIND_PORT_DEFAULT); |
| } |
| |
| /** |
| * Get the local directory where ratis logs will be stored. |
| */ |
| public static String getOMRatisDirectory(Configuration conf) { |
| String storageDir = conf.get(OMConfigKeys.OZONE_OM_RATIS_STORAGE_DIR); |
| |
| if (Strings.isNullOrEmpty(storageDir)) { |
| storageDir = HddsServerUtil.getDefaultRatisDirectory(conf); |
| } |
| return storageDir; |
| } |
| |
| public static String getOMRatisSnapshotDirectory(Configuration conf) { |
| String snapshotDir = conf.get(OMConfigKeys.OZONE_OM_RATIS_SNAPSHOT_DIR); |
| |
| if (Strings.isNullOrEmpty(snapshotDir)) { |
| snapshotDir = Paths.get(getOMRatisDirectory(conf), |
| "snapshot").toString(); |
| } |
| return snapshotDir; |
| } |
| |
| public static File createOMDir(String dirPath) { |
| File dirFile = new File(dirPath); |
| if (!dirFile.exists() && !dirFile.mkdirs()) { |
| throw new IllegalArgumentException("Unable to create path: " + dirFile); |
| } |
| return dirFile; |
| } |
| |
| /** |
| * Returns the DB key name of a deleted key in OM metadata store. The |
| * deleted key name is the <keyName>_<deletionTimestamp>. |
| * @param key Original key name |
| * @param timestamp timestamp of deletion |
| * @return Deleted key name |
| */ |
| public static String getDeletedKeyName(String key, long timestamp) { |
| return key + "_" + timestamp; |
| } |
| } |