| // |
| // 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 |
| // |
| // http://www.apache.org/licenses/LICENSE-2.0 |
| // |
| // 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 com.cloud.utils; |
| |
| import java.io.File; |
| import java.io.IOException; |
| import java.io.InputStream; |
| import java.net.HttpURLConnection; |
| import java.net.Inet6Address; |
| import java.net.InetAddress; |
| import java.net.URI; |
| import java.net.URISyntaxException; |
| import java.net.URLEncoder; |
| import java.net.UnknownHostException; |
| import java.util.ArrayList; |
| import java.util.Collections; |
| import java.util.Comparator; |
| import java.util.HashMap; |
| import java.util.List; |
| import java.util.ListIterator; |
| import java.util.Map; |
| import java.util.Set; |
| import java.util.StringTokenizer; |
| import java.util.function.Predicate; |
| |
| import javax.xml.parsers.DocumentBuilder; |
| import javax.xml.parsers.DocumentBuilderFactory; |
| |
| import org.apache.cloudstack.utils.security.ParserUtils; |
| import org.apache.commons.httpclient.Credentials; |
| import org.apache.commons.httpclient.HttpClient; |
| import org.apache.commons.httpclient.HttpStatus; |
| import org.apache.commons.httpclient.MultiThreadedHttpConnectionManager; |
| import org.apache.commons.httpclient.UsernamePasswordCredentials; |
| import org.apache.commons.httpclient.auth.AuthScope; |
| import org.apache.commons.httpclient.methods.GetMethod; |
| import org.apache.commons.httpclient.util.URIUtil; |
| import org.apache.commons.lang3.StringUtils; |
| import org.apache.http.NameValuePair; |
| import org.apache.http.client.utils.URIBuilder; |
| import org.apache.http.client.utils.URLEncodedUtils; |
| import org.apache.http.message.BasicNameValuePair; |
| import org.apache.logging.log4j.Logger; |
| import org.apache.logging.log4j.LogManager; |
| import org.w3c.dom.Document; |
| import org.w3c.dom.Element; |
| import org.w3c.dom.NamedNodeMap; |
| import org.w3c.dom.Node; |
| import org.w3c.dom.NodeList; |
| |
| import com.cloud.utils.crypt.DBEncryptionUtil; |
| import com.cloud.utils.exception.CloudRuntimeException; |
| import com.google.common.collect.ImmutableMap; |
| import com.google.common.collect.ImmutableSet; |
| |
| public class UriUtils { |
| |
| protected static Logger LOGGER = LogManager.getLogger(UriUtils.class); |
| |
| public static String formNfsUri(String host, String path) { |
| try { |
| URI uri = new URI("nfs", host, path, null); |
| return uri.toString(); |
| } catch (URISyntaxException e) { |
| throw new CloudRuntimeException("Unable to form nfs URI: " + host + " - " + path); |
| } |
| } |
| |
| public static String formIscsiUri(String host, String iqn, Integer lun) { |
| try { |
| String path = iqn; |
| if (lun != null) { |
| path += "/" + lun.toString(); |
| } |
| URI uri = new URI("iscsi", host, path, null); |
| return uri.toString(); |
| } catch (URISyntaxException e) { |
| throw new CloudRuntimeException("Unable to form iscsi URI: " + host + " - " + iqn + " - " + lun); |
| } |
| } |
| |
| public static String formFileUri(String path) { |
| File file = new File(path); |
| |
| return file.toURI().toString(); |
| } |
| |
| // a simple URI component helper (Note: it does not deal with URI paramemeter area) |
| public static String encodeURIComponent(String url) { |
| int schemeTail = url.indexOf("://"); |
| |
| int pathStart = 0; |
| if (schemeTail > 0) |
| pathStart = url.indexOf('/', schemeTail + 3); |
| else |
| pathStart = url.indexOf('/'); |
| |
| if (pathStart > 0) { |
| String[] tokens = url.substring(pathStart + 1).split("/"); |
| StringBuilder sb = new StringBuilder(url.substring(0, pathStart)); |
| for (String token : tokens) { |
| sb.append("/").append(URLEncoder.encode(token)); |
| } |
| |
| return sb.toString(); |
| } |
| |
| // no need to do URL component encoding |
| return url; |
| } |
| |
| public static String getCifsUriParametersProblems(URI uri) { |
| if (!UriUtils.hostAndPathPresent(uri)) { |
| String errMsg = "cifs URI missing host and/or path. Make sure it's of the format cifs://hostname/path"; |
| LOGGER.warn(errMsg); |
| return errMsg; |
| } |
| return null; |
| } |
| |
| public static boolean hostAndPathPresent(URI uri) { |
| return !(uri.getHost() == null || uri.getHost().trim().isEmpty() || uri.getPath() == null || uri.getPath().trim().isEmpty()); |
| } |
| |
| public static boolean cifsCredentialsPresent(URI uri) { |
| List<NameValuePair> args = URLEncodedUtils.parse(uri, "UTF-8"); |
| boolean foundUser = false; |
| boolean foundPswd = false; |
| for (NameValuePair nvp : args) { |
| String name = nvp.getName(); |
| if (name.equals("user")) { |
| foundUser = true; |
| LOGGER.debug("foundUser is" + foundUser); |
| } else if (name.equals("password")) { |
| foundPswd = true; |
| LOGGER.debug("foundPswd is" + foundPswd); |
| } |
| } |
| return (foundUser && foundPswd); |
| } |
| |
| public static String getUpdateUri(String url, boolean encrypt) { |
| String updatedPath = null; |
| try { |
| String query = URIUtil.getQuery(url); |
| URIBuilder builder = new URIBuilder(url); |
| builder.removeQuery(); |
| |
| StringBuilder updatedQuery = new StringBuilder(); |
| List<NameValuePair> queryParams = getUserDetails(query); |
| ListIterator<NameValuePair> iterator = queryParams.listIterator(); |
| while (iterator.hasNext()) { |
| NameValuePair param = iterator.next(); |
| String value = null; |
| if ("password".equalsIgnoreCase(param.getName()) && |
| param.getValue() != null) { |
| value = encrypt ? DBEncryptionUtil.encrypt(param.getValue()) : DBEncryptionUtil.decrypt(param.getValue()); |
| } else { |
| value = param.getValue(); |
| } |
| |
| if (updatedQuery.length() == 0) { |
| updatedQuery.append(param.getName()).append('=') |
| .append(value); |
| } else { |
| updatedQuery.append('&').append(param.getName()) |
| .append('=').append(value); |
| } |
| } |
| |
| String schemeAndHost = ""; |
| URI newUri = builder.build(); |
| if (newUri.getScheme() != null) { |
| schemeAndHost = newUri.getScheme() + "://" + newUri.getHost(); |
| } |
| |
| updatedPath = schemeAndHost + newUri.getPath() + "?" + updatedQuery; |
| } catch (URISyntaxException e) { |
| throw new CloudRuntimeException("Couldn't generate an updated uri. " + e.getMessage()); |
| } |
| |
| return updatedPath; |
| } |
| |
| private static List<NameValuePair> getUserDetails(String query) { |
| List<NameValuePair> details = new ArrayList<NameValuePair>(); |
| if (query != null && !query.isEmpty()) { |
| StringTokenizer allParams = new StringTokenizer(query, "&"); |
| while (allParams.hasMoreTokens()) { |
| String param = allParams.nextToken(); |
| details.add(new BasicNameValuePair(param.substring(0, param.indexOf("=")), |
| param.substring(param.indexOf("=") + 1))); |
| } |
| } |
| |
| return details; |
| } |
| |
| // Get the size of a file from URL response header. |
| public static long getRemoteSize(String url, Boolean followRedirect) { |
| long remoteSize = 0L; |
| final String[] methods = new String[]{"HEAD", "GET"}; |
| IllegalArgumentException exception = null; |
| // Attempting first a HEAD request to avoid downloading the whole file. If |
| // it fails (for example with S3 presigned URL), fallback on a standard GET |
| // request. |
| for (String method : methods) { |
| HttpURLConnection httpConn = null; |
| try { |
| URI uri = new URI(url); |
| httpConn = (HttpURLConnection)uri.toURL().openConnection(); |
| httpConn.setRequestMethod(method); |
| httpConn.setConnectTimeout(2000); |
| httpConn.setReadTimeout(5000); |
| httpConn.setInstanceFollowRedirects(Boolean.TRUE.equals(followRedirect)); |
| String contentLength = httpConn.getHeaderField("content-length"); |
| if (contentLength != null) { |
| remoteSize = Long.parseLong(contentLength); |
| } else if (method.equals("GET") && httpConn.getResponseCode() < 300) { |
| // Calculate the content size based on the input stream content |
| byte[] buf = new byte[1024]; |
| int length; |
| while ((length = httpConn.getInputStream().read(buf, 0, buf.length)) != -1) { |
| remoteSize += length; |
| } |
| } |
| return remoteSize; |
| } catch (URISyntaxException e) { |
| throw new IllegalArgumentException("Invalid URL " + url); |
| } catch (IOException e) { |
| exception = new IllegalArgumentException("Unable to establish connection with URL " + url); |
| } finally { |
| if (httpConn != null) { |
| httpConn.disconnect(); |
| } |
| } |
| } |
| if (exception != null) { |
| throw exception; |
| } |
| return 0L; |
| } |
| |
| public static Pair<String, Integer> validateUrl(String url) throws IllegalArgumentException { |
| return validateUrl(null, url); |
| } |
| |
| public static Pair<String, Integer> validateUrl(String format, String url) throws IllegalArgumentException { |
| return validateUrl(format, url, false); |
| } |
| |
| public static Pair<String, Integer> validateUrl(String format, String url, boolean skipIpv6Check) throws IllegalArgumentException { |
| try { |
| URI uri = new URI(url); |
| if ((uri.getScheme() == null) || |
| (!uri.getScheme().equalsIgnoreCase("http") && !uri.getScheme().equalsIgnoreCase("https") && !uri.getScheme().equalsIgnoreCase("file"))) { |
| throw new IllegalArgumentException("Unsupported scheme for url: " + url); |
| } |
| |
| int port = uri.getPort(); |
| if (port == -1 && uri.getScheme().equalsIgnoreCase("https")) { |
| port = 443; |
| } else if (port == -1 && uri.getScheme().equalsIgnoreCase("http")) { |
| port = 80; |
| } |
| |
| String host = uri.getHost(); |
| try { |
| InetAddress hostAddr = InetAddress.getByName(host); |
| if (hostAddr.isAnyLocalAddress() || hostAddr.isLinkLocalAddress() || hostAddr.isLoopbackAddress() || hostAddr.isMulticastAddress()) { |
| throw new IllegalArgumentException("Illegal host specified in url"); |
| } |
| if (!skipIpv6Check && hostAddr instanceof Inet6Address) { |
| throw new IllegalArgumentException("IPV6 addresses not supported (" + hostAddr.getHostAddress() + ")"); |
| } |
| } catch (UnknownHostException uhe) { |
| throw new IllegalArgumentException("Unable to resolve " + host); |
| } |
| |
| // verify format |
| if (format != null) { |
| String uripath = uri.getPath(); |
| checkFormat(format, uripath); |
| } |
| return new Pair<String, Integer>(host, port); |
| } catch (URISyntaxException use) { |
| throw new IllegalArgumentException("Invalid URL: " + url); |
| } |
| } |
| |
| /** |
| * Add element to priority list examining node attributes: priority (for urls) and type (for checksums) |
| */ |
| protected static void addPriorityListElementExaminingNode(String tagName, Node node, List<Pair<String, Integer>> priorityList) { |
| Integer priority = Integer.MAX_VALUE; |
| String first = node.getTextContent(); |
| if (node.hasAttributes()) { |
| NamedNodeMap attributes = node.getAttributes(); |
| for (int k=0; k<attributes.getLength(); k++) { |
| Node attr = attributes.item(k); |
| if (tagName.equals("url") && attr.getNodeName().equals("priority")) { |
| String prio = attr.getNodeValue().replace("\"", ""); |
| priority = Integer.parseInt(prio); |
| break; |
| } else if (tagName.equals("hash") && attr.getNodeName().equals("type")) { |
| first = "{" + attr.getNodeValue() + "}" + first; |
| break; |
| } |
| } |
| } |
| priorityList.add(new Pair<>(first, priority)); |
| } |
| |
| /** |
| * Return the list of first elements on the list of pairs |
| */ |
| protected static List<String> getListOfFirstElements(List<Pair<String, Integer>> priorityList) { |
| List<String> values = new ArrayList<>(); |
| for (Pair<String, Integer> pair : priorityList) { |
| values.add(pair.first()); |
| } |
| return values; |
| } |
| |
| /** |
| * Return HttpClient with connection timeout |
| */ |
| private static HttpClient getHttpClient() { |
| MultiThreadedHttpConnectionManager s_httpClientManager = new MultiThreadedHttpConnectionManager(); |
| s_httpClientManager.getParams().setConnectionTimeout(5000); |
| return new HttpClient(s_httpClientManager); |
| } |
| |
| /** |
| * Retrieve values from XML documents ordered by ascending priority for each tag name |
| */ |
| public static Map<String, List<String>> getMultipleValuesFromXML(InputStream is, String[] tagNames) { |
| Map<String, List<String>> returnValues = new HashMap<String, List<String>>(); |
| try { |
| DocumentBuilderFactory factory = ParserUtils.getSaferDocumentBuilderFactory(); |
| DocumentBuilder docBuilder = factory.newDocumentBuilder(); |
| Document doc = docBuilder.parse(is); |
| Element rootElement = doc.getDocumentElement(); |
| for (int i = 0; i < tagNames.length; i++) { |
| NodeList targetNodes = rootElement.getElementsByTagName(tagNames[i]); |
| if (targetNodes.getLength() <= 0) { |
| LOGGER.error("no " + tagNames[i] + " tag in XML response..."); |
| } else { |
| List<Pair<String, Integer>> priorityList = new ArrayList<>(); |
| for (int j = 0; j < targetNodes.getLength(); j++) { |
| Node node = targetNodes.item(j); |
| addPriorityListElementExaminingNode(tagNames[i], node, priorityList); |
| } |
| priorityList.sort(Comparator.comparing(x -> x.second())); |
| returnValues.put(tagNames[i], getListOfFirstElements(priorityList)); |
| } |
| } |
| } catch (Exception ex) { |
| LOGGER.error(ex); |
| } |
| return returnValues; |
| } |
| |
| /** |
| * Get list of urls on metalink ordered by ascending priority (for those which priority tag is not defined, highest priority value is assumed) |
| */ |
| public static List<String> getMetalinkUrls(String metalinkUrl) { |
| HttpClient httpClient = getHttpClient(); |
| GetMethod getMethod = new GetMethod(metalinkUrl); |
| List<String> urls = new ArrayList<>(); |
| int status; |
| try { |
| status = httpClient.executeMethod(getMethod); |
| } catch (IOException e) { |
| LOGGER.error("Error retrieving urls form metalink: " + metalinkUrl); |
| getMethod.releaseConnection(); |
| return null; |
| } |
| try { |
| InputStream is = getMethod.getResponseBodyAsStream(); |
| if (status == HttpStatus.SC_OK) { |
| Map<String, List<String>> metalinkUrlsMap = getMultipleValuesFromXML(is, new String[] {"url"}); |
| if (metalinkUrlsMap.containsKey("url")) { |
| List<String> metalinkUrls = metalinkUrlsMap.get("url"); |
| urls.addAll(metalinkUrls); |
| } |
| } |
| } catch (IOException e) { |
| LOGGER.warn(e.getMessage()); |
| } finally { |
| getMethod.releaseConnection(); |
| } |
| return urls; |
| } |
| |
| public static final Set<String> COMPRESSION_FORMATS = ImmutableSet.of("zip", "bz2", "gz"); |
| |
| public static final Set<String> buildExtensionSet(boolean metalink, String... baseExtensions) { |
| final ImmutableSet.Builder<String> builder = ImmutableSet.builder(); |
| |
| for (String baseExtension : baseExtensions) { |
| builder.add("." + baseExtension); |
| for (String format : COMPRESSION_FORMATS) { |
| builder.add("." + baseExtension + "." + format); |
| } |
| } |
| |
| if (metalink) { |
| builder.add(".metalink"); |
| } |
| |
| return builder.build(); |
| } |
| |
| private final static Map<String, Set<String>> SUPPORTED_EXTENSIONS_BY_FORMAT = |
| ImmutableMap.<String, Set<String>>builder() |
| .put("vhd", buildExtensionSet(false, "vhd")) |
| .put("vhdx", buildExtensionSet(false, "vhdx")) |
| .put("qcow2", buildExtensionSet(true, "qcow2", "img")) |
| .put("ova", buildExtensionSet(true, "ova")) |
| .put("tar", buildExtensionSet(false, "tar")) |
| .put("raw", buildExtensionSet(false, "img", "raw")) |
| .put("vmdk", buildExtensionSet(false, "vmdk")) |
| .put("iso", buildExtensionSet(true, "iso")) |
| .build(); |
| |
| public final static Set<String> getSupportedExtensions(String format) { |
| return SUPPORTED_EXTENSIONS_BY_FORMAT.get(format); |
| } |
| |
| // verify if a URI path is compliance with the file format given |
| private static void checkFormat(String format, String uripath) { |
| final String lowerCaseUri = uripath.toLowerCase(); |
| |
| final boolean unknownExtensionForFormat = SUPPORTED_EXTENSIONS_BY_FORMAT.get(format.toLowerCase()) |
| .stream() |
| .noneMatch(lowerCaseUri::endsWith); |
| |
| if (unknownExtensionForFormat) { |
| final Predicate<Set<String>> uriMatchesAnyExtension = |
| supportedExtensions -> supportedExtensions.stream() |
| .anyMatch(lowerCaseUri::endsWith); |
| |
| boolean unknownExtension = SUPPORTED_EXTENSIONS_BY_FORMAT.values() |
| .stream() |
| .noneMatch(uriMatchesAnyExtension); |
| |
| if (unknownExtension) { |
| throw new IllegalArgumentException("Please specify a valid " + format.toLowerCase()); |
| } |
| |
| throw new IllegalArgumentException("Please specify a valid URL. " |
| + "URL:" + uripath + " is an invalid for the format " + format.toLowerCase()); |
| } |
| } |
| |
| public static InputStream getInputStreamFromUrl(String url, String user, String password) { |
| |
| try { |
| Pair<String, Integer> hostAndPort = validateUrl(url); |
| HttpClient httpclient = new HttpClient(new MultiThreadedHttpConnectionManager()); |
| if ((user != null) && (password != null)) { |
| httpclient.getParams().setAuthenticationPreemptive(true); |
| Credentials defaultcreds = new UsernamePasswordCredentials(user, password); |
| httpclient.getState().setCredentials(new AuthScope(hostAndPort.first(), hostAndPort.second(), AuthScope.ANY_REALM), defaultcreds); |
| LOGGER.info("Added username=" + user + ", password=" + password + "for host " + hostAndPort.first() + ":" + hostAndPort.second()); |
| } |
| // Execute the method. |
| GetMethod method = new GetMethod(url); |
| int statusCode = httpclient.executeMethod(method); |
| |
| if (statusCode != HttpStatus.SC_OK) { |
| LOGGER.error("Failed to read from URL: " + url); |
| return null; |
| } |
| |
| return method.getResponseBodyAsStream(); |
| } catch (Exception ex) { |
| LOGGER.error("Failed to read from URL: " + url); |
| return null; |
| } |
| } |
| |
| /** |
| * Expands a given vlan URI to a list of vlan IDs |
| * @param vlanAuthority the URI part without the vlan:// scheme |
| * @return returns list of vlan integer ids |
| */ |
| public static List<Integer> expandVlanUri(final String vlanAuthority) { |
| final List<Integer> expandedVlans = new ArrayList<>(); |
| if (StringUtils.isEmpty(vlanAuthority)) { |
| return expandedVlans; |
| } |
| for (final String vlanPart: vlanAuthority.split(",")) { |
| if (StringUtils.isEmpty(vlanPart)) { |
| continue; |
| } |
| final String[] range = vlanPart.split("-"); |
| if (range.length == 2) { |
| Integer start = NumbersUtil.parseInt(range[0], -1); |
| Integer end = NumbersUtil.parseInt(range[1], -1); |
| if (start <= end && end > -1 && start > -1) { |
| while (start <= end) { |
| expandedVlans.add(start++); |
| } |
| } |
| } else { |
| final Integer value = NumbersUtil.parseInt(range[0], -1); |
| if (value > -1) { |
| expandedVlans.add(value); |
| } |
| } |
| } |
| return expandedVlans; |
| } |
| |
| /** |
| * Checks if given vlan URI authorities overlap |
| * @param vlanRange1 |
| * @param vlanRange2 |
| * @return true if they overlap |
| */ |
| public static boolean checkVlanUriOverlap(final String vlanRange1, final String vlanRange2) { |
| final List<Integer> vlans1 = expandVlanUri(vlanRange1); |
| final List<Integer> vlans2 = expandVlanUri(vlanRange2); |
| if (vlans1 == null || vlans2 == null) { |
| return true; |
| } |
| return !Collections.disjoint(vlans1, vlans2); |
| } |
| |
| public static List<Integer> expandPvlanUri(String pvlanRange) { |
| final List<Integer> expandedVlans = new ArrayList<>(); |
| if (StringUtils.isEmpty(pvlanRange)) { |
| return expandedVlans; |
| } |
| String[] parts = pvlanRange.split("-\\w"); |
| expandedVlans.add(Integer.parseInt(parts[0])); |
| expandedVlans.add(Integer.parseInt(parts[1])); |
| return expandedVlans; |
| } |
| |
| public static class UriInfo { |
| String scheme; |
| String storageHost; |
| String storagePath; |
| String userInfo; |
| int port = -1; |
| |
| public UriInfo() { |
| } |
| |
| public UriInfo(String scheme, String storageHost, String storagePath, String userInfo, int port) { |
| this.scheme = scheme; |
| this.storageHost = storageHost; |
| this.storagePath = storagePath; |
| this.userInfo = userInfo; |
| this.port = port; |
| } |
| |
| public String getScheme() { |
| return scheme; |
| } |
| |
| public String getStorageHost() { |
| return storageHost; |
| } |
| |
| public String getStoragePath() { |
| return storagePath; |
| } |
| |
| public String getUserInfo() { |
| return userInfo; |
| } |
| |
| public int getPort() { |
| return port; |
| } |
| |
| @Override |
| public String toString() { |
| return String.format("%s://%s%s%s%s", scheme, |
| userInfo == null ? "" : userInfo + "@", |
| storageHost, |
| port == -1 ? "" : ":" + port, |
| storagePath == null ? "" : storagePath); |
| } |
| } |
| |
| public static UriInfo getUriInfo(String url) { |
| try { |
| if (url == null) { |
| return new UriInfo(); |
| } |
| if (url.startsWith("rbd://")) { |
| return getRbdUrlInfo(url); |
| } |
| URI uri = new URI(UriUtils.encodeURIComponent(url)); |
| return new UriInfo(uri.getScheme(), uri.getHost(), uri.getPath(), uri.getUserInfo(), uri.getPort()); |
| } catch (URISyntaxException e) { |
| throw new CloudRuntimeException(url + " is not a valid uri"); |
| } |
| } |
| |
| private static UriInfo getRbdUrlInfo(String url) { |
| if (url == null || !url.toLowerCase().startsWith("rbd://")) { |
| throw new CloudRuntimeException("RBD URL must start with \"rbd://\""); |
| } |
| String schema = StringUtils.substring(url, 0, 6); |
| url = StringUtils.substring(url, 6, url.length()); |
| int firstAt = StringUtils.indexOf(url, "@"); |
| String credentials = (firstAt == -1) ? null : StringUtils.substring(url, 0, firstAt); |
| String hostInfo = (firstAt == -1) ? url : StringUtils.substring(url, firstAt + 1, url.length()); |
| |
| int firstSlash = StringUtils.indexOf(hostInfo, "/"); |
| int lastColon = StringUtils.lastIndexOf(hostInfo,":"); |
| int lastSquareBracket = StringUtils.lastIndexOf(hostInfo,"]"); |
| int endOfHost = lastColon == -1 ? (firstSlash > 0 ? firstSlash : hostInfo.length() + 1) : |
| (lastSquareBracket > lastColon ? lastSquareBracket + 1 : lastColon); |
| String storageHosts = StringUtils.substring(hostInfo, 0, endOfHost); |
| String firstHost = storageHosts.split(",")[0]; |
| String strAfterHosts = StringUtils.substring(hostInfo, endOfHost); |
| try { |
| URI uri = new URI(UriUtils.encodeURIComponent(schema + firstHost + strAfterHosts)); |
| if (credentials != null) { |
| credentials = credentials.replace("+", "-"); |
| credentials = credentials.replace("/", "_"); |
| } |
| return new UriInfo(uri.getScheme(), storageHosts, uri.getPath(), credentials, uri.getPort()); |
| } catch (URISyntaxException e) { |
| throw new CloudRuntimeException(url + " is not a valid uri for RBD"); |
| } |
| } |
| |
| public static boolean isUrlForCompressedFile(String url) { |
| return UriUtils.COMPRESSION_FORMATS.stream().anyMatch(f -> url.toLowerCase().endsWith(f)); |
| } |
| } |