http://git-wip-us.apache.org/repos/asf/cloudstack/blob/83fd8f60/utils/src/main/java/com/cloud/utils/security/CertificateHelper.java ---------------------------------------------------------------------- diff --git a/utils/src/main/java/com/cloud/utils/security/CertificateHelper.java b/utils/src/main/java/com/cloud/utils/security/CertificateHelper.java new file mode 100644 index 0000000..d43542f --- /dev/null +++ b/utils/src/main/java/com/cloud/utils/security/CertificateHelper.java @@ -0,0 +1,166 @@ +// +// 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.security; + +import java.io.BufferedInputStream; +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.StringReader; +import java.security.Key; +import java.security.KeyFactory; +import java.security.KeyStore; +import java.security.KeyStoreException; +import java.security.MessageDigest; +import java.security.NoSuchAlgorithmException; +import java.security.cert.Certificate; +import java.security.cert.CertificateEncodingException; +import java.security.cert.CertificateException; +import java.security.cert.CertificateFactory; +import java.security.cert.X509Certificate; +import java.security.spec.InvalidKeySpecException; +import java.security.spec.PKCS8EncodedKeySpec; +import java.util.ArrayList; +import java.util.List; + +import com.cloud.utils.exception.CloudRuntimeException; +import org.apache.commons.codec.binary.Base64; + +import com.cloud.utils.Ternary; +import org.bouncycastle.openssl.PEMReader; + +public class CertificateHelper { + public static byte[] buildAndSaveKeystore(String alias, String cert, String privateKey, String storePassword) throws KeyStoreException, CertificateException, + NoSuchAlgorithmException, InvalidKeySpecException, IOException { + KeyStore ks = buildKeystore(alias, cert, privateKey, storePassword); + + ByteArrayOutputStream os = new ByteArrayOutputStream(); + ks.store(os, storePassword != null ? storePassword.toCharArray() : null); + os.close(); + return os.toByteArray(); + } + + public static byte[] buildAndSaveKeystore(List<Ternary<String, String, String>> certs, String storePassword) throws KeyStoreException, NoSuchAlgorithmException, + CertificateException, IOException, InvalidKeySpecException { + KeyStore ks = KeyStore.getInstance("JKS"); + ks.load(null, storePassword != null ? storePassword.toCharArray() : null); + + //name,cert,key + for (Ternary<String, String, String> cert : certs) { + if (cert.third() == null) { + Certificate c = buildCertificate(cert.second()); + ks.setCertificateEntry(cert.first(), c); + } else { + Certificate[] c = new Certificate[certs.size()]; + int i = certs.size(); + for (Ternary<String, String, String> ct : certs) { + c[i - 1] = buildCertificate(ct.second()); + i--; + } + ks.setKeyEntry(cert.first(), buildPrivateKey(cert.third()), storePassword != null ? storePassword.toCharArray() : null, c); + } + } + + ByteArrayOutputStream os = new ByteArrayOutputStream(); + ks.store(os, storePassword != null ? storePassword.toCharArray() : null); + os.close(); + return os.toByteArray(); + } + + public static KeyStore loadKeystore(byte[] ksData, String storePassword) throws KeyStoreException, CertificateException, NoSuchAlgorithmException, IOException { + assert (ksData != null); + KeyStore ks = KeyStore.getInstance("JKS"); + ks.load(new ByteArrayInputStream(ksData), storePassword != null ? storePassword.toCharArray() : null); + + return ks; + } + + public static KeyStore buildKeystore(String alias, String cert, String privateKey, String storePassword) throws KeyStoreException, CertificateException, + NoSuchAlgorithmException, InvalidKeySpecException, IOException { + + KeyStore ks = KeyStore.getInstance("JKS"); + ks.load(null, storePassword != null ? storePassword.toCharArray() : null); + Certificate[] certs = new Certificate[1]; + certs[0] = buildCertificate(cert); + ks.setKeyEntry(alias, buildPrivateKey(privateKey), storePassword != null ? storePassword.toCharArray() : null, certs); + return ks; + } + + public static Certificate buildCertificate(String content) throws CertificateException { + assert (content != null); + + BufferedInputStream bis = new BufferedInputStream(new ByteArrayInputStream(content.getBytes())); + CertificateFactory cf = CertificateFactory.getInstance("X.509"); + return cf.generateCertificate(bis); + } + + public static Key buildPrivateKey(String base64EncodedKeyContent) throws NoSuchAlgorithmException, InvalidKeySpecException, IOException { + KeyFactory kf = KeyFactory.getInstance("RSA"); + PKCS8EncodedKeySpec keysp = new PKCS8EncodedKeySpec(Base64.decodeBase64(base64EncodedKeyContent)); + return kf.generatePrivate(keysp); + } + + public static List<Certificate> parseChain(String chain) throws IOException { + + List<Certificate> certs = new ArrayList<Certificate>(); + PEMReader reader = new PEMReader(new StringReader(chain)); + + Certificate crt = null; + + while ((crt = (Certificate)reader.readObject()) != null) { + if (crt instanceof X509Certificate) { + certs.add(crt); + } + } + if (certs.size() == 0) + throw new IllegalArgumentException("Unable to decode certificate chain"); + + return certs; + } + + public static String generateFingerPrint(Certificate cert) { + + final char[] HEX = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F'}; + + StringBuilder buffer = new StringBuilder(60); + try { + + MessageDigest md = MessageDigest.getInstance("SHA-1"); + byte[] data = md.digest(cert.getEncoded()); + + for (int i = 0; i < data.length; i++) { + if (buffer.length() > 0) { + buffer.append(":"); + } + + buffer.append(HEX[(0xF0 & data[i]) >>> 4]); + buffer.append(HEX[0x0F & data[i]]); + } + + } catch (CertificateEncodingException e) { + throw new CloudRuntimeException("Bad certificate encoding"); + } catch (NoSuchAlgorithmException e) { + throw new CloudRuntimeException("Bad certificate algorithm"); + } + + return buffer.toString(); + } + +}
http://git-wip-us.apache.org/repos/asf/cloudstack/blob/83fd8f60/utils/src/main/java/com/cloud/utils/ssh/SSHCmdHelper.java ---------------------------------------------------------------------- diff --git a/utils/src/main/java/com/cloud/utils/ssh/SSHCmdHelper.java b/utils/src/main/java/com/cloud/utils/ssh/SSHCmdHelper.java new file mode 100644 index 0000000..e35a3ea --- /dev/null +++ b/utils/src/main/java/com/cloud/utils/ssh/SSHCmdHelper.java @@ -0,0 +1,179 @@ +// +// 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.ssh; + +import java.io.IOException; +import java.io.InputStream; + +import org.apache.log4j.Logger; + +import com.trilead.ssh2.ChannelCondition; +import com.trilead.ssh2.Session; + +public class SSHCmdHelper { + private static final Logger s_logger = Logger.getLogger(SSHCmdHelper.class); + + public static com.trilead.ssh2.Connection acquireAuthorizedConnection(String ip, String username, String password) { + return acquireAuthorizedConnection(ip, 22, username, password); + } + + public static com.trilead.ssh2.Connection acquireAuthorizedConnection(String ip, int port, String username, String password) { + com.trilead.ssh2.Connection sshConnection = new com.trilead.ssh2.Connection(ip, port); + try { + sshConnection.connect(null, 60000, 60000); + if (!sshConnection.authenticateWithPassword(username, password)) { + String[] methods = sshConnection.getRemainingAuthMethods(username); + StringBuffer mStr = new StringBuffer(); + for (int i = 0; i < methods.length; i++) { + mStr.append(methods[i]); + } + s_logger.warn("SSH authorizes failed, support authorized methods are " + mStr); + return null; + } + return sshConnection; + } catch (IOException e) { + s_logger.warn("Get SSH connection failed", e); + return null; + } + } + + public static void releaseSshConnection(com.trilead.ssh2.Connection sshConnection) { + if (sshConnection != null) { + sshConnection.close(); + } + } + + public static boolean sshExecuteCmd(com.trilead.ssh2.Connection sshConnection, String cmd, int nTimes) { + for (int i = 0; i < nTimes; i++) { + try { + if (sshExecuteCmdOneShot(sshConnection, cmd)) + return true; + } catch (SshException e) { + continue; + } + } + return false; + } + + public static int sshExecuteCmdWithExitCode(com.trilead.ssh2.Connection sshConnection, String cmd) { + return sshExecuteCmdWithExitCode(sshConnection, cmd, 3); + } + + public static int sshExecuteCmdWithExitCode(com.trilead.ssh2.Connection sshConnection, String cmd, int nTimes) { + for (int i = 0; i < nTimes; i++) { + try { + return sshExecuteCmdOneShotWithExitCode(sshConnection, cmd); + } catch (SshException e) { + continue; + } + } + return -1; + } + + public static boolean sshExecuteCmd(com.trilead.ssh2.Connection sshConnection, String cmd) { + return sshExecuteCmd(sshConnection, cmd, 3); + } + + public static int sshExecuteCmdOneShotWithExitCode(com.trilead.ssh2.Connection sshConnection, String cmd) throws SshException { + s_logger.debug("Executing cmd: " + cmd); + Session sshSession = null; + try { + sshSession = sshConnection.openSession(); + // There is a bug in Trilead library, wait a second before + // starting a shell and executing commands, from http://spci.st.ewi.tudelft.nl/chiron/xref/nl/tudelft/swerl/util/SSHConnection.html + Thread.sleep(1000); + + if (sshSession == null) { + throw new SshException("Cannot open ssh session"); + } + + sshSession.execCommand(cmd); + + InputStream stdout = sshSession.getStdout(); + InputStream stderr = sshSession.getStderr(); + + byte[] buffer = new byte[8192]; + StringBuffer sbResult = new StringBuffer(); + + int currentReadBytes = 0; + while (true) { + if (stdout == null || stderr == null) { + throw new SshException("stdout or stderr of ssh session is null"); + } + if ((stdout.available() == 0) && (stderr.available() == 0)) { + int conditions = sshSession.waitForCondition(ChannelCondition.STDOUT_DATA + | ChannelCondition.STDERR_DATA | ChannelCondition.EOF | ChannelCondition.EXIT_STATUS, + 120000); + + if ((conditions & ChannelCondition.TIMEOUT) != 0) { + String msg = "Timed out in waiting SSH execution result"; + s_logger.error(msg); + throw new Exception(msg); + } + + if ((conditions & ChannelCondition.EXIT_STATUS) != 0) { + if ((conditions & (ChannelCondition.STDOUT_DATA | ChannelCondition.STDERR_DATA)) == 0) { + break; + } + } + + if ((conditions & ChannelCondition.EOF) != 0) { + if ((conditions & (ChannelCondition.STDOUT_DATA | ChannelCondition.STDERR_DATA)) == 0) { + break; + } + } + } + + while (stdout.available() > 0) { + currentReadBytes = stdout.read(buffer); + sbResult.append(new String(buffer, 0, currentReadBytes)); + } + + while (stderr.available() > 0) { + currentReadBytes = stderr.read(buffer); + sbResult.append(new String(buffer, 0, currentReadBytes)); + } + } + + String result = sbResult.toString(); + if (result != null && !result.isEmpty()) + s_logger.debug(cmd + " output:" + result); + // exit status delivery might get delayed + for(int i = 0 ; i<10 ; i++ ) { + Integer status = sshSession.getExitStatus(); + if( status != null ) { + return status; + } + Thread.sleep(100); + } + return -1; + } catch (Exception e) { + s_logger.debug("Ssh executed failed", e); + throw new SshException("Ssh executed failed " + e.getMessage()); + } finally { + if (sshSession != null) + sshSession.close(); + } + } + + public static boolean sshExecuteCmdOneShot(com.trilead.ssh2.Connection sshConnection, String cmd) throws SshException { + return sshExecuteCmdOneShotWithExitCode(sshConnection, cmd) == 0; + } +} http://git-wip-us.apache.org/repos/asf/cloudstack/blob/83fd8f60/utils/src/main/java/com/cloud/utils/ssh/SSHKeysHelper.java ---------------------------------------------------------------------- diff --git a/utils/src/main/java/com/cloud/utils/ssh/SSHKeysHelper.java b/utils/src/main/java/com/cloud/utils/ssh/SSHKeysHelper.java new file mode 100644 index 0000000..39db5c4 --- /dev/null +++ b/utils/src/main/java/com/cloud/utils/ssh/SSHKeysHelper.java @@ -0,0 +1,115 @@ +// +// 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.ssh; + +import java.io.ByteArrayOutputStream; +import java.security.MessageDigest; +import java.security.NoSuchAlgorithmException; + +import org.apache.commons.codec.binary.Base64; + +import com.jcraft.jsch.JSch; +import com.jcraft.jsch.JSchException; +import com.jcraft.jsch.KeyPair; + +public class SSHKeysHelper { + + private KeyPair keyPair; + private static final char[] hexChars = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'}; + + private static String toHexString(byte[] b) { + StringBuffer sb = new StringBuffer(); + for (int i = 0; i < b.length; i++) { + sb.append(hexChars[(b[i] >> 4) & 0x0f]); + sb.append(hexChars[(b[i]) & 0x0f]); + } + return sb.toString(); + } + + public SSHKeysHelper() { + try { + keyPair = KeyPair.genKeyPair(new JSch(), KeyPair.RSA); + } catch (JSchException e) { + e.printStackTrace(); + } + } + + public String getPublicKeyFingerPrint() { + return getPublicKeyFingerprint(getPublicKey()); + } + + public static String getPublicKeyFingerprint(String publicKey) { + String key[] = publicKey.split(" "); + if (key.length < 2) { + throw new RuntimeException("Incorrect public key is passed in"); + } + byte[] keyBytes = Base64.decodeBase64(key[1]); + + MessageDigest md5 = null; + try { + md5 = MessageDigest.getInstance("MD5"); + } catch (NoSuchAlgorithmException e) { + e.printStackTrace(); + } + + String rString = ""; + String sumString = ""; + if (md5 != null) { + sumString = toHexString(md5.digest(keyBytes)); + } + + for (int i = 2; i <= sumString.length(); i += 2) { + rString += sumString.substring(i - 2, i); + if (i != sumString.length()) + rString += ":"; + } + + return rString; + } + + public static String getPublicKeyFromKeyMaterial(String keyMaterial) { + if (!keyMaterial.contains(" ")) + keyMaterial = new String(Base64.decodeBase64(keyMaterial.getBytes())); + + if ((!keyMaterial.startsWith("ssh-rsa") && !keyMaterial.startsWith("ssh-dss")) || !keyMaterial.contains(" ")) + return null; + + String[] key = keyMaterial.split(" "); + if (key.length < 2) + return null; + + return key[0].concat(" ").concat(key[1]); + } + + public String getPublicKey() { + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + keyPair.writePublicKey(baos, ""); + + return baos.toString(); + } + + public String getPrivateKey() { + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + keyPair.writePrivateKey(baos); + + return baos.toString(); + } + +} http://git-wip-us.apache.org/repos/asf/cloudstack/blob/83fd8f60/utils/src/main/java/com/cloud/utils/ssh/SshException.java ---------------------------------------------------------------------- diff --git a/utils/src/main/java/com/cloud/utils/ssh/SshException.java b/utils/src/main/java/com/cloud/utils/ssh/SshException.java new file mode 100644 index 0000000..a9d9ed8 --- /dev/null +++ b/utils/src/main/java/com/cloud/utils/ssh/SshException.java @@ -0,0 +1,30 @@ +// +// 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.ssh; + +import com.cloud.utils.SerialVersionUID; + +public class SshException extends Exception { + private static final long serialVersionUID = SerialVersionUID.sshException; + + public SshException(String msg) { + super(msg); + } +} http://git-wip-us.apache.org/repos/asf/cloudstack/blob/83fd8f60/utils/src/main/java/com/cloud/utils/ssh/SshHelper.java ---------------------------------------------------------------------- diff --git a/utils/src/main/java/com/cloud/utils/ssh/SshHelper.java b/utils/src/main/java/com/cloud/utils/ssh/SshHelper.java new file mode 100644 index 0000000..3aac427 --- /dev/null +++ b/utils/src/main/java/com/cloud/utils/ssh/SshHelper.java @@ -0,0 +1,209 @@ +// +// 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.ssh; + +import java.io.File; +import java.io.InputStream; + +import org.apache.log4j.Logger; + +import com.trilead.ssh2.ChannelCondition; + +import com.cloud.utils.Pair; + +public class SshHelper { + private static final int DEFAULT_CONNECT_TIMEOUT = 60000; + private static final int DEFAULT_KEX_TIMEOUT = 60000; + + private static final Logger s_logger = Logger.getLogger(SshHelper.class); + + public static Pair<Boolean, String> sshExecute(String host, int port, String user, File pemKeyFile, String password, String command) throws Exception { + + return sshExecute(host, port, user, pemKeyFile, password, command, 60000, 60000, 120000); + } + + public static void scpTo(String host, int port, String user, File pemKeyFile, String password, String remoteTargetDirectory, String localFile, String fileMode) + throws Exception { + + scpTo(host, port, user, pemKeyFile, password, remoteTargetDirectory, localFile, fileMode, DEFAULT_CONNECT_TIMEOUT, DEFAULT_KEX_TIMEOUT); + } + + public static void scpTo(String host, int port, String user, File pemKeyFile, String password, String remoteTargetDirectory, byte[] data, String remoteFileName, + String fileMode) throws Exception { + + scpTo(host, port, user, pemKeyFile, password, remoteTargetDirectory, data, remoteFileName, fileMode, DEFAULT_CONNECT_TIMEOUT, DEFAULT_KEX_TIMEOUT); + } + + public static void scpTo(String host, int port, String user, File pemKeyFile, String password, String remoteTargetDirectory, String localFile, String fileMode, + int connectTimeoutInMs, int kexTimeoutInMs) throws Exception { + + com.trilead.ssh2.Connection conn = null; + com.trilead.ssh2.SCPClient scpClient = null; + + try { + conn = new com.trilead.ssh2.Connection(host, port); + conn.connect(null, connectTimeoutInMs, kexTimeoutInMs); + + if (pemKeyFile == null) { + if (!conn.authenticateWithPassword(user, password)) { + String msg = "Failed to authentication SSH user " + user + " on host " + host; + s_logger.error(msg); + throw new Exception(msg); + } + } else { + if (!conn.authenticateWithPublicKey(user, pemKeyFile, password)) { + String msg = "Failed to authentication SSH user " + user + " on host " + host; + s_logger.error(msg); + throw new Exception(msg); + } + } + + scpClient = conn.createSCPClient(); + + if (fileMode != null) + scpClient.put(localFile, remoteTargetDirectory, fileMode); + else + scpClient.put(localFile, remoteTargetDirectory); + } finally { + if (conn != null) + conn.close(); + } + } + + public static void scpTo(String host, int port, String user, File pemKeyFile, String password, String remoteTargetDirectory, byte[] data, String remoteFileName, + String fileMode, int connectTimeoutInMs, int kexTimeoutInMs) throws Exception { + + com.trilead.ssh2.Connection conn = null; + com.trilead.ssh2.SCPClient scpClient = null; + + try { + conn = new com.trilead.ssh2.Connection(host, port); + conn.connect(null, connectTimeoutInMs, kexTimeoutInMs); + + if (pemKeyFile == null) { + if (!conn.authenticateWithPassword(user, password)) { + String msg = "Failed to authentication SSH user " + user + " on host " + host; + s_logger.error(msg); + throw new Exception(msg); + } + } else { + if (!conn.authenticateWithPublicKey(user, pemKeyFile, password)) { + String msg = "Failed to authentication SSH user " + user + " on host " + host; + s_logger.error(msg); + throw new Exception(msg); + } + } + + scpClient = conn.createSCPClient(); + if (fileMode != null) + scpClient.put(data, remoteFileName, remoteTargetDirectory, fileMode); + else + scpClient.put(data, remoteFileName, remoteTargetDirectory); + } finally { + if (conn != null) + conn.close(); + } + } + + public static Pair<Boolean, String> sshExecute(String host, int port, String user, File pemKeyFile, String password, String command, int connectTimeoutInMs, + int kexTimeoutInMs, int waitResultTimeoutInMs) throws Exception { + + com.trilead.ssh2.Connection conn = null; + com.trilead.ssh2.Session sess = null; + try { + conn = new com.trilead.ssh2.Connection(host, port); + conn.connect(null, connectTimeoutInMs, kexTimeoutInMs); + + if (pemKeyFile == null) { + if (!conn.authenticateWithPassword(user, password)) { + String msg = "Failed to authentication SSH user " + user + " on host " + host; + s_logger.error(msg); + throw new Exception(msg); + } + } else { + if (!conn.authenticateWithPublicKey(user, pemKeyFile, password)) { + String msg = "Failed to authentication SSH user " + user + " on host " + host; + s_logger.error(msg); + throw new Exception(msg); + } + } + sess = conn.openSession(); + + sess.execCommand(command); + + InputStream stdout = sess.getStdout(); + InputStream stderr = sess.getStderr(); + + byte[] buffer = new byte[8192]; + StringBuffer sbResult = new StringBuffer(); + + int currentReadBytes = 0; + while (true) { + if ((stdout.available() == 0) && (stderr.available() == 0)) { + int conditions = + sess.waitForCondition(ChannelCondition.STDOUT_DATA | ChannelCondition.STDERR_DATA | ChannelCondition.EOF | ChannelCondition.EXIT_STATUS, + waitResultTimeoutInMs); + + if ((conditions & ChannelCondition.TIMEOUT) != 0) { + String msg = "Timed out in waiting SSH execution result"; + s_logger.error(msg); + throw new Exception(msg); + } + + if ((conditions & ChannelCondition.EXIT_STATUS) != 0) { + if ((conditions & (ChannelCondition.STDOUT_DATA | ChannelCondition.STDERR_DATA)) == 0) { + break; + } + } + } + + while (stdout.available() > 0) { + currentReadBytes = stdout.read(buffer); + sbResult.append(new String(buffer, 0, currentReadBytes)); + } + + while (stderr.available() > 0) { + currentReadBytes = stderr.read(buffer); + sbResult.append(new String(buffer, 0, currentReadBytes)); + } + } + + String result = sbResult.toString(); + + if (sess.getExitStatus() == null) { + //Exit status is NOT available. Returning failure result. + return new Pair<Boolean, String>(false, result); + } + + if (sess.getExitStatus() != null && sess.getExitStatus().intValue() != 0) { + s_logger.error("SSH execution of command " + command + " has an error status code in return. result output: " + result); + return new Pair<Boolean, String>(false, result); + } + + return new Pair<Boolean, String>(true, result); + } finally { + if (sess != null) + sess.close(); + + if (conn != null) + conn.close(); + } + } +} http://git-wip-us.apache.org/repos/asf/cloudstack/blob/83fd8f60/utils/src/main/java/com/cloud/utils/storage/encoding/DecodedDataObject.java ---------------------------------------------------------------------- diff --git a/utils/src/main/java/com/cloud/utils/storage/encoding/DecodedDataObject.java b/utils/src/main/java/com/cloud/utils/storage/encoding/DecodedDataObject.java new file mode 100644 index 0000000..56be699 --- /dev/null +++ b/utils/src/main/java/com/cloud/utils/storage/encoding/DecodedDataObject.java @@ -0,0 +1,56 @@ +// +// 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.storage.encoding; + +public class DecodedDataObject { + private String objType; + private Long size; + private String name; + private String path; + private DecodedDataStore store; + + public DecodedDataObject(String objType, Long size, String name, String path, DecodedDataStore store) { + this.objType = objType; + this.size = size; + this.name = name; + this.path = path; + this.store = store; + } + + public String getObjType() { + return objType; + } + + public Long getSize() { + return size; + } + + public String getName() { + return name; + } + + public String getPath() { + return path; + } + + public DecodedDataStore getStore() { + return store; + } +} http://git-wip-us.apache.org/repos/asf/cloudstack/blob/83fd8f60/utils/src/main/java/com/cloud/utils/storage/encoding/DecodedDataStore.java ---------------------------------------------------------------------- diff --git a/utils/src/main/java/com/cloud/utils/storage/encoding/DecodedDataStore.java b/utils/src/main/java/com/cloud/utils/storage/encoding/DecodedDataStore.java new file mode 100644 index 0000000..ac8af8b --- /dev/null +++ b/utils/src/main/java/com/cloud/utils/storage/encoding/DecodedDataStore.java @@ -0,0 +1,68 @@ +// +// 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.storage.encoding; + +public class DecodedDataStore { + private final String role; + private final String uuid; + private final String providerName; + private final String scheme; + private final String url; + private final String server; + private final String path; + + public DecodedDataStore(String role, String uuid, String providerName, String scheme, String url, String server, String path) { + this.role = role; + this.uuid = uuid; + this.providerName = providerName; + this.scheme = scheme; + this.url = url; + this.server = server; + this.path = path; + } + + public String getRole() { + return role; + } + + public String getUuid() { + return uuid; + } + + public String getProviderName() { + return providerName; + } + + public String getScheme() { + return scheme; + } + + public String getUrl() { + return url; + } + + public String getServer() { + return server; + } + + public String getPath() { + return path; + } +} http://git-wip-us.apache.org/repos/asf/cloudstack/blob/83fd8f60/utils/src/main/java/com/cloud/utils/storage/encoding/Decoder.java ---------------------------------------------------------------------- diff --git a/utils/src/main/java/com/cloud/utils/storage/encoding/Decoder.java b/utils/src/main/java/com/cloud/utils/storage/encoding/Decoder.java new file mode 100644 index 0000000..c7c61d3 --- /dev/null +++ b/utils/src/main/java/com/cloud/utils/storage/encoding/Decoder.java @@ -0,0 +1,66 @@ +// +// 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.storage.encoding; + +import java.net.URI; +import java.net.URISyntaxException; +import java.util.Arrays; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import org.apache.log4j.Logger; + +public class Decoder { + private static final Logger s_logger = Logger.getLogger(Decoder.class); + + private static Map<String, String> getParameters(URI uri) { + String parameters = uri.getQuery(); + Map<String, String> params = new HashMap<String, String>(); + List<String> paraLists = Arrays.asList(parameters.split("&")); + for (String para : paraLists) { + String[] pair = para.split("="); + if (!pair[1].equalsIgnoreCase("null")) { + params.put(pair[0], pair[1]); + } + + } + return params; + } + + public static DecodedDataObject decode(String url) throws URISyntaxException { + URI uri = new URI(url); + Map<String, String> params = getParameters(uri); + DecodedDataStore store = + new DecodedDataStore(params.get(EncodingType.ROLE.toString()), params.get(EncodingType.STOREUUID.toString()), + params.get(EncodingType.PROVIDERNAME.toString()), uri.getScheme(), uri.getScheme() + uri.getHost() + uri.getPath(), uri.getHost(), uri.getPath()); + + Long size = null; + try { + size = Long.parseLong(params.get(EncodingType.SIZE.toString())); + } catch (NumberFormatException e) { + s_logger.info("[ignored] number not recognised",e); + } + DecodedDataObject obj = + new DecodedDataObject(params.get(EncodingType.OBJTYPE.toString()), size, params.get(EncodingType.NAME.toString()), params.get(EncodingType.PATH.toString()), + store); + return obj; + } +} http://git-wip-us.apache.org/repos/asf/cloudstack/blob/83fd8f60/utils/src/main/java/com/cloud/utils/storage/encoding/EncodingType.java ---------------------------------------------------------------------- diff --git a/utils/src/main/java/com/cloud/utils/storage/encoding/EncodingType.java b/utils/src/main/java/com/cloud/utils/storage/encoding/EncodingType.java new file mode 100644 index 0000000..f40f4f9 --- /dev/null +++ b/utils/src/main/java/com/cloud/utils/storage/encoding/EncodingType.java @@ -0,0 +1,32 @@ +// +// 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.storage.encoding; + +public enum EncodingType { + //object + OBJTYPE, + SIZE, + NAME, + PATH, + //dataStore + ROLE, + STOREUUID, + PROVIDERNAME +} http://git-wip-us.apache.org/repos/asf/cloudstack/blob/83fd8f60/utils/src/main/java/com/cloud/utils/time/InaccurateClock.java ---------------------------------------------------------------------- diff --git a/utils/src/main/java/com/cloud/utils/time/InaccurateClock.java b/utils/src/main/java/com/cloud/utils/time/InaccurateClock.java new file mode 100644 index 0000000..2a22853 --- /dev/null +++ b/utils/src/main/java/com/cloud/utils/time/InaccurateClock.java @@ -0,0 +1,102 @@ +// +// 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.time; + +import java.util.concurrent.Executors; +import java.util.concurrent.ScheduledExecutorService; +import java.util.concurrent.TimeUnit; + +import javax.management.StandardMBean; + +import org.apache.log4j.Logger; + +import com.cloud.utils.concurrency.NamedThreadFactory; +import com.cloud.utils.mgmt.JmxUtil; + +/** + */ + +public class InaccurateClock extends StandardMBean implements InaccurateClockMBean { + private static final Logger s_logger = Logger.getLogger(InaccurateClock.class); + static ScheduledExecutorService s_executor = null; + static final InaccurateClock s_timer = new InaccurateClock(); + private static long time; + + public InaccurateClock() { + super(InaccurateClockMBean.class, false); + time = System.currentTimeMillis(); + restart(); + try { + JmxUtil.registerMBean("InaccurateClock", "InaccurateClock", this); + } catch (Exception e) { + s_logger.warn("Unable to initialize inaccurate clock", e); + } + } + + @Override + public long[] getCurrentTimes() { + long[] results = new long[2]; + results[0] = time; + results[1] = System.currentTimeMillis(); + + return results; + } + + @Override + public synchronized String restart() { + turnOff(); + s_executor = Executors.newScheduledThreadPool(1, new NamedThreadFactory("InaccurateClock")); + s_executor.scheduleAtFixedRate(new SetTimeTask(), 0, 60, TimeUnit.SECONDS); + return "Restarted"; + } + + @Override + public String turnOff() { + if (s_executor != null) { + try { + s_executor.shutdown(); + } catch (Throwable th) { + s_logger.error("Unable to shutdown the Executor", th); + return "Unable to turn off check logs"; + } + } + s_executor = null; + return "Off"; + } + + public static long getTime() { + return s_executor != null ? time : System.currentTimeMillis(); + } + + public static long getTimeInSeconds() { + return time / 1000; + } + + protected class SetTimeTask implements Runnable { + @Override + public void run() { + try { + time = System.currentTimeMillis(); + } catch (Throwable th) { + s_logger.error("Unable to time", th); + } + } + } +} http://git-wip-us.apache.org/repos/asf/cloudstack/blob/83fd8f60/utils/src/main/java/com/cloud/utils/time/InaccurateClockMBean.java ---------------------------------------------------------------------- diff --git a/utils/src/main/java/com/cloud/utils/time/InaccurateClockMBean.java b/utils/src/main/java/com/cloud/utils/time/InaccurateClockMBean.java new file mode 100644 index 0000000..acc4615 --- /dev/null +++ b/utils/src/main/java/com/cloud/utils/time/InaccurateClockMBean.java @@ -0,0 +1,28 @@ +// +// 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.time; + +public interface InaccurateClockMBean { + String restart(); + + String turnOff(); + + long[] getCurrentTimes(); +} http://git-wip-us.apache.org/repos/asf/cloudstack/blob/83fd8f60/utils/src/main/java/com/cloud/utils/xmlobject/XmlObject.java ---------------------------------------------------------------------- diff --git a/utils/src/main/java/com/cloud/utils/xmlobject/XmlObject.java b/utils/src/main/java/com/cloud/utils/xmlobject/XmlObject.java new file mode 100644 index 0000000..42af945 --- /dev/null +++ b/utils/src/main/java/com/cloud/utils/xmlobject/XmlObject.java @@ -0,0 +1,214 @@ +// +// 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.xmlobject; + +import java.lang.reflect.Field; +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.HashMap; +import java.util.Iterator; +import java.util.List; +import java.util.Map; +import java.util.logging.Logger; + +import com.cloud.utils.exception.CloudRuntimeException; + +public class XmlObject { + private final Logger logger = Logger.getLogger(XmlObject.class.getName()); + private final Map<String, Object> elements = new HashMap<String, Object>(); + private String text; + private String tag; + + XmlObject() { + } + + public void removeAllChildren() { + elements.clear(); + } + + public XmlObject(String tag) { + this.tag = tag; + } + + public XmlObject putElement(String key, Object e) { + if (e == null) { + throw new IllegalArgumentException(String.format("element[%s] can not be null", key)); + } + Object old = elements.get(key); + if (old == null) { + //System.out.println(String.format("no %s, add new", key)); + elements.put(key, e); + } else { + if (old instanceof List) { + //System.out.println(String.format("already list %s, add", key)); + ((List)old).add(e); + } else { + //System.out.println(String.format("not list list %s, add list", key)); + List lst = new ArrayList(); + lst.add(old); + lst.add(e); + elements.put(key, lst); + } + } + + return this; + } + + public void removeElement(String key) { + elements.remove(key); + } + + private Object recurGet(XmlObject obj, Iterator<String> it) { + String key = it.next(); + Object e = obj.elements.get(key); + if (e == null) { + return null; + } + + if (!it.hasNext()) { + return e; + } else { + if (!(e instanceof XmlObject)) { + throw new CloudRuntimeException(String.format("%s doesn't reference to a XmlObject", it.next())); + } + return recurGet((XmlObject)e, it); + } + } + + public <T> T get(String elementStr) { + String[] strs = elementStr.split("\\."); + List<String> lst = new ArrayList<String>(strs.length); + Collections.addAll(lst, strs); + return (T)recurGet(this, lst.iterator()); + } + + public <T> List<T> getAsList(String elementStr) { + Object e = get(elementStr); + if (e instanceof List) { + return (List<T>)e; + } + + List lst = new ArrayList(1); + if (e != null) { + lst.add(e); + } + + return lst; + } + + public String getText() { + return text; + } + + public XmlObject setText(String text) { + this.text = text; + return this; + } + + public String getTag() { + return tag; + } + + public XmlObject setTag(String tag) { + this.tag = tag; + return this; + } + + public String dump() { + StringBuilder sb = new StringBuilder(); + sb.append("<").append(tag); + List<XmlObject> children = new ArrayList<XmlObject>(); + for (Map.Entry<String, Object> e : elements.entrySet()) { + String key = e.getKey(); + Object val = e.getValue(); + if (val instanceof String) { + sb.append(String.format(" %s=\"%s\"", key, val.toString())); + } else if (val instanceof XmlObject) { + children.add((XmlObject)val); + } else if (val instanceof List) { + children.addAll((Collection<? extends XmlObject>)val); + } else { + throw new CloudRuntimeException(String.format("unsupported element type[tag:%s, class: %s], only allowed type of [String, List<XmlObject>, Object]", key, + val.getClass().getName())); + } + } + + if (!children.isEmpty() && text != null) { + logger.info(String.format("element %s cannot have both text[%s] and child elements, set text to null", tag, text)); + text = null; + } + + if (!children.isEmpty()) { + sb.append(">"); + for (XmlObject x : children) { + sb.append(x.dump()); + } + sb.append(String.format("</%s>", tag)); + } else { + if (text != null) { + sb.append(">"); + sb.append(text); + sb.append(String.format("</%s>", tag)); + } else { + sb.append(" />"); + } + } + return sb.toString(); + } + + @Override + public String toString() { + StringBuilder sb = new StringBuilder("<" + tag); + for (Map.Entry<String, Object> e : elements.entrySet()) { + String key = e.getKey(); + Object value = e.getValue(); + if (!(value instanceof String)) { + continue; + } + sb.append(String.format(" %s=\"%s\"", key, value.toString())); + } + + if (text == null || "".equals(text.trim())) { + sb.append(" />"); + } else { + sb.append(">").append(text).append(String.format("</ %s>", tag)); + } + return sb.toString(); + } + + public <T> T evaluateObject(T obj) { + Class<?> clazz = obj.getClass(); + try { + do { + Field[] fs = clazz.getDeclaredFields(); + for (Field f : fs) { + f.setAccessible(true); + Object value = get(f.getName()); + f.set(obj, value); + } + clazz = clazz.getSuperclass(); + } while (clazz != null && clazz != Object.class); + return obj; + } catch (Exception e) { + throw new CloudRuntimeException(e); + } + } +} http://git-wip-us.apache.org/repos/asf/cloudstack/blob/83fd8f60/utils/src/main/java/com/cloud/utils/xmlobject/XmlObjectParser.java ---------------------------------------------------------------------- diff --git a/utils/src/main/java/com/cloud/utils/xmlobject/XmlObjectParser.java b/utils/src/main/java/com/cloud/utils/xmlobject/XmlObjectParser.java new file mode 100644 index 0000000..f0e8ce3 --- /dev/null +++ b/utils/src/main/java/com/cloud/utils/xmlobject/XmlObjectParser.java @@ -0,0 +1,128 @@ +// +// 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.xmlobject; + +import com.cloud.utils.exception.CloudRuntimeException; +import org.xml.sax.Attributes; +import org.xml.sax.SAXException; +import org.xml.sax.helpers.DefaultHandler; + +import javax.xml.parsers.SAXParser; +import javax.xml.parsers.SAXParserFactory; +import java.io.ByteArrayInputStream; +import java.io.File; +import java.io.FileInputStream; +import java.io.FileNotFoundException; +import java.io.InputStream; +import java.util.Stack; + +public class XmlObjectParser { + final private InputStream is; + + private class XmlHandler extends DefaultHandler { + private Stack<XmlObject> stack; + private String currentValue; + private XmlObject root; + + XmlHandler() { + stack = new Stack<XmlObject>(); + } + + @Override + public void startElement(String namespaceURI, String localName, String qName, Attributes atts) throws SAXException { + //System.out.println(String.format("startElement: namespaceURI:%s, localName:%s, qName:%s", namespaceURI, localName, qName)); + currentValue = null; + XmlObject obj = new XmlObject(); + for (int i = 0; i < atts.getLength(); i++) { + obj.putElement(atts.getQName(i), atts.getValue(i)); + } + obj.setTag(qName); + if (!stack.isEmpty()) { + XmlObject parent = stack.peek(); + parent.putElement(qName, obj); + } + stack.push(obj); + } + + @Override + public void endElement(String namespaceURI, String localName, String qName) throws SAXException { + XmlObject currObj = stack.pop(); + if (currentValue != null) { + currObj.setText(currentValue); + } + + if (stack.isEmpty()) { + root = currObj; + } + + //System.out.println(String.format("endElement: namespaceURI:%s, localName:%s, qName:%s", namespaceURI, localName, qName)); + } + + @Override + public void characters(char[] ch, int start, int length) throws SAXException { + StringBuilder str = new StringBuilder(); + str.append(ch, start, length); + currentValue = str.toString(); + //System.out.println(String.format("characters: %s", str.toString())); + } + + XmlObject getRoot() { + return root; + } + } + + private XmlObjectParser(InputStream is) { + super(); + this.is = is; + } + + public static XmlObject parseFromFile(String filePath) { + FileInputStream fs; + try { + fs = new FileInputStream(new File(filePath)); + XmlObjectParser p = new XmlObjectParser(fs); + return p.parse(); + } catch (FileNotFoundException e) { + throw new CloudRuntimeException(e); + } + } + + public static XmlObject parseFromString(String xmlString) { + InputStream stream = new ByteArrayInputStream(xmlString.getBytes()); + XmlObjectParser p = new XmlObjectParser(stream); + XmlObject obj = p.parse(); + if (obj.getText() != null && obj.getText().replaceAll("\\n", "").replaceAll("\\r", "").replaceAll(" ", "").isEmpty()) { + obj.setText(null); + } + return obj; + } + + private XmlObject parse() { + SAXParserFactory spfactory = SAXParserFactory.newInstance(); + try { + SAXParser saxParser = spfactory.newSAXParser(); + XmlHandler handler = new XmlHandler(); + saxParser.parse(is, handler); + return handler.getRoot(); + } catch (Exception e) { + throw new CloudRuntimeException(e); + } + } +} http://git-wip-us.apache.org/repos/asf/cloudstack/blob/83fd8f60/utils/src/main/java/org/apache/cloudstack/utils/baremetal/BaremetalUtils.java ---------------------------------------------------------------------- diff --git a/utils/src/main/java/org/apache/cloudstack/utils/baremetal/BaremetalUtils.java b/utils/src/main/java/org/apache/cloudstack/utils/baremetal/BaremetalUtils.java new file mode 100644 index 0000000..85704a5 --- /dev/null +++ b/utils/src/main/java/org/apache/cloudstack/utils/baremetal/BaremetalUtils.java @@ -0,0 +1,24 @@ +// +// 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 org.apache.cloudstack.utils.baremetal; + +public class BaremetalUtils { + public static final String BAREMETAL_SYSTEM_ACCOUNT_NAME = "baremetal-system-account"; +} http://git-wip-us.apache.org/repos/asf/cloudstack/blob/83fd8f60/utils/src/main/java/org/apache/cloudstack/utils/graphite/GraphiteClient.java ---------------------------------------------------------------------- diff --git a/utils/src/main/java/org/apache/cloudstack/utils/graphite/GraphiteClient.java b/utils/src/main/java/org/apache/cloudstack/utils/graphite/GraphiteClient.java new file mode 100644 index 0000000..4143f09 --- /dev/null +++ b/utils/src/main/java/org/apache/cloudstack/utils/graphite/GraphiteClient.java @@ -0,0 +1,123 @@ +// +// 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 org.apache.cloudstack.utils.graphite; + +import java.io.IOException; +import java.net.DatagramPacket; +import java.net.DatagramSocket; +import java.net.InetAddress; +import java.net.UnknownHostException; +import java.util.HashMap; +import java.util.Map; + +public class GraphiteClient { + + private String graphiteHost; + private int graphitePort; + + /** + * Create a new Graphite client + * + * @param graphiteHost Hostname of the Graphite host + * @param graphitePort UDP port of the Graphite host + */ + public GraphiteClient(String graphiteHost, int graphitePort) { + this.graphiteHost = graphiteHost; + this.graphitePort = graphitePort; + } + + /** + * Create a new Graphite client + * + * @param graphiteHost Hostname of the Graphite host. Will default to port 2003 + */ + public GraphiteClient(String graphiteHost) { + this.graphiteHost = graphiteHost; + graphitePort = 2003; + } + + /** + * Get the current system timestamp to pass to Graphite + * + * @return Seconds passed since epoch (01-01-1970) + */ + protected long getCurrentSystemTime() { + return System.currentTimeMillis() / 1000; + } + + /** + * Send a array of metrics to graphite. + * + * @param metrics the metrics as key-value-pairs + */ + public void sendMetrics(Map<String, Integer> metrics) { + sendMetrics(metrics, getCurrentSystemTime()); + } + + /** + * Send a array of metrics with a given timestamp to graphite. + * + * @param metrics the metrics as key-value-pairs + * @param timeStamp the timestamp + */ + public void sendMetrics(Map<String, Integer> metrics, long timeStamp) { + try (DatagramSocket sock = new DatagramSocket()){ + java.security.Security.setProperty("networkaddress.cache.ttl", "0"); + InetAddress addr = InetAddress.getByName(this.graphiteHost); + + for (Map.Entry<String, Integer> metric: metrics.entrySet()) { + byte[] message = new String(metric.getKey() + " " + metric.getValue() + " " + timeStamp + "\n").getBytes(); + DatagramPacket packet = new DatagramPacket(message, message.length, addr, graphitePort); + sock.send(packet); + } + } catch (UnknownHostException e) { + throw new GraphiteException("Unknown host: " + graphiteHost); + } catch (IOException e) { + throw new GraphiteException("Error while writing to graphite: " + e.getMessage(), e); + } + } + + /** + * Send a single metric with the current time as timestamp to graphite. + * + * @param key The metric key + * @param value the metric value + * + * @throws GraphiteException if sending data to graphite failed + */ + public void sendMetric(String key, int value) { + sendMetric(key, value, getCurrentSystemTime()); + } + + /** + * Send a single metric with a given timestamp to graphite. + * + * @param key The metric key + * @param value The metric value + * @param timeStamp the timestamp to use + * + * @throws GraphiteException if sending data to graphite failed + */ + public void sendMetric(final String key, final int value, long timeStamp) { + HashMap metrics = new HashMap<String, Integer>(); + metrics.put(key, value); + sendMetrics(metrics, timeStamp); + } +} http://git-wip-us.apache.org/repos/asf/cloudstack/blob/83fd8f60/utils/src/main/java/org/apache/cloudstack/utils/graphite/GraphiteException.java ---------------------------------------------------------------------- diff --git a/utils/src/main/java/org/apache/cloudstack/utils/graphite/GraphiteException.java b/utils/src/main/java/org/apache/cloudstack/utils/graphite/GraphiteException.java new file mode 100644 index 0000000..9148764 --- /dev/null +++ b/utils/src/main/java/org/apache/cloudstack/utils/graphite/GraphiteException.java @@ -0,0 +1,31 @@ +// +// 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 org.apache.cloudstack.utils.graphite; + +public class GraphiteException extends RuntimeException { + + public GraphiteException(String message) { + super(message); + } + + public GraphiteException(String message, Throwable cause) { + super(message, cause); + } +} \ No newline at end of file http://git-wip-us.apache.org/repos/asf/cloudstack/blob/83fd8f60/utils/src/main/java/org/apache/cloudstack/utils/identity/ManagementServerNode.java ---------------------------------------------------------------------- diff --git a/utils/src/main/java/org/apache/cloudstack/utils/identity/ManagementServerNode.java b/utils/src/main/java/org/apache/cloudstack/utils/identity/ManagementServerNode.java new file mode 100644 index 0000000..fdd80f7 --- /dev/null +++ b/utils/src/main/java/org/apache/cloudstack/utils/identity/ManagementServerNode.java @@ -0,0 +1,63 @@ +// +// 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 org.apache.cloudstack.utils.identity; + +import javax.ejb.Local; + +import org.apache.log4j.Logger; + +import com.cloud.utils.component.AdapterBase; +import com.cloud.utils.component.ComponentLifecycle; +import com.cloud.utils.component.SystemIntegrityChecker; +import com.cloud.utils.exception.CloudRuntimeException; +import com.cloud.utils.net.MacAddress; + +@Local(value = {SystemIntegrityChecker.class}) +public class ManagementServerNode extends AdapterBase implements SystemIntegrityChecker { + private static final Logger s_logger = Logger.getLogger(ManagementServerNode.class); + + private static final long s_nodeId = MacAddress.getMacAddress().toLong(); + + public ManagementServerNode() { + setRunLevel(ComponentLifecycle.RUN_LEVEL_FRAMEWORK_BOOTSTRAP); + } + + @Override + public void check() { + if (s_nodeId <= 0) { + throw new CloudRuntimeException("Unable to get the management server node id"); + } + } + + public static long getManagementServerId() { + return s_nodeId; + } + + @Override + public boolean start() { + try { + check(); + } catch (Exception e) { + s_logger.error("System integrity check exception", e); + System.exit(1); + } + return true; + } +} http://git-wip-us.apache.org/repos/asf/cloudstack/blob/83fd8f60/utils/src/main/java/org/apache/cloudstack/utils/imagestore/ImageStoreUtil.java ---------------------------------------------------------------------- diff --git a/utils/src/main/java/org/apache/cloudstack/utils/imagestore/ImageStoreUtil.java b/utils/src/main/java/org/apache/cloudstack/utils/imagestore/ImageStoreUtil.java new file mode 100644 index 0000000..ed13360 --- /dev/null +++ b/utils/src/main/java/org/apache/cloudstack/utils/imagestore/ImageStoreUtil.java @@ -0,0 +1,110 @@ +/* + * 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 org.apache.cloudstack.utils.imagestore; + +import com.cloud.utils.script.Script; +import org.apache.commons.lang.StringUtils; +import org.apache.log4j.Logger; + +public class ImageStoreUtil { + public static final Logger s_logger = Logger.getLogger(ImageStoreUtil.class.getName()); + + public static String generatePostUploadUrl(String ssvmUrlDomain, String ipAddress, String uuid) { + String hostname = ipAddress; + + //if ssvm url domain is present, use it to construct hostname in the format 1-2-3-4.domain + // if the domain name is not present, ssl validation fails and has to be ignored + if(StringUtils.isNotBlank(ssvmUrlDomain)) { + hostname = ipAddress.replace(".", "-"); + hostname = hostname + ssvmUrlDomain.substring(1); + } + + //only https works with postupload and url format is fixed + return "https://" + hostname + "/upload/" + uuid; + } + + // given a path, returns empty if path is supported image, and the file type if unsupported + // this is meant to catch things like accidental upload of ASCII text .vmdk descriptor + public static String checkTemplateFormat(String path, String uripath) { + // note 'path' was generated by us so it should be safe on the cmdline, be wary of 'url' + String command = "file "; + if (isCompressedExtension(uripath)) { + command = "file -z "; + } + String output = Script.runSimpleBashScript(command + path + " | cut -d: -f2", 60000); + + // vmdk + if ((output.contains("VMware") || output.contains("data")) && isCorrectExtension(uripath, "vmdk")) { + s_logger.debug("File at path " + path + " looks like a vmware image :" + output); + return ""; + } + // raw + if ((output.contains("x86 boot") || output.contains("data")) && (isCorrectExtension(uripath, "raw") || isCorrectExtension(uripath, "img"))) { + s_logger.debug("File at path " + path + " looks like a raw image :" + output); + return ""; + } + // qcow2 + if (output.contains("QEMU QCOW") && isCorrectExtension(uripath, "qcow2")) { + s_logger.debug("File at path " + path + " looks like QCOW2 : " + output); + return ""; + } + // vhd + if (output.contains("Microsoft Disk Image") && (isCorrectExtension(uripath, "vhd") || isCorrectExtension(uripath, "vhdx"))) { + s_logger.debug("File at path " + path + " looks like vhd : " + output); + return ""; + } + // ova + if (output.contains("POSIX tar") && isCorrectExtension(uripath, "ova")) { + s_logger.debug("File at path " + path + " looks like ova : " + output); + return ""; + } + + //lxc + if (output.contains("POSIX tar") && isCorrectExtension(uripath, "tar")) { + s_logger.debug("File at path " + path + " looks like just tar : " + output); + return ""; + } + + if (output.contains("ISO 9660") && isCorrectExtension(uripath, "iso")) { + s_logger.debug("File at path " + path + " looks like an iso : " + output); + return ""; + } + return output; + } + + private static boolean isCorrectExtension(String path, String ext) { + if (path.toLowerCase().endsWith(ext) + || path.toLowerCase().endsWith(ext + ".gz") + || path.toLowerCase().endsWith(ext + ".bz2") + || path.toLowerCase().endsWith(ext + ".zip")) { + return true; + } + return false; + } + + private static boolean isCompressedExtension(String path) { + if (path.toLowerCase().endsWith(".gz") + || path.toLowerCase().endsWith(".bz2") + || path.toLowerCase().endsWith(".zip")) { + return true; + } + return false; + } +} + http://git-wip-us.apache.org/repos/asf/cloudstack/blob/83fd8f60/utils/src/main/java/org/apache/cloudstack/utils/security/SSLUtils.java ---------------------------------------------------------------------- diff --git a/utils/src/main/java/org/apache/cloudstack/utils/security/SSLUtils.java b/utils/src/main/java/org/apache/cloudstack/utils/security/SSLUtils.java new file mode 100644 index 0000000..c1fc2d6 --- /dev/null +++ b/utils/src/main/java/org/apache/cloudstack/utils/security/SSLUtils.java @@ -0,0 +1,58 @@ +// +// 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 org.apache.cloudstack.utils.security; + +import org.apache.log4j.Logger; + +import javax.net.ssl.SSLContext; +import java.security.NoSuchAlgorithmException; +import java.security.NoSuchProviderException; +import java.util.Arrays; +import java.util.HashSet; +import java.util.Set; + +public class SSLUtils { + public static final Logger s_logger = Logger.getLogger(SSLUtils.class); + + public static String[] getSupportedProtocols(String[] protocols) { + Set<String> set = new HashSet<String>(); + for (String s : protocols) { + if (s.equals("SSLv3") || s.equals("SSLv2Hello")) { + continue; + } + set.add(s); + } + return (String[]) set.toArray(new String[set.size()]); + } + + public static String[] getSupportedCiphers() throws NoSuchAlgorithmException { + String[] availableCiphers = getSSLContext().getSocketFactory().getSupportedCipherSuites(); + Arrays.sort(availableCiphers); + return availableCiphers; + } + + public static SSLContext getSSLContext() throws NoSuchAlgorithmException { + return SSLContext.getInstance("TLSv1"); + } + + public static SSLContext getSSLContext(String provider) throws NoSuchAlgorithmException, NoSuchProviderException { + return SSLContext.getInstance("TLSv1", provider); + } +} http://git-wip-us.apache.org/repos/asf/cloudstack/blob/83fd8f60/utils/src/main/java/org/apache/cloudstack/utils/security/SecureSSLSocketFactory.java ---------------------------------------------------------------------- diff --git a/utils/src/main/java/org/apache/cloudstack/utils/security/SecureSSLSocketFactory.java b/utils/src/main/java/org/apache/cloudstack/utils/security/SecureSSLSocketFactory.java new file mode 100644 index 0000000..fa9d492 --- /dev/null +++ b/utils/src/main/java/org/apache/cloudstack/utils/security/SecureSSLSocketFactory.java @@ -0,0 +1,124 @@ +// +// 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 org.apache.cloudstack.utils.security; + +import org.apache.log4j.Logger; + +import javax.net.ssl.KeyManager; +import javax.net.ssl.SSLContext; +import javax.net.ssl.SSLSocket; +import javax.net.ssl.SSLSocketFactory; +import javax.net.ssl.TrustManager; +import java.io.IOException; +import java.net.InetAddress; +import java.net.Socket; +import java.net.UnknownHostException; +import java.security.KeyManagementException; +import java.security.NoSuchAlgorithmException; +import java.security.SecureRandom; + +public class SecureSSLSocketFactory extends SSLSocketFactory { + + public static final Logger s_logger = Logger.getLogger(SecureSSLSocketFactory.class); + private SSLContext _sslContext; + + public SecureSSLSocketFactory() throws NoSuchAlgorithmException { + _sslContext = SSLUtils.getSSLContext(); + } + + public SecureSSLSocketFactory(SSLContext sslContext) throws NoSuchAlgorithmException { + if (sslContext != null) { + _sslContext = sslContext; + } else { + _sslContext = SSLUtils.getSSLContext(); + } + } + + public SecureSSLSocketFactory(KeyManager[] km, TrustManager[] tm, SecureRandom random) throws NoSuchAlgorithmException, KeyManagementException, IOException { + _sslContext = SSLUtils.getSSLContext(); + _sslContext.init(km, tm, random); + } + + @Override + public String[] getDefaultCipherSuites() { + return getSupportedCipherSuites(); + } + + @Override + public String[] getSupportedCipherSuites() { + String[] ciphers = null; + try { + ciphers = SSLUtils.getSupportedCiphers(); + } catch (NoSuchAlgorithmException e) { + s_logger.error("SecureSSLSocketFactory::getDefaultCipherSuites found no cipher suites"); + } + return ciphers; + } + + @Override + public Socket createSocket(Socket s, String host, int port, boolean autoClose) throws IOException { + SSLSocketFactory factory = _sslContext.getSocketFactory(); + Socket socket = factory.createSocket(s, host, port, autoClose); + if (socket instanceof SSLSocket) { + ((SSLSocket)socket).setEnabledProtocols(SSLUtils.getSupportedProtocols(((SSLSocket)socket).getEnabledProtocols())); + } + return socket; + } + + @Override + public Socket createSocket(String host, int port) throws IOException, UnknownHostException { + SSLSocketFactory factory = _sslContext.getSocketFactory(); + Socket socket = factory.createSocket(host, port); + if (socket instanceof SSLSocket) { + ((SSLSocket)socket).setEnabledProtocols(SSLUtils.getSupportedProtocols(((SSLSocket)socket).getEnabledProtocols())); + } + return socket; + } + + @Override + public Socket createSocket(String host, int port, InetAddress inetAddress, int localPort) throws IOException, UnknownHostException { + SSLSocketFactory factory = _sslContext.getSocketFactory(); + Socket socket = factory.createSocket(host, port, inetAddress, localPort); + if (socket instanceof SSLSocket) { + ((SSLSocket)socket).setEnabledProtocols(SSLUtils.getSupportedProtocols(((SSLSocket)socket).getEnabledProtocols())); + } + return socket; + } + + @Override + public Socket createSocket(InetAddress inetAddress, int localPort) throws IOException { + SSLSocketFactory factory = _sslContext.getSocketFactory(); + Socket socket = factory.createSocket(inetAddress, localPort); + if (socket instanceof SSLSocket) { + ((SSLSocket)socket).setEnabledProtocols(SSLUtils.getSupportedProtocols(((SSLSocket)socket).getEnabledProtocols())); + } + return socket; + } + + @Override + public Socket createSocket(InetAddress address, int port, InetAddress localAddress, int localPort) throws IOException { + SSLSocketFactory factory = this._sslContext.getSocketFactory(); + Socket socket = factory.createSocket(address, port, localAddress, localPort); + if (socket instanceof SSLSocket) { + ((SSLSocket)socket).setEnabledProtocols(SSLUtils.getSupportedProtocols(((SSLSocket)socket).getEnabledProtocols())); + } + return socket; + } +} \ No newline at end of file http://git-wip-us.apache.org/repos/asf/cloudstack/blob/83fd8f60/utils/src/main/java/org/apache/cloudstack/utils/usage/UsageUtils.java ---------------------------------------------------------------------- diff --git a/utils/src/main/java/org/apache/cloudstack/utils/usage/UsageUtils.java b/utils/src/main/java/org/apache/cloudstack/utils/usage/UsageUtils.java new file mode 100644 index 0000000..a97aed1 --- /dev/null +++ b/utils/src/main/java/org/apache/cloudstack/utils/usage/UsageUtils.java @@ -0,0 +1,24 @@ +// +// 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 org.apache.cloudstack.utils.usage; + +public class UsageUtils { + public static final int USAGE_AGGREGATION_RANGE_MIN = 1; +}