From 7a341942373b777cbea22dd4981444cda67478fb Mon Sep 17 00:00:00 2001 From: Wei Zhou Date: Wed, 8 May 2024 13:25:47 +0200 Subject: [PATCH 001/350] cleanup: remove test/src-not-used/ (#9007) --- .../cloud/sample/UserCloudAPIExecutor.java | 188 -- .../cloud/test/longrun/BuildGuestNetwork.java | 123 - .../com/cloud/test/longrun/GuestNetwork.java | 107 - .../test/longrun/PerformanceWithAPI.java | 190 -- .../java/com/cloud/test/longrun/User.java | 202 -- .../cloud/test/longrun/VirtualMachine.java | 95 - .../com/cloud/test/regression/ApiCommand.java | 848 ------ .../com/cloud/test/regression/ConfigTest.java | 125 - .../test/regression/DelegatedAdminTest.java | 129 - .../com/cloud/test/regression/Deploy.java | 109 - .../cloud/test/regression/EventsApiTest.java | 176 -- .../java/com/cloud/test/regression/HA.java | 80 - .../test/regression/LoadBalancingTest.java | 142 - .../test/regression/PortForwardingTest.java | 143 - .../com/cloud/test/regression/SanityTest.java | 86 - .../java/com/cloud/test/regression/Test.java | 88 - .../com/cloud/test/regression/TestCase.java | 138 - .../cloud/test/regression/TestCaseEngine.java | 275 -- .../com/cloud/test/regression/VMApiTest.java | 91 - .../java/com/cloud/test/stress/SshTest.java | 90 - .../test/stress/StressTestDirectAttach.java | 1353 ---------- .../cloud/test/stress/TestClientWithAPI.java | 2289 ----------------- .../java/com/cloud/test/stress/WgetTest.java | 150 -- .../test/ui/AbstractSeleniumTestCase.java | 55 - .../com/cloud/test/ui/AddAndDeleteAISO.java | 127 - .../cloud/test/ui/AddAndDeleteATemplate.java | 126 - .../com/cloud/test/ui/UIScenarioTest.java | 86 - .../com/cloud/test/utils/ConsoleProxy.java | 110 - .../com/cloud/test/utils/IpSqlGenerator.java | 89 - .../com/cloud/test/utils/ProxyLoadTemp.java | 112 - .../java/com/cloud/test/utils/SignEC2.java | 143 - .../com/cloud/test/utils/SignRequest.java | 112 - .../cloud/test/utils/SqlDataGenerator.java | 49 - .../java/com/cloud/test/utils/SubmitCert.java | 198 -- .../java/com/cloud/test/utils/TestClient.java | 385 --- .../com/cloud/test/utils/UtilsForTest.java | 210 -- 36 files changed, 9019 deletions(-) delete mode 100644 test/src-not-used/main/java/com/cloud/sample/UserCloudAPIExecutor.java delete mode 100644 test/src-not-used/main/java/com/cloud/test/longrun/BuildGuestNetwork.java delete mode 100644 test/src-not-used/main/java/com/cloud/test/longrun/GuestNetwork.java delete mode 100644 test/src-not-used/main/java/com/cloud/test/longrun/PerformanceWithAPI.java delete mode 100644 test/src-not-used/main/java/com/cloud/test/longrun/User.java delete mode 100644 test/src-not-used/main/java/com/cloud/test/longrun/VirtualMachine.java delete mode 100644 test/src-not-used/main/java/com/cloud/test/regression/ApiCommand.java delete mode 100644 test/src-not-used/main/java/com/cloud/test/regression/ConfigTest.java delete mode 100644 test/src-not-used/main/java/com/cloud/test/regression/DelegatedAdminTest.java delete mode 100644 test/src-not-used/main/java/com/cloud/test/regression/Deploy.java delete mode 100644 test/src-not-used/main/java/com/cloud/test/regression/EventsApiTest.java delete mode 100644 test/src-not-used/main/java/com/cloud/test/regression/HA.java delete mode 100644 test/src-not-used/main/java/com/cloud/test/regression/LoadBalancingTest.java delete mode 100644 test/src-not-used/main/java/com/cloud/test/regression/PortForwardingTest.java delete mode 100644 test/src-not-used/main/java/com/cloud/test/regression/SanityTest.java delete mode 100644 test/src-not-used/main/java/com/cloud/test/regression/Test.java delete mode 100644 test/src-not-used/main/java/com/cloud/test/regression/TestCase.java delete mode 100644 test/src-not-used/main/java/com/cloud/test/regression/TestCaseEngine.java delete mode 100644 test/src-not-used/main/java/com/cloud/test/regression/VMApiTest.java delete mode 100644 test/src-not-used/main/java/com/cloud/test/stress/SshTest.java delete mode 100644 test/src-not-used/main/java/com/cloud/test/stress/StressTestDirectAttach.java delete mode 100644 test/src-not-used/main/java/com/cloud/test/stress/TestClientWithAPI.java delete mode 100644 test/src-not-used/main/java/com/cloud/test/stress/WgetTest.java delete mode 100644 test/src-not-used/main/java/com/cloud/test/ui/AbstractSeleniumTestCase.java delete mode 100644 test/src-not-used/main/java/com/cloud/test/ui/AddAndDeleteAISO.java delete mode 100644 test/src-not-used/main/java/com/cloud/test/ui/AddAndDeleteATemplate.java delete mode 100644 test/src-not-used/main/java/com/cloud/test/ui/UIScenarioTest.java delete mode 100644 test/src-not-used/main/java/com/cloud/test/utils/ConsoleProxy.java delete mode 100644 test/src-not-used/main/java/com/cloud/test/utils/IpSqlGenerator.java delete mode 100644 test/src-not-used/main/java/com/cloud/test/utils/ProxyLoadTemp.java delete mode 100644 test/src-not-used/main/java/com/cloud/test/utils/SignEC2.java delete mode 100644 test/src-not-used/main/java/com/cloud/test/utils/SignRequest.java delete mode 100644 test/src-not-used/main/java/com/cloud/test/utils/SqlDataGenerator.java delete mode 100644 test/src-not-used/main/java/com/cloud/test/utils/SubmitCert.java delete mode 100644 test/src-not-used/main/java/com/cloud/test/utils/TestClient.java delete mode 100644 test/src-not-used/main/java/com/cloud/test/utils/UtilsForTest.java diff --git a/test/src-not-used/main/java/com/cloud/sample/UserCloudAPIExecutor.java b/test/src-not-used/main/java/com/cloud/sample/UserCloudAPIExecutor.java deleted file mode 100644 index 6baadb8b035..00000000000 --- a/test/src-not-used/main/java/com/cloud/sample/UserCloudAPIExecutor.java +++ /dev/null @@ -1,188 +0,0 @@ -// 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.sample; - -import java.io.FileInputStream; -import java.net.URLEncoder; -import java.util.ArrayList; -import java.util.Collections; -import java.util.List; -import java.util.Properties; -import java.util.StringTokenizer; - -import javax.crypto.Mac; -import javax.crypto.spec.SecretKeySpec; - -import org.apache.commons.codec.binary.Base64; -import org.apache.commons.httpclient.HttpClient; -import org.apache.commons.httpclient.HttpMethod; -import org.apache.commons.httpclient.methods.GetMethod; - -/** - * - * - * - * - * - * - * - * - * - */ - -/** - * Sample CloudStack Management User API Executor. - * - * Prerequisites: - Edit usercloud.properties to include your host, apiUrl, apiKey, and secretKey - Use ./executeUserAPI.sh to - * execute this test class - * - * - */ -public class UserCloudAPIExecutor { - public static void main(String[] args) { - // Host - String host = null; - - // Fully qualified URL with http(s)://host:port - String apiUrl = null; - - // ApiKey and secretKey as given by your CloudStack vendor - String apiKey = null; - String secretKey = null; - - try { - Properties prop = new Properties(); - prop.load(new FileInputStream("usercloud.properties")); - - // host - host = prop.getProperty("host"); - if (host == null) { - System.out.println("Please specify a valid host in the format of http(s)://:/client/api in your usercloud.properties file."); - } - - // apiUrl - apiUrl = prop.getProperty("apiUrl"); - if (apiUrl == null) { - System.out.println("Please specify a valid API URL in the format of command=¶m1=¶m2=... in your usercloud.properties file."); - } - - // apiKey - apiKey = prop.getProperty("apiKey"); - if (apiKey == null) { - System.out.println("Please specify your API Key as provided by your CloudStack vendor in your usercloud.properties file."); - } - - // secretKey - secretKey = prop.getProperty("secretKey"); - if (secretKey == null) { - System.out.println("Please specify your secret Key as provided by your CloudStack vendor in your usercloud.properties file."); - } - - if (apiUrl == null || apiKey == null || secretKey == null) { - return; - } - - System.out.println("Constructing API call to host = '" + host + "' with API command = '" + apiUrl + "' using apiKey = '" + apiKey + "' and secretKey = '" + - secretKey + "'"); - - // Step 1: Make sure your APIKey is URL encoded - String encodedApiKey = URLEncoder.encode(apiKey, "UTF-8"); - - // Step 2: URL encode each parameter value, then sort the parameters and apiKey in - // alphabetical order, and then toLowerCase all the parameters, parameter values and apiKey. - // Please note that if any parameters with a '&' as a value will cause this test client to fail since we are using - // '&' to delimit - // the string - List sortedParams = new ArrayList(); - sortedParams.add("apikey=" + encodedApiKey.toLowerCase()); - StringTokenizer st = new StringTokenizer(apiUrl, "&"); - String url = null; - boolean first = true; - while (st.hasMoreTokens()) { - String paramValue = st.nextToken(); - String param = paramValue.substring(0, paramValue.indexOf("=")); - String value = URLEncoder.encode(paramValue.substring(paramValue.indexOf("=") + 1, paramValue.length()), "UTF-8"); - if (first) { - url = param + "=" + value; - first = false; - } else { - url = url + "&" + param + "=" + value; - } - sortedParams.add(param.toLowerCase() + "=" + value.toLowerCase()); - } - Collections.sort(sortedParams); - - System.out.println("Sorted Parameters: " + sortedParams); - - // Step 3: Construct the sorted URL and sign and URL encode the sorted URL with your secret key - String sortedUrl = null; - first = true; - for (String param : sortedParams) { - if (first) { - sortedUrl = param; - first = false; - } else { - sortedUrl = sortedUrl + "&" + param; - } - } - System.out.println("sorted URL : " + sortedUrl); - String encodedSignature = signRequest(sortedUrl, secretKey); - - // Step 4: Construct the final URL we want to send to the CloudStack Management Server - // Final result should look like: - // http(s)://://client/api?&apiKey=&signature= - String finalUrl = host + "?" + url + "&apiKey=" + apiKey + "&signature=" + encodedSignature; - System.out.println("final URL : " + finalUrl); - - // Step 5: Perform a HTTP GET on this URL to execute the command - HttpClient client = new HttpClient(); - HttpMethod method = new GetMethod(finalUrl); - int responseCode = client.executeMethod(method); - if (responseCode == 200) { - // SUCCESS! - System.out.println("Successfully executed command"); - } else { - // FAILED! - System.out.println("Unable to execute command with response code: " + responseCode); - } - - } catch (Throwable t) { - System.out.println(t); - } - } - - /** - * 1. Signs a string with a secret key using SHA-1 2. Base64 encode the result 3. URL encode the final result - * - * @param request - * @param key - * @return - */ - public static String signRequest(String request, String key) { - try { - Mac mac = Mac.getInstance("HmacSHA1"); - SecretKeySpec keySpec = new SecretKeySpec(key.getBytes(), "HmacSHA1"); - mac.init(keySpec); - mac.update(request.getBytes()); - byte[] encryptedBytes = mac.doFinal(); - return URLEncoder.encode(Base64.encodeBase64String(encryptedBytes), "UTF-8"); - } catch (Exception ex) { - System.out.println(ex); - } - return null; - } -} diff --git a/test/src-not-used/main/java/com/cloud/test/longrun/BuildGuestNetwork.java b/test/src-not-used/main/java/com/cloud/test/longrun/BuildGuestNetwork.java deleted file mode 100644 index 3a823ab9733..00000000000 --- a/test/src-not-used/main/java/com/cloud/test/longrun/BuildGuestNetwork.java +++ /dev/null @@ -1,123 +0,0 @@ -// 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.test.longrun; - -import java.util.Arrays; -import java.util.Iterator; -import java.util.List; -import java.util.Random; - -import org.apache.log4j.Logger; - -public class BuildGuestNetwork { - - public static final Logger s_logger = Logger.getLogger(BuildGuestNetwork.class.getClass()); - private static final int ApiPort = 8096; - private static final int DeveloperPort = 8080; - private static final String ApiUrl = "/client/api"; - private static int numVM = 1; - private static long zoneId = -1L; - private static long templateId = 3; - private static long serviceOfferingId = 1; - - public static void main(String[] args) { - - List argsList = Arrays.asList(args); - Iterator iter = argsList.iterator(); - String host = "http://localhost"; - int numThreads = 1; - - while (iter.hasNext()) { - String arg = iter.next(); - if (arg.equals("-h")) { - host = "http://" + iter.next(); - } - if (arg.equals("-t")) { - numThreads = Integer.parseInt(iter.next()); - } - if (arg.equals("-n")) { - numVM = Integer.parseInt(iter.next()); - } - if (arg.equals("-z")) { - zoneId = Integer.parseInt(iter.next()); - } - - if (arg.equals("-e")) { - templateId = Integer.parseInt(iter.next()); - } - - if (arg.equals("-s")) { - serviceOfferingId = Integer.parseInt(iter.next()); - } - } - - final String server = host + ":" + ApiPort + "/"; - final String developerServer = host + ":" + DeveloperPort + ApiUrl; - s_logger.info("Starting test in " + numThreads + " thread(s). Each thread is launching " + numVM + " VMs"); - - for (int i = 0; i < numThreads; i++) { - new Thread(new Runnable() { - @Override - public void run() { - try { - - String username = null; - String singlePrivateIp = null; - Random ran = new Random(); - username = Math.abs(ran.nextInt()) + "-user"; - - //Create User - User myUser = new User(username, username, server, developerServer); - try { - myUser.launchUser(); - myUser.registerUser(); - } catch (Exception e) { - s_logger.warn("Error code: ", e); - } - - if (myUser.getUserId() != null) { - s_logger.info("User " + myUser.getUserName() + " was created successfully, starting VM creation"); - //create VMs for the user - for (int i = 0; i < numVM; i++) { - //Create a new VM, add it to the list of user's VMs - VirtualMachine myVM = new VirtualMachine(myUser.getUserId()); - myVM.deployVM(zoneId, serviceOfferingId, templateId, myUser.getDeveloperServer(), myUser.getApiKey(), myUser.getSecretKey()); - myUser.getVirtualMachines().add(myVM); - singlePrivateIp = myVM.getPrivateIp(); - - if (singlePrivateIp != null) { - s_logger.info("VM with private Ip " + singlePrivateIp + " was successfully created"); - } else { - s_logger.info("Problems with VM creation for a user" + myUser.getUserName()); - s_logger.info("Deployment failed"); - break; - } - } - - s_logger.info("Deployment done..." + numVM + " VMs were created."); - } - - } catch (Exception e) { - s_logger.error(e); - } - } - }).start(); - - } - } - -} diff --git a/test/src-not-used/main/java/com/cloud/test/longrun/GuestNetwork.java b/test/src-not-used/main/java/com/cloud/test/longrun/GuestNetwork.java deleted file mode 100644 index 226e31ae4cc..00000000000 --- a/test/src-not-used/main/java/com/cloud/test/longrun/GuestNetwork.java +++ /dev/null @@ -1,107 +0,0 @@ -// 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.test.longrun; - -import java.util.ArrayList; -import java.util.Random; - -import org.apache.log4j.Logger; -import org.apache.log4j.NDC; - -import com.trilead.ssh2.Connection; -import com.trilead.ssh2.Session; - -public class GuestNetwork implements Runnable { - public static final Logger s_logger = Logger.getLogger(GuestNetwork.class.getClass()); - - private String publicIp; - private ArrayList virtualMachines; - private int retryNum; - - public GuestNetwork(String publicIp, int retryNum) { - this.publicIp = publicIp; - this.retryNum = retryNum; - } - - public ArrayList getVirtualMachines() { - return virtualMachines; - } - - public void setVirtualMachines(ArrayList virtualMachines) { - this.virtualMachines = virtualMachines; - } - - @Override - public void run() { - NDC.push("Following thread has started" + Thread.currentThread().getName()); - int retry = 0; - - //Start copying files between machines in the network - s_logger.info("The size of the array is " + this.virtualMachines.size()); - while (true) { - try { - if (retry > 0) { - s_logger.info("Retry attempt : " + retry + " ...sleeping 120 seconds before next attempt"); - Thread.sleep(120000); - } - for (VirtualMachine vm : this.virtualMachines) { - - s_logger.info("Attempting to SSH into linux host " + this.publicIp + " with retry attempt: " + retry); - Connection conn = new Connection(this.publicIp); - conn.connect(null, 600000, 600000); - - s_logger.info("SSHed successfully into linux host " + this.publicIp); - - boolean isAuthenticated = conn.authenticateWithPassword("root", "password"); - - if (isAuthenticated == false) { - s_logger.info("Authentication failed"); - } - //execute copy command - Session sess = conn.openSession(); - String fileName; - Random ran = new Random(); - fileName = Math.abs(ran.nextInt()) + "-file"; - String copyCommand = new String("./scpScript " + vm.getPrivateIp() + " " + fileName); - s_logger.info("Executing " + copyCommand); - sess.execCommand(copyCommand); - Thread.sleep(120000); - sess.close(); - - //execute wget command - sess = conn.openSession(); - String downloadCommand = - new String("wget http://172.16.0.220/scripts/checkDiskSpace.sh; chmod +x *sh; ./checkDiskSpace.sh; rm -rf checkDiskSpace.sh"); - s_logger.info("Executing " + downloadCommand); - sess.execCommand(downloadCommand); - Thread.sleep(120000); - sess.close(); - - //close the connection - conn.close(); - } - } catch (Exception ex) { - s_logger.error(ex); - retry++; - if (retry == retryNum) { - s_logger.info("Performance Guest Network test failed with error " + ex.getMessage()); - } - } - } - - } -} diff --git a/test/src-not-used/main/java/com/cloud/test/longrun/PerformanceWithAPI.java b/test/src-not-used/main/java/com/cloud/test/longrun/PerformanceWithAPI.java deleted file mode 100644 index f1a3725d01f..00000000000 --- a/test/src-not-used/main/java/com/cloud/test/longrun/PerformanceWithAPI.java +++ /dev/null @@ -1,190 +0,0 @@ -// 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.test.longrun; - -import java.io.IOException; -import java.io.InputStream; -import java.net.URLEncoder; -import java.util.Arrays; -import java.util.Iterator; -import java.util.List; -import java.util.Map; -import java.util.Random; - -import org.apache.commons.httpclient.HttpClient; -import org.apache.commons.httpclient.HttpMethod; -import org.apache.commons.httpclient.methods.GetMethod; -import org.apache.log4j.Logger; - -import com.cloud.test.stress.TestClientWithAPI; - -public class PerformanceWithAPI { - - public static final Logger s_logger = Logger.getLogger(PerformanceWithAPI.class.getClass()); - private static final int Retry = 10; - private static final int ApiPort = 8096; - private static int s_numVM = 2; - private static final long ZoneId = -1L; - private static final long TemplateId = 3; - private static final long ServiceOfferingId = 1; - private static final String ApiUrl = "/client/api"; - private static final int DeveloperPort = 8080; - - public static void main(String[] args) { - - List argsList = Arrays.asList(args); - Iterator iter = argsList.iterator(); - String host = "http://localhost"; - int numThreads = 1; - - while (iter.hasNext()) { - String arg = iter.next(); - if (arg.equals("-h")) { - host = "http://" + iter.next(); - } - if (arg.equals("-t")) { - numThreads = Integer.parseInt(iter.next()); - } - if (arg.equals("-n")) { - s_numVM = Integer.parseInt(iter.next()); - } - } - - final String server = host + ":" + ApiPort + "/"; - final String developerServer = host + ":" + DeveloperPort + ApiUrl; - - s_logger.info("Starting test in " + numThreads + " thread(s). Each thread is launching " + s_numVM + " VMs"); - - for (int i = 0; i < numThreads; i++) { - new Thread(new Runnable() { - @Override - public void run() { - try { - - String username = null; - String singlePrivateIp = null; - String singlePublicIp = null; - Random ran = new Random(); - username = Math.abs(ran.nextInt()) + "-user"; - - //Create User - User myUser = new User(username, username, server, developerServer); - try { - myUser.launchUser(); - myUser.registerUser(); - } catch (Exception e) { - s_logger.warn("Error code: ", e); - } - - if (myUser.getUserId() != null) { - s_logger.info("User " + myUser.getUserName() + " was created successfully, starting VM creation"); - //create VMs for the user - for (int i = 0; i < s_numVM; i++) { - //Create a new VM, add it to the list of user's VMs - VirtualMachine myVM = new VirtualMachine(myUser.getUserId()); - myVM.deployVM(ZoneId, ServiceOfferingId, TemplateId, myUser.getDeveloperServer(), myUser.getApiKey(), myUser.getSecretKey()); - myUser.getVirtualMachines().add(myVM); - singlePrivateIp = myVM.getPrivateIp(); - - if (singlePrivateIp != null) { - s_logger.info("VM with private Ip " + singlePrivateIp + " was successfully created"); - } else { - s_logger.info("Problems with VM creation for a user" + myUser.getUserName()); - break; - } - - //get public IP address for the User - myUser.retrievePublicIp(ZoneId); - singlePublicIp = myUser.getPublicIp().get(myUser.getPublicIp().size() - 1); - if (singlePublicIp != null) { - s_logger.info("Successfully got public Ip " + singlePublicIp + " for user " + myUser.getUserName()); - } else { - s_logger.info("Problems with getting public Ip address for user" + myUser.getUserName()); - break; - } - - //create ForwardProxy rules for user's VMs - int responseCode = CreateForwardingRule(myUser, singlePrivateIp, singlePublicIp, "22", "22"); - if (responseCode == 500) - break; - } - - s_logger.info("Deployment successful..." + s_numVM + " VMs were created. Waiting for 5 min before performance test"); - Thread.sleep(300000L); // Wait - - //Start performance test for the user - s_logger.info("Starting performance test for Guest network that has " + myUser.getPublicIp().size() + " public IP addresses"); - for (int j = 0; j < myUser.getPublicIp().size(); j++) { - s_logger.info("Starting test for user which has " + myUser.getVirtualMachines().size() + " vms. Public IP for the user is " + - myUser.getPublicIp().get(j) + " , number of retries is " + Retry + " , private IP address of the machine is" + - myUser.getVirtualMachines().get(j).getPrivateIp()); - GuestNetwork myNetwork = new GuestNetwork(myUser.getPublicIp().get(j), Retry); - myNetwork.setVirtualMachines(myUser.getVirtualMachines()); - new Thread(myNetwork).start(); - } - - } - } catch (Exception e) { - s_logger.error(e); - } - } - }).start(); - - } - } - - private static int CreateForwardingRule(User myUser, String privateIp, String publicIp, String publicPort, String privatePort) throws IOException { - String encodedPrivateIp = URLEncoder.encode("" + privateIp, "UTF-8"); - String encodedPublicIp = URLEncoder.encode("" + publicIp, "UTF-8"); - String encodedPrivatePort = URLEncoder.encode("" + privatePort, "UTF-8"); - String encodedPublicPort = URLEncoder.encode("" + publicPort, "UTF-8"); - String encodedApiKey = URLEncoder.encode(myUser.getApiKey(), "UTF-8"); - int responseCode = 500; - - String requestToSign = - "apiKey=" + encodedApiKey + "&command=createOrUpdateIpForwardingRule&privateIp=" + encodedPrivateIp + "&privatePort=" + encodedPrivatePort + - "&protocol=tcp&publicIp=" + encodedPublicIp + "&publicPort=" + encodedPublicPort; - - requestToSign = requestToSign.toLowerCase(); - s_logger.info("Request to sign is " + requestToSign); - - String signature = TestClientWithAPI.signRequest(requestToSign, myUser.getSecretKey()); - String encodedSignature = URLEncoder.encode(signature, "UTF-8"); - - String url = - myUser.getDeveloperServer() + "?command=createOrUpdateIpForwardingRule" + "&publicIp=" + encodedPublicIp + "&publicPort=" + encodedPublicPort + - "&privateIp=" + encodedPrivateIp + "&privatePort=" + encodedPrivatePort + "&protocol=tcp&apiKey=" + encodedApiKey + "&signature=" + encodedSignature; - - s_logger.info("Trying to create IP forwarding rule: " + url); - HttpClient client = new HttpClient(); - HttpMethod method = new GetMethod(url); - responseCode = client.executeMethod(method); - s_logger.info("create ip forwarding rule response code: " + responseCode); - if (responseCode == 200) { - s_logger.info("The rule is created successfully"); - } else if (responseCode == 500) { - InputStream is = method.getResponseBodyAsStream(); - Map errorInfo = TestClientWithAPI.getSingleValueFromXML(is, new String[] {"errorCode", "description"}); - s_logger.error("create ip forwarding rule (linux) test failed with errorCode: " + errorInfo.get("errorCode") + " and description: " + - errorInfo.get("description")); - } else { - s_logger.error("internal error processing request: " + method.getStatusText()); - } - return responseCode; - } - -} diff --git a/test/src-not-used/main/java/com/cloud/test/longrun/User.java b/test/src-not-used/main/java/com/cloud/test/longrun/User.java deleted file mode 100644 index 06234c846ab..00000000000 --- a/test/src-not-used/main/java/com/cloud/test/longrun/User.java +++ /dev/null @@ -1,202 +0,0 @@ -// 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.test.longrun; - -import java.io.IOException; -import java.io.InputStream; -import java.net.URLEncoder; -import java.util.ArrayList; -import java.util.Map; - -import org.apache.commons.httpclient.HttpClient; -import org.apache.commons.httpclient.HttpException; -import org.apache.commons.httpclient.HttpMethod; -import org.apache.commons.httpclient.methods.GetMethod; -import org.apache.log4j.Logger; - -import com.cloud.test.stress.TestClientWithAPI; - -public class User { - public static final Logger s_logger = Logger.getLogger(User.class.getClass()); - - private ArrayList virtualMachines; - private ArrayList publicIp; - private String server; - private String developerServer; - private String userName; - private String userId; - private String apiKey; - private String secretKey; - private String password; - private String encryptedPassword; - - public User(String userName, String password, String server, String developerServer) { - this.server = server; - this.developerServer = developerServer; - this.userName = userName; - this.password = password; - this.virtualMachines = new ArrayList(); - this.publicIp = new ArrayList(); - } - - public ArrayList getVirtualMachines() { - return virtualMachines; - } - - public void setVirtualMachines(ArrayList virtualMachines) { - this.virtualMachines = virtualMachines; - } - - public String getUserId() { - return userId; - } - - public void setUserId(String userId) { - this.userId = userId; - } - - public ArrayList getPublicIp() { - return publicIp; - } - - public void setPublicIp(ArrayList publicIp) { - this.publicIp = publicIp; - } - - public String getServer() { - return server; - } - - public void setServer(String server) { - this.server = server; - } - - public String getUserName() { - return userName; - } - - public void setUserName(String userName) { - this.userName = userName; - } - - public String getApiKey() { - return apiKey; - } - - public void setApiKey(String apiKey) { - this.apiKey = apiKey; - } - - public String getPassword() { - return password; - } - - public void setPassword(String password) { - this.password = password; - } - - public String getSecretKey() { - return secretKey; - } - - public void setSecretKey(String secretKey) { - this.secretKey = secretKey; - } - - public String getDeveloperServer() { - return developerServer; - } - - public void setDeveloperServer(String developerServer) { - this.developerServer = developerServer; - } - - public void launchUser() throws IOException { - String encodedUsername = URLEncoder.encode(this.getUserName(), "UTF-8"); - this.encryptedPassword = TestClientWithAPI.createMD5Password(this.getPassword()); - String encodedPassword = URLEncoder.encode(this.encryptedPassword, "UTF-8"); - String url = - this.server + "?command=createUser&username=" + encodedUsername + "&password=" + encodedPassword + - "&firstname=Test&lastname=Test&email=alena@vmops.com&domainId=1"; - String userIdStr = null; - HttpClient client = new HttpClient(); - HttpMethod method = new GetMethod(url); - int responseCode = client.executeMethod(method); - - if (responseCode == 200) { - InputStream is = method.getResponseBodyAsStream(); - Map userIdValues = TestClientWithAPI.getSingleValueFromXML(is, new String[] {"id"}); - userIdStr = userIdValues.get("id"); - if ((userIdStr != null) && (Long.parseLong(userIdStr) != -1)) { - this.setUserId(userIdStr); - } - } - } - - public void retrievePublicIp(long zoneId) throws IOException { - - String encodedApiKey = URLEncoder.encode(this.apiKey, "UTF-8"); - String encodedZoneId = URLEncoder.encode("" + zoneId, "UTF-8"); - String requestToSign = "apiKey=" + encodedApiKey + "&command=associateIpAddress" + "&zoneId=" + encodedZoneId; - requestToSign = requestToSign.toLowerCase(); - String signature = TestClientWithAPI.signRequest(requestToSign, this.secretKey); - String encodedSignature = URLEncoder.encode(signature, "UTF-8"); - - String url = this.developerServer + "?command=associateIpAddress" + "&apiKey=" + encodedApiKey + "&zoneId=" + encodedZoneId + "&signature=" + encodedSignature; - - HttpClient client = new HttpClient(); - HttpMethod method = new GetMethod(url); - int responseCode = client.executeMethod(method); - if (responseCode == 200) { - InputStream is = method.getResponseBodyAsStream(); - Map values = TestClientWithAPI.getSingleValueFromXML(is, new String[] {"ipaddress"}); - this.getPublicIp().add(values.get("ipaddress")); - s_logger.info("Ip address is " + values.get("ipaddress")); - } else if (responseCode == 500) { - InputStream is = method.getResponseBodyAsStream(); - Map errorInfo = TestClientWithAPI.getSingleValueFromXML(is, new String[] {"errorcode", "description"}); - s_logger.error("associate ip test failed with errorCode: " + errorInfo.get("errorCode") + " and description: " + errorInfo.get("description")); - } else { - s_logger.error("internal error processing request: " + method.getStatusText()); - } - - } - - public void registerUser() throws HttpException, IOException { - - String encodedUsername = URLEncoder.encode(this.userName, "UTF-8"); - String encodedPassword = URLEncoder.encode(this.password, "UTF-8"); - String url = server + "?command=register&username=" + encodedUsername + "&domainid=1"; - s_logger.info("registering: " + this.userName + " with url " + url); - HttpClient client = new HttpClient(); - HttpMethod method = new GetMethod(url); - int responseCode = client.executeMethod(method); - if (responseCode == 200) { - InputStream is = method.getResponseBodyAsStream(); - Map requestKeyValues = TestClientWithAPI.getSingleValueFromXML(is, new String[] {"apikey", "secretkey"}); - this.setApiKey(requestKeyValues.get("apikey")); - this.setSecretKey(requestKeyValues.get("secretkey")); - } else if (responseCode == 500) { - InputStream is = method.getResponseBodyAsStream(); - Map errorInfo = TestClientWithAPI.getSingleValueFromXML(is, new String[] {"errorcode", "description"}); - s_logger.error("registration failed with errorCode: " + errorInfo.get("errorCode") + " and description: " + errorInfo.get("description")); - } else { - s_logger.error("internal error processing request: " + method.getStatusText()); - } - } - -} diff --git a/test/src-not-used/main/java/com/cloud/test/longrun/VirtualMachine.java b/test/src-not-used/main/java/com/cloud/test/longrun/VirtualMachine.java deleted file mode 100644 index eaed0a2bd12..00000000000 --- a/test/src-not-used/main/java/com/cloud/test/longrun/VirtualMachine.java +++ /dev/null @@ -1,95 +0,0 @@ -// 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.test.longrun; - -import java.io.IOException; -import java.io.InputStream; -import java.net.URLEncoder; -import java.util.Map; - -import org.apache.commons.httpclient.HttpClient; -import org.apache.commons.httpclient.HttpMethod; -import org.apache.commons.httpclient.methods.GetMethod; -import org.apache.log4j.Logger; - -import com.cloud.test.stress.TestClientWithAPI; - -public class VirtualMachine { - public static final Logger s_logger = Logger.getLogger(VirtualMachine.class.getClass()); - - private String privateIp; - private String userId; - - public VirtualMachine(String userId) { - this.userId = userId; - } - - public String getPrivateIp() { - return privateIp; - } - - public void setPrivateIp(String privateIp) { - this.privateIp = privateIp; - } - - public String getUserId() { - return userId; - } - - public void setUserId(String userId) { - this.userId = userId; - } - - public void deployVM(long zoneId, long serviceOfferingId, long templateId, String server, String apiKey, String secretKey) throws IOException { - - String encodedZoneId = URLEncoder.encode("" + zoneId, "UTF-8"); - String encodedServiceOfferingId = URLEncoder.encode("" + serviceOfferingId, "UTF-8"); - String encodedTemplateId = URLEncoder.encode("" + templateId, "UTF-8"); - String encodedApiKey = URLEncoder.encode(apiKey, "UTF-8"); - String requestToSign = - "apiKey=" + encodedApiKey + "&command=deployVirtualMachine&serviceOfferingId=" + encodedServiceOfferingId + "&templateId=" + encodedTemplateId + "&zoneId=" + - encodedZoneId; - - requestToSign = requestToSign.toLowerCase(); - String signature = TestClientWithAPI.signRequest(requestToSign, secretKey); - String encodedSignature = URLEncoder.encode(signature, "UTF-8"); - String url = - server + "?command=deployVirtualMachine" + "&zoneId=" + encodedZoneId + "&serviceOfferingId=" + encodedServiceOfferingId + "&templateId=" + - encodedTemplateId + "&apiKey=" + encodedApiKey + "&signature=" + encodedSignature; - - s_logger.info("Sending this request to deploy a VM: " + url); - HttpClient client = new HttpClient(); - HttpMethod method = new GetMethod(url); - int responseCode = client.executeMethod(method); - s_logger.info("deploy linux vm response code: " + responseCode); - if (responseCode == 200) { - InputStream is = method.getResponseBodyAsStream(); - Map values = TestClientWithAPI.getSingleValueFromXML(is, new String[] {"id", "ipaddress"}); - long linuxVMId = Long.parseLong(values.get("id")); - s_logger.info("got linux virtual machine id: " + linuxVMId); - this.setPrivateIp(values.get("ipaddress")); - - } else if (responseCode == 500) { - InputStream is = method.getResponseBodyAsStream(); - Map errorInfo = TestClientWithAPI.getSingleValueFromXML(is, new String[] {"errorcode", "description"}); - s_logger.error("deploy linux vm test failed with errorCode: " + errorInfo.get("errorCode") + " and description: " + errorInfo.get("description")); - } else { - s_logger.error("internal error processing request: " + method.getStatusText()); - } - } - -} diff --git a/test/src-not-used/main/java/com/cloud/test/regression/ApiCommand.java b/test/src-not-used/main/java/com/cloud/test/regression/ApiCommand.java deleted file mode 100644 index 9c9fc83bce9..00000000000 --- a/test/src-not-used/main/java/com/cloud/test/regression/ApiCommand.java +++ /dev/null @@ -1,848 +0,0 @@ -// 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.test.regression; - -import java.io.File; -import java.io.FileInputStream; -import java.io.InputStream; -import java.net.URLEncoder; -import java.sql.Connection; -import java.sql.ResultSet; -import java.sql.Statement; -import java.util.ArrayList; -import java.util.Enumeration; -import java.util.HashMap; -import java.util.Iterator; -import java.util.Map; -import java.util.Properties; -import java.util.Random; -import java.util.Set; -import java.util.TreeMap; - -import javax.xml.parsers.DocumentBuilder; -import javax.xml.parsers.DocumentBuilderFactory; - -import org.apache.commons.httpclient.HttpClient; -import org.apache.commons.httpclient.HttpMethod; -import org.apache.commons.httpclient.methods.GetMethod; -import org.apache.log4j.Logger; -import org.w3c.dom.Document; -import org.w3c.dom.Element; -import org.w3c.dom.Node; -import org.w3c.dom.NodeList; - -import com.cloud.test.utils.UtilsForTest; - -public class ApiCommand { - public static final Logger s_logger = Logger.getLogger(ApiCommand.class.getName()); - - public static enum CommandType { - HTTP, MYSQL, SCRIPT; - } - - public static enum ResponseType { - ERROR, EMPTY; - } - - private Element xmlCommand; - private String commandName; - private String testCaseInfo; - private boolean isUserCommand; - private boolean isAsync = false; - private CommandType commandType; - private ResponseType responseType; - - private TreeMap urlParam; - private HashMap verifyParam = new HashMap();; - private HashMap setParam = new HashMap();; - private int responseCode; - private Element responseBody; - - private String command; - private String host; - private boolean list; - private Element listName; - private Element listId; - private boolean required = false; - private ResultSet result; - - public ApiCommand(Element fstElmnt, HashMap param, HashMap commands) { - this.setXmlCommand(fstElmnt); - this.setCommandName(); - this.setResponseType(); - this.setUserCommand(); - this.setCommandType(); - this.setTestCaseInfo(); - this.setUrlParam(param); - this.setVerifyParam(param); - this.setHost("http://" + param.get("hostip")); - this.setCommand(param); - String async = commands.get(this.getName()); - if (async != null && async.equals("yes")) { - this.isAsync = true; - - } - } - - public Element getXmlCommand() { - return xmlCommand; - } - - public void setXmlCommand(Element xmlCommand) { - this.xmlCommand = xmlCommand; - } - - // ================FOLLOWING METHODS USE INPUT XML FILE=======================// - public void setCommandName() { - NodeList commandName = this.xmlCommand.getElementsByTagName("name"); - Element commandElmnt = (Element)commandName.item(0); - NodeList commandNm = commandElmnt.getChildNodes(); - this.commandName = (commandNm.item(0).getNodeValue()); - } - - public String getName() { - return commandName; - } - - public void setTestCaseInfo() { - this.testCaseInfo = getElementByName("testcase"); - } - - public String getHost() { - return host; - } - - public void setHost(String host) { - this.host = host; - } - - public void setResponseType() { - boolean result = verifyTagValue("error", "true"); - if (result) { - this.responseType = ResponseType.ERROR; - return; - } - result = verifyTagValue("empty", "true"); - if (result) { - this.responseType = ResponseType.EMPTY; - } - } - - public void setResponseType(ResponseType responseType) { - this.responseType = responseType; - } - - public ResponseType getResponseType() { - return responseType; - } - - public void setUserCommand() { - boolean result = verifyTagValue("usercommand", "true"); - this.isUserCommand = result; - } - - public void setCommandType() { - boolean result = verifyTagValue("mysql", "true"); - if (result) { - this.commandType = CommandType.MYSQL; - return; - } - result = verifyTagValue("script", "true"); - if (result) { - this.commandType = CommandType.SCRIPT; - return; - } - this.commandType = CommandType.HTTP; - } - - public CommandType getCommandType() { - return commandType; - } - - public String getTestCaseInfo() { - return testCaseInfo; - } - - public Boolean getRequired() { - return required; - } - - public void setUrlParam(HashMap param) { - this.urlParam = new TreeMap(); - NodeList parameterLst = this.xmlCommand.getElementsByTagName("parameters"); - if (parameterLst != null) { - for (int j = 0; j < parameterLst.getLength(); j++) { - Element parameterElement = (Element)parameterLst.item(j); - NodeList itemLst = parameterElement.getElementsByTagName("item"); - for (int k = 0; k < itemLst.getLength(); k++) { - Node item = itemLst.item(k); - if (item.getNodeType() == Node.ELEMENT_NODE) { - Element itemElement = (Element)item; - NodeList itemName = itemElement.getElementsByTagName("name"); - Element itemNameElement = (Element)itemName.item(0); - - // get value - Element itemValueElement = null; - if ((itemElement.getElementsByTagName("value") != null) && (itemElement.getElementsByTagName("value").getLength() != 0)) { - NodeList itemValue = itemElement.getElementsByTagName("value"); - itemValueElement = (Element)itemValue.item(0); - } - - Element itemParamElement = null; - // getparam - if ((itemElement.getElementsByTagName("param") != null) && (itemElement.getElementsByTagName("param").getLength() != 0)) { - NodeList itemParam = itemElement.getElementsByTagName("param"); - itemParamElement = (Element)itemParam.item(0); - } - - if ((itemElement.getAttribute("getparam").equals("true")) && (itemParamElement != null)) { - this.urlParam.put(itemNameElement.getTextContent(), param.get(itemParamElement.getTextContent())); - } else if (itemValueElement != null) { - this.urlParam.put(itemNameElement.getTextContent(), itemValueElement.getTextContent()); - } else if (itemElement.getAttribute("random").equals("true")) { - Random ran = new Random(); - String randomString = Math.abs(ran.nextInt()) + "-randomName"; - this.urlParam.put(itemNameElement.getTextContent(), randomString); - if ((itemElement.getAttribute("setparam").equals("true")) && (itemParamElement != null)) { - param.put(itemParamElement.getTextContent(), randomString); - } - } else if (itemElement.getAttribute("randomnumber").equals("true")) { - Random ran = new Random(); - Integer randomNumber = Math.abs(ran.nextInt(65535)); - this.urlParam.put(itemNameElement.getTextContent(), randomNumber.toString()); - if ((itemElement.getAttribute("setparam").equals("true")) && (itemParamElement != null)) { - param.put(itemParamElement.getTextContent(), randomNumber.toString()); - } - } - } - } - } - } - } - - // Set command URL - public void setCommand(HashMap param) { - - if (this.getCommandType() == CommandType.SCRIPT) { - String temp = "bash xen/" + this.commandName; - Set c = this.urlParam.entrySet(); - Iterator it = c.iterator(); - while (it.hasNext()) { - Map.Entry me = (Map.Entry)it.next(); - String key = (String)me.getKey(); - String value = (String)me.getValue(); - try { - temp = temp + " -" + key + " " + value; - } catch (Exception ex) { - s_logger.error("Unable to set parameter " + key + " for the command " + this.getName()); - } - } - this.command = temp; - } else if (this.getCommandType() == CommandType.MYSQL) { - String temp = this.commandName + " where "; - Set c = this.urlParam.entrySet(); - Iterator it = c.iterator(); - while (it.hasNext()) { - Map.Entry me = (Map.Entry)it.next(); - String key = (String)me.getKey(); - String value = (String)me.getValue(); - try { - temp = temp + key + "=" + value; - } catch (Exception ex) { - s_logger.error("Unable to set parameter " + key + " for the command " + this.getName()); - } - } - this.command = temp; - s_logger.info("The command is " + this.command); - - } else { - if ((param.get("apikey") == null) || (param.get("secretkey") == null) || (this.isUserCommand == false)) { - String temp = this.host + ":8096/?command=" + this.commandName; - Set c = this.urlParam.entrySet(); - Iterator it = c.iterator(); - while (it.hasNext()) { - Map.Entry me = (Map.Entry)it.next(); - String key = (String)me.getKey(); - String value = (String)me.getValue(); - try { - temp = temp + "&" + key + "=" + URLEncoder.encode(value, "UTF-8"); - } catch (Exception ex) { - s_logger.error("Unable to set parameter " + key + " for the command " + this.getName()); - } - } - this.command = temp; - } else if (isUserCommand == true) { - String apiKey = param.get("apikey"); - String secretKey = param.get("secretkey"); - - String temp = ""; - this.urlParam.put("apikey", apiKey); - this.urlParam.put("command", this.commandName); - - // sort url hash map by key - Set c = this.urlParam.entrySet(); - Iterator it = c.iterator(); - while (it.hasNext()) { - Map.Entry me = (Map.Entry)it.next(); - String key = (String)me.getKey(); - String value = (String)me.getValue(); - try { - temp = temp + key + "=" + URLEncoder.encode(value, "UTF-8") + "&"; - } catch (Exception ex) { - s_logger.error("Unable to set parameter " + value + " for the command " + this.getName()); - } - - } - temp = temp.substring(0, temp.length() - 1); - String requestToSign = temp.toLowerCase(); - String signature = UtilsForTest.signRequest(requestToSign, secretKey); - String encodedSignature = ""; - try { - encodedSignature = URLEncoder.encode(signature, "UTF-8"); - } catch (Exception ex) { - s_logger.error(ex); - } - this.command = this.host + ":8080/client/api/?" + temp + "&signature=" + encodedSignature; - } - } - } - - public void setVerifyParam(HashMap param) { - NodeList returnLst = this.xmlCommand.getElementsByTagName("returnvalue"); - if (returnLst != null) { - for (int m = 0; m < returnLst.getLength(); m++) { - Element returnElement = (Element)returnLst.item(m); - if (returnElement.getAttribute("list").equals("true")) { - this.list = true; - NodeList elementLst = returnElement.getElementsByTagName("element"); - this.listId = (Element)elementLst.item(0); - NodeList elementName = returnElement.getElementsByTagName("name"); - this.listName = (Element)elementName.item(0); - } else { - this.list = false; - } - - NodeList itemLst1 = returnElement.getElementsByTagName("item"); - if (itemLst1 != null) { - for (int n = 0; n < itemLst1.getLength(); n++) { - Node item = itemLst1.item(n); - if (item.getNodeType() == Node.ELEMENT_NODE) { - Element itemElement = (Element)item; - // get parameter name - NodeList itemName = itemElement.getElementsByTagName("name"); - Element itemNameElement = (Element)itemName.item(0); - - // Get parameters for future use - if (itemElement.getAttribute("setparam").equals("true")) { - NodeList itemVariable = itemElement.getElementsByTagName("param"); - Element itemVariableElement = (Element)itemVariable.item(0); - setParam.put(itemVariableElement.getTextContent(), itemNameElement.getTextContent()); - this.required = true; - } else if (itemElement.getAttribute("getparam").equals("true")) { - NodeList itemVariable = itemElement.getElementsByTagName("param"); - Element itemVariableElement = (Element)itemVariable.item(0); - this.verifyParam.put(itemNameElement.getTextContent(), param.get(itemVariableElement.getTextContent())); - } else if ((itemElement.getElementsByTagName("value") != null) && (itemElement.getElementsByTagName("value").getLength() != 0)) { - NodeList itemVariable = itemElement.getElementsByTagName("value"); - Element itemVariableElement = (Element)itemVariable.item(0); - this.verifyParam.put(itemNameElement.getTextContent(), itemVariableElement.getTextContent()); - } else { - this.verifyParam.put(itemNameElement.getTextContent(), "no value"); - } - } - } - } - } - } - } - - public int getResponseCode() { - return responseCode; - } - - // Send api command to the server - public void sendCommand(HttpClient client, Connection conn) { - if (TestCaseEngine.s_printUrl == true) { - s_logger.info("url is " + this.command); - } - - if (this.getCommandType() == CommandType.SCRIPT) { - try { - s_logger.info("Executing command " + this.command); - Runtime rtime = Runtime.getRuntime(); - Process child = rtime.exec(this.command); - Thread.sleep(10000); - int retCode = child.waitFor(); - if (retCode != 0) { - this.responseCode = retCode; - } else { - this.responseCode = 200; - } - - } catch (Exception ex) { - s_logger.error("Unable to execute a command " + this.command, ex); - } - } else if (this.getCommandType() == CommandType.MYSQL) { - try { - Statement stmt = conn.createStatement(); - this.result = stmt.executeQuery(this.command); - this.responseCode = 200; - } catch (Exception ex) { - this.responseCode = 400; - s_logger.error("Unable to execute mysql query " + this.command, ex); - } - } else { - HttpMethod method = new GetMethod(this.command); - try { - this.responseCode = client.executeMethod(method); - - if (this.responseCode == 200) { - InputStream is = method.getResponseBodyAsStream(); - DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance(); - DocumentBuilder builder = factory.newDocumentBuilder(); - Document doc = builder.parse(is); - doc.getDocumentElement().normalize(); - - if (!(this.isAsync)) { - this.responseBody = doc.getDocumentElement(); - } else { - // get async job result - Element jobTag = (Element)doc.getDocumentElement().getElementsByTagName("jobid").item(0); - String jobId = jobTag.getTextContent(); - Element responseBodyAsyncEl = queryAsyncJobResult(jobId); - if (responseBodyAsyncEl == null) { - s_logger.error("Can't get a async result"); - } else { - this.responseBody = responseBodyAsyncEl; - // get status of the job - Element jobStatusTag = (Element)responseBodyAsyncEl.getElementsByTagName("jobstatus").item(0); - String jobStatus = jobStatusTag.getTextContent(); - if (!jobStatus.equals("1")) { // Need to modify with different error codes for jobAsync -// results - // set fake response code by now - this.responseCode = 400; - } - } - } - } - - if (TestCaseEngine.s_printUrl == true) { - s_logger.info("Response code is " + this.responseCode); - } - } catch (Exception ex) { - s_logger.error("Command " + command + " failed with exception " + ex.getMessage()); - } finally { - method.releaseConnection(); - } - } - } - - // verify if response is empty (contains only root element) - public boolean isEmpty() { - boolean result = false; - if (!this.responseBody.hasChildNodes()) - result = true; - return result; - } - - // ================FOLLOWING METHODS USE RETURN XML FILE=======================// - - public boolean setParam(HashMap param) { - if ((this.responseBody == null) && (this.commandType == CommandType.HTTP)) { - s_logger.error("Response body is empty"); - return false; - } - Boolean result = true; - - if (this.getCommandType() == CommandType.MYSQL) { - Set set = this.setParam.entrySet(); - Iterator it = set.iterator(); - while (it.hasNext()) { - Map.Entry me = (Map.Entry)it.next(); - String key = (String)me.getKey(); - String value = (String)me.getValue(); - try { - String itemName = null; - while (this.result.next()) { - itemName = this.result.getString(value); - } - if (itemName != null) { - param.put(key, itemName); - } else { - s_logger.error("Following return parameter is missing: " + value); - result = false; - } - } catch (Exception ex) { - s_logger.error("Unable to set parameter " + value, ex); - } - } - } else if (this.getCommandType() == CommandType.HTTP) { - if (this.list == false) { - Set set = this.setParam.entrySet(); - Iterator it = set.iterator(); - - while (it.hasNext()) { - Map.Entry me = (Map.Entry)it.next(); - String key = (String)me.getKey(); - String value = (String)me.getValue(); - // set parameters needed for the future use - NodeList itemName = this.responseBody.getElementsByTagName(value); - if ((itemName != null) && (itemName.getLength() != 0)) { - for (int i = 0; i < itemName.getLength(); i++) { - Element itemNameElement = (Element)itemName.item(i); - if (itemNameElement.getChildNodes().getLength() <= 1) { - param.put(key, itemNameElement.getTextContent()); - break; - } - } - } else { - s_logger.error("Following return parameter is missing: " + value); - result = false; - } - } - } else { - Set set = this.setParam.entrySet(); - Iterator it = set.iterator(); - NodeList returnLst = this.responseBody.getElementsByTagName(this.listName.getTextContent()); - Node requiredNode = returnLst.item(Integer.parseInt(this.listId.getTextContent())); - - if (requiredNode.getNodeType() == Node.ELEMENT_NODE) { - Element fstElmnt = (Element)requiredNode; - - while (it.hasNext()) { - Map.Entry me = (Map.Entry)it.next(); - String key = (String)me.getKey(); - String value = (String)me.getValue(); - NodeList itemName = fstElmnt.getElementsByTagName(value); - if ((itemName != null) && (itemName.getLength() != 0)) { - Element itemNameElement = (Element)itemName.item(0); - if (itemNameElement.getChildNodes().getLength() <= 1) { - param.put(key, itemNameElement.getTextContent()); - } - } else { - s_logger.error("Following return parameter is missing: " + value); - result = false; - } - } - } - } - } - return result; - } - - public String getUrl() { - return command; - } - - public boolean verifyParam() { - boolean result = true; - if (this.getCommandType() == CommandType.HTTP) { - if (this.list == false) { - Set set = verifyParam.entrySet(); - Iterator it = set.iterator(); - - while (it.hasNext()) { - Map.Entry me = (Map.Entry)it.next(); - String key = (String)me.getKey(); - String value = (String)me.getValue(); - if (value == null) { - s_logger.error("Parameter " + key + " is missing in the list of global parameters"); - return false; - } - - NodeList itemName = this.responseBody.getElementsByTagName(key); - if ((itemName.getLength() != 0) && (itemName != null)) { - Element itemNameElement = (Element)itemName.item(0); - if (itemNameElement.hasChildNodes()) { - continue; - } - if (!(verifyParam.get(key).equals("no value")) && !(itemNameElement.getTextContent().equals(verifyParam.get(key)))) { - s_logger.error("Incorrect value for the following tag: " + key + ". Expected value is " + verifyParam.get(key) + " while actual value is " + - itemNameElement.getTextContent()); - result = false; - } - } else { - s_logger.error("Following xml element is missing in the response: " + key); - result = false; - } - } - } - // for multiple elements - else { - Set set = verifyParam.entrySet(); - Iterator it = set.iterator(); - // get list element specified by id - NodeList returnLst = this.responseBody.getElementsByTagName(this.listName.getTextContent()); - Node requiredNode = returnLst.item(Integer.parseInt(this.listId.getTextContent())); - - if (requiredNode.getNodeType() == Node.ELEMENT_NODE) { - Element fstElmnt = (Element)requiredNode; - - while (it.hasNext()) { - Map.Entry me = (Map.Entry)it.next(); - String key = (String)me.getKey(); - String value = (String)me.getValue(); - if (value == null) { - s_logger.error("Parameter " + key + " is missing in the list of global parameters"); - return false; - } - NodeList itemName = fstElmnt.getElementsByTagName(key); - if ((itemName.getLength() != 0) && (itemName != null)) { - Element itemNameElement = (Element)itemName.item(0); - if (!(verifyParam.get(key).equals("no value")) && !(itemNameElement.getTextContent().equals(verifyParam.get(key)))) { - s_logger.error("Incorrect value for the following tag: " + key + ". Expected value is " + verifyParam.get(key) + - " while actual value is " + itemNameElement.getTextContent()); - result = false; - } - } else { - s_logger.error("Following xml element is missing in the response: " + key); - result = false; - } - } - } - } - } else if (this.getCommandType() == CommandType.MYSQL) { - Set set = verifyParam.entrySet(); - Iterator it = set.iterator(); - - while (it.hasNext()) { - Map.Entry me = (Map.Entry)it.next(); - String key = (String)me.getKey(); - String value = (String)me.getValue(); - if (value == null) { - s_logger.error("Parameter " + key + " is missing in the list of global parameters"); - return false; - } - - String itemName = null; - try { - while (this.result.next()) { - itemName = this.result.getString(key); - } - } catch (Exception ex) { - s_logger.error("Unable to get element from result set " + key); - } - - if (!(value.equals("no value")) && !(itemName.equals(verifyParam.get(key)))) { - s_logger.error("Incorrect value for the following tag: " + key + ". Expected value is " + verifyParam.get(key) + " while actual value is " + itemName); - result = false; - } - } - } - return result; - } - - public static boolean verifyEvents(String fileName, String level, String host, String account) { - boolean result = false; - HashMap expectedEvents = new HashMap(); - HashMap actualEvents = new HashMap(); - String key = ""; - - File file = new File(fileName); - if (file.exists()) { - Properties pro = new Properties(); - try { - // get expected events - FileInputStream in = new FileInputStream(file); - pro.load(in); - Enumeration en = pro.propertyNames(); - while (en.hasMoreElements()) { - key = (String)en.nextElement(); - expectedEvents.put(key, Integer.parseInt(pro.getProperty(key))); - } - - // get actual events - String url = host + "/?command=listEvents&account=" + account + "&level=" + level + "&domainid=1&pagesize=100"; - s_logger.info("Getting events with the following url " + url); - HttpClient client = new HttpClient(); - HttpMethod method = new GetMethod(url); - int responseCode = client.executeMethod(method); - if (responseCode == 200) { - InputStream is = method.getResponseBodyAsStream(); - ArrayList> eventValues = UtilsForTest.parseMulXML(is, new String[] {"event"}); - - for (int i = 0; i < eventValues.size(); i++) { - HashMap element = eventValues.get(i); - if (element.get("level").equals(level)) { - if (actualEvents.containsKey(element.get("type")) == true) { - actualEvents.put(element.get("type"), actualEvents.get(element.get("type")) + 1); - } else { - actualEvents.put(element.get("type"), 1); - } - } - } - } - method.releaseConnection(); - - // compare actual events with expected events - - // compare expected result and actual result - Iterator iterator = expectedEvents.keySet().iterator(); - Integer expected; - Integer actual; - int fail = 0; - while (iterator.hasNext()) { - expected = null; - actual = null; - String type = iterator.next().toString(); - expected = expectedEvents.get(type); - actual = actualEvents.get(type); - if (actual == null) { - s_logger.error("Event of type " + type + " and level " + level + " is missing in the listEvents response. Expected number of these events is " + - expected); - fail++; - } else if (expected.compareTo(actual) != 0) { - fail++; - s_logger.info("Amount of events of " + type + " type and level " + level + " is incorrect. Expected number of these events is " + expected + - ", actual number is " + actual); - } - } - if (fail == 0) { - result = true; - } - } catch (Exception ex) { - s_logger.error(ex); - } - } else { - s_logger.info("File " + fileName + " not found"); - } - return result; - } - - public static boolean verifyEvents(HashMap expectedEvents, String level, String host, String parameters) { - boolean result = false; - HashMap actualEvents = new HashMap(); - try { - // get actual events - String url = host + "/?command=listEvents&" + parameters; - HttpClient client = new HttpClient(); - HttpMethod method = new GetMethod(url); - int responseCode = client.executeMethod(method); - if (responseCode == 200) { - InputStream is = method.getResponseBodyAsStream(); - ArrayList> eventValues = UtilsForTest.parseMulXML(is, new String[] {"event"}); - - for (int i = 0; i < eventValues.size(); i++) { - HashMap element = eventValues.get(i); - if (element.get("level").equals(level)) { - if (actualEvents.containsKey(element.get("type")) == true) { - actualEvents.put(element.get("type"), actualEvents.get(element.get("type")) + 1); - } else { - actualEvents.put(element.get("type"), 1); - } - } - } - } - method.releaseConnection(); - } catch (Exception ex) { - s_logger.error(ex); - } - - // compare actual events with expected events - Iterator iterator = expectedEvents.keySet().iterator(); - Integer expected; - Integer actual; - int fail = 0; - while (iterator.hasNext()) { - expected = null; - actual = null; - String type = iterator.next().toString(); - expected = expectedEvents.get(type); - actual = actualEvents.get(type); - if (actual == null) { - s_logger.error("Event of type " + type + " and level " + level + " is missing in the listEvents response. Expected number of these events is " + expected); - fail++; - } else if (expected.compareTo(actual) != 0) { - fail++; - s_logger.info("Amount of events of " + type + " type and level " + level + " is incorrect. Expected number of these events is " + expected + - ", actual number is " + actual); - } - } - - if (fail == 0) { - result = true; - } - - return result; - } - - public Element queryAsyncJobResult(String jobId) { - Element returnBody = null; - int code = 400; - String resultUrl = this.host + ":8096/?command=queryAsyncJobResult&jobid=" + jobId; - HttpClient client = new HttpClient(); - HttpMethod method = new GetMethod(resultUrl); - while (true) { - try { - code = client.executeMethod(method); - if (code == 200) { - InputStream is = method.getResponseBodyAsStream(); - DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance(); - DocumentBuilder builder = factory.newDocumentBuilder(); - Document doc = builder.parse(is); - doc.getDocumentElement().normalize(); - returnBody = doc.getDocumentElement(); - Element jobStatusTag = (Element)returnBody.getElementsByTagName("jobstatus").item(0); - String jobStatus = jobStatusTag.getTextContent(); - if (jobStatus.equals("0")) { - try { - Thread.sleep(1000); - } catch (InterruptedException e) { - s_logger.debug("[ignored] interrupted while during async job result query."); - } - } else { - break; - } - method.releaseConnection(); - } else { - s_logger.error("Error during queryJobAsync. Error code is " + code); - this.responseCode = code; - return null; - } - } catch (Exception ex) { - s_logger.error(ex); - } - } - return returnBody; - } - - private String getElementByName(String elementName) { - NodeList commandName = this.xmlCommand.getElementsByTagName(elementName); - if (commandName.getLength() != 0) { - Element commandElmnt = (Element)commandName.item(0); - NodeList commandNm = commandElmnt.getChildNodes(); - return commandNm.item(0).getNodeValue(); - } else { - return null; - } - } - - private boolean verifyTagValue(String elementName, String expectedValue) { - NodeList tag = this.xmlCommand.getElementsByTagName(elementName); - if (tag.getLength() != 0) { - Element commandElmnt = (Element)tag.item(0); - NodeList commandNm = commandElmnt.getChildNodes(); - if (commandNm.item(0).getNodeValue().equals(expectedValue)) { - return true; - } else { - return false; - } - } else { - return false; - } - } -} diff --git a/test/src-not-used/main/java/com/cloud/test/regression/ConfigTest.java b/test/src-not-used/main/java/com/cloud/test/regression/ConfigTest.java deleted file mode 100644 index 8d5c3587a54..00000000000 --- a/test/src-not-used/main/java/com/cloud/test/regression/ConfigTest.java +++ /dev/null @@ -1,125 +0,0 @@ -// 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.test.regression; - -import java.util.HashMap; - -import org.apache.log4j.Logger; -import org.w3c.dom.Element; -import org.w3c.dom.Node; -import org.w3c.dom.NodeList; - -import com.trilead.ssh2.Connection; -import com.trilead.ssh2.Session; - -import com.cloud.test.regression.ApiCommand.ResponseType; - -public class ConfigTest extends TestCase { - public static final Logger s_logger = Logger.getLogger(ConfigTest.class.getName()); - - public ConfigTest() { - this.setClient(); - this.setParam(new HashMap()); - } - - @Override - public boolean executeTest() { - - int error = 0; - Element rootElement = this.getInputFile().get(0).getDocumentElement(); - NodeList commandLst = rootElement.getElementsByTagName("command"); - - //Analyze each command, send request and build the array list of api commands - for (int i = 0; i < commandLst.getLength(); i++) { - Node fstNode = commandLst.item(i); - Element fstElmnt = (Element)fstNode; - - //new command - ApiCommand api = new ApiCommand(fstElmnt, this.getParam(), this.getCommands()); - - if (api.getName().equals("rebootManagementServer")) { - - s_logger.info("Attempting to SSH into management server " + this.getParam().get("hostip")); - try { - Connection conn = new Connection(this.getParam().get("hostip")); - conn.connect(null, 60000, 60000); - - s_logger.info("SSHed successfully into management server " + this.getParam().get("hostip")); - - boolean isAuthenticated = conn.authenticateWithPassword("root", "password"); - - if (isAuthenticated == false) { - s_logger.info("Authentication failed for root with password"); - return false; - } - - String restartCommand = "service cloud-management restart; service cloud-usage restart"; - Session sess = conn.openSession(); - s_logger.info("Executing : " + restartCommand); - sess.execCommand(restartCommand); - Thread.sleep(120000); - sess.close(); - conn.close(); - - } catch (Exception ex) { - s_logger.error(ex); - return false; - } - } else { - //send a command - api.sendCommand(this.getClient(), null); - - //verify the response of the command - if ((api.getResponseType() == ResponseType.ERROR) && (api.getResponseCode() == 200) && (api.getTestCaseInfo() != null)) { - s_logger.error("Test case " + api.getTestCaseInfo() + - "failed. Command that was supposed to fail, passed. The command was sent with the following url " + api.getUrl()); - error++; - } else if ((api.getResponseType() != ResponseType.ERROR) && (api.getResponseCode() == 200)) { - //set parameters for the future use - if (api.setParam(this.getParam()) == false) { - s_logger.error("Exiting the test...Command " + api.getName() + - " didn't return parameters needed for the future use. The command was sent with url " + api.getUrl()); - return false; - } else { - //verify parameters - if (api.verifyParam() == false) { - s_logger.error("Command " + api.getName() + " failed. Verification for returned parameters failed. Command was sent with url " + api.getUrl()); - error++; - } else if (api.getTestCaseInfo() != null) { - s_logger.info("Test case " + api.getTestCaseInfo() + " passed. Command was sent with the url " + api.getUrl()); - } - } - } else if ((api.getResponseType() != ResponseType.ERROR) && (api.getResponseCode() != 200)) { - s_logger.error("Command " + api.getName() + " failed with an error code " + api.getResponseCode() + " . Command was sent with url " + api.getUrl() + - " Required: " + api.getRequired()); - if (api.getRequired() == true) { - s_logger.info("The command is required for the future use, so exiging"); - return false; - } - error++; - } else if (api.getTestCaseInfo() != null) { - s_logger.info("Test case " + api.getTestCaseInfo() + " passed. Command that was supposed to fail, failed - test passed. Command was sent with url " + - api.getUrl()); - } - } - } - if (error != 0) - return false; - else - return true; - } -} diff --git a/test/src-not-used/main/java/com/cloud/test/regression/DelegatedAdminTest.java b/test/src-not-used/main/java/com/cloud/test/regression/DelegatedAdminTest.java deleted file mode 100644 index cad22db07e2..00000000000 --- a/test/src-not-used/main/java/com/cloud/test/regression/DelegatedAdminTest.java +++ /dev/null @@ -1,129 +0,0 @@ -// 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.test.regression; - -import java.util.HashMap; - -import org.apache.log4j.Logger; -import org.w3c.dom.Document; -import org.w3c.dom.Element; -import org.w3c.dom.Node; -import org.w3c.dom.NodeList; - -import com.cloud.test.regression.ApiCommand.ResponseType; - -public class DelegatedAdminTest extends TestCase { - - public static final Logger s_logger = Logger.getLogger(DelegatedAdminTest.class.getName()); - - public DelegatedAdminTest() { - this.setClient(); - this.setParam(new HashMap()); - } - - @Override - public boolean executeTest() { - int error = 0; - - for (Document eachElement : this.getInputFile()) { - - Element rootElement = eachElement.getDocumentElement(); - NodeList commandLst = rootElement.getElementsByTagName("command"); - - //Analyze each command, send request and build the array list of api commands - for (int i = 0; i < commandLst.getLength(); i++) { - boolean verify = false; - Node fstNode = commandLst.item(i); - Element fstElmnt = (Element)fstNode; - - //new command - ApiCommand api = new ApiCommand(fstElmnt, this.getParam(), this.getCommands()); - - if ((eachElement.getElementsByTagName("delegated_admin_verify_part2").getLength() != 0) && !(api.getName().equals("registerUserKeys"))) { - if (api.getName().startsWith("list")) { - - if (this.denyToExecute()) { - api.setResponseType(ResponseType.EMPTY); - } - verify = true; - } - - if (this.denyToExecute()) { - api.setResponseType(ResponseType.ERROR); - } - } - - //send a command - api.sendCommand(this.getClient(), null); - - //verify the response of the command - if ((verify == true) && !(api.getResponseType() == ResponseType.ERROR || api.getResponseType() == ResponseType.EMPTY)) { - s_logger.error("Test case " + api.getTestCaseInfo() + - " failed. Command that was supposed to fail, passed. The command was sent with the following url " + api.getUrl()); - error++; - } else if ((verify == true) && (api.getResponseType() == ResponseType.ERROR || api.getResponseType() == ResponseType.EMPTY)) { - s_logger.info("Test case " + api.getTestCaseInfo() + " passed"); - } else if ((api.getResponseType() == ResponseType.ERROR) && (api.getResponseCode() == 200)) { - s_logger.error("Test case " + api.getTestCaseInfo() + - " failed. Command that was supposed to fail, passed. The command was sent with the following url " + api.getUrl()); - error++; - } else if ((api.getResponseType() != ResponseType.ERROR) && (api.getResponseCode() == 200)) { - //set parameters for the future use - if (api.setParam(this.getParam()) == false) { - s_logger.error("Exiting the test...Command " + api.getName() + - " didn't return parameters needed for the future use. The command was sent with url " + api.getUrl()); - return false; - } else if (api.getTestCaseInfo() != null) { - s_logger.info("Test case " + api.getTestCaseInfo() + " passed"); - } - } else if ((api.getResponseType() != ResponseType.ERROR) && (api.getResponseCode() != 200)) { - s_logger.error("Test case " + api.getTestCaseInfo() + " failed with an error code " + api.getResponseCode() + " . Command was sent with url " + - api.getUrl()); - if (api.getRequired() == true) { - s_logger.info("The command is required for the future use, so exiging"); - return false; - } - error++; - } else if (api.getTestCaseInfo() != null) { - s_logger.info("Test case " + api.getTestCaseInfo() + " passed"); - - } - } - } - - if (error != 0) - return false; - else - return true; - } - - public boolean denyToExecute() { - boolean result = true; - Integer level1 = Integer.valueOf(this.getParam().get("domainlevel1")); - Integer level2 = Integer.valueOf(this.getParam().get("domainlevel2")); - String domain1 = this.getParam().get("domainname1"); - String domain2 = this.getParam().get("domainname2"); - - if (this.getParam().get("accounttype2").equals("1")) { - result = false; - } else if ((level2.compareTo(level1) < 0) && (domain1.startsWith(domain2)) && (this.getParam().get("accounttype2").equals("2"))) { - result = false; - } - - return result; - } -} diff --git a/test/src-not-used/main/java/com/cloud/test/regression/Deploy.java b/test/src-not-used/main/java/com/cloud/test/regression/Deploy.java deleted file mode 100644 index 716e6277bec..00000000000 --- a/test/src-not-used/main/java/com/cloud/test/regression/Deploy.java +++ /dev/null @@ -1,109 +0,0 @@ -// 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.test.regression; - -import java.util.ArrayList; -import java.util.Arrays; -import java.util.HashMap; -import java.util.Iterator; -import java.util.List; - -import org.apache.log4j.Logger; -import org.w3c.dom.Element; -import org.w3c.dom.Node; -import org.w3c.dom.NodeList; - -public class Deploy extends TestCase { - public static final Logger s_logger = Logger.getLogger(Deploy.class.getName()); - - public Deploy() { - this.setClient(); - this.setParam(new HashMap()); - } - - @Override - public boolean executeTest() { - int error = 0; - Element rootElement = this.getInputFile().get(0).getDocumentElement(); - NodeList commandLst = rootElement.getElementsByTagName("command"); - - //Analyze each command, send request and build the array list of api commands - for (int i = 0; i < commandLst.getLength(); i++) { - Node fstNode = commandLst.item(i); - Element fstElmnt = (Element)fstNode; - - //new command - ApiCommand api = new ApiCommand(fstElmnt, this.getParam(), this.getCommands()); - - //send a command - api.sendCommand(this.getClient(), null); - - //verify the response of the command - if (api.getResponseCode() != 200) { - error++; - s_logger.error("The command " + api.getUrl() + " failed"); - } else { - s_logger.info("The command " + api.getUrl() + " passsed"); - } - } - if (error != 0) - return false; - else - return true; - } - - public static void main(String[] args) { - - List argsList = Arrays.asList(args); - Iterator iter = argsList.iterator(); - String host = null; - String file = null; - - while (iter.hasNext()) { - String arg = iter.next(); - // management server host - if (arg.equals("-h")) { - host = iter.next(); - } - if (arg.equals("-f")) { - file = iter.next(); - } - } - - Deploy deploy = new Deploy(); - - ArrayList inputFile = new ArrayList(); - inputFile.add(file); - deploy.setInputFile(inputFile); - deploy.setTestCaseName("Management server deployment"); - deploy.getParam().put("hostip", host); - deploy.getParam().put("apicommands", "../metadata/func/commands"); - deploy.setCommands(); - - s_logger.info("Starting deployment against host " + host); - - boolean result = deploy.executeTest(); - if (result == false) { - s_logger.error("DEPLOYMENT FAILED"); - System.exit(1); - } else { - s_logger.info("DEPLOYMENT IS SUCCESSFUL"); - } - - } - -} diff --git a/test/src-not-used/main/java/com/cloud/test/regression/EventsApiTest.java b/test/src-not-used/main/java/com/cloud/test/regression/EventsApiTest.java deleted file mode 100644 index 6d724c02587..00000000000 --- a/test/src-not-used/main/java/com/cloud/test/regression/EventsApiTest.java +++ /dev/null @@ -1,176 +0,0 @@ -// 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.test.regression; - -import java.sql.Statement; -import java.util.HashMap; - -import org.apache.log4j.Logger; -import org.w3c.dom.Element; -import org.w3c.dom.Node; -import org.w3c.dom.NodeList; - -import com.trilead.ssh2.Connection; -import com.trilead.ssh2.Session; - -import com.cloud.test.regression.ApiCommand.ResponseType; - -public class EventsApiTest extends TestCase { - public static final Logger s_logger = Logger.getLogger(EventsApiTest.class.getName()); - - public EventsApiTest() { - this.setClient(); - this.setParam(new HashMap()); - } - - @Override - public boolean executeTest() { - int error = 0; - Element rootElement = this.getInputFile().get(0).getDocumentElement(); - NodeList commandLst = rootElement.getElementsByTagName("command"); - - //Analyze each command, send request and build the array list of api commands - for (int i = 0; i < commandLst.getLength(); i++) { - Node fstNode = commandLst.item(i); - Element fstElmnt = (Element)fstNode; - - //!!!check if we need to execute mySql command - NodeList commandName = fstElmnt.getElementsByTagName("name"); - Element commandElmnt = (Element)commandName.item(0); - NodeList commandNm = commandElmnt.getChildNodes(); - if (commandNm.item(0).getNodeValue().equals("mysqlupdate")) { - //establish connection to mysql server and execute an update command - NodeList mysqlList = fstElmnt.getElementsByTagName("mysqlcommand"); - for (int j = 0; j < mysqlList.getLength(); j++) { - Element itemVariableElement = (Element)mysqlList.item(j); - - s_logger.info("Executing mysql command " + itemVariableElement.getTextContent()); - try { - Statement st = this.getConn().createStatement(); - st.executeUpdate(itemVariableElement.getTextContent()); - } catch (Exception ex) { - s_logger.error(ex); - return false; - } - } - } - - else if (commandNm.item(0).getNodeValue().equals("agentcommand")) { - //connect to all the agents and execute agent command - NodeList commandList = fstElmnt.getElementsByTagName("commandname"); - Element commandElement = (Element)commandList.item(0); - NodeList ipList = fstElmnt.getElementsByTagName("ip"); - for (int j = 0; j < ipList.getLength(); j++) { - Element itemVariableElement = (Element)ipList.item(j); - - s_logger.info("Attempting to SSH into agent " + itemVariableElement.getTextContent()); - try { - Connection conn = new Connection(itemVariableElement.getTextContent()); - conn.connect(null, 60000, 60000); - - s_logger.info("SSHed successfully into agent " + itemVariableElement.getTextContent()); - - boolean isAuthenticated = conn.authenticateWithPassword("root", "password"); - - if (isAuthenticated == false) { - s_logger.info("Authentication failed for root with password"); - return false; - } - - Session sess = conn.openSession(); - s_logger.info("Executing : " + commandElement.getTextContent()); - sess.execCommand(commandElement.getTextContent()); - Thread.sleep(60000); - sess.close(); - conn.close(); - - } catch (Exception ex) { - s_logger.error(ex); - return false; - } - } - } - - else { - //new command - ApiCommand api = new ApiCommand(fstElmnt, this.getParam(), this.getCommands()); - - //send a command - api.sendCommand(this.getClient(), null); - - //verify the response of the command - if ((api.getResponseType() == ResponseType.ERROR) && (api.getResponseCode() == 200)) { - s_logger.error("Test case " + api.getTestCaseInfo() + - " failed. Command that was supposed to fail, passed. The command was sent with the following url " + api.getUrl()); - error++; - } else if ((api.getResponseType() != ResponseType.ERROR) && (api.getResponseCode() == 200)) { - //verify if response is suppposed to be empty - if (api.getResponseType() == ResponseType.EMPTY) { - if (api.isEmpty() == true) { - s_logger.info("Test case " + api.getTestCaseInfo() + " passed. Empty response was returned as expected. Command was sent with url " + - api.getUrl()); - } else { - s_logger.error("Test case " + api.getTestCaseInfo() + " failed. Empty response was expected. Command was sent with url " + api.getUrl()); - } - } else { - if (api.isEmpty() != false) - s_logger.error("Test case " + api.getTestCaseInfo() + " failed. Non-empty response was expected. Command was sent with url " + api.getUrl()); - else { - //set parameters for the future use - if (api.setParam(this.getParam()) == false) { - s_logger.error("Exiting the test...Command " + api.getName() + - " didn't return parameters needed for the future use. The command was sent with url " + api.getUrl()); - return false; - } else if (api.getTestCaseInfo() != null) { - s_logger.info("Test case " + api.getTestCaseInfo() + " passed. Command was sent with the url " + api.getUrl()); - } - } - } - } else if ((api.getResponseType() != ResponseType.ERROR) && (api.getResponseCode() != 200)) { - s_logger.error("Command " + api.getName() + " failed with an error code " + api.getResponseCode() + " . Command was sent with url " + api.getUrl()); - if (api.getRequired() == true) { - s_logger.info("The command is required for the future use, so exiging"); - return false; - } - error++; - } else if (api.getTestCaseInfo() != null) { - s_logger.info("Test case " + api.getTestCaseInfo() + " passed. Command that was supposed to fail, failed. Command was sent with url " + api.getUrl()); - - } - } - } - - //verify events with userid parameter - test case 97 - HashMap expectedEvents = new HashMap(); - expectedEvents.put("VM.START", 1); - boolean eventResult = - ApiCommand.verifyEvents(expectedEvents, "INFO", "http://" + this.getParam().get("hostip") + ":8096", "userid=" + this.getParam().get("userid1") + - "&type=VM.START"); - s_logger.info("Test case 97 - listEvent command verification result is " + eventResult); - - //verify error events - eventResult = - ApiCommand.verifyEvents("../metadata/error_events.properties", "ERROR", "http://" + this.getParam().get("hostip") + ":8096", - this.getParam().get("erroruseraccount")); - s_logger.info("listEvent command verification result is " + eventResult); - - if (error != 0) - return false; - else - return true; - } -} diff --git a/test/src-not-used/main/java/com/cloud/test/regression/HA.java b/test/src-not-used/main/java/com/cloud/test/regression/HA.java deleted file mode 100644 index 0a17a5b7582..00000000000 --- a/test/src-not-used/main/java/com/cloud/test/regression/HA.java +++ /dev/null @@ -1,80 +0,0 @@ -// 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.test.regression; - -import org.apache.log4j.Logger; -import org.w3c.dom.Element; -import org.w3c.dom.Node; -import org.w3c.dom.NodeList; - -import com.cloud.test.regression.ApiCommand.ResponseType; - -public class HA extends TestCase { - - public static final Logger s_logger = Logger.getLogger(HA.class.getName()); - - public HA() { - this.setClient(); - } - - @Override - public boolean executeTest() { - int error = 0; - Element rootElement = this.getInputFile().get(0).getDocumentElement(); - NodeList commandLst = rootElement.getElementsByTagName("command"); - - //Analyze each command, send request and build the array list of api commands - for (int i = 0; i < commandLst.getLength(); i++) { - - Node fstNode = commandLst.item(i); - Element fstElmnt = (Element)fstNode; - - //new command - ApiCommand api = new ApiCommand(fstElmnt, this.getParam(), this.getCommands()); - - //send a command - api.sendCommand(this.getClient(), this.getConn()); - - //verify the response parameters - if ((api.getResponseCode() != 200) && (api.getRequired() == true)) { - s_logger.error("Exiting the test....Command " + api.getName() + " required for the future run, failed with an error code " + api.getResponseCode() + - ". Command was sent with the url " + api.getUrl()); - return false; - } else if ((api.getResponseCode() != 200) && (api.getResponseType() != ResponseType.ERROR)) { - error++; - s_logger.error("Command " + api.getTestCaseInfo() + " failed with an error code " + api.getResponseCode() + " . Command was sent with url " + - api.getUrl()); - } else if ((api.getResponseCode() == 200) && (api.getResponseType() == ResponseType.ERROR)) { - error++; - s_logger.error("Command " + api.getTestCaseInfo() + " which was supposed to failed, passed. The command was sent with url " + api.getUrl()); - } else { - //set parameters for the future use - if (api.setParam(this.getParam()) == false) { - s_logger.error("Exiting the test...Command " + api.getName() + " didn't return parameters needed for the future use. Command was sent with url " + - api.getUrl()); - return false; - } - s_logger.info("Command " + api.getTestCaseInfo() + " passed"); - } - } - - if (error != 0) - return false; - else - return true; - } -} diff --git a/test/src-not-used/main/java/com/cloud/test/regression/LoadBalancingTest.java b/test/src-not-used/main/java/com/cloud/test/regression/LoadBalancingTest.java deleted file mode 100644 index cdbc5364137..00000000000 --- a/test/src-not-used/main/java/com/cloud/test/regression/LoadBalancingTest.java +++ /dev/null @@ -1,142 +0,0 @@ -// 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.test.regression; - -import java.util.HashMap; - -import org.apache.log4j.Logger; -import org.w3c.dom.Element; -import org.w3c.dom.Node; -import org.w3c.dom.NodeList; - -import com.cloud.test.regression.ApiCommand.ResponseType; - -public class LoadBalancingTest extends TestCase { - - public static final Logger s_logger = Logger.getLogger(LoadBalancingTest.class.getName()); - - public LoadBalancingTest() { - this.setClient(); - this.setParam(new HashMap()); - } - - @Override - public boolean executeTest() { - - int error = 0; - Element rootElement = this.getInputFile().get(0).getDocumentElement(); - NodeList commandLst = rootElement.getElementsByTagName("command"); - - //Analyze each command, send request and build the array list of api commands - for (int i = 0; i < commandLst.getLength(); i++) { - - Node fstNode = commandLst.item(i); - Element fstElmnt = (Element)fstNode; - - //new command - ApiCommand api = new ApiCommand(fstElmnt, this.getParam(), this.getCommands()); - - //send a command - api.sendCommand(this.getClient(), null); - - //verify the response of the command - if ((api.getResponseType() == ResponseType.ERROR) && (api.getResponseCode() == 200)) { - s_logger.error("Test case " + api.getTestCaseInfo() + " failed. Command that was supposed to fail, passed. The command was sent with the following url " + - api.getUrl()); - error++; - } else if ((api.getResponseType() != ResponseType.ERROR) && (api.getResponseCode() == 200)) { - //verify if response is suppposed to be empty - if (api.getResponseType() == ResponseType.EMPTY) { - if (api.isEmpty() == true) { - s_logger.info("Test case " + api.getTestCaseInfo() + " passed"); - } else { - s_logger.error("Test case " + api.getTestCaseInfo() + " failed. Empty response was expected. Command was sent with url " + api.getUrl()); - } - } else { - if (api.isEmpty() != false) - s_logger.error("Test case " + api.getTestCaseInfo() + " failed. Non-empty response was expected. Command was sent with url " + api.getUrl()); - else { - //set parameters for the future use - if (api.setParam(this.getParam()) == false) { - s_logger.error("Exiting the test...Command " + api.getName() + - " didn't return parameters needed for the future use. The command was sent with url " + api.getUrl()); - return false; - } else if (api.getTestCaseInfo() != null) { - s_logger.info("Test case " + api.getTestCaseInfo() + " passed"); - } - } - } - } else if ((api.getResponseType() != ResponseType.ERROR) && (api.getResponseCode() != 200)) { - s_logger.error("Test case " + api.getTestCaseInfo() + " failed. Command was sent with url " + api.getUrl()); - if (api.getRequired() == true) { - s_logger.info("The command is required for the future use, so exiging"); - return false; - } - error++; - } else if (api.getTestCaseInfo() != null) { - s_logger.info("Test case " + api.getTestCaseInfo() + " passed"); - - } - } - -// //Try to create portForwarding rule for all available private/public ports -// ArrayList port = new ArrayList(); -// for (int i=1; i<65536; i++){ -// port.add(Integer.toString(i)); -// } -// -// //try all public ports -// for (String portValue : port) { -// try { -// String url = this.getHost() + ":8096/?command=createOrUpdateLoadBalancerRule&account=" + this.getParam().get("accountname") + "&publicip=" + this.getParam().get("boundaryip") + -// "&privateip=" + this.getParam().get("vmipaddress") + "&privateport=22&protocol=tcp&publicport=" + portValue; -// HttpClient client = new HttpClient(); -// HttpMethod method = new GetMethod(url); -// int responseCode = client.executeMethod(method); -// if (responseCode != 200 ) { -// error++; -// s_logger.error("Can't create LB rule for the public port " + portValue + ". Request was sent with url " + url); -// } -// }catch (Exception ex) { -// s_logger.error(ex); -// } -// } -// -// //try all private ports -// for (String portValue : port) { -// try { -// String url = this.getHost() + ":8096/?command=createOrUpdateLoadBalancerRule&account=" + this.getParam().get("accountname") + "&publicip=" + this.getParam().get("boundaryip") + -// "&privateip=" + this.getParam().get("vmipaddress") + "&publicport=22&protocol=tcp&privateport=" + portValue; -// HttpClient client = new HttpClient(); -// HttpMethod method = new GetMethod(url); -// int responseCode = client.executeMethod(method); -// if (responseCode != 200 ) { -// error++; -// s_logger.error("Can't create LB rule for the private port " + portValue + ". Request was sent with url " + url); -// } -// }catch (Exception ex) { -// s_logger.error(ex); -// } -// } - - if (error != 0) - return false; - else - return true; - } - -} diff --git a/test/src-not-used/main/java/com/cloud/test/regression/PortForwardingTest.java b/test/src-not-used/main/java/com/cloud/test/regression/PortForwardingTest.java deleted file mode 100644 index 24415fdc69a..00000000000 --- a/test/src-not-used/main/java/com/cloud/test/regression/PortForwardingTest.java +++ /dev/null @@ -1,143 +0,0 @@ -// 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.test.regression; - -import java.util.HashMap; - -import org.apache.log4j.Logger; -import org.w3c.dom.Element; -import org.w3c.dom.Node; -import org.w3c.dom.NodeList; - -import com.cloud.test.regression.ApiCommand.ResponseType; - -public class PortForwardingTest extends TestCase { - public static final Logger s_logger = Logger.getLogger(PortForwardingTest.class.getName()); - - public PortForwardingTest() { - setClient(); - setParam(new HashMap()); - } - - @Override - public boolean executeTest() { - - int error = 0; - Element rootElement = getInputFile().get(0).getDocumentElement(); - NodeList commandLst = rootElement.getElementsByTagName("command"); - - //Analyze each command, send request and build the array list of api commands - for (int i = 0; i < commandLst.getLength(); i++) { - - Node fstNode = commandLst.item(i); - Element fstElmnt = (Element)fstNode; - - //new command - ApiCommand api = new ApiCommand(fstElmnt, getParam(), getCommands()); - - //send a command - api.sendCommand(getClient(), null); - - //verify the response of the command - if ((api.getResponseType() != ResponseType.ERROR) && (api.getResponseCode() == 200)) { - s_logger.error("Test case " + api.getTestCaseInfo() + " failed. Command that was supposed to fail, passed. The command was sent with the following url " + - api.getUrl()); - error++; - } else if ((api.getResponseType() != ResponseType.ERROR) && (api.getResponseCode() == 200)) { - //verify if response is suppposed to be empty - if (api.getResponseType() == ResponseType.EMPTY) { - if (api.isEmpty() == true) { - s_logger.info("Test case " + api.getTestCaseInfo() + " passed"); - } else { - s_logger.error("Test case " + api.getTestCaseInfo() + " failed. Empty response was expected. Command was sent with url " + api.getUrl()); - } - } else { - if (api.isEmpty() != false) - s_logger.error("Test case " + api.getTestCaseInfo() + " failed. Non-empty response was expected. Command was sent with url " + api.getUrl()); - else { - //set parameters for the future use - if (api.setParam(getParam()) == false) { - s_logger.error("Exiting the test...Command " + api.getName() + - " didn't return parameters needed for the future use. The command was sent with url " + api.getUrl()); - return false; - } else if (api.getTestCaseInfo() != null) { - s_logger.info("Test case " + api.getTestCaseInfo() + " passed"); - } - } - } - } else if ((api.getResponseType() != ResponseType.ERROR) && (api.getResponseCode() != 200)) { - s_logger.error("Test case " + api.getTestCaseInfo() + " failed . Command was sent with url " + api.getUrl()); - if (api.getRequired() == true) { - s_logger.info("The command is required for the future use, so exiging"); - return false; - } - error++; - } else if (api.getTestCaseInfo() != null) { - s_logger.info("Test case " + api.getTestCaseInfo() + " passed"); - - } - } - -// //Try to create portForwarding rule for all available private/public ports -// ArrayList port = new ArrayList(); -// for (int i=1; i<65536; i++){ -// port.add(Integer.toString(i)); -// } -// -// //try all public ports -// for (String portValue : port) { -// try { -// s_logger.info("public port is " + portValue); -// String url = this.getHost() + ":8096/?command=createOrUpdateIpForwardingRule&account=" + this.getParam().get("accountname") + "&publicip=" + this.getParam().get("boundaryip") + -// "&privateip=" + this.getParam().get("vmipaddress") + "&privateport=22&protocol=tcp&publicport=" + portValue; -// HttpClient client = new HttpClient(); -// HttpMethod method = new GetMethod(url); -// int responseCode = client.executeMethod(method); -// if (responseCode != 200 ) { -// error++; -// s_logger.error("Can't create portForwarding rule for the public port " + portValue + ". Request was sent with url " + url); -// } -// }catch (Exception ex) { -// s_logger.error(ex); -// } -// } -// -// //try all private ports -// for (String portValue : port) { -// try { -// String url = this.getHost() + ":8096/?command=createOrUpdateIpForwardingRule&account=" + -// this.getParam().get("accountname") + "&publicip=" + this.getParam().get("boundaryip") + -// "&privateip=" + this.getParam().get("vmipaddress") + "&publicport=22&protocol=tcp&privateport=" + portValue; -// HttpClient client = new HttpClient(); -// HttpMethod method = new GetMethod(url); -// int responseCode = client.executeMethod(method); -// if (responseCode != 200 ) { -// error++; -// s_logger.error("Can't create portForwarding rule for the private port " + portValue + ". Request was sent with url " + url); -// } -// }catch (Exception ex) { -// s_logger.error(ex); -// } -// } - - if (error != 0) - return false; - else - return true; - } - -} diff --git a/test/src-not-used/main/java/com/cloud/test/regression/SanityTest.java b/test/src-not-used/main/java/com/cloud/test/regression/SanityTest.java deleted file mode 100644 index defb232b5af..00000000000 --- a/test/src-not-used/main/java/com/cloud/test/regression/SanityTest.java +++ /dev/null @@ -1,86 +0,0 @@ -// 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.test.regression; - -import org.apache.log4j.Logger; -import org.w3c.dom.Element; -import org.w3c.dom.Node; -import org.w3c.dom.NodeList; - -public class SanityTest extends TestCase { - - public static final Logger s_logger = Logger.getLogger(SanityTest.class.getName()); - - public SanityTest() { - this.setClient(); - } - - @Override - public boolean executeTest() { - int error = 0; - Element rootElement = this.getInputFile().get(0).getDocumentElement(); - NodeList commandLst = rootElement.getElementsByTagName("command"); - //Analyze each command, send request and build the array list of api commands - for (int i = 0; i < commandLst.getLength(); i++) { - - Node fstNode = commandLst.item(i); - Element fstElmnt = (Element)fstNode; - - //new command - ApiCommand api = new ApiCommand(fstElmnt, this.getParam(), this.getCommands()); - - api.sendCommand(this.getClient(), null); - - //verify the response parameters - if ((api.getResponseCode() != 200) && (api.getRequired() == true)) { - s_logger.error("Exiting the test....Command " + api.getName() + " required for the future run, failed with an error code " + api.getResponseCode() + - ". Command was sent with the url " + api.getUrl()); - return false; - } else if (api.getResponseCode() != 200) { - error++; - s_logger.error("Test " + api.getTestCaseInfo() + " failed with an error code " + api.getResponseCode() + " . Command was sent with url " + api.getUrl()); - } else { - //set parameters for the future use - if (api.setParam(this.getParam()) == false) { - s_logger.error("Exiting the test...Command " + api.getName() + " didn't return parameters needed for the future use. Command was sent with url " + - api.getUrl()); - return false; - } - - //verify parameters - if (api.verifyParam() == false) { - s_logger.error("Test " + api.getTestCaseInfo() + " failed. Verification for returned parameters failed. The command was sent with url " + - api.getUrl()); - error++; - } else if (api.getTestCaseInfo() != null) { - s_logger.info("Test " + api.getTestCaseInfo() + " passed"); - } - } - } - - //verify event - boolean eventResult = - ApiCommand.verifyEvents("../metadata/func/regression_events.properties", "INFO", "http://" + this.getParam().get("hostip") + ":8096", - this.getParam().get("accountname")); - s_logger.info("listEvent command verification result is " + eventResult); - - if (error != 0) - return false; - else - return true; - } -} diff --git a/test/src-not-used/main/java/com/cloud/test/regression/Test.java b/test/src-not-used/main/java/com/cloud/test/regression/Test.java deleted file mode 100644 index 5c6b336de9d..00000000000 --- a/test/src-not-used/main/java/com/cloud/test/regression/Test.java +++ /dev/null @@ -1,88 +0,0 @@ -// 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.test.regression; - -import java.util.ArrayList; -import java.util.HashMap; - -import org.apache.commons.httpclient.HttpClient; -import org.apache.commons.httpclient.HttpMethod; -import org.apache.commons.httpclient.methods.GetMethod; -import org.apache.log4j.Logger; -import org.w3c.dom.Element; -import org.w3c.dom.Node; -import org.w3c.dom.NodeList; - -public class Test extends TestCase { - public static final Logger s_logger = Logger.getLogger(Test.class.getName()); - - public Test() { - this.setClient(); - this.setParam(new HashMap()); - } - - @Override - public boolean executeTest() { - - int error = 0; - Element rootElement = this.getInputFile().get(0).getDocumentElement(); - NodeList commandLst = rootElement.getElementsByTagName("command"); - - //Analyze each command, send request and build the array list of api commands - for (int i = 0; i < commandLst.getLength(); i++) { - Node fstNode = commandLst.item(i); - Element fstElmnt = (Element)fstNode; - - //new command - ApiCommand api = new ApiCommand(fstElmnt, this.getParam(), this.getCommands()); - - //send a command - api.sendCommand(this.getClient(), null); - - } - - //Try to create portForwarding rule for all available private/public ports - ArrayList port = new ArrayList(); - for (int j = 1; j < 1000; j++) { - port.add(Integer.toString(j)); - } - - //try all public ports - for (String portValue : port) { - try { - s_logger.info("public port is " + portValue); - String url = - "http://" + this.getParam().get("hostip") + ":8096/?command=createNetworkRule&publicPort=" + portValue + - "&privatePort=22&protocol=tcp&isForward=true&securityGroupId=1&account=admin"; - HttpClient client = new HttpClient(); - HttpMethod method = new GetMethod(url); - int responseCode = client.executeMethod(method); - if (responseCode != 200) { - error++; - s_logger.error("Can't create portForwarding network rule for the public port " + portValue + ". Request was sent with url " + url); - } - } catch (Exception ex) { - s_logger.error(ex); - } - } - - if (error != 0) - return false; - else - return true; - } -} diff --git a/test/src-not-used/main/java/com/cloud/test/regression/TestCase.java b/test/src-not-used/main/java/com/cloud/test/regression/TestCase.java deleted file mode 100644 index cb7395c07bf..00000000000 --- a/test/src-not-used/main/java/com/cloud/test/regression/TestCase.java +++ /dev/null @@ -1,138 +0,0 @@ -// 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.test.regression; - -import java.io.File; -import java.io.FileInputStream; -import java.sql.Connection; -import java.sql.DriverManager; -import java.util.ArrayList; -import java.util.Enumeration; -import java.util.HashMap; -import java.util.Properties; - -import javax.xml.parsers.DocumentBuilder; -import javax.xml.parsers.DocumentBuilderFactory; - -import org.apache.commons.httpclient.HttpClient; -import org.apache.log4j.Logger; -import org.w3c.dom.Document; - -public abstract class TestCase { - - public static Logger s_logger = Logger.getLogger(TestCase.class.getName()); - private Connection conn; - private ArrayList inputFile = new ArrayList(); - private HttpClient client; - private String testCaseName; - private HashMap param = new HashMap(); - private HashMap commands = new HashMap(); - - public HashMap getParam() { - return param; - } - - public void setParam(HashMap param) { - this.param = param; - } - - public HashMap getCommands() { - return commands; - } - - public void setCommands() { - File asyncCommands = null; - if (param.get("apicommands") == null) { - s_logger.info("Unable to get the list of commands, exiting"); - System.exit(1); - } else { - asyncCommands = new File(param.get("apicommands")); - } - try { - Properties pro = new Properties(); - FileInputStream in = new FileInputStream(asyncCommands); - pro.load(in); - Enumeration en = pro.propertyNames(); - while (en.hasMoreElements()) { - String key = (String)en.nextElement(); - commands.put(key, pro.getProperty(key)); - } - } catch (Exception ex) { - s_logger.info("Unable to find the file " + param.get("apicommands") + " due to following exception " + ex); - } - - } - - public Connection getConn() { - return conn; - } - - public void setConn(String dbPassword) { - this.conn = null; - try { - Class.forName("com.mysql.jdbc.Driver"); - this.conn = DriverManager.getConnection("jdbc:mysql://" + param.get("db") + "/cloud", "root", dbPassword); - if (!this.conn.isValid(0)) { - s_logger.error("Connection to DB failed to establish"); - } - - } catch (Exception ex) { - s_logger.error(ex); - } - } - - public void setInputFile(ArrayList fileNameInput) { - for (String fileName : fileNameInput) { - File file = new File(fileName); - DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance(); - Document doc = null; - try { - DocumentBuilder builder = factory.newDocumentBuilder(); - doc = builder.parse(file); - doc.getDocumentElement().normalize(); - } catch (Exception ex) { - s_logger.error("Unable to load " + fileName + " due to ", ex); - } - this.inputFile.add(doc); - } - } - - public ArrayList getInputFile() { - return inputFile; - } - - public void setTestCaseName(String testCaseName) { - this.testCaseName = testCaseName; - } - - public String getTestCaseName() { - return this.testCaseName; - } - - public void setClient() { - HttpClient client = new HttpClient(); - this.client = client; - } - - public HttpClient getClient() { - return this.client; - } - - //abstract methods - public abstract boolean executeTest(); - -} diff --git a/test/src-not-used/main/java/com/cloud/test/regression/TestCaseEngine.java b/test/src-not-used/main/java/com/cloud/test/regression/TestCaseEngine.java deleted file mode 100644 index 4207e1786f5..00000000000 --- a/test/src-not-used/main/java/com/cloud/test/regression/TestCaseEngine.java +++ /dev/null @@ -1,275 +0,0 @@ -// 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.test.regression; - -import java.io.File; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.HashMap; -import java.util.Iterator; -import java.util.List; -import java.util.Map; -import java.util.Random; -import java.util.Set; - -import javax.xml.parsers.DocumentBuilder; -import javax.xml.parsers.DocumentBuilderFactory; - -import org.apache.log4j.Logger; -import org.w3c.dom.Document; -import org.w3c.dom.Element; -import org.w3c.dom.Node; -import org.w3c.dom.NodeList; - -public class TestCaseEngine { - - public static final Logger s_logger = Logger.getLogger(TestCaseEngine.class.getName()); - public static String s_fileName = "../metadata/adapter.xml"; - public static HashMap s_globalParameters = new HashMap(); - protected static HashMap s_componentMap = new HashMap(); - protected static HashMap> s_inputFile = new HashMap>(); - protected static String s_testCaseName = new String(); - protected static ArrayList s_keys = new ArrayList(); - private static ThreadLocal s_result = new ThreadLocal(); - public static int s_numThreads = 1; - public static boolean s_repeat = false; - public static boolean s_printUrl = false; - public static String s_type = "All"; - public static boolean s_isSanity = false; - public static boolean s_isRegression = false; - private static int s_failure = 0; - - public static void main(String args[]) { - - // Parameters - List argsList = Arrays.asList(args); - Iterator iter = argsList.iterator(); - while (iter.hasNext()) { - String arg = iter.next(); - // is stress? - if (arg.equals("-t")) { - s_numThreads = Integer.parseInt(iter.next()); - } - // do you want to print url for all commands? - if (arg.equals("-p")) { - s_printUrl = true; - } - - //type of the test: sanity, regression, all (default) - if (arg.equals("-type")) { - s_type = iter.next(); - } - - if (arg.equals("-repeat")) { - s_repeat = Boolean.valueOf(iter.next()); - } - - if (arg.equals("-filename")) { - s_fileName = iter.next(); - } - } - - if (s_type.equalsIgnoreCase("sanity")) - s_isSanity = true; - else if (s_type.equalsIgnoreCase("regression")) - s_isRegression = true; - - try { - // parse adapter.xml file to get list of tests to execute - File file = new File(s_fileName); - DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance(); - DocumentBuilder builder = factory.newDocumentBuilder(); - Document doc = builder.parse(file); - doc.getDocumentElement().normalize(); - Element root = doc.getDocumentElement(); - - // set global parameters - setGlobalParams(root); - - // populate _componentMap - setComponent(root); - - // set error to 0 by default - - // execute test - for (int i = 0; i < s_numThreads; i++) { - if (s_numThreads > 1) { - s_logger.info("STARTING STRESS TEST IN " + s_numThreads + " THREADS"); - } else { - s_logger.info("STARTING FUNCTIONAL TEST"); - } - new Thread(new Runnable() { - @Override - public void run() { - do { - if (s_numThreads == 1) { - try { - for (String key : s_keys) { - Class c = Class.forName(s_componentMap.get(key)); - TestCase component = (TestCase)c.newInstance(); - executeTest(key, c, component); - } - } catch (Exception ex1) { - s_logger.error(ex1); - } finally { - if (s_failure > 0) { - System.exit(1); - } - } - } else { - Random ran = new Random(); - Integer randomNumber = Math.abs(ran.nextInt(s_keys.size())); - try { - String key = s_keys.get(randomNumber); - Class c = Class.forName(s_componentMap.get(key)); - TestCase component = (TestCase)c.newInstance(); - executeTest(key, c, component); - } catch (Exception e) { - s_logger.error("Error in thread ", e); - } - } - } while (s_repeat); - } - }).start(); - } - - } catch (Exception exc) { - s_logger.error(exc); - } - } - - public static void setGlobalParams(Element rootElement) { - NodeList globalParam = rootElement.getElementsByTagName("globalparam"); - Element parameter = (Element)globalParam.item(0); - NodeList paramLst = parameter.getElementsByTagName("param"); - - for (int i = 0; i < paramLst.getLength(); i++) { - Element paramElement = (Element)paramLst.item(i); - - if (paramElement.getNodeType() == Node.ELEMENT_NODE) { - Element itemElement = paramElement; - NodeList itemName = itemElement.getElementsByTagName("name"); - Element itemNameElement = (Element)itemName.item(0); - NodeList itemVariable = itemElement.getElementsByTagName("variable"); - Element itemVariableElement = (Element)itemVariable.item(0); - s_globalParameters.put(itemVariableElement.getTextContent(), itemNameElement.getTextContent()); - } - } - } - - public static void setComponent(Element rootElement) { - NodeList testLst = rootElement.getElementsByTagName("test"); - for (int j = 0; j < testLst.getLength(); j++) { - Element testElement = (Element)testLst.item(j); - - if (testElement.getNodeType() == Node.ELEMENT_NODE) { - Element itemElement = testElement; - - // get test case name - NodeList testCaseNameList = itemElement.getElementsByTagName("testname"); - if (testCaseNameList != null) { - s_testCaseName = ((Element)testCaseNameList.item(0)).getTextContent(); - } - - if (s_isSanity == true && !s_testCaseName.equals("SANITY TEST")) - continue; - else if (s_isRegression == true && !(s_testCaseName.equals("SANITY TEST") || s_testCaseName.equals("REGRESSION TEST"))) - continue; - - // set class name - NodeList className = itemElement.getElementsByTagName("class"); - if ((className.getLength() == 0) || (className == null)) { - s_componentMap.put(s_testCaseName, "com.cloud.test.regression.VMApiTest"); - } else { - String name = ((Element)className.item(0)).getTextContent(); - s_componentMap.put(s_testCaseName, name); - } - - // set input file name - NodeList inputFileNameLst = itemElement.getElementsByTagName("filename"); - s_inputFile.put(s_testCaseName, new ArrayList()); - for (int k = 0; k < inputFileNameLst.getLength(); k++) { - String inputFileName = ((Element)inputFileNameLst.item(k)).getTextContent(); - s_inputFile.get(s_testCaseName).add(inputFileName); - } - } - } - - //If sanity test required, make sure that SANITY TEST componennt got loaded - if (s_isSanity == true && s_componentMap.size() == 0) { - s_logger.error("FAILURE!!! Failed to load SANITY TEST component. Verify that the test is uncommented in adapter.xml"); - System.exit(1); - } - - if (s_isRegression == true && s_componentMap.size() != 2) { - s_logger.error("FAILURE!!! Failed to load SANITY TEST or REGRESSION TEST components. Verify that these tests are uncommented in adapter.xml"); - System.exit(1); - } - - // put all keys from _componentMap to the ArrayList - Set set = s_componentMap.entrySet(); - Iterator it = set.iterator(); - while (it.hasNext()) { - Map.Entry me = (Map.Entry)it.next(); - String key = (String)me.getKey(); - s_keys.add(key); - } - - } - - public static boolean executeTest(String key, Class c, TestCase component) { - boolean finalResult = false; - try { - s_logger.info("Starting \"" + key + "\" test...\n\n"); - - // set global parameters - HashMap updateParam = new HashMap(); - updateParam.putAll(s_globalParameters); - component.setParam(updateParam); - - // set DB ip address - component.setConn(s_globalParameters.get("dbPassword")); - - // set commands list - component.setCommands(); - - // set input file - if (s_inputFile.get(key) != null) { - component.setInputFile(s_inputFile.get(key)); - } - - // set test case name - if (key != null) { - component.setTestCaseName(s_testCaseName); - } - - // execute method - s_result.set(component.executeTest()); - if (s_result.get().toString().equals("false")) { - s_logger.error("FAILURE!!! Test \"" + key + "\" failed\n\n\n"); - s_failure++; - } else { - finalResult = true; - s_logger.info("SUCCESS!!! Test \"" + key + "\" passed\n\n\n"); - } - - } catch (Exception ex) { - s_logger.error("error during test execution ", ex); - } - return finalResult; - } -} diff --git a/test/src-not-used/main/java/com/cloud/test/regression/VMApiTest.java b/test/src-not-used/main/java/com/cloud/test/regression/VMApiTest.java deleted file mode 100644 index 28877c59705..00000000000 --- a/test/src-not-used/main/java/com/cloud/test/regression/VMApiTest.java +++ /dev/null @@ -1,91 +0,0 @@ -// 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.test.regression; - -import java.util.HashMap; - -import org.apache.log4j.Logger; -import org.w3c.dom.Element; -import org.w3c.dom.Node; -import org.w3c.dom.NodeList; - -import com.cloud.test.regression.ApiCommand.ResponseType; - -public class VMApiTest extends TestCase { - public static final Logger s_logger = Logger.getLogger(VMApiTest.class.getName()); - - public VMApiTest() { - this.setClient(); - this.setParam(new HashMap()); - } - - @Override - public boolean executeTest() { - int error = 0; - Element rootElement = this.getInputFile().get(0).getDocumentElement(); - NodeList commandLst = rootElement.getElementsByTagName("command"); - - //Analyze each command, send request and build the array list of api commands - for (int i = 0; i < commandLst.getLength(); i++) { - Node fstNode = commandLst.item(i); - Element fstElmnt = (Element)fstNode; - - //new command - ApiCommand api = new ApiCommand(fstElmnt, this.getParam(), this.getCommands()); - - //send a command - api.sendCommand(this.getClient(), this.getConn()); - - //verify the response of the command - if ((api.getResponseType() == ResponseType.ERROR) && (api.getResponseCode() == 200)) { - s_logger.error("Test case " + api.getTestCaseInfo() + " failed. Command that was supposed to fail, passed. The command was sent with the following url " + - api.getUrl()); - error++; - } else if ((api.getResponseType() != ResponseType.ERROR) && (api.getResponseCode() == 200)) { - //set parameters for the future use - if (api.setParam(this.getParam()) == false) { - s_logger.error("Exiting the test...Command " + api.getName() + " didn't return parameters needed for the future use. The command was sent with url " + - api.getUrl()); - return false; - } - //verify parameters - if (api.verifyParam() == false) { - s_logger.error("Test " + api.getTestCaseInfo() + " failed. Verification for returned parameters failed. The command was sent with url " + - api.getUrl()); - error++; - } else { - s_logger.info("Test " + api.getTestCaseInfo() + " passed"); - } - } else if ((api.getResponseType() != ResponseType.ERROR) && (api.getResponseCode() != 200)) { - s_logger.error("Test case " + api.getTestCaseInfo() + " failed with an error code " + api.getResponseCode() + " . Command was sent with url " + - api.getUrl()); - if (api.getRequired() == true) { - s_logger.info("The command is required for the future use, so exiging"); - return false; - } - error++; - } else if (api.getTestCaseInfo() != null) { - s_logger.info("Test case " + api.getTestCaseInfo() + " passed"); - - } - } - if (error != 0) - return false; - else - return true; - } -} diff --git a/test/src-not-used/main/java/com/cloud/test/stress/SshTest.java b/test/src-not-used/main/java/com/cloud/test/stress/SshTest.java deleted file mode 100644 index a49dbadf1d5..00000000000 --- a/test/src-not-used/main/java/com/cloud/test/stress/SshTest.java +++ /dev/null @@ -1,90 +0,0 @@ -// 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.test.stress; - -import java.util.Arrays; -import java.util.Iterator; -import java.util.List; - -import org.apache.log4j.Logger; - -import com.trilead.ssh2.Connection; -import com.trilead.ssh2.Session; - -public class SshTest { - - public static final Logger s_logger = Logger.getLogger(SshTest.class.getName()); - public static String host = ""; - public static String password = "password"; - public static String url = "http://google.com"; - - public static void main(String[] args) { - - // Parameters - List argsList = Arrays.asList(args); - Iterator iter = argsList.iterator(); - while (iter.hasNext()) { - String arg = iter.next(); - if (arg.equals("-h")) { - host = iter.next(); - } - if (arg.equals("-p")) { - password = iter.next(); - } - - if (arg.equals("-u")) { - url = iter.next(); - } - } - - if (host == null || host.equals("")) { - s_logger.info("Did not receive a host back from test, ignoring ssh test"); - System.exit(2); - } - - if (password == null) { - s_logger.info("Did not receive a password back from test, ignoring ssh test"); - System.exit(2); - } - - try { - s_logger.info("Attempting to SSH into host " + host); - Connection conn = new Connection(host); - conn.connect(null, 60000, 60000); - - s_logger.info("User + ssHed successfully into host " + host); - - boolean isAuthenticated = conn.authenticateWithPassword("root", password); - - if (isAuthenticated == false) { - s_logger.info("Authentication failed for root with password" + password); - System.exit(2); - } - - String linuxCommand = "wget " + url; - Session sess = conn.openSession(); - sess.execCommand(linuxCommand); - sess.close(); - conn.close(); - - } catch (Exception e) { - s_logger.error("SSH test fail with error", e); - System.exit(2); - } - } - -} diff --git a/test/src-not-used/main/java/com/cloud/test/stress/StressTestDirectAttach.java b/test/src-not-used/main/java/com/cloud/test/stress/StressTestDirectAttach.java deleted file mode 100644 index 5625830b238..00000000000 --- a/test/src-not-used/main/java/com/cloud/test/stress/StressTestDirectAttach.java +++ /dev/null @@ -1,1353 +0,0 @@ -// 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.test.stress; - -import java.io.IOException; -import java.io.InputStream; -import java.math.BigInteger; -import java.net.URLEncoder; -import java.security.MessageDigest; -import java.security.NoSuchAlgorithmException; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.HashMap; -import java.util.Iterator; -import java.util.List; -import java.util.Map; -import java.util.Random; - -import javax.crypto.Mac; -import javax.crypto.spec.SecretKeySpec; -import javax.xml.parsers.DocumentBuilder; -import javax.xml.parsers.DocumentBuilderFactory; - -import org.apache.commons.codec.binary.Base64; -import org.apache.commons.httpclient.HttpClient; -import org.apache.commons.httpclient.HttpException; -import org.apache.commons.httpclient.HttpMethod; -import org.apache.commons.httpclient.methods.GetMethod; -import org.apache.log4j.Logger; -import org.apache.log4j.NDC; -import org.w3c.dom.Document; -import org.w3c.dom.Element; -import org.w3c.dom.Node; -import org.w3c.dom.NodeList; - -import com.trilead.ssh2.ChannelCondition; -import com.trilead.ssh2.Connection; -import com.trilead.ssh2.SCPClient; -import com.trilead.ssh2.Session; - -import com.cloud.utils.exception.CloudRuntimeException; - -public class StressTestDirectAttach { - private static long sleepTime = 180000L; // default 0 - private static boolean cleanUp = true; - public static final Logger s_logger = Logger.getLogger(StressTestDirectAttach.class.getName()); - private static boolean repeat = true; - private static String[] users = null; - private static boolean internet = false; - private static ThreadLocal s_linuxIP = new ThreadLocal(); - private static ThreadLocal s_linuxVmId = new ThreadLocal(); - private static ThreadLocal s_linuxVmId1 = new ThreadLocal(); - private static ThreadLocal s_linuxPassword = new ThreadLocal(); - private static ThreadLocal s_windowsIP = new ThreadLocal(); - private static ThreadLocal s_secretKey = new ThreadLocal(); - private static ThreadLocal s_apiKey = new ThreadLocal(); - private static ThreadLocal s_userId = new ThreadLocal(); - private static ThreadLocal s_account = new ThreadLocal(); - private static ThreadLocal s_domainRouterId = new ThreadLocal(); - private static ThreadLocal s_newVolume = new ThreadLocal(); - private static ThreadLocal s_newVolume1 = new ThreadLocal(); - private static DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance(); - private static int usageIterator = 1; - private static int numThreads = 1; - private static int wait = 5000; - private static String accountName = null; - private static String zoneId = "1"; - private static String serviceOfferingId = "13"; - private static String diskOfferingId = "11"; - private static String diskOfferingId1 = "12"; - - private static final int MAX_RETRY_LINUX = 10; - private static final int MAX_RETRY_WIN = 10; - - public static void main(String[] args) { - String host = "http://localhost"; - String port = "8092"; - String devPort = "8080"; - String apiUrl = "/client/api"; - - try { - // Parameters - List argsList = Arrays.asList(args); - Iterator iter = argsList.iterator(); - while (iter.hasNext()) { - String arg = iter.next(); - // host - if (arg.equals("-h")) { - host = "http://" + iter.next(); - } - - if (arg.equals("-p")) { - port = iter.next(); - } - if (arg.equals("-dp")) { - devPort = iter.next(); - } - - if (arg.equals("-t")) { - numThreads = Integer.parseInt(iter.next()); - } - - if (arg.equals("-s")) { - sleepTime = Long.parseLong(iter.next()); - } - if (arg.equals("-a")) { - accountName = iter.next(); - } - - if (arg.equals("-c")) { - cleanUp = Boolean.parseBoolean(iter.next()); - if (!cleanUp) - sleepTime = 0L; // no need to wait if we don't ever - // cleanup - } - - if (arg.equals("-r")) { - repeat = Boolean.parseBoolean(iter.next()); - } - - if (arg.equals("-i")) { - internet = Boolean.parseBoolean(iter.next()); - } - - if (arg.equals("-w")) { - wait = Integer.parseInt(iter.next()); - } - - if (arg.equals("-z")) { - zoneId = iter.next(); - } - - if (arg.equals("-so")) { - serviceOfferingId = iter.next(); - } - - } - - final String server = host + ":" + port + "/"; - final String developerServer = host + ":" + devPort + apiUrl; - s_logger.info("Starting test against server: " + server + " with " + numThreads + " thread(s)"); - if (cleanUp) - s_logger.info("Clean up is enabled, each test will wait " + sleepTime + " ms before cleaning up"); - - for (int i = 0; i < numThreads; i++) { - new Thread(new Runnable() { - @Override - public void run() { - do { - String username = null; - try { - long now = System.currentTimeMillis(); - Random ran = new Random(); - username = Math.abs(ran.nextInt()) + "-user"; - NDC.push(username); - - s_logger.info("Starting test for the user " + username); - int response = executeDeployment(server, developerServer, username); - boolean success = false; - String reason = null; - - if (response == 200) { - success = true; - if (internet) { - s_logger.info("Deploy successful...waiting 5 minute before SSH tests"); - Thread.sleep(300000L); // Wait 60 - // seconds so - // the windows VM - // can boot up and do a sys prep. - - s_logger.info("Begin Linux SSH test for account " + s_account.get()); - reason = sshTest(s_linuxIP.get(), s_linuxPassword.get()); - - if (reason == null) { - s_logger.info("Linux SSH test successful for account " + s_account.get()); - } - } - if (reason == null) { - if (internet) { - s_logger.info("Windows SSH test successful for account " + s_account.get()); - } else { - s_logger.info("deploy test successful....now cleaning up"); - if (cleanUp) { - s_logger.info("Waiting " + sleepTime + " ms before cleaning up vms"); - Thread.sleep(sleepTime); - } else { - success = true; - } - } - - if (usageIterator >= numThreads) { - int eventsAndBillingResponseCode = executeEventsAndBilling(server, developerServer); - s_logger.info("events and usage records command finished with response code: " + eventsAndBillingResponseCode); - usageIterator = 1; - - } else { - s_logger.info("Skipping events and usage records for this user: usageIterator " + usageIterator + " and number of Threads " + - numThreads); - usageIterator++; - } - - if ((users == null) && (accountName == null)) { - s_logger.info("Sending cleanup command"); - int cleanupResponseCode = executeCleanup(server, developerServer, username); - s_logger.info("cleanup command finished with response code: " + cleanupResponseCode); - success = (cleanupResponseCode == 200); - } else { - s_logger.info("Sending stop DomR / destroy VM command"); - int stopResponseCode = executeStop(server, developerServer, username); - s_logger.info("stop(destroy) command finished with response code: " + stopResponseCode); - success = (stopResponseCode == 200); - } - - } else { - // Just stop but don't destroy the - // VMs/Routers - s_logger.info("SSH test failed for account " + s_account.get() + "with reason '" + reason + "', stopping VMs"); - int stopResponseCode = executeStop(server, developerServer, username); - s_logger.info("stop command finished with response code: " + stopResponseCode); - success = false; // since the SSH test - // failed, mark the - // whole test as - // failure - } - } else { - // Just stop but don't destroy the - // VMs/Routers - s_logger.info("Deploy test failed with reason '" + reason + "', stopping VMs"); - int stopResponseCode = executeStop(server, developerServer, username); - s_logger.info("stop command finished with response code: " + stopResponseCode); - success = false; // since the deploy test - // failed, mark the - // whole test as failure - } - - if (success) { - s_logger.info("***** Completed test for user : " + username + " in " + ((System.currentTimeMillis() - now) / 1000L) + " seconds"); - - } else { - s_logger.info("##### FAILED test for user : " + username + " in " + ((System.currentTimeMillis() - now) / 1000L) + - " seconds with reason : " + reason); - } - s_logger.info("Sleeping for " + wait + " seconds before starting next iteration"); - Thread.sleep(wait); - } catch (Exception e) { - s_logger.warn("Error in thread", e); - try { - int stopResponseCode = executeStop(server, developerServer, username); - s_logger.info("stop response code: " + stopResponseCode); - } catch (Exception e1) { - s_logger.info("[ignored]" - + "error executing stop during stress test: " + e1.getLocalizedMessage()); - } - } finally { - NDC.clear(); - } - } while (repeat); - } - }).start(); - } - } catch (Exception e) { - s_logger.error(e); - } - } - - public static Map> getMultipleValuesFromXML(InputStream is, String[] tagNames) { - Map> returnValues = new HashMap>(); - try { - 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) { - s_logger.error("no " + tagNames[i] + " tag in XML response...returning null"); - } else { - List valueList = new ArrayList(); - for (int j = 0; j < targetNodes.getLength(); j++) { - Node node = targetNodes.item(j); - valueList.add(node.getTextContent()); - } - returnValues.put(tagNames[i], valueList); - } - } - } catch (Exception ex) { - s_logger.error(ex); - } - return returnValues; - } - - public static Map getSingleValueFromXML(InputStream is, String[] tagNames) { - Map returnValues = new HashMap(); - try { - 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) { - s_logger.error("no " + tagNames[i] + " tag in XML response...returning null"); - } else { - returnValues.put(tagNames[i], targetNodes.item(0).getTextContent()); - } - } - } catch (Exception ex) { - s_logger.error("error processing XML", ex); - } - return returnValues; - } - - public static Map getSingleValueFromXML(Element rootElement, String[] tagNames) { - Map returnValues = new HashMap(); - if (rootElement == null) { - s_logger.error("Root element is null, can't get single value from xml"); - return null; - } - try { - for (int i = 0; i < tagNames.length; i++) { - NodeList targetNodes = rootElement.getElementsByTagName(tagNames[i]); - if (targetNodes.getLength() <= 0) { - s_logger.error("no " + tagNames[i] + " tag in XML response...returning null"); - } else { - returnValues.put(tagNames[i], targetNodes.item(0).getTextContent()); - } - } - } catch (Exception ex) { - s_logger.error("error processing XML", ex); - } - return returnValues; - } - - private static List getNonSourceNatIPs(InputStream is) { - List returnValues = new ArrayList(); - try { - DocumentBuilder docBuilder = factory.newDocumentBuilder(); - Document doc = docBuilder.parse(is); - Element rootElement = doc.getDocumentElement(); - NodeList allocatedIpAddrNodes = rootElement.getElementsByTagName("publicipaddress"); - for (int i = 0; i < allocatedIpAddrNodes.getLength(); i++) { - Node allocatedIpAddrNode = allocatedIpAddrNodes.item(i); - NodeList childNodes = allocatedIpAddrNode.getChildNodes(); - String ipAddress = null; - boolean isSourceNat = true; // assume it's source nat until we - // find otherwise - for (int j = 0; j < childNodes.getLength(); j++) { - Node n = childNodes.item(j); - if ("ipaddress".equals(n.getNodeName())) { - ipAddress = n.getTextContent(); - } else if ("issourcenat".equals(n.getNodeName())) { - isSourceNat = Boolean.parseBoolean(n.getTextContent()); - } - } - if ((ipAddress != null) && !isSourceNat) { - returnValues.add(ipAddress); - } - } - } catch (Exception ex) { - s_logger.error(ex); - } - return returnValues; - } - - private static List getSourceNatIPs(InputStream is) { - List returnValues = new ArrayList(); - try { - DocumentBuilder docBuilder = factory.newDocumentBuilder(); - Document doc = docBuilder.parse(is); - Element rootElement = doc.getDocumentElement(); - NodeList allocatedIpAddrNodes = rootElement.getElementsByTagName("publicipaddress"); - for (int i = 0; i < allocatedIpAddrNodes.getLength(); i++) { - Node allocatedIpAddrNode = allocatedIpAddrNodes.item(i); - NodeList childNodes = allocatedIpAddrNode.getChildNodes(); - String ipAddress = null; - boolean isSourceNat = false; // assume it's *not* source nat until we find otherwise - for (int j = 0; j < childNodes.getLength(); j++) { - Node n = childNodes.item(j); - if ("ipaddress".equals(n.getNodeName())) { - ipAddress = n.getTextContent(); - } else if ("issourcenat".equals(n.getNodeName())) { - isSourceNat = Boolean.parseBoolean(n.getTextContent()); - } - } - if ((ipAddress != null) && isSourceNat) { - returnValues.add(ipAddress); - } - } - } catch (Exception ex) { - s_logger.error(ex); - } - return returnValues; - } - - private static String executeRegistration(String server, String username, String password) throws HttpException, IOException { - String url = server + "?command=registerUserKeys&id=" + s_userId.get().toString(); - s_logger.info("registering: " + username); - String returnValue = null; - HttpClient client = new HttpClient(); - HttpMethod method = new GetMethod(url); - int responseCode = client.executeMethod(method); - if (responseCode == 200) { - InputStream is = method.getResponseBodyAsStream(); - Map requestKeyValues = getSingleValueFromXML(is, new String[] {"apikey", "secretkey"}); - s_apiKey.set(requestKeyValues.get("apikey")); - returnValue = requestKeyValues.get("secretkey"); - } else { - s_logger.error("registration failed with error code: " + responseCode); - } - return returnValue; - } - - private static Integer executeDeployment(String server, String developerServer, String username) throws HttpException, IOException { - // test steps: - // - create user - // - deploy Windows VM - // - deploy Linux VM - // - associate IP address - // - create two IP forwarding rules - // - create load balancer rule - // - list IP forwarding rules - // - list load balancer rules - - // ----------------------------- - // CREATE USER - // ----------------------------- - String encodedUsername = URLEncoder.encode(username, "UTF-8"); - String encryptedPassword = createMD5Password(username); - String encodedPassword = URLEncoder.encode(encryptedPassword, "UTF-8"); - - String url = - server + "?command=createUser&username=" + encodedUsername + "&password=" + encodedPassword + - "&firstname=Test&lastname=Test&email=test@vmops.com&domainId=1&accounttype=0"; - if (accountName != null) { - url = - server + "?command=createUser&username=" + encodedUsername + "&password=" + encodedPassword + - "&firstname=Test&lastname=Test&email=test@vmops.com&domainId=1&accounttype=0&account=" + accountName; - } - HttpClient client = new HttpClient(); - HttpMethod method = new GetMethod(url); - int responseCode = client.executeMethod(method); - long userId = -1; - if (responseCode == 200) { - InputStream is = method.getResponseBodyAsStream(); - Map userIdValues = getSingleValueFromXML(is, new String[] {"id", "account"}); - String userIdStr = userIdValues.get("id"); - s_logger.info("created user " + username + " with id " + userIdStr); - if (userIdStr != null) { - userId = Long.parseLong(userIdStr); - s_userId.set(userId); - s_account.set(userIdValues.get("account")); - if (userId == -1) { - s_logger.error("create user (" + username + ") failed to retrieve a valid user id, aborting depolyment test"); - return -1; - } - } - } else { - s_logger.error("create user test failed for user " + username + " with error code :" + responseCode); - return responseCode; - } - - s_secretKey.set(executeRegistration(server, username, username)); - - if (s_secretKey.get() == null) { - s_logger.error("FAILED to retrieve secret key during registration, skipping user: " + username); - return -1; - } else { - s_logger.info("got secret key: " + s_secretKey.get()); - s_logger.info("got api key: " + s_apiKey.get()); - } - - // --------------------------------- - // CREATE NETWORK GROUP AND ADD INGRESS RULE TO IT - // --------------------------------- - String networkAccount = null; - if (accountName != null) { - networkAccount = accountName; - } else { - networkAccount = encodedUsername; - } - String encodedApiKey = URLEncoder.encode(s_apiKey.get(), "UTF-8"); - String requestToSign = "apikey=" + encodedApiKey + "&command=createSecurityGroup&name=" + encodedUsername; - requestToSign = requestToSign.toLowerCase(); - String signature = signRequest(requestToSign, s_secretKey.get()); - String encodedSignature = URLEncoder.encode(signature, "UTF-8"); - url = developerServer + "?command=createSecurityGroup&name=" + encodedUsername + "&apikey=" + encodedApiKey + "&signature=" + encodedSignature; - method = new GetMethod(url); - responseCode = client.executeMethod(method); - if (responseCode == 200) { - InputStream is = method.getResponseBodyAsStream(); - Map values = getSingleValueFromXML(is, new String[] {"id"}); - - if (values.get("id") == null) { - s_logger.info("Create network rule response code: 401"); - return 401; - } else { - s_logger.info("Create security group response code: " + responseCode); - } - } else { - s_logger.error("Create security group failed with error code: " + responseCode + ". Following URL was sent: " + url); - return responseCode; - } - - String encodedCidr = URLEncoder.encode("192.168.1.143/32", "UTF-8"); - url = - server + "?command=authorizeSecurityGroupIngress&cidrlist=" + encodedCidr + "&endport=22&" + "securitygroupname=" + encodedUsername + - "&protocol=tcp&startport=22&account=" + networkAccount + "&domainid=1"; - - method = new GetMethod(url); - responseCode = client.executeMethod(method); - if (responseCode == 200) { - InputStream input = method.getResponseBodyAsStream(); - Element el = queryAsyncJobResult(server, input); - Map values = getSingleValueFromXML(el, new String[] {"id"}); - - if (values.get("id") == null) { - s_logger.info("Authorise security group ingress response code: 401"); - return 401; - } else { - s_logger.info("Authorise security group ingress response code: " + responseCode); - } - } else { - s_logger.error("Authorise security group ingress failed with error code: " + responseCode + ". Following URL was sent: " + url); - return responseCode; - } - - // --------------------------------- - // DEPLOY LINUX VM - // --------------------------------- - { - long templateId = 2; - String encodedZoneId = URLEncoder.encode("" + zoneId, "UTF-8"); - String encodedServiceOfferingId = URLEncoder.encode("" + serviceOfferingId, "UTF-8"); - String encodedTemplateId = URLEncoder.encode("" + templateId, "UTF-8"); - encodedApiKey = URLEncoder.encode(s_apiKey.get(), "UTF-8"); - requestToSign = - "apikey=" + encodedApiKey + "&command=deployVirtualMachine&securitygrouplist=" + encodedUsername + "&serviceofferingid=" + encodedServiceOfferingId + - "&templateid=" + encodedTemplateId + "&zoneid=" + encodedZoneId; - requestToSign = requestToSign.toLowerCase(); - signature = signRequest(requestToSign, s_secretKey.get()); - encodedSignature = URLEncoder.encode(signature, "UTF-8"); - url = - developerServer + "?command=deployVirtualMachine&securitygrouplist=" + encodedUsername + "&zoneid=" + encodedZoneId + "&serviceofferingid=" + - encodedServiceOfferingId + "&templateid=" + encodedTemplateId + "&apikey=" + encodedApiKey + "&signature=" + encodedSignature; - - method = new GetMethod(url); - responseCode = client.executeMethod(method); - if (responseCode == 200) { - InputStream input = method.getResponseBodyAsStream(); - Element el = queryAsyncJobResult(server, input); - Map values = getSingleValueFromXML(el, new String[] {"id", "ipaddress"}); - - if ((values.get("ipaddress") == null) || (values.get("id") == null)) { - s_logger.info("deploy linux vm response code: 401"); - return 401; - } else { - s_logger.info("deploy linux vm response code: " + responseCode); - long linuxVMId = Long.parseLong(values.get("id")); - s_logger.info("got linux virtual machine id: " + linuxVMId); - s_linuxVmId.set(values.get("id")); - s_linuxIP.set(values.get("ipaddress")); - s_linuxPassword.set("rs-ccb35ea5"); - } - } else { - s_logger.error("deploy linux vm failed with error code: " + responseCode + ". Following URL was sent: " + url); - return responseCode; - } - } - - //Create a new volume - { - url = server + "?command=createVolume&diskofferingid=" + diskOfferingId + "&zoneid=" + zoneId + "&name=newvolume&account=" + s_account.get() + "&domainid=1"; - s_logger.info("Creating volume...."); - client = new HttpClient(); - method = new GetMethod(url); - responseCode = client.executeMethod(method); - if (responseCode == 200) { - InputStream input = method.getResponseBodyAsStream(); - Element el = queryAsyncJobResult(server, input); - Map values = getSingleValueFromXML(el, new String[] {"id"}); - - if (values.get("id") == null) { - s_logger.info("create volume response code: 401"); - return 401; - } else { - s_logger.info("create volume response code: " + responseCode); - String volumeId = values.get("id"); - s_logger.info("got volume id: " + volumeId); - s_newVolume.set(volumeId); - } - } else { - s_logger.error("create volume failed with error code: " + responseCode + ". Following URL was sent: " + url); - return responseCode; - } - } - - //attach a new volume to the vm - { - url = server + "?command=attachVolume&id=" + s_newVolume.get() + "&virtualmachineid=" + s_linuxVmId.get(); - s_logger.info("Attaching volume with id " + s_newVolume.get() + " to the vm " + s_linuxVmId.get()); - client = new HttpClient(); - method = new GetMethod(url); - responseCode = client.executeMethod(method); - s_logger.info("Attach data volume response code: " + responseCode); - if (responseCode == 200) { - InputStream input = method.getResponseBodyAsStream(); - Element el = queryAsyncJobResult(server, input); - Map values = getSingleValueFromXML(el, new String[] {"id"}); - - if (values.get("id") == null) { - s_logger.info("Attach volume response code: 401"); - return 401; - } else { - s_logger.info("Attach volume response code: " + responseCode); - } - } else { - s_logger.error("Attach volume failed with error code: " + responseCode + ". Following URL was sent: " + url); - return responseCode; - } - } - - //DEPLOY SECOND VM, ADD VOLUME TO IT - - // --------------------------------- - // DEPLOY another linux vm - // --------------------------------- - { - long templateId = 2; - String encodedZoneId = URLEncoder.encode("" + zoneId, "UTF-8"); - String encodedServiceOfferingId = URLEncoder.encode("" + serviceOfferingId, "UTF-8"); - String encodedTemplateId = URLEncoder.encode("" + templateId, "UTF-8"); - encodedApiKey = URLEncoder.encode(s_apiKey.get(), "UTF-8"); - requestToSign = - "apikey=" + encodedApiKey + "&command=deployVirtualMachine&securitygrouplist=" + encodedUsername + "&serviceofferingid=" + encodedServiceOfferingId + - "&templateid=" + encodedTemplateId + "&zoneid=" + encodedZoneId; - requestToSign = requestToSign.toLowerCase(); - signature = signRequest(requestToSign, s_secretKey.get()); - encodedSignature = URLEncoder.encode(signature, "UTF-8"); - url = - developerServer + "?command=deployVirtualMachine&securitygrouplist=" + encodedUsername + "&zoneid=" + encodedZoneId + "&serviceofferingid=" + - encodedServiceOfferingId + "&templateid=" + encodedTemplateId + "&apikey=" + encodedApiKey + "&signature=" + encodedSignature; - - method = new GetMethod(url); - responseCode = client.executeMethod(method); - if (responseCode == 200) { - InputStream input = method.getResponseBodyAsStream(); - Element el = queryAsyncJobResult(server, input); - Map values = getSingleValueFromXML(el, new String[] {"id", "ipaddress"}); - - if ((values.get("ipaddress") == null) || (values.get("id") == null)) { - s_logger.info("deploy linux vm response code: 401"); - return 401; - } else { - s_logger.info("deploy linux vm response code: " + responseCode); - long linuxVMId = Long.parseLong(values.get("id")); - s_logger.info("got linux virtual machine id: " + linuxVMId); - s_linuxVmId1.set(values.get("id")); - } - } else { - s_logger.error("deploy linux vm failed with error code: " + responseCode + ". Following URL was sent: " + url); - return responseCode; - } - } - - //Create a new volume - { - url = server + "?command=createVolume&diskofferingid=" + diskOfferingId1 + "&zoneid=" + zoneId + "&name=newvolume1&account=" + s_account.get() + "&domainid=1"; - s_logger.info("Creating volume...."); - client = new HttpClient(); - method = new GetMethod(url); - responseCode = client.executeMethod(method); - if (responseCode == 200) { - InputStream input = method.getResponseBodyAsStream(); - Element el = queryAsyncJobResult(server, input); - Map values = getSingleValueFromXML(el, new String[] {"id"}); - - if (values.get("id") == null) { - s_logger.info("create volume response code: 401"); - return 401; - } else { - s_logger.info("create volume response code: " + responseCode); - String volumeId = values.get("id"); - s_logger.info("got volume id: " + volumeId); - s_newVolume1.set(volumeId); - } - } else { - s_logger.error("create volume failed with error code: " + responseCode + ". Following URL was sent: " + url); - return responseCode; - } - } - - //attach a new volume to the vm - { - url = server + "?command=attachVolume&id=" + s_newVolume1.get() + "&virtualmachineid=" + s_linuxVmId1.get(); - s_logger.info("Attaching volume with id " + s_newVolume1.get() + " to the vm " + s_linuxVmId1.get()); - client = new HttpClient(); - method = new GetMethod(url); - responseCode = client.executeMethod(method); - s_logger.info("Attach data volume response code: " + responseCode); - if (responseCode == 200) { - InputStream input = method.getResponseBodyAsStream(); - Element el = queryAsyncJobResult(server, input); - Map values = getSingleValueFromXML(el, new String[] {"id"}); - - if (values.get("id") == null) { - s_logger.info("Attach volume response code: 401"); - return 401; - } else { - s_logger.info("Attach volume response code: " + responseCode); - } - } else { - s_logger.error("Attach volume failed with error code: " + responseCode + ". Following URL was sent: " + url); - return responseCode; - } - } - return 200; - } - - private static int executeCleanup(String server, String developerServer, String username) throws HttpException, IOException { - // test steps: - // - get user - // - delete user - - // ----------------------------- - // GET USER - // ----------------------------- - String userId = s_userId.get().toString(); - String encodedUserId = URLEncoder.encode(userId, "UTF-8"); - String url = server + "?command=listUsers&id=" + encodedUserId; - s_logger.info("Cleaning up resources for user: " + userId + " with url " + url); - HttpClient client = new HttpClient(); - HttpMethod method = new GetMethod(url); - int responseCode = client.executeMethod(method); - s_logger.info("get user response code: " + responseCode); - if (responseCode == 200) { - InputStream is = method.getResponseBodyAsStream(); - Map userInfo = getSingleValueFromXML(is, new String[] {"username", "id", "account"}); - if (!username.equals(userInfo.get("username"))) { - s_logger.error("get user failed to retrieve requested user, aborting cleanup test" + ". Following URL was sent: " + url); - return -1; - } - - } else { - s_logger.error("get user failed with error code: " + responseCode + ". Following URL was sent: " + url); - return responseCode; - } - - // ----------------------------- - // UPDATE USER - // ----------------------------- - { - url = server + "?command=updateUser&id=" + userId + "&firstname=delete&lastname=me"; - client = new HttpClient(); - method = new GetMethod(url); - responseCode = client.executeMethod(method); - s_logger.info("update user response code: " + responseCode); - if (responseCode == 200) { - InputStream is = method.getResponseBodyAsStream(); - Map success = getSingleValueFromXML(is, new String[] {"success"}); - s_logger.info("update user..success? " + success.get("success")); - } else { - s_logger.error("update user failed with error code: " + responseCode + ". Following URL was sent: " + url); - return responseCode; - } - } - - // ----------------------------- - // Execute reboot/stop/start commands for the VMs before deleting the account - made to exercise xen - // ----------------------------- - - //Reboot centos VM - String encodedApiKey = URLEncoder.encode(s_apiKey.get(), "UTF-8"); - String requestToSign = "apikey=" + encodedApiKey + "&command=rebootVirtualMachine&id=" + s_linuxVmId.get(); - requestToSign = requestToSign.toLowerCase(); - String signature = signRequest(requestToSign, s_secretKey.get()); - String encodedSignature = URLEncoder.encode(signature, "UTF-8"); - - url = developerServer + "?command=rebootVirtualMachine&id=" + s_linuxVmId.get() + "&apikey=" + encodedApiKey + "&signature=" + encodedSignature; - client = new HttpClient(); - method = new GetMethod(url); - responseCode = client.executeMethod(method); - s_logger.info("Reboot VM response code: " + responseCode); - if (responseCode == 200) { - InputStream input = method.getResponseBodyAsStream(); - Element el = queryAsyncJobResult(server, input); - Map success = getSingleValueFromXML(el, new String[] {"success"}); - s_logger.info("VM was rebooted with the status: " + success.get("success")); - } else { - s_logger.error(" VM test failed with error code: " + responseCode + ". Following URL was sent: " + url); - return responseCode; - } - - //Stop centos VM - requestToSign = "apikey=" + encodedApiKey + "&command=stopVirtualMachine&id=" + s_linuxVmId.get(); - requestToSign = requestToSign.toLowerCase(); - signature = signRequest(requestToSign, s_secretKey.get()); - encodedSignature = URLEncoder.encode(signature, "UTF-8"); - - url = developerServer + "?command=stopVirtualMachine&id=" + s_linuxVmId.get() + "&apikey=" + encodedApiKey + "&signature=" + encodedSignature; - client = new HttpClient(); - method = new GetMethod(url); - responseCode = client.executeMethod(method); - s_logger.info("Stop VM response code: " + responseCode); - if (responseCode == 200) { - InputStream input = method.getResponseBodyAsStream(); - Element el = queryAsyncJobResult(server, input); - Map success = getSingleValueFromXML(el, new String[] {"success"}); - s_logger.info("VM was stopped with the status: " + success.get("success")); - } else { - s_logger.error("Stop VM test failed with error code: " + responseCode + ". Following URL was sent: " + url); - return responseCode; - } - - //Start centos VM - requestToSign = "apikey=" + encodedApiKey + "&command=startVirtualMachine&id=" + s_linuxVmId.get(); - requestToSign = requestToSign.toLowerCase(); - signature = signRequest(requestToSign, s_secretKey.get()); - encodedSignature = URLEncoder.encode(signature, "UTF-8"); - - url = developerServer + "?command=startVirtualMachine&id=" + s_linuxVmId.get() + "&apikey=" + encodedApiKey + "&signature=" + encodedSignature; - client = new HttpClient(); - method = new GetMethod(url); - responseCode = client.executeMethod(method); - s_logger.info("Start VM response code: " + responseCode); - - if (responseCode == 200) { - InputStream input = method.getResponseBodyAsStream(); - Element el = queryAsyncJobResult(server, input); - Map success = getSingleValueFromXML(el, new String[] {"id"}); - - if (success.get("id") == null) { - s_logger.info("Start linux vm response code: 401"); - return 401; - } else { - s_logger.info("Start vm response code: " + responseCode); - } - - s_logger.info("VM was started with the status: " + success.get("success")); - } else { - s_logger.error("Start VM test failed with error code: " + responseCode + ". Following URL was sent: " + url); - return responseCode; - } - -//// // ----------------------------- -//// // DISABLE USER -//// // ----------------------------- -// { -// url = server + "?command=disableUser&id=" + userId; -// client = new HttpClient(); -// method = new GetMethod(url); -// responseCode = client.executeMethod(method); -// s_logger.info("disable user response code: " + responseCode); -// if (responseCode == 200) { -// InputStream input = method.getResponseBodyAsStream(); -// Element el = queryAsyncJobResult(server, input); -// s_logger -// .info("Disabled user successfully"); -// } else { -// s_logger.error("disable user failed with error code: " + responseCode + ". Following URL was sent: " + url); -// return responseCode; -// } -// } - - // ----------------------------- - // DELETE USER - // ----------------------------- - { - url = server + "?command=deleteUser&id=" + userId; - client = new HttpClient(); - method = new GetMethod(url); - responseCode = client.executeMethod(method); - s_logger.info("delete user response code: " + responseCode); - if (responseCode == 200) { - InputStream input = method.getResponseBodyAsStream(); - Element el = queryAsyncJobResult(server, input); - s_logger.info("Deleted user successfully"); - } else { - s_logger.error("delete user failed with error code: " + responseCode + ". Following URL was sent: " + url); - return responseCode; - } - } - return responseCode; - } - - private static int executeEventsAndBilling(String server, String developerServer) throws HttpException, IOException { - // test steps: - // - get all the events in the system for all users in the system - // - generate all the usage records in the system - // - get all the usage records in the system - - // ----------------------------- - // GET EVENTS - // ----------------------------- - String url = server + "?command=listEvents&page=1&account=" + s_account.get(); - - s_logger.info("Getting events for the account " + s_account.get()); - HttpClient client = new HttpClient(); - HttpMethod method = new GetMethod(url); - int responseCode = client.executeMethod(method); - s_logger.info("get events response code: " + responseCode); - if (responseCode == 200) { - InputStream is = method.getResponseBodyAsStream(); - Map> eventDescriptions = getMultipleValuesFromXML(is, new String[] {"description"}); - List descriptionText = eventDescriptions.get("description"); - if (descriptionText == null) { - s_logger.info("no events retrieved..."); - } else { - for (String text : descriptionText) { - s_logger.info("event: " + text); - } - } - } else { - s_logger.error("list events failed with error code: " + responseCode + ". Following URL was sent: " + url); - - return responseCode; - } - return responseCode; - } - - private static int executeStop(String server, String developerServer, String username) throws HttpException, IOException { - // test steps: - // - get userId for the given username - // - list virtual machines for the user - // - stop all virtual machines - // - get ip addresses for the user - // - release ip addresses - - // ----------------------------- - // GET USER - // ----------------------------- - String userId = s_userId.get().toString(); - String encodedUserId = URLEncoder.encode(userId, "UTF-8"); - - String url = server + "?command=listUsers&id=" + encodedUserId; - s_logger.info("Stopping resources for user: " + username); - HttpClient client = new HttpClient(); - HttpMethod method = new GetMethod(url); - int responseCode = client.executeMethod(method); - s_logger.info("get user response code: " + responseCode); - if (responseCode == 200) { - InputStream is = method.getResponseBodyAsStream(); - Map userIdValues = getSingleValueFromXML(is, new String[] {"id"}); - String userIdStr = userIdValues.get("id"); - if (userIdStr != null) { - userId = userIdStr; - if (userId == null) { - s_logger.error("get user failed to retrieve a valid user id, aborting depolyment test" + ". Following URL was sent: " + url); - return -1; - } - } - } else { - s_logger.error("get user failed with error code: " + responseCode + ". Following URL was sent: " + url); - return responseCode; - } - - { - // ---------------------------------- - // LIST VIRTUAL MACHINES - // ---------------------------------- - String encodedApiKey = URLEncoder.encode(s_apiKey.get(), "UTF-8"); - String requestToSign = "apikey=" + encodedApiKey + "&command=listVirtualMachines"; - requestToSign = requestToSign.toLowerCase(); - String signature = signRequest(requestToSign, s_secretKey.get()); - String encodedSignature = URLEncoder.encode(signature, "UTF-8"); - - url = developerServer + "?command=listVirtualMachines&apikey=" + encodedApiKey + "&signature=" + encodedSignature; - - s_logger.info("Listing all virtual machines for the user with url " + url); - String[] vmIds = null; - client = new HttpClient(); - method = new GetMethod(url); - responseCode = client.executeMethod(method); - s_logger.info("list virtual machines response code: " + responseCode); - if (responseCode == 200) { - InputStream is = method.getResponseBodyAsStream(); - Map> vmIdValues = getMultipleValuesFromXML(is, new String[] {"id"}); - if (vmIdValues.containsKey("id")) { - List vmIdList = vmIdValues.get("id"); - if (vmIdList != null) { - vmIds = new String[vmIdList.size()]; - vmIdList.toArray(vmIds); - String vmIdLogStr = ""; - if ((vmIds != null) && (vmIds.length > 0)) { - vmIdLogStr = vmIds[0]; - for (int i = 1; i < vmIds.length; i++) { - vmIdLogStr = vmIdLogStr + "," + vmIds[i]; - } - } - s_logger.info("got virtual machine ids: " + vmIdLogStr); - } - } - - } else { - s_logger.error("list virtual machines test failed with error code: " + responseCode + ". Following URL was sent: " + url); - return responseCode; - } - - // ---------------------------------- - // STOP/DESTROY VIRTUAL MACHINES - // ---------------------------------- - if (vmIds != null) { - for (String vmId : vmIds) { - requestToSign = "apikey=" + encodedApiKey + "&command=stopVirtualMachine&id=" + vmId; - requestToSign = requestToSign.toLowerCase(); - signature = signRequest(requestToSign, s_secretKey.get()); - encodedSignature = URLEncoder.encode(signature, "UTF-8"); - - url = developerServer + "?command=stopVirtualMachine&id=" + vmId + "&apikey=" + encodedApiKey + "&signature=" + encodedSignature; - client = new HttpClient(); - method = new GetMethod(url); - responseCode = client.executeMethod(method); - s_logger.info("StopVirtualMachine" + " [" + vmId + "] response code: " + responseCode); - if (responseCode == 200) { - InputStream input = method.getResponseBodyAsStream(); - Element el = queryAsyncJobResult(server, input); - Map success = getSingleValueFromXML(el, new String[] {"success"}); - s_logger.info("StopVirtualMachine..success? " + success.get("success")); - } else { - s_logger.error("Stop virtual machine test failed with error code: " + responseCode + ". Following URL was sent: " + url); - return responseCode; - } - } - } - -// { -// url = server + "?command=deleteUser&id=" + userId; -// client = new HttpClient(); -// method = new GetMethod(url); -// responseCode = client.executeMethod(method); -// s_logger.info("delete user response code: " + responseCode); -// if (responseCode == 200) { -// InputStream input = method.getResponseBodyAsStream(); -// Element el = queryAsyncJobResult(server, input); -// s_logger -// .info("Deleted user successfully"); -// } else { -// s_logger.error("delete user failed with error code: " + responseCode + ". Following URL was sent: " + url); -// return responseCode; -// } -// } - - } - - s_linuxIP.set(""); - s_linuxVmId.set(""); - s_linuxPassword.set(""); - s_windowsIP.set(""); - s_secretKey.set(""); - s_apiKey.set(""); - s_userId.set(Long.parseLong("0")); - s_account.set(""); - s_domainRouterId.set(""); - return responseCode; - } - - public static String signRequest(String request, String key) { - try { - Mac mac = Mac.getInstance("HmacSHA1"); - SecretKeySpec keySpec = new SecretKeySpec(key.getBytes(), "HmacSHA1"); - mac.init(keySpec); - mac.update(request.getBytes()); - byte[] encryptedBytes = mac.doFinal(); - return Base64.encodeBase64String(encryptedBytes); - } catch (Exception ex) { - s_logger.error("unable to sign request", ex); - } - return null; - } - - private static String sshWinTest(String host) { - if (host == null) { - s_logger.info("Did not receive a host back from test, ignoring win ssh test"); - return null; - } - - // We will retry 5 times before quitting - int retry = 1; - - while (true) { - try { - if (retry > 0) { - s_logger.info("Retry attempt : " + retry + " ...sleeping 300 seconds before next attempt. Account is " + s_account.get()); - Thread.sleep(300000); - } - - s_logger.info("Attempting to SSH into windows host " + host + " with retry attempt: " + retry + " for account " + s_account.get()); - - Connection conn = new Connection(host); - conn.connect(null, 60000, 60000); - - s_logger.info("User " + s_account.get() + " ssHed successfully into windows host " + host); - boolean success = false; - boolean isAuthenticated = conn.authenticateWithPassword("Administrator", "password"); - if (isAuthenticated == false) { - return "Authentication failed"; - } else { - s_logger.info("Authentication is successful"); - } - - try { - SCPClient scp = new SCPClient(conn); - scp.put("wget.exe", "wget.exe", "C:\\Users\\Administrator", "0777"); - s_logger.info("Successfully put wget.exe file"); - } catch (Exception ex) { - s_logger.error("Unable to put wget.exe " + ex); - } - - if (conn == null) { - s_logger.error("Connection is null"); - } - Session sess = conn.openSession(); - - s_logger.info("User + " + s_account.get() + " executing : wget http://192.168.1.250/dump.bin"); - sess.execCommand("wget http://192.168.1.250/dump.bin && dir dump.bin"); - - InputStream stdout = sess.getStdout(); - InputStream stderr = sess.getStderr(); - - byte[] buffer = new byte[8192]; - while (true) { - if ((stdout.available() == 0) && (stderr.available() == 0)) { - int conditions = sess.waitForCondition(ChannelCondition.STDOUT_DATA | ChannelCondition.STDERR_DATA | ChannelCondition.EOF, 120000); - - if ((conditions & ChannelCondition.TIMEOUT) != 0) { - s_logger.info("Timeout while waiting for data from peer."); - return null; - } - - if ((conditions & ChannelCondition.EOF) != 0) { - if ((conditions & (ChannelCondition.STDOUT_DATA | ChannelCondition.STDERR_DATA)) == 0) { - break; - } - } - } - - while (stdout.available() > 0) { - success = true; - int len = stdout.read(buffer); - if (len > 0) // this check is somewhat paranoid - s_logger.info(new String(buffer, 0, len)); - } - - while (stderr.available() > 0) { - /* int len = */stderr.read(buffer); - } - } - sess.close(); - conn.close(); - - if (success) { - Thread.sleep(120000); - return null; - } else { - retry++; - if (retry == MAX_RETRY_WIN) { - return "SSH Windows Network test fail for account " + s_account.get(); - } - } - } catch (Exception e) { - s_logger.error(e); - retry++; - if (retry == MAX_RETRY_WIN) { - return "SSH Windows Network test fail with error " + e.getMessage(); - } - } - } - } - - private static String sshTest(String host, String password) { - int i = 0; - if (host == null) { - s_logger.info("Did not receive a host back from test, ignoring ssh test"); - return null; - } - - if (password == null) { - s_logger.info("Did not receive a password back from test, ignoring ssh test"); - return null; - } - - // We will retry 5 times before quitting - String result = null; - int retry = 0; - - while (true) { - try { - if (retry > 0) { - s_logger.info("Retry attempt : " + retry + " ...sleeping 120 seconds before next attempt. Account is " + s_account.get()); - Thread.sleep(120000); - } - - s_logger.info("Attempting to SSH into linux host " + host + " with retry attempt: " + retry + ". Account is " + s_account.get()); - - Connection conn = new Connection(host); - conn.connect(null, 60000, 60000); - - s_logger.info("User + " + s_account.get() + " ssHed successfully into linux host " + host); - - boolean isAuthenticated = conn.authenticateWithPassword("root", password); - - if (isAuthenticated == false) { - s_logger.info("Authentication failed for root with password" + password); - return "Authentication failed"; - - } - - boolean success = false; - String linuxCommand = null; - - if (i % 10 == 0) - linuxCommand = "rm -rf *; wget http://192.168.1.250/dump.bin && ls -al dump.bin"; - else - linuxCommand = "wget http://192.168.1.250/dump.bin && ls -al dump.bin"; - - Session sess = conn.openSession(); - s_logger.info("User " + s_account.get() + " executing : " + linuxCommand); - sess.execCommand(linuxCommand); - - InputStream stdout = sess.getStdout(); - InputStream stderr = sess.getStderr(); - - byte[] buffer = new byte[8192]; - while (true) { - if ((stdout.available() == 0) && (stderr.available() == 0)) { - int conditions = sess.waitForCondition(ChannelCondition.STDOUT_DATA | ChannelCondition.STDERR_DATA | ChannelCondition.EOF, 120000); - - if ((conditions & ChannelCondition.TIMEOUT) != 0) { - s_logger.info("Timeout while waiting for data from peer."); - return null; - } - - if ((conditions & ChannelCondition.EOF) != 0) { - if ((conditions & (ChannelCondition.STDOUT_DATA | ChannelCondition.STDERR_DATA)) == 0) { - break; - } - } - } - - while (stdout.available() > 0) { - success = true; - int len = stdout.read(buffer); - if (len > 0) // this check is somewhat paranoid - s_logger.info(new String(buffer, 0, len)); - } - - while (stderr.available() > 0) { - /* int len = */stderr.read(buffer); - } - } - - sess.close(); - conn.close(); - - if (!success) { - retry++; - if (retry == MAX_RETRY_LINUX) { - result = "SSH Linux Network test fail"; - } - } - - return result; - } catch (Exception e) { - retry++; - s_logger.error("SSH Linux Network test fail with error"); - if (retry == MAX_RETRY_LINUX) { - return "SSH Linux Network test fail with error " + e.getMessage(); - } - } - i++; - } - } - - public static String createMD5Password(String password) { - MessageDigest md5; - - try { - md5 = MessageDigest.getInstance("MD5"); - } catch (NoSuchAlgorithmException e) { - throw new CloudRuntimeException("Error", e); - } - - md5.reset(); - BigInteger pwInt = new BigInteger(1, md5.digest(password.getBytes())); - - // make sure our MD5 hash value is 32 digits long... - StringBuffer sb = new StringBuffer(); - String pwStr = pwInt.toString(16); - int padding = 32 - pwStr.length(); - for (int i = 0; i < padding; i++) { - sb.append('0'); - } - sb.append(pwStr); - return sb.toString(); - } - - public static Element queryAsyncJobResult(String host, InputStream inputStream) { - Element returnBody = null; - - Map values = getSingleValueFromXML(inputStream, new String[] {"jobid"}); - String jobId = values.get("jobid"); - - if (jobId == null) { - s_logger.error("Unable to get a jobId"); - return null; - } - - //s_logger.info("Job id is " + jobId); - String resultUrl = host + "?command=queryAsyncJobResult&jobid=" + jobId; - HttpClient client = new HttpClient(); - HttpMethod method = new GetMethod(resultUrl); - while (true) { - try { - client.executeMethod(method); - //s_logger.info("Method is executed successfully. Following url was sent " + resultUrl); - InputStream is = method.getResponseBodyAsStream(); - DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance(); - DocumentBuilder builder = factory.newDocumentBuilder(); - Document doc = builder.parse(is); - returnBody = doc.getDocumentElement(); - doc.getDocumentElement().normalize(); - Element jobStatusTag = (Element)returnBody.getElementsByTagName("jobstatus").item(0); - String jobStatus = jobStatusTag.getTextContent(); - if (jobStatus.equals("0")) { - try { - Thread.sleep(1000); - } catch (InterruptedException e) { - s_logger.debug("[ignored] interrupted while during async job result query."); - } - } else { - break; - } - - } catch (Exception ex) { - s_logger.error(ex); - } - } - return returnBody; - } - -} diff --git a/test/src-not-used/main/java/com/cloud/test/stress/TestClientWithAPI.java b/test/src-not-used/main/java/com/cloud/test/stress/TestClientWithAPI.java deleted file mode 100644 index 3d43a9440c7..00000000000 --- a/test/src-not-used/main/java/com/cloud/test/stress/TestClientWithAPI.java +++ /dev/null @@ -1,2289 +0,0 @@ -// 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.test.stress; - -import java.io.IOException; -import java.io.InputStream; -import java.math.BigInteger; -import java.net.URLEncoder; -import java.security.MessageDigest; -import java.security.NoSuchAlgorithmException; -import java.text.DateFormat; -import java.text.SimpleDateFormat; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Date; -import java.util.HashMap; -import java.util.Iterator; -import java.util.List; -import java.util.Map; -import java.util.Random; - -import javax.crypto.Mac; -import javax.crypto.spec.SecretKeySpec; -import javax.xml.parsers.DocumentBuilder; -import javax.xml.parsers.DocumentBuilderFactory; - -import org.apache.commons.httpclient.HttpClient; -import org.apache.commons.httpclient.HttpException; -import org.apache.commons.httpclient.HttpMethod; -import org.apache.commons.httpclient.methods.GetMethod; -import org.apache.log4j.Logger; -import org.apache.log4j.NDC; -import org.w3c.dom.Document; -import org.w3c.dom.Element; -import org.w3c.dom.Node; -import org.w3c.dom.NodeList; - -import com.trilead.ssh2.ChannelCondition; -import com.trilead.ssh2.Connection; -import com.trilead.ssh2.SCPClient; -import com.trilead.ssh2.Session; - -import com.cloud.utils.exception.CloudRuntimeException; - -public class TestClientWithAPI { - private static long sleepTime = 180000L; // default 0 - private static boolean cleanUp = true; - public static final Logger s_logger = Logger.getLogger(TestClientWithAPI.class); - private static boolean repeat = true; - private static int numOfUsers = 0; - private static String[] users = null; - private static boolean internet = false; - private static ThreadLocal s_linuxIP = new ThreadLocal(); - private static ThreadLocal s_linuxIpId = new ThreadLocal(); - private static ThreadLocal s_linuxVmId = new ThreadLocal(); - private static ThreadLocal s_linuxPassword = new ThreadLocal(); - private static ThreadLocal s_windowsIP = new ThreadLocal(); - private static ThreadLocal s_windowsIpId = new ThreadLocal(); - private static ThreadLocal s_windowsVmId = new ThreadLocal(); - private static ThreadLocal s_secretKey = new ThreadLocal(); - private static ThreadLocal s_apiKey = new ThreadLocal(); - private static ThreadLocal s_userId = new ThreadLocal(); - private static ThreadLocal s_accountId = new ThreadLocal(); - private static ThreadLocal s_account = new ThreadLocal(); - private static ThreadLocal s_domainRouterId = new ThreadLocal(); - private static ThreadLocal s_pfGroupId = new ThreadLocal(); - private static ThreadLocal s_windowsLb = new ThreadLocal(); - private static ThreadLocal s_linuxLb = new ThreadLocal(); - private static ThreadLocal s_dataVolume = new ThreadLocal(); - private static ThreadLocal s_rootVolume = new ThreadLocal(); - private static ThreadLocal s_newVolume = new ThreadLocal(); - private static ThreadLocal s_snapshot = new ThreadLocal(); - private static ThreadLocal s_volumeFromSnapshot = new ThreadLocal(); - private static ThreadLocal s_networkId = new ThreadLocal(); - private static ThreadLocal s_publicIpId = new ThreadLocal(); - private static ThreadLocal s_winipfwdid = new ThreadLocal(); - private static ThreadLocal s_linipfwdid = new ThreadLocal(); - private static DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance(); - private static int usageIterator = 1; - private static int numThreads = 1; - private static int wait = 5000; - private static String accountName = null; - private static String zoneId = "1"; - private static String snapshotTest = "no"; - private static String serviceOfferingId = "1"; - private static String diskOfferingId = "4"; - private static String networkOfferingId = "6"; - private static String vmPassword = "rs-ccb35ea5"; - private static String downloadUrl = "192.168.1.250/dump.bin"; - - private static final int MAX_RETRY_LINUX = 10; - private static final int MAX_RETRY_WIN = 10; - - public static void main(String[] args) { - String host = "http://localhost"; - String port = "8092"; - String devPort = "8080"; - String apiUrl = "/client/api"; - - try { - // Parameters - List argsList = Arrays.asList(args); - Iterator iter = argsList.iterator(); - while (iter.hasNext()) { - String arg = iter.next(); - // host - if (arg.equals("-h")) { - host = "http://" + iter.next(); - } - - if (arg.equals("-p")) { - port = iter.next(); - } - if (arg.equals("-dp")) { - devPort = iter.next(); - } - - if (arg.equals("-t")) { - numThreads = Integer.parseInt(iter.next()); - } - - if (arg.equals("-s")) { - sleepTime = Long.parseLong(iter.next()); - } - if (arg.equals("-a")) { - accountName = iter.next(); - } - - if (arg.equals("-c")) { - cleanUp = Boolean.parseBoolean(iter.next()); - if (!cleanUp) - sleepTime = 0L; // no need to wait if we don't ever - // cleanup - } - - if (arg.equals("-r")) { - repeat = Boolean.parseBoolean(iter.next()); - } - - if (arg.equals("-u")) { - numOfUsers = Integer.parseInt(iter.next()); - } - - if (arg.equals("-i")) { - internet = Boolean.parseBoolean(iter.next()); - } - - if (arg.equals("-w")) { - wait = Integer.parseInt(iter.next()); - } - - if (arg.equals("-z")) { - zoneId = iter.next(); - } - - if (arg.equals("-snapshot")) { - snapshotTest = "yes"; - } - - if (arg.equals("-so")) { - serviceOfferingId = iter.next(); - } - - if (arg.equals("-do")) { - diskOfferingId = iter.next(); - } - - if (arg.equals("-no")) { - networkOfferingId = iter.next(); - } - - if (arg.equals("-pass")) { - vmPassword = iter.next(); - } - - if (arg.equals("-url")) { - downloadUrl = iter.next(); - } - - } - - final String server = host + ":" + port + "/"; - final String developerServer = host + ":" + devPort + apiUrl; - s_logger.info("Starting test against server: " + server + " with " + numThreads + " thread(s)"); - if (cleanUp) - s_logger.info("Clean up is enabled, each test will wait " + sleepTime + " ms before cleaning up"); - - if (numOfUsers > 0) { - s_logger.info("Pre-generating users for test of size : " + numOfUsers); - users = new String[numOfUsers]; - Random ran = new Random(); - for (int i = 0; i < numOfUsers; i++) { - users[i] = Math.abs(ran.nextInt()) + "-user"; - } - } - - for (int i = 0; i < numThreads; i++) { - new Thread(new Runnable() { - @Override - public void run() { - do { - String username = null; - try { - long now = System.currentTimeMillis(); - Random ran = new Random(); - if (users != null) { - username = users[Math.abs(ran.nextInt()) % numOfUsers]; - } else { - username = Math.abs(ran.nextInt()) + "-user"; - } - NDC.push(username); - - s_logger.info("Starting test for the user " + username); - int response = executeDeployment(server, developerServer, username, snapshotTest); - boolean success = false; - String reason = null; - - if (response == 200) { - success = true; - if (internet) { - s_logger.info("Deploy successful...waiting 5 minute before SSH tests"); - Thread.sleep(300000L); // Wait 60 - // seconds so - // the windows VM - // can boot up and do a sys prep. - - if (accountName == null) { - s_logger.info("Begin Linux SSH test for account " + s_account.get()); - reason = sshTest(s_linuxIP.get(), s_linuxPassword.get(), snapshotTest); - } - - if (reason == null) { - s_logger.info("Linux SSH test successful for account " + s_account.get()); - s_logger.info("Begin WindowsSSH test for account " + s_account.get()); - - reason = sshTest(s_linuxIP.get(), s_linuxPassword.get(), snapshotTest); - // reason = sshWinTest(s_windowsIP.get()); - } - - // release the linux IP now... - s_linuxIP.set(null); - // release the Windows IP now - s_windowsIP.set(null); - } - - // sleep for 3 min before getting the latest network stat - // s_logger.info("Sleeping for 5 min before getting the lates network stat for the account"); - // Thread.sleep(300000); - // verify that network stat is correct for the user; if it's not - stop all the resources - // for the user - // if ((reason == null) && (getNetworkStat(server) == false) ) { - // s_logger.error("Stopping all the resources for the account " + s_account.get() + - // " as network stat is incorrect"); - // int stopResponseCode = executeStop( - // server, developerServer, - // username, false); - // s_logger - // .info("stop command finished with response code: " - // + stopResponseCode); - // success = false; // since the SSH test - // - // } else - if (reason == null) { - if (internet) { - s_logger.info("Windows SSH test successful for account " + s_account.get()); - } else { - s_logger.info("deploy test successful....now cleaning up"); - if (cleanUp) { - s_logger.info("Waiting " + sleepTime + " ms before cleaning up vms"); - Thread.sleep(sleepTime); - } else { - success = true; - } - } - - if (usageIterator >= numThreads) { - int eventsAndBillingResponseCode = executeEventsAndBilling(server, developerServer); - s_logger.info("events and usage records command finished with response code: " + eventsAndBillingResponseCode); - usageIterator = 1; - - } else { - s_logger.info("Skipping events and usage records for this user: usageIterator " + usageIterator + " and number of Threads " + - numThreads); - usageIterator++; - } - - if ((users == null) && (accountName == null)) { - s_logger.info("Sending cleanup command"); - int cleanupResponseCode = executeCleanup(server, developerServer, username); - s_logger.info("cleanup command finished with response code: " + cleanupResponseCode); - success = (cleanupResponseCode == 200); - } else { - s_logger.info("Sending stop DomR / destroy VM command"); - int stopResponseCode = executeStop(server, developerServer, username, true); - s_logger.info("stop(destroy) command finished with response code: " + stopResponseCode); - success = (stopResponseCode == 200); - } - - } else { - // Just stop but don't destroy the - // VMs/Routers - s_logger.info("SSH test failed for account " + s_account.get() + "with reason '" + reason + "', stopping VMs"); - int stopResponseCode = executeStop(server, developerServer, username, false); - s_logger.info("stop command finished with response code: " + stopResponseCode); - success = false; // since the SSH test - // failed, mark the - // whole test as - // failure - } - } else { - // Just stop but don't destroy the - // VMs/Routers - s_logger.info("Deploy test failed with reason '" + reason + "', stopping VMs"); - int stopResponseCode = executeStop(server, developerServer, username, true); - s_logger.info("stop command finished with response code: " + stopResponseCode); - success = false; // since the deploy test - // failed, mark the - // whole test as failure - } - - if (success) { - s_logger.info("***** Completed test for user : " + username + " in " + ((System.currentTimeMillis() - now) / 1000L) + " seconds"); - - } else { - s_logger.info("##### FAILED test for user : " + username + " in " + ((System.currentTimeMillis() - now) / 1000L) + - " seconds with reason : " + reason); - } - s_logger.info("Sleeping for " + wait + " seconds before starting next iteration"); - Thread.sleep(wait); - } catch (Exception e) { - s_logger.warn("Error in thread", e); - try { - int stopResponseCode = executeStop(server, developerServer, username, true); - s_logger.info("stop response code: " + stopResponseCode); - } catch (Exception e1) { - s_logger.info("[ignored]" - + "error executing stop during api test: " + e1.getLocalizedMessage()); - } - } finally { - NDC.clear(); - } - } while (repeat); - } - }).start(); - } - } catch (Exception e) { - s_logger.error(e); - } - } - - public static Map> getMultipleValuesFromXML(InputStream is, String[] tagNames) { - Map> returnValues = new HashMap>(); - try { - 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) { - s_logger.error("no " + tagNames[i] + " tag in XML response...returning null"); - } else { - List valueList = new ArrayList(); - for (int j = 0; j < targetNodes.getLength(); j++) { - Node node = targetNodes.item(j); - valueList.add(node.getTextContent()); - } - returnValues.put(tagNames[i], valueList); - } - } - } catch (Exception ex) { - s_logger.error(ex); - } - return returnValues; - } - - public static Map getSingleValueFromXML(InputStream is, String[] tagNames) { - Map returnValues = new HashMap(); - try { - 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) { - s_logger.error("no " + tagNames[i] + " tag in XML response...returning null"); - } else { - returnValues.put(tagNames[i], targetNodes.item(0).getTextContent()); - } - } - } catch (Exception ex) { - s_logger.error("error processing XML", ex); - } - return returnValues; - } - - public static Map getSingleValueFromXML(Element rootElement, String[] tagNames) { - Map returnValues = new HashMap(); - if (rootElement == null) { - s_logger.error("Root element is null, can't get single value from xml"); - return null; - } - try { - for (int i = 0; i < tagNames.length; i++) { - NodeList targetNodes = rootElement.getElementsByTagName(tagNames[i]); - if (targetNodes.getLength() <= 0) { - s_logger.error("no " + tagNames[i] + " tag in XML response...returning null"); - } else { - returnValues.put(tagNames[i], targetNodes.item(0).getTextContent()); - } - } - } catch (Exception ex) { - s_logger.error("error processing XML", ex); - } - return returnValues; - } - - private static List getNonSourceNatIPs(InputStream is) { - List returnValues = new ArrayList(); - try { - DocumentBuilder docBuilder = factory.newDocumentBuilder(); - Document doc = docBuilder.parse(is); - Element rootElement = doc.getDocumentElement(); - NodeList allocatedIpAddrNodes = rootElement.getElementsByTagName("publicipaddress"); - for (int i = 0; i < allocatedIpAddrNodes.getLength(); i++) { - Node allocatedIpAddrNode = allocatedIpAddrNodes.item(i); - NodeList childNodes = allocatedIpAddrNode.getChildNodes(); - String ipAddress = null; - boolean isSourceNat = true; // assume it's source nat until we - // find otherwise - for (int j = 0; j < childNodes.getLength(); j++) { - Node n = childNodes.item(j); - if ("id".equals(n.getNodeName())) { - // if ("ipaddress".equals(n.getNodeName())) { - ipAddress = n.getTextContent(); - } else if ("issourcenat".equals(n.getNodeName())) { - isSourceNat = Boolean.parseBoolean(n.getTextContent()); - } - } - if ((ipAddress != null) && !isSourceNat) { - returnValues.add(ipAddress); - } - } - } catch (Exception ex) { - s_logger.error(ex); - } - return returnValues; - } - - private static List getIPs(InputStream is, boolean sourceNat) { - List returnValues = new ArrayList(); - try { - DocumentBuilder docBuilder = factory.newDocumentBuilder(); - Document doc = docBuilder.parse(is); - Element rootElement = doc.getDocumentElement(); - NodeList allocatedIpAddrNodes = rootElement.getElementsByTagName("publicipaddress"); - for (int i = 0; i < allocatedIpAddrNodes.getLength(); i++) { - Node allocatedIpAddrNode = allocatedIpAddrNodes.item(i); - NodeList childNodes = allocatedIpAddrNode.getChildNodes(); - String ipAddress = null; - String ipAddressId = null; - boolean isSourceNat = false; // assume it's *not* source nat until we find otherwise - for (int j = 0; j < childNodes.getLength(); j++) { - Node n = childNodes.item(j); - //Id is being used instead of ipaddress. Changes need to done later to ipaddress variable - if ("id".equals(n.getNodeName())) { - ipAddressId = n.getTextContent(); - } else if ("ipaddress".equals(n.getNodeName())) { - ipAddress = n.getTextContent(); - } else if ("issourcenat".equals(n.getNodeName())) { - isSourceNat = Boolean.parseBoolean(n.getTextContent()); - } - } - if ((ipAddress != null) && isSourceNat == sourceNat) { - returnValues.add(ipAddressId); - returnValues.add(ipAddress); - } - } - } catch (Exception ex) { - s_logger.error(ex); - } - return returnValues; - } - - private static String executeRegistration(String server, String username, String password) throws HttpException, IOException { - String url = server + "?command=registerUserKeys&id=" + s_userId.get().toString(); - s_logger.info("registering: " + username); - String returnValue = null; - HttpClient client = new HttpClient(); - HttpMethod method = new GetMethod(url); - int responseCode = client.executeMethod(method); - if (responseCode == 200) { - InputStream is = method.getResponseBodyAsStream(); - Map requestKeyValues = getSingleValueFromXML(is, new String[] {"apikey", "secretkey"}); - s_apiKey.set(requestKeyValues.get("apikey")); - returnValue = requestKeyValues.get("secretkey"); - } else { - s_logger.error("registration failed with error code: " + responseCode); - } - return returnValue; - } - - private static Integer executeDeployment(String server, String developerServer, String username, String snapshotTest) throws HttpException, IOException { - // test steps: - // - create user - // - deploy Windows VM - // - deploy Linux VM - // - associate IP address - // - create two IP forwarding rules - // - create load balancer rule - // - list IP forwarding rules - // - list load balancer rules - - // ----------------------------- - // CREATE ACCOUNT - // ----------------------------- - String encodedUsername = URLEncoder.encode(username, "UTF-8"); - String encryptedPassword = createMD5Password(username); - String encodedPassword = URLEncoder.encode(encryptedPassword, "UTF-8"); - - String url = - server + "?command=createAccount&username=" + encodedUsername + "&account=" + encodedUsername + "&password=" + encodedPassword + - "&firstname=Test&lastname=Test&email=test@vmops.com&domainId=1&accounttype=0"; - - HttpClient client = new HttpClient(); - HttpMethod method = new GetMethod(url); - int responseCode = client.executeMethod(method); - long accountId = -1; - if (responseCode == 200) { - InputStream is = method.getResponseBodyAsStream(); - Map accountValues = getSingleValueFromXML(is, new String[] {"id", "name"}); - String accountIdStr = accountValues.get("id"); - s_logger.info("created account " + username + " with id " + accountIdStr); - if (accountIdStr != null) { - accountId = Long.parseLong(accountIdStr); - s_accountId.set(accountId); - s_account.set(accountValues.get("name")); - if (accountId == -1) { - s_logger.error("create account (" + username + ") failed to retrieve a valid user id, aborting depolyment test"); - return -1; - } - } - } else { - s_logger.error("create account test failed for account " + username + " with error code :" + responseCode + - ", aborting deployment test. The command was sent with url " + url); - return -1; - } - - // LIST JUST CREATED USER TO GET THE USER ID - url = server + "?command=listUsers&username=" + encodedUsername + "&account=" + encodedUsername + "&domainId=1"; - client = new HttpClient(); - method = new GetMethod(url); - responseCode = client.executeMethod(method); - long userId = -1; - if (responseCode == 200) { - InputStream is = method.getResponseBodyAsStream(); - Map userIdValues = getSingleValueFromXML(is, new String[] {"id"}); - String userIdStr = userIdValues.get("id"); - s_logger.info("listed user " + username + " with id " + userIdStr); - if (userIdStr != null) { - userId = Long.parseLong(userIdStr); - s_userId.set(userId); - if (userId == -1) { - s_logger.error("list user by username " + username + ") failed to retrieve a valid user id, aborting depolyment test"); - return -1; - } - } - } else { - s_logger.error("list user test failed for account " + username + " with error code :" + responseCode + - ", aborting deployment test. The command was sent with url " + url); - return -1; - } - - s_secretKey.set(executeRegistration(server, username, username)); - - if (s_secretKey.get() == null) { - s_logger.error("FAILED to retrieve secret key during registration, skipping user: " + username); - return -1; - } else { - s_logger.info("got secret key: " + s_secretKey.get()); - s_logger.info("got api key: " + s_apiKey.get()); - } - - // --------------------------------- - // CREATE VIRTUAL NETWORK - // --------------------------------- - url = - server + "?command=createNetwork&networkofferingid=" + networkOfferingId + "&account=" + encodedUsername + "&domainId=1" + "&zoneId=" + zoneId + - "&name=virtualnetwork-" + encodedUsername + "&displaytext=virtualnetwork-" + encodedUsername; - client = new HttpClient(); - method = new GetMethod(url); - responseCode = client.executeMethod(method); - if (responseCode == 200) { - InputStream is = method.getResponseBodyAsStream(); - Map networkValues = getSingleValueFromXML(is, new String[] {"id"}); - String networkIdStr = networkValues.get("id"); - s_logger.info("Created virtual network with name virtualnetwork-" + encodedUsername + " and id " + networkIdStr); - if (networkIdStr != null) { - s_networkId.set(networkIdStr); - } - } else { - s_logger.error("Create virtual network failed for account " + username + " with error code :" + responseCode + - ", aborting deployment test. The command was sent with url " + url); - return -1; - } - /* - // --------------------------------- - // CREATE DIRECT NETWORK - // --------------------------------- - url = server + "?command=createNetwork&networkofferingid=" + networkOfferingId_dir + "&account=" + encodedUsername + "&domainId=1" + "&zoneId=" + zoneId + "&name=directnetwork-" + encodedUsername + "&displaytext=directnetwork-" + encodedUsername; - client = new HttpClient(); - method = new GetMethod(url); - responseCode = client.executeMethod(method); - if (responseCode == 200) { - InputStream is = method.getResponseBodyAsStream(); - Map networkValues = getSingleValueFromXML(is, new String[] { "id" }); - String networkIdStr = networkValues.get("id"); - s_logger.info("Created direct network with name directnetwork-" + encodedUsername + " and id " + networkIdStr); - if (networkIdStr != null) { - s_networkId_dir.set(networkIdStr); - } - } else { - s_logger.error("Create direct network failed for account " + username + " with error code :" + responseCode + ", aborting deployment test. The command was sent with url " + url); - return -1; - } - */ - - // --------------------------------- - // DEPLOY LINUX VM - // --------------------------------- - String linuxVMPrivateIP = null; - { - // long templateId = 3; - long templateId = 4; - String encodedZoneId = URLEncoder.encode("" + zoneId, "UTF-8"); - String encodedServiceOfferingId = URLEncoder.encode("" + serviceOfferingId, "UTF-8"); - String encodedTemplateId = URLEncoder.encode("" + templateId, "UTF-8"); - String encodedApiKey = URLEncoder.encode(s_apiKey.get(), "UTF-8"); - String encodedNetworkIds = URLEncoder.encode(s_networkId.get() + ",206", "UTF-8"); - String requestToSign = - "apikey=" + encodedApiKey + "&command=deployVirtualMachine&diskofferingid=" + diskOfferingId + "&networkids=" + encodedNetworkIds + - "&serviceofferingid=" + encodedServiceOfferingId + "&templateid=" + encodedTemplateId + "&zoneid=" + encodedZoneId; - requestToSign = requestToSign.toLowerCase(); - String signature = signRequest(requestToSign, s_secretKey.get()); - String encodedSignature = URLEncoder.encode(signature, "UTF-8"); - url = - developerServer + "?command=deployVirtualMachine" + "&zoneid=" + encodedZoneId + "&serviceofferingid=" + encodedServiceOfferingId + "&diskofferingid=" + - diskOfferingId + "&networkids=" + encodedNetworkIds + "&templateid=" + encodedTemplateId + "&apikey=" + encodedApiKey + "&signature=" + - encodedSignature; - - method = new GetMethod(url); - responseCode = client.executeMethod(method); - if (responseCode == 200) { - InputStream input = method.getResponseBodyAsStream(); - Element el = queryAsyncJobResult(server, input); - Map values = getSingleValueFromXML(el, new String[] {"id", "ipaddress"}); - - if ((values.get("ipaddress") == null) || (values.get("id") == null)) { - s_logger.info("deploy linux vm response code: 401, the command was sent with url " + url); - return 401; - } else { - s_logger.info("deploy linux vm response code: " + responseCode); - long linuxVMId = Long.parseLong(values.get("id")); - s_logger.info("got linux virtual machine id: " + linuxVMId); - s_linuxVmId.set(values.get("id")); - linuxVMPrivateIP = values.get("ipaddress"); - // s_linuxPassword.set(values.get("password")); - s_linuxPassword.set(vmPassword); - s_logger.info("got linux virtual machine password: " + s_linuxPassword.get()); - } - } else { - s_logger.error("deploy linux vm failed with error code: " + responseCode + ". Following URL was sent: " + url); - return responseCode; - } - } - - { - // --------------------------------- - // ASSOCIATE IP for windows - // --------------------------------- - String ipAddr = null; - - String encodedApiKey = URLEncoder.encode(s_apiKey.get(), "UTF-8"); - String requestToSign = "apikey=" + encodedApiKey + "&command=associateIpAddress" + "&zoneid=" + zoneId; - requestToSign = requestToSign.toLowerCase(); - String signature = signRequest(requestToSign, s_secretKey.get()); - String encodedSignature = URLEncoder.encode(signature, "UTF-8"); - - url = developerServer + "?command=associateIpAddress" + "&apikey=" + encodedApiKey + "&zoneid=" + zoneId + "&signature=" + encodedSignature; - - method = new GetMethod(url); - responseCode = client.executeMethod(method); - if (responseCode == 200) { - InputStream is = method.getResponseBodyAsStream(); - /*Asynchronous Job - Corresponding Changes Made*/ - Element associpel = queryAsyncJobResult(server, is); - Map values = getSingleValueFromXML(associpel, new String[] {"id", "ipaddress"}); - - if ((values.get("ipaddress") == null) || (values.get("id") == null)) { - s_logger.info("associate ip for Windows response code: 401, the command was sent with url " + url); - return 401; - } else { - s_logger.info("Associate IP Address response code: " + responseCode); - long publicIpId = Long.parseLong(values.get("id")); - s_logger.info("Associate IP's Id: " + publicIpId); - s_publicIpId.set(values.get("id")); - } - } else { - s_logger.error("associate ip address for windows vm failed with error code: " + responseCode + ". Following URL was sent: " + url); - return responseCode; - } - - String encodedPublicIpId = URLEncoder.encode(s_publicIpId.get(), "UTF-8"); - requestToSign = "apikey=" + encodedApiKey + "&command=listPublicIpAddresses" + "&id=" + encodedPublicIpId; - requestToSign = requestToSign.toLowerCase(); - signature = signRequest(requestToSign, s_secretKey.get()); - encodedSignature = URLEncoder.encode(signature, "UTF-8"); - - url = developerServer + "?command=listPublicIpAddresses&apikey=" + encodedApiKey + "&id=" + encodedPublicIpId + "&signature=" + encodedSignature; - client = new HttpClient(); - method = new GetMethod(url); - responseCode = client.executeMethod(method); - s_logger.info("url is " + url); - s_logger.info("list ip addresses for user " + userId + " response code: " + responseCode); - if (responseCode == 200) { - InputStream is = method.getResponseBodyAsStream(); - // InputStream ips = method.getResponseBodyAsStream(); - List ipAddressValues = getIPs(is, false); - // List ipAddressVals = getIPs(is, false, true); - if ((ipAddressValues != null) && !ipAddressValues.isEmpty()) { - s_windowsIpId.set(ipAddressValues.get(0)); - s_windowsIP.set(ipAddressValues.get(1)); - s_logger.info("For Windows, using non-sourceNat IP address ID: " + ipAddressValues.get(0)); - s_logger.info("For Windows, using non-sourceNat IP address: " + ipAddressValues.get(1)); - } - } else { - s_logger.error("list ip addresses failed with error code: " + responseCode + ". Following URL was sent: " + url); - return responseCode; - } - - // --------------------------------- - // Use the SourceNat IP for linux - // --------------------------------- - { - requestToSign = "apikey=" + encodedApiKey + "&command=listPublicIpAddresses"; - requestToSign = requestToSign.toLowerCase(); - signature = signRequest(requestToSign, s_secretKey.get()); - encodedSignature = URLEncoder.encode(signature, "UTF-8"); - - url = developerServer + "?command=listPublicIpAddresses&apikey=" + encodedApiKey + "&signature=" + encodedSignature; - client = new HttpClient(); - method = new GetMethod(url); - responseCode = client.executeMethod(method); - s_logger.info("url is " + url); - s_logger.info("list ip addresses for user " + userId + " response code: " + responseCode); - if (responseCode == 200) { - InputStream is = method.getResponseBodyAsStream(); -// InputStream ips = method.getResponseBodyAsStream(); - List ipAddressValues = getIPs(is, true); -// is = method.getResponseBodyAsStream(); -// List ipAddressVals = getIPs(is, true, true); - if ((ipAddressValues != null) && !ipAddressValues.isEmpty()) { - s_linuxIpId.set(ipAddressValues.get(0)); - s_linuxIP.set(ipAddressValues.get(1)); - s_logger.info("For linux, using sourceNat IP address ID: " + ipAddressValues.get(0)); - s_logger.info("For linux, using sourceNat IP address: " + ipAddressValues.get(1)); - } - } else { - s_logger.error("list ip addresses failed with error code: " + responseCode + ". Following URL was sent: " + url); - return responseCode; - } - } - - //-------------------------------------------- - // Enable Static NAT for the Source NAT Ip - //-------------------------------------------- - String encodedSourceNatPublicIpId = URLEncoder.encode(s_linuxIpId.get(), "UTF-8"); - - /* requestToSign = "apikey=" + encodedApiKey + "&command=enableStaticNat"+"&id=" + encodedSourceNatPublicIpId + "&virtualMachineId=" + encodedVmId;; - requestToSign = requestToSign.toLowerCase(); - signature = signRequest(requestToSign, s_secretKey.get()); - encodedSignature = URLEncoder.encode(signature, "UTF-8"); - - url = developerServer + "?command=enableStaticNat&apikey=" + encodedApiKey + "&signature=" + encodedSignature + "&id=" + encodedSourceNatPublicIpId + "&virtualMachineId=" + encodedVmId; - client = new HttpClient(); - method = new GetMethod(url); - responseCode = client.executeMethod(method); - s_logger.info("url is " + url); - s_logger.info("list ip addresses for user " + userId + " response code: " + responseCode); - if (responseCode == 200) { - InputStream is = method.getResponseBodyAsStream(); - Map success = getSingleValueFromXML(is, new String[] { "success" }); - s_logger.info("Enable Static NAT..success? " + success.get("success")); - } else { - s_logger.error("Enable Static NAT failed with error code: " + responseCode + ". Following URL was sent: " + url); - return responseCode; - } - */ - // ------------------------------------------------------------- - // CREATE IP FORWARDING RULE -- Linux VM - // ------------------------------------------------------------- - String encodedVmId = URLEncoder.encode(s_linuxVmId.get(), "UTF-8"); - String encodedIpAddress = URLEncoder.encode(s_linuxIpId.get(), "UTF-8"); - requestToSign = - "apikey=" + encodedApiKey + "&command=createPortForwardingRule&ipaddressid=" + encodedIpAddress + "&privateport=22&protocol=TCP&publicport=22" + - "&virtualmachineid=" + encodedVmId; - requestToSign = requestToSign.toLowerCase(); - signature = signRequest(requestToSign, s_secretKey.get()); - encodedSignature = URLEncoder.encode(signature, "UTF-8"); - - url = - developerServer + "?command=createPortForwardingRule&apikey=" + encodedApiKey + "&ipaddressid=" + encodedIpAddress + - "&privateport=22&protocol=TCP&publicport=22&virtualmachineid=" + encodedVmId + "&signature=" + encodedSignature; - - s_logger.info("Created port forwarding rule with " + url); - method = new GetMethod(url); - responseCode = client.executeMethod(method); - if (responseCode == 200) { - InputStream input = method.getResponseBodyAsStream(); - Element el = queryAsyncJobResult(server, input); - Map values = getSingleValueFromXML(el, new String[] {"id"}); - s_logger.info("Port forwarding rule was assigned successfully to Linux VM"); - long ipfwdid = Long.parseLong(values.get("id")); - s_logger.info("got Port Forwarding Rule's Id:" + ipfwdid); - s_linipfwdid.set(values.get("id")); - - } else { - s_logger.error("Port forwarding rule creation failed with error code: " + responseCode + ". Following URL was sent: " + url); - return responseCode; - } - - // Create snapshot recurring policy if needed; otherwise create windows vm - if (snapshotTest.equals("yes")) { - - // list volumes for linux vm - { - url = server + "?command=listVolumes&virtualMachineId=" + s_linuxVmId.get() + "&type=root"; - s_logger.info("Getting rootDisk id of Centos vm"); - client = new HttpClient(); - method = new GetMethod(url); - responseCode = client.executeMethod(method); - s_logger.info("List volumes response code: " + responseCode); - if (responseCode == 200) { - InputStream is = method.getResponseBodyAsStream(); - Map success = getSingleValueFromXML(is, new String[] {"id"}); - if (success.get("id") == null) { - s_logger.error("Unable to get root volume for linux vm. Followin url was sent: " + url); - } - s_logger.info("Got rootVolume for linux vm with id " + success.get("id")); - s_rootVolume.set(success.get("id")); - } else { - s_logger.error("List volumes for linux vm failed with error code: " + responseCode + ". Following URL was sent: " + url); - return responseCode; - } - } - // Create recurring snapshot policy for linux vm - { - String encodedTimeZone = URLEncoder.encode("America/Los Angeles", "UTF-8"); - url = - server + "?command=createSnapshotPolicy&intervaltype=hourly&schedule=10&maxsnaps=4&volumeid=" + s_rootVolume.get() + "&timezone=" + - encodedTimeZone; - s_logger.info("Creating recurring snapshot policy for linux vm ROOT disk"); - client = new HttpClient(); - method = new GetMethod(url); - responseCode = client.executeMethod(method); - s_logger.info("Create recurring snapshot policy for linux vm ROOT disk: " + responseCode); - if (responseCode != 200) { - s_logger.error("Create recurring snapshot policy for linux vm ROOT disk failed with error code: " + responseCode + ". Following URL was sent: " + - url); - return responseCode; - } - } - } else { - // --------------------------------- - // DEPLOY WINDOWS VM - // --------------------------------- - String windowsVMPrivateIP = null; - { - // long templateId = 6; - long templateId = 4; - String encodedZoneId = URLEncoder.encode("" + zoneId, "UTF-8"); - String encodedServiceOfferingId = URLEncoder.encode("" + serviceOfferingId, "UTF-8"); - String encodedTemplateId = URLEncoder.encode("" + templateId, "UTF-8"); - encodedApiKey = URLEncoder.encode(s_apiKey.get(), "UTF-8"); - String encodedNetworkIds = URLEncoder.encode(s_networkId.get() + ",206", "UTF-8"); - - requestToSign = - "apikey=" + encodedApiKey + "&command=deployVirtualMachine&diskofferingid=" + diskOfferingId + "&networkids=" + encodedNetworkIds + - "&serviceofferingid=" + encodedServiceOfferingId + "&templateid=" + encodedTemplateId + "&zoneid=" + encodedZoneId; - requestToSign = requestToSign.toLowerCase(); - signature = signRequest(requestToSign, s_secretKey.get()); - encodedSignature = URLEncoder.encode(signature, "UTF-8"); - - url = - developerServer + "?command=deployVirtualMachine" + "&zoneid=" + encodedZoneId + "&serviceofferingid=" + encodedServiceOfferingId + - "&diskofferingid=" + diskOfferingId + "&networkids=" + encodedNetworkIds + "&templateid=" + encodedTemplateId + "&apikey=" + encodedApiKey + - "&signature=" + encodedSignature; - - method = new GetMethod(url); - responseCode = client.executeMethod(method); - if (responseCode == 200) { - InputStream input = method.getResponseBodyAsStream(); - Element el = queryAsyncJobResult(server, input); - Map values = getSingleValueFromXML(el, new String[] {"id", "ipaddress"}); - - if ((values.get("ipaddress") == null) || (values.get("id") == null)) { - s_logger.info("deploy windows vm response code: 401, the command was sent with url " + url); - return 401; - } else { - s_logger.info("deploy windows vm response code: " + responseCode); - windowsVMPrivateIP = values.get("ipaddress"); - long windowsVMId = Long.parseLong(values.get("id")); - s_logger.info("got windows virtual machine id: " + windowsVMId); - s_windowsVmId.set(values.get("id")); - } - } else { - s_logger.error("deploy windows vm failes with error code: " + responseCode + ". Following URL was sent: " + url); - return responseCode; - } - } - - //-------------------------------------------- - // Enable Static NAT for the Non Source NAT Ip - //-------------------------------------------- - - encodedVmId = URLEncoder.encode(s_windowsVmId.get(), "UTF-8"); - encodedPublicIpId = URLEncoder.encode(s_publicIpId.get(), "UTF-8"); - requestToSign = "apikey=" + encodedApiKey + "&command=enableStaticNat" + "&ipaddressid=" + encodedPublicIpId + "&virtualMachineId=" + encodedVmId; - requestToSign = requestToSign.toLowerCase(); - signature = signRequest(requestToSign, s_secretKey.get()); - encodedSignature = URLEncoder.encode(signature, "UTF-8"); - - url = - developerServer + "?command=enableStaticNat&apikey=" + encodedApiKey + "&ipaddressid=" + encodedPublicIpId + "&signature=" + encodedSignature + - "&virtualMachineId=" + encodedVmId; - client = new HttpClient(); - method = new GetMethod(url); - responseCode = client.executeMethod(method); - s_logger.info("url is " + url); - s_logger.info("list ip addresses for user " + userId + " response code: " + responseCode); - if (responseCode == 200) { - InputStream is = method.getResponseBodyAsStream(); - Map success = getSingleValueFromXML(is, new String[] {"success"}); - s_logger.info("Enable Static NAT..success? " + success.get("success")); - } else { - s_logger.error("Enable Static NAT failed with error code: " + responseCode + ". Following URL was sent: " + url); - return responseCode; - } - - // ------------------------------------------------------------- - // CREATE IP FORWARDING RULE -- Windows VM - // ------------------------------------------------------------- - - // create port forwarding rule for window vm - encodedIpAddress = URLEncoder.encode(s_windowsIpId.get(), "UTF-8"); - //encodedVmId = URLEncoder.encode(s_windowsVmId.get(), "UTF-8"); - - requestToSign = "apikey=" + encodedApiKey + "&command=createIpForwardingRule&endPort=22&ipaddressid=" + encodedIpAddress + "&protocol=TCP&startPort=22"; - requestToSign = requestToSign.toLowerCase(); - signature = signRequest(requestToSign, s_secretKey.get()); - encodedSignature = URLEncoder.encode(signature, "UTF-8"); - - url = - developerServer + "?command=createIpForwardingRule&apikey=" + encodedApiKey + "&endPort=22&ipaddressid=" + encodedIpAddress + - "&protocol=TCP&signature=" + encodedSignature + "&startPort=22"; - - s_logger.info("Created Ip forwarding rule with " + url); - method = new GetMethod(url); - responseCode = client.executeMethod(method); - if (responseCode == 200) { - InputStream input = method.getResponseBodyAsStream(); - Element el = queryAsyncJobResult(server, input); - Map values = getSingleValueFromXML(el, new String[] {"id"}); - s_logger.info("Port forwarding rule was assigned successfully to Windows VM"); - long ipfwdid = Long.parseLong(values.get("id")); - s_logger.info("got Ip Forwarding Rule's Id:" + ipfwdid); - s_winipfwdid.set(values.get("id")); - } else { - s_logger.error("Port forwarding rule creation failed with error code: " + responseCode + ". Following URL was sent: " + url); - return responseCode; - } - } - } - return responseCode; - } - - private static int executeCleanup(String server, String developerServer, String username) throws HttpException, IOException { - // test steps: - // - get user - // - delete user - - // ----------------------------- - // GET USER - // ----------------------------- - String userId = s_userId.get().toString(); - String encodedUserId = URLEncoder.encode(userId, "UTF-8"); - String url = server + "?command=listUsers&id=" + encodedUserId; - s_logger.info("Cleaning up resources for user: " + userId + " with url " + url); - HttpClient client = new HttpClient(); - HttpMethod method = new GetMethod(url); - int responseCode = client.executeMethod(method); - s_logger.info("get user response code: " + responseCode); - if (responseCode == 200) { - InputStream is = method.getResponseBodyAsStream(); - Map userInfo = getSingleValueFromXML(is, new String[] {"username", "id", "account"}); - if (!username.equals(userInfo.get("username"))) { - s_logger.error("get user failed to retrieve requested user, aborting cleanup test" + ". Following URL was sent: " + url); - return -1; - } - - } else { - s_logger.error("get user failed with error code: " + responseCode + ". Following URL was sent: " + url); - return responseCode; - } - - // ----------------------------- - // UPDATE USER - // ----------------------------- - { - url = server + "?command=updateUser&id=" + userId + "&firstname=delete&lastname=me"; - client = new HttpClient(); - method = new GetMethod(url); - responseCode = client.executeMethod(method); - s_logger.info("update user response code: " + responseCode); - if (responseCode == 200) { - InputStream is = method.getResponseBodyAsStream(); - Map success = getSingleValueFromXML(is, new String[] {"success"}); - s_logger.info("update user..success? " + success.get("success")); - } else { - s_logger.error("update user failed with error code: " + responseCode + ". Following URL was sent: " + url); - return responseCode; - } - } - - // ----------------------------- - // Detach existin dataVolume, create a new volume, attach it to the vm - // ----------------------------- - { - url = server + "?command=listVolumes&virtualMachineId=" + s_linuxVmId.get() + "&type=dataDisk"; - s_logger.info("Getting dataDisk id of Centos vm"); - client = new HttpClient(); - method = new GetMethod(url); - responseCode = client.executeMethod(method); - s_logger.info("List volumes response code: " + responseCode); - if (responseCode == 200) { - InputStream is = method.getResponseBodyAsStream(); - Map success = getSingleValueFromXML(is, new String[] {"id"}); - s_logger.info("Got dataDiskVolume with id " + success.get("id")); - s_dataVolume.set(success.get("id")); - } else { - s_logger.error("List volumes failed with error code: " + responseCode + ". Following URL was sent: " + url); - return responseCode; - } - } - - // Detach volume - { - url = server + "?command=detachVolume&id=" + s_dataVolume.get(); - s_logger.info("Detaching volume with id " + s_dataVolume.get()); - client = new HttpClient(); - method = new GetMethod(url); - responseCode = client.executeMethod(method); - s_logger.info("Detach data volume response code: " + responseCode); - if (responseCode == 200) { - InputStream input = method.getResponseBodyAsStream(); - Element el = queryAsyncJobResult(server, input); - s_logger.info("The volume was detached successfully"); - } else { - s_logger.error("Detach data disk failed with error code: " + responseCode + ". Following URL was sent: " + url); - return responseCode; - } - } - - // Delete a volume - { - url = server + "?command=deleteVolume&id=" + s_dataVolume.get(); - s_logger.info("Deleting volume with id " + s_dataVolume.get()); - client = new HttpClient(); - method = new GetMethod(url); - responseCode = client.executeMethod(method); - s_logger.info("Delete data volume response code: " + responseCode); - if (responseCode == 200) { - s_logger.info("The volume was deleted successfully"); - } else { - s_logger.error("Delete volume failed with error code: " + responseCode + ". Following URL was sent: " + url); - return responseCode; - } - } - - // Create a new volume - { - url = server + "?command=createVolume&diskofferingid=" + diskOfferingId + "&zoneid=" + zoneId + "&name=newvolume&account=" + s_account.get() + "&domainid=1"; - s_logger.info("Creating volume...."); - client = new HttpClient(); - method = new GetMethod(url); - responseCode = client.executeMethod(method); - if (responseCode == 200) { - InputStream input = method.getResponseBodyAsStream(); - Element el = queryAsyncJobResult(server, input); - Map values = getSingleValueFromXML(el, new String[] {"id"}); - - if (values.get("id") == null) { - s_logger.info("create volume response code: 401"); - return 401; - } else { - s_logger.info("create volume response code: " + responseCode); - long volumeId = Long.parseLong(values.get("id")); - s_logger.info("got volume id: " + volumeId); - s_newVolume.set(values.get("id")); - } - } else { - s_logger.error("create volume failed with error code: " + responseCode + ". Following URL was sent: " + url); - return responseCode; - } - } - - // attach a new volume to the vm - { - url = server + "?command=attachVolume&id=" + s_newVolume.get() + "&virtualmachineid=" + s_linuxVmId.get(); - s_logger.info("Attaching volume with id " + s_newVolume.get() + " to the vm " + s_linuxVmId.get()); - client = new HttpClient(); - method = new GetMethod(url); - responseCode = client.executeMethod(method); - s_logger.info("Attach data volume response code: " + responseCode); - if (responseCode == 200) { - InputStream input = method.getResponseBodyAsStream(); - Element el = queryAsyncJobResult(server, input); - s_logger.info("The volume was attached successfully"); - } else { - s_logger.error("Attach volume failed with error code: " + responseCode + ". Following URL was sent: " + url); - return responseCode; - } - } - - // Create a snapshot - // list volumes - { - url = server + "?command=listVolumes&virtualMachineId=" + s_linuxVmId.get() + "&type=root"; - s_logger.info("Getting rootDisk id of Centos vm"); - client = new HttpClient(); - method = new GetMethod(url); - responseCode = client.executeMethod(method); - s_logger.info("List volumes response code: " + responseCode); - if (responseCode == 200) { - InputStream is = method.getResponseBodyAsStream(); - Map success = getSingleValueFromXML(is, new String[] {"id"}); - if (success.get("id") == null) { - s_logger.error("Unable to get root volume. Followin url was sent: " + url); - } - s_logger.info("Got rootVolume with id " + success.get("id")); - s_rootVolume.set(success.get("id")); - } else { - s_logger.error("List volumes failed with error code: " + responseCode + ". Following URL was sent: " + url); - return responseCode; - } - } - - // //Create snapshot from root disk volume - String encodedApiKey = URLEncoder.encode(s_apiKey.get(), "UTF-8"); - String requestToSign = "apikey=" + encodedApiKey + "&command=createSnapshot&volumeid=" + s_rootVolume.get(); - requestToSign = requestToSign.toLowerCase(); - String signature = signRequest(requestToSign, s_secretKey.get()); - String encodedSignature = URLEncoder.encode(signature, "UTF-8"); - - url = developerServer + "?command=createSnapshot&volumeid=" + s_rootVolume.get() + "&apikey=" + encodedApiKey + "&signature=" + encodedSignature; - client = new HttpClient(); - method = new GetMethod(url); - responseCode = client.executeMethod(method); - s_logger.info("Create snapshot response code: " + responseCode); - if (responseCode == 200) { - InputStream input = method.getResponseBodyAsStream(); - Element el = queryAsyncJobResult(server, input); - Map values = getSingleValueFromXML(el, new String[] {"id"}); - - if (values.get("id") == null) { - s_logger.info("create snapshot response code: 401"); - return 401; - } else { - s_logger.info("create snapshot response code: " + responseCode + ". Got snapshot with id " + values.get("id")); - s_snapshot.set(values.get("id")); - } - } else { - s_logger.error("create snapshot failed with error code: " + responseCode + ". Following URL was sent: " + url); - return responseCode; - } - - // Create volume from the snapshot created on the previous step and attach it to the running vm - /* encodedApiKey = URLEncoder.encode(s_apiKey.get(), "UTF-8"); - requestToSign = "apikey=" + encodedApiKey + "&command=createVolume&name=" + s_account.get() + "&snapshotid=" + s_snapshot.get(); - requestToSign = requestToSign.toLowerCase(); - signature = signRequest(requestToSign, s_secretKey.get()); - encodedSignature = URLEncoder.encode(signature, "UTF-8"); - - url = developerServer + "?command=createVolume&name=" + s_account.get() + "&snapshotid=" + s_snapshot.get() + "&apikey=" + encodedApiKey + "&signature=" + encodedSignature; - client = new HttpClient(); - method = new GetMethod(url); - responseCode = client.executeMethod(method); - s_logger.info("Create volume from snapshot response code: " + responseCode); - if (responseCode == 200) { - InputStream input = method.getResponseBodyAsStream(); - Element el = queryAsyncJobResult(server, input); - Map values = getSingleValueFromXML(el, new String[] { "id" }); - - if (values.get("id") == null) { - s_logger.info("create volume from snapshot response code: 401"); - return 401; - } else { - s_logger.info("create volume from snapshot response code: " + responseCode + ". Got volume with id " + values.get("id") + ". The command was sent with url " + url); - s_volumeFromSnapshot.set(values.get("id")); - } - } else { - s_logger.error("create volume from snapshot failed with error code: " + responseCode + ". Following URL was sent: " + url); - return responseCode; - } - - { - url = server + "?command=attachVolume&id=" + s_volumeFromSnapshot.get() + "&virtualmachineid=" + s_linuxVmId.get(); - s_logger.info("Attaching volume with id " + s_volumeFromSnapshot.get() + " to the vm " + s_linuxVmId.get()); - client = new HttpClient(); - method = new GetMethod(url); - responseCode = client.executeMethod(method); - s_logger.info("Attach volume from snapshot to linux vm response code: " + responseCode); - if (responseCode == 200) { - InputStream input = method.getResponseBodyAsStream(); - Element el = queryAsyncJobResult(server, input); - s_logger.info("The volume created from snapshot was attached successfully to linux vm"); - } else { - s_logger.error("Attach volume created from snapshot failed with error code: " + responseCode + ". Following URL was sent: " + url); - return responseCode; - } - } - */ - // ----------------------------- - // Execute reboot/stop/start commands for the VMs before deleting the account - made to exercise xen - // ----------------------------- - - // Reboot windows VM - requestToSign = "apikey=" + encodedApiKey + "&command=rebootVirtualMachine&id=" + s_windowsVmId.get(); - requestToSign = requestToSign.toLowerCase(); - signature = signRequest(requestToSign, s_secretKey.get()); - encodedSignature = URLEncoder.encode(signature, "UTF-8"); - - url = developerServer + "?command=rebootVirtualMachine&id=" + s_windowsVmId.get() + "&apikey=" + encodedApiKey + "&signature=" + encodedSignature; - client = new HttpClient(); - method = new GetMethod(url); - responseCode = client.executeMethod(method); - s_logger.info("Reboot windows Vm response code: " + responseCode); - if (responseCode == 200) { - InputStream input = method.getResponseBodyAsStream(); - Element el = queryAsyncJobResult(server, input); - Map success = getSingleValueFromXML(el, new String[] {"success"}); - s_logger.info("Windows VM was rebooted with the status: " + success.get("success")); - } else { - s_logger.error("Reboot windows VM test failed with error code: " + responseCode + ". Following URL was sent: " + url); - return responseCode; - } - - // Stop centos VM - requestToSign = "apikey=" + encodedApiKey + "&command=stopVirtualMachine&id=" + s_linuxVmId.get(); - requestToSign = requestToSign.toLowerCase(); - signature = signRequest(requestToSign, s_secretKey.get()); - encodedSignature = URLEncoder.encode(signature, "UTF-8"); - - url = developerServer + "?command=stopVirtualMachine&id=" + s_linuxVmId.get() + "&apikey=" + encodedApiKey + "&signature=" + encodedSignature; - client = new HttpClient(); - method = new GetMethod(url); - responseCode = client.executeMethod(method); - s_logger.info("Stop linux Vm response code: " + responseCode); - if (responseCode == 200) { - InputStream input = method.getResponseBodyAsStream(); - Element el = queryAsyncJobResult(server, input); - Map success = getSingleValueFromXML(el, new String[] {"success"}); - s_logger.info("Linux VM was stopped with the status: " + success.get("success")); - } else { - s_logger.error("Stop linux VM test failed with error code: " + responseCode + ". Following URL was sent: " + url); - return responseCode; - } - - // Create private template from root disk volume - requestToSign = - "apikey=" + encodedApiKey + "&command=createTemplate" + "&displaytext=" + s_account.get() + "&name=" + s_account.get() + "&ostypeid=11" + "&snapshotid=" + - s_snapshot.get(); - requestToSign = requestToSign.toLowerCase(); - signature = signRequest(requestToSign, s_secretKey.get()); - encodedSignature = URLEncoder.encode(signature, "UTF-8"); - - url = - developerServer + "?command=createTemplate" + "&displaytext=" + s_account.get() + "&name=" + s_account.get() + "&ostypeid=11" + "&snapshotid=" + - s_snapshot.get() + "&apikey=" + encodedApiKey + "&signature=" + encodedSignature; - client = new HttpClient(); - method = new GetMethod(url); - responseCode = client.executeMethod(method); - s_logger.info("Create private template response code: " + responseCode); - if (responseCode == 200) { - InputStream input = method.getResponseBodyAsStream(); - Element el = queryAsyncJobResult(server, input); - Map values = getSingleValueFromXML(el, new String[] {"id"}); - - if (values.get("id") == null) { - s_logger.info("create private template response code: 401"); - return 401; - } else { - s_logger.info("create private template response code: " + responseCode); - } - } else { - s_logger.error("create private template failed with error code: " + responseCode + ". Following URL was sent: " + url); - return responseCode; - } - - // Start centos VM - requestToSign = "apikey=" + encodedApiKey + "&command=startVirtualMachine&id=" + s_windowsVmId.get(); - requestToSign = requestToSign.toLowerCase(); - signature = signRequest(requestToSign, s_secretKey.get()); - encodedSignature = URLEncoder.encode(signature, "UTF-8"); - - url = developerServer + "?command=startVirtualMachine&id=" + s_windowsVmId.get() + "&apikey=" + encodedApiKey + "&signature=" + encodedSignature; - client = new HttpClient(); - method = new GetMethod(url); - responseCode = client.executeMethod(method); - s_logger.info("Start linux Vm response code: " + responseCode); - if (responseCode != 200) { - s_logger.error("Start linux VM test failed with error code: " + responseCode + ". Following URL was sent: " + url); - return responseCode; - } - - // get domainRouter id - { - url = server + "?command=listRouters&zoneid=" + zoneId + "&account=" + s_account.get() + "&domainid=1"; - client = new HttpClient(); - method = new GetMethod(url); - responseCode = client.executeMethod(method); - s_logger.info("List domain routers response code: " + responseCode); - if (responseCode == 200) { - InputStream is = method.getResponseBodyAsStream(); - Map success = getSingleValueFromXML(is, new String[] {"id"}); - s_logger.info("Got the domR with id " + success.get("id")); - s_domainRouterId.set(success.get("id")); - } else { - s_logger.error("List domain routers failed with error code: " + responseCode + ". Following URL was sent: " + url); - return responseCode; - } - } - - // reboot the domain router - { - url = server + "?command=rebootRouter&id=" + s_domainRouterId.get(); - s_logger.info("Rebooting domR with id " + s_domainRouterId.get()); - client = new HttpClient(); - method = new GetMethod(url); - responseCode = client.executeMethod(method); - s_logger.info("Reboot domain router response code: " + responseCode); - if (responseCode == 200) { - InputStream input = method.getResponseBodyAsStream(); - Element el = queryAsyncJobResult(server, input); - s_logger.info("Domain router was rebooted successfully"); - } else { - s_logger.error("Reboot domain routers failed with error code: " + responseCode + ". Following URL was sent: " + url); - return responseCode; - } - } - - // ----------------------------- - // DELETE ACCOUNT - // ----------------------------- - { - url = server + "?command=deleteAccount&id=" + s_accountId.get(); - client = new HttpClient(); - method = new GetMethod(url); - responseCode = client.executeMethod(method); - s_logger.info("delete account response code: " + responseCode); - if (responseCode == 200) { - InputStream input = method.getResponseBodyAsStream(); - Element el = queryAsyncJobResult(server, input); - s_logger.info("Deleted account successfully"); - } else { - s_logger.error("delete account failed with error code: " + responseCode + ". Following URL was sent: " + url); - return responseCode; - } - } - return responseCode; - } - - private static int executeEventsAndBilling(String server, String developerServer) throws HttpException, IOException { - // test steps: - // - get all the events in the system for all users in the system - // - generate all the usage records in the system - // - get all the usage records in the system - - // ----------------------------- - // GET EVENTS - // ----------------------------- - String url = server + "?command=listEvents&page=1&pagesize=100&&account=" + s_account.get(); - - s_logger.info("Getting events for the account " + s_account.get()); - HttpClient client = new HttpClient(); - HttpMethod method = new GetMethod(url); - int responseCode = client.executeMethod(method); - s_logger.info("get events response code: " + responseCode); - if (responseCode == 200) { - InputStream is = method.getResponseBodyAsStream(); - Map> eventDescriptions = getMultipleValuesFromXML(is, new String[] {"description"}); - List descriptionText = eventDescriptions.get("description"); - if (descriptionText == null) { - s_logger.info("no events retrieved..."); - } else { - for (String text : descriptionText) { - s_logger.info("event: " + text); - } - } - } else { - s_logger.error("list events failed with error code: " + responseCode + ". Following URL was sent: " + url); - - return responseCode; - } - - // ------------------------------------------------------------------------------------- - // GENERATE USAGE RECORDS (note: typically this is done infrequently) - // ------------------------------------------------------------------------------------- - DateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd"); - Date currentDate = new Date(); - String endDate = dateFormat.format(currentDate); - s_logger.info("Generating usage records from September 1st till " + endDate); - url = server + "?command=generateUsageRecords&startdate=2009-09-01&enddate=" + endDate; // generate - // all usage record till today - client = new HttpClient(); - method = new GetMethod(url); - responseCode = client.executeMethod(method); - s_logger.info("generate usage records response code: " + responseCode); - if (responseCode == 200) { - InputStream is = method.getResponseBodyAsStream(); - Map successStr = getSingleValueFromXML(is, new String[] {"success"}); - s_logger.info("successfully generated usage records? " + successStr.get("success")); - } else { - s_logger.error("generate usage records failed with error code: " + responseCode + ". Following URL was sent: " + url); - return responseCode; - } - - // Sleeping for a 2 minutes before getting a usage records from the database - try { - Thread.sleep(120000); - } catch (Exception ex) { - s_logger.error(ex); - } - - // -------------------------------- - // GET USAGE RECORDS - // -------------------------------- - url = server + "?command=listUsageRecords&startdate=2009-09-01&enddate=" + endDate + "&account=" + s_account.get() + "&domaindid=1"; - s_logger.info("Getting all usage records with request: " + url); - client = new HttpClient(); - method = new GetMethod(url); - responseCode = client.executeMethod(method); - s_logger.info("get usage records response code: " + responseCode); - if (responseCode == 200) { - InputStream is = method.getResponseBodyAsStream(); - Map> usageRecValues = getMultipleValuesFromXML(is, new String[] {"description", "usage"}); - if ((usageRecValues.containsKey("description") == true) && (usageRecValues.containsKey("usage") == true)) { - List descriptions = usageRecValues.get("description"); - List usages = usageRecValues.get("usage"); - for (int i = 0; i < descriptions.size(); i++) { - String desc = descriptions.get(i); - String usage = ""; - if (usages != null) { - if (i < usages.size()) { - usage = ", usage: " + usages.get(i); - } - } - s_logger.info("desc: " + desc + usage); - } - } - - } else { - s_logger.error("list usage records failed with error code: " + responseCode + ". Following URL was sent: " + url); - return responseCode; - } - - return responseCode; - } - - private static boolean getNetworkStat(String server) { - try { - String url = server + "?command=listAccountStatistics&account=" + s_account.get(); - HttpClient client = new HttpClient(); - HttpMethod method = new GetMethod(url); - int responseCode = client.executeMethod(method); - s_logger.info("listAccountStatistics response code: " + responseCode); - if (responseCode == 200) { - InputStream is = method.getResponseBodyAsStream(); - Map requestKeyValues = getSingleValueFromXML(is, new String[] {"receivedbytes", "sentbytes"}); - int bytesReceived = Integer.parseInt(requestKeyValues.get("receivedbytes")); - int bytesSent = Integer.parseInt(requestKeyValues.get("sentbytes")); - if ((bytesReceived > 100000000) && (bytesSent > 0)) { - s_logger.info("Network stat is correct for account" + s_account.get() + "; bytest received is " + toHumanReadableSize(bytesReceived) + " and bytes sent is " + toHumanReadableSize(bytesSent)); - return true; - } else { - s_logger.error("Incorrect value for bytes received/sent for the account " + s_account.get() + ". We got " + toHumanReadableSize(bytesReceived) + " bytes received; " + - " and " + toHumanReadableSize(bytesSent) + " bytes sent"); - return false; - } - - } else { - s_logger.error("listAccountStatistics failed with error code: " + responseCode + ". Following URL was sent: " + url); - return false; - } - } catch (Exception ex) { - s_logger.error("Exception while sending command listAccountStatistics"); - return false; - } - } - - private static int executeStop(String server, String developerServer, String username, boolean destroy) throws HttpException, IOException { - // test steps: - // - get userId for the given username - // - list virtual machines for the user - // - stop all virtual machines - // - get ip addresses for the user - // - release ip addresses - - // ----------------------------- - // GET USER - // ----------------------------- - String userId = s_userId.get().toString(); - String encodedUserId = URLEncoder.encode(userId, "UTF-8"); - - String url = server + "?command=listUsers&id=" + encodedUserId; - s_logger.info("Stopping resources for user: " + username); - HttpClient client = new HttpClient(); - HttpMethod method = new GetMethod(url); - int responseCode = client.executeMethod(method); - s_logger.info("get user response code: " + responseCode); - if (responseCode == 200) { - InputStream is = method.getResponseBodyAsStream(); - Map userIdValues = getSingleValueFromXML(is, new String[] {"id"}); - String userIdStr = userIdValues.get("id"); - if (userIdStr != null) { - userId = userIdStr; - - } else { - s_logger.error("get user failed to retrieve a valid user id, aborting depolyment test" + ". Following URL was sent: " + url); - return -1; - } - } else { - s_logger.error("get user failed with error code: " + responseCode + ". Following URL was sent: " + url); - return responseCode; - } - - { - // ---------------------------------- - // LIST VIRTUAL MACHINES - // ---------------------------------- - String encodedApiKey = URLEncoder.encode(s_apiKey.get(), "UTF-8"); - String requestToSign = "apikey=" + encodedApiKey + "&command=listVirtualMachines"; - requestToSign = requestToSign.toLowerCase(); - String signature = signRequest(requestToSign, s_secretKey.get()); - String encodedSignature = URLEncoder.encode(signature, "UTF-8"); - - url = developerServer + "?command=listVirtualMachines&apikey=" + encodedApiKey + "&signature=" + encodedSignature; - - s_logger.info("Listing all virtual machines for the user with url " + url); - String[] vmIds = null; - client = new HttpClient(); - method = new GetMethod(url); - responseCode = client.executeMethod(method); - s_logger.info("list virtual machines response code: " + responseCode); - if (responseCode == 200) { - InputStream is = method.getResponseBodyAsStream(); - Map> vmIdValues = getMultipleValuesFromXML(is, new String[] {"id"}); - if (vmIdValues.containsKey("id")) { - List vmIdList = vmIdValues.get("id"); - if (vmIdList != null) { - vmIds = new String[vmIdList.size()]; - vmIdList.toArray(vmIds); - String vmIdLogStr = ""; - if ((vmIds != null) && (vmIds.length > 0)) { - vmIdLogStr = vmIds[0]; - for (int i = 1; i < vmIds.length; i++) { - vmIdLogStr = vmIdLogStr + "," + vmIds[i]; - } - } - s_logger.info("got virtual machine ids: " + vmIdLogStr); - } - } - - } else { - s_logger.error("list virtual machines test failed with error code: " + responseCode + ". Following URL was sent: " + url); - return responseCode; - } - - // ---------------------------------- - // LIST USER IP ADDRESSES - // ---------------------------------- - - requestToSign = "apikey=" + encodedApiKey + "&command=listPublicIpAddresses"; - requestToSign = requestToSign.toLowerCase(); - signature = signRequest(requestToSign, s_secretKey.get()); - encodedSignature = URLEncoder.encode(signature, "UTF-8"); - - url = developerServer + "?command=listPublicIpAddresses&apikey=" + encodedApiKey + "&signature=" + encodedSignature; - String[] ipAddresses = null; - client = new HttpClient(); - method = new GetMethod(url); - responseCode = client.executeMethod(method); - s_logger.info("list ip addresses for user " + userId + " response code: " + responseCode); - if (responseCode == 200) { - InputStream is = method.getResponseBodyAsStream(); - Map> ipAddressValues = getMultipleValuesFromXML(is, new String[] {"ipaddress"}); - if (ipAddressValues.containsKey("ipaddress")) { - List ipAddressList = ipAddressValues.get("ipaddress"); - if (ipAddressList != null) { - ipAddresses = new String[ipAddressList.size()]; - ipAddressList.toArray(ipAddresses); - String ipAddressLogStr = ""; - if ((ipAddresses != null) && (ipAddresses.length > 0)) { - ipAddressLogStr = ipAddresses[0]; - for (int i = 1; i < ipAddresses.length; i++) { - ipAddressLogStr = ipAddressLogStr + "," + ipAddresses[i]; - } - } - s_logger.info("got IP addresses: " + ipAddressLogStr); - } - } - - } else { - s_logger.error("list user ip addresses failed with error code: " + responseCode + ". Following URL was sent: " + url); - return responseCode; - } - - // ---------------------------------- - // LIST ZONES - // ---------------------------------- - - requestToSign = "apikey=" + encodedApiKey + "&command=listZones"; - requestToSign = requestToSign.toLowerCase(); - signature = signRequest(requestToSign, s_secretKey.get()); - encodedSignature = URLEncoder.encode(signature, "UTF-8"); - - url = developerServer + "?command=listZones&apikey=" + encodedApiKey + "&signature=" + encodedSignature; - String[] zoneNames = null; - client = new HttpClient(); - method = new GetMethod(url); - responseCode = client.executeMethod(method); - s_logger.info("list zones response code: " + responseCode); - if (responseCode == 200) { - InputStream is = method.getResponseBodyAsStream(); - Map> zoneNameValues = getMultipleValuesFromXML(is, new String[] {"name"}); - if (zoneNameValues.containsKey("name")) { - List zoneNameList = zoneNameValues.get("name"); - if (zoneNameList != null) { - zoneNames = new String[zoneNameList.size()]; - zoneNameList.toArray(zoneNames); - String zoneNameLogStr = "\n\n"; - if ((zoneNames != null) && (zoneNames.length > 0)) { - zoneNameLogStr += zoneNames[0]; - for (int i = 1; i < zoneNames.length; i++) { - zoneNameLogStr = zoneNameLogStr + "\n" + zoneNames[i]; - } - - } - zoneNameLogStr += "\n\n"; - s_logger.info("got zones names: " + zoneNameLogStr); - } - } - - } else { - s_logger.error("list zones failed with error code: " + responseCode + ". Following URL was sent: " + url); - return responseCode; - } - - // ---------------------------------- - // LIST ACCOUNT STATISTICS - // ---------------------------------- - - requestToSign = "apikey=" + encodedApiKey + "&command=listAccounts"; - requestToSign = requestToSign.toLowerCase(); - signature = signRequest(requestToSign, s_secretKey.get()); - encodedSignature = URLEncoder.encode(signature, "UTF-8"); - - url = developerServer + "?command=listAccounts&apikey=" + encodedApiKey + "&signature=" + encodedSignature; - String[] statNames = null; - client = new HttpClient(); - method = new GetMethod(url); - responseCode = client.executeMethod(method); - s_logger.info("listAccountStatistics response code: " + responseCode); - if (responseCode == 200) { - InputStream is = method.getResponseBodyAsStream(); - Map> statValues = getMultipleValuesFromXML(is, new String[] {"receivedbytes"}); - if (statValues.containsKey("receivedbytes")) { - List statList = statValues.get("receivedbytes"); - if (statList != null) { - statNames = new String[statList.size()]; - statList.toArray(statNames); - String statLogStr = "\n\n"; - if ((statNames != null) && (zoneNames.length > 0)) { - statLogStr += statNames[0]; - for (int i = 1; i < statNames.length; i++) { - statLogStr = statLogStr + "\n" + zoneNames[i]; - } - - } - statLogStr += "\n\n"; - s_logger.info("got accountstatistics: " + statLogStr); - } - } - - } else { - s_logger.error("listAccountStatistics failed with error code: " + responseCode + ". Following URL was sent: " + url); - return responseCode; - } - - // ---------------------------------- - // LIST TEMPLATES - // ---------------------------------- - - requestToSign = "apikey=" + encodedApiKey + "&command=listTemplates@templatefilter=self"; - requestToSign = requestToSign.toLowerCase(); - signature = signRequest(requestToSign, s_secretKey.get()); - encodedSignature = URLEncoder.encode(signature, "UTF-8"); - - url = developerServer + "?command=listTemplates&apikey=" + encodedApiKey + "&templatefilter=self&signature=" + encodedSignature; - String[] templateNames = null; - client = new HttpClient(); - method = new GetMethod(url); - responseCode = client.executeMethod(method); - s_logger.info("list templates response code: " + responseCode); - if (responseCode == 200) { - InputStream is = method.getResponseBodyAsStream(); - Map> templateNameValues = getMultipleValuesFromXML(is, new String[] {"name"}); - - if (templateNameValues.containsKey("name")) { - List templateNameList = templateNameValues.get("name"); - if (templateNameList != null) { - templateNames = new String[templateNameList.size()]; - templateNameList.toArray(templateNames); - String templateNameLogStr = "\n\n"; - if ((templateNames != null) && (templateNames.length > 0)) { - templateNameLogStr += templateNames[0]; - for (int i = 1; i < templateNames.length; i++) { - templateNameLogStr = templateNameLogStr + "\n" + templateNames[i]; - } - - } - templateNameLogStr += "\n\n"; - s_logger.info("got template names: " + templateNameLogStr); - } - } - - } else { - s_logger.error("list templates failed with error code: " + responseCode + ". Following URL was sent: " + url); - return responseCode; - } - - // ---------------------------------- - // LIST SERVICE OFFERINGS - // ---------------------------------- - - requestToSign = "apikey=" + encodedApiKey + "&command=listServiceOfferings"; - requestToSign = requestToSign.toLowerCase(); - signature = signRequest(requestToSign, s_secretKey.get()); - encodedSignature = URLEncoder.encode(signature, "UTF-8"); - - url = developerServer + "?command=listServiceOfferings&apikey=" + encodedApiKey + "&signature=" + encodedSignature; - String[] serviceOfferingNames = null; - client = new HttpClient(); - method = new GetMethod(url); - responseCode = client.executeMethod(method); - s_logger.info("list service offerings response code: " + responseCode); - if (responseCode == 200) { - InputStream is = method.getResponseBodyAsStream(); - Map> serviceOfferingNameValues = getMultipleValuesFromXML(is, new String[] {"name"}); - - if (serviceOfferingNameValues.containsKey("name")) { - List serviceOfferingNameList = serviceOfferingNameValues.get("name"); - if (serviceOfferingNameList != null) { - serviceOfferingNames = new String[serviceOfferingNameList.size()]; - serviceOfferingNameList.toArray(serviceOfferingNames); - String serviceOfferingNameLogStr = ""; - if ((serviceOfferingNames != null) && (serviceOfferingNames.length > 0)) { - serviceOfferingNameLogStr = serviceOfferingNames[0]; - for (int i = 1; i < serviceOfferingNames.length; i++) { - serviceOfferingNameLogStr = serviceOfferingNameLogStr + ", " + serviceOfferingNames[i]; - } - } - s_logger.info("got service offering names: " + serviceOfferingNameLogStr); - } - } - - } else { - s_logger.error("list service offerings failed with error code: " + responseCode + ". Following URL was sent: " + url); - return responseCode; - } - - // ---------------------------------- - // LIST EVENTS - // --------------------------------- - - url = server + "?command=listEvents&page=1&pagesize=100&&account=" + s_account.get(); - String[] eventDescriptions = null; - client = new HttpClient(); - method = new GetMethod(url); - responseCode = client.executeMethod(method); - s_logger.info("list events response code: " + responseCode); - if (responseCode == 200) { - InputStream is = method.getResponseBodyAsStream(); - Map> eventNameValues = getMultipleValuesFromXML(is, new String[] {"description"}); - - if (eventNameValues.containsKey("description")) { - List eventNameList = eventNameValues.get("description"); - if (eventNameList != null) { - eventDescriptions = new String[eventNameList.size()]; - eventNameList.toArray(eventDescriptions); - String eventNameLogStr = "\n\n"; - if ((eventDescriptions != null) && (eventDescriptions.length > 0)) { - eventNameLogStr += eventDescriptions[0]; - for (int i = 1; i < eventDescriptions.length; i++) { - eventNameLogStr = eventNameLogStr + "\n" + eventDescriptions[i]; - } - } - eventNameLogStr += "\n\n"; - s_logger.info("got event descriptions: " + eventNameLogStr); - } - } - } else { - s_logger.error("list events failed with error code: " + responseCode + ". Following URL was sent: " + url); - return responseCode; - } - - // ---------------------------------- - // STOP/DESTROY VIRTUAL MACHINES - // ---------------------------------- - if (vmIds != null) { - String cmdName = (destroy ? "destroyVirtualMachine" : "stopVirtualMachine"); - for (String vmId : vmIds) { - requestToSign = "apikey=" + encodedApiKey + "&command=" + cmdName + "&id=" + vmId; - requestToSign = requestToSign.toLowerCase(); - signature = signRequest(requestToSign, s_secretKey.get()); - encodedSignature = URLEncoder.encode(signature, "UTF-8"); - - url = developerServer + "?command=" + cmdName + "&id=" + vmId + "&apikey=" + encodedApiKey + "&signature=" + encodedSignature; - client = new HttpClient(); - method = new GetMethod(url); - responseCode = client.executeMethod(method); - s_logger.info(cmdName + " [" + vmId + "] response code: " + responseCode); - if (responseCode == 200) { - InputStream input = method.getResponseBodyAsStream(); - Element el = queryAsyncJobResult(server, input); - Map success = getSingleValueFromXML(el, new String[] {"success"}); - s_logger.info(cmdName + "..success? " + success.get("success")); - } else { - s_logger.error(cmdName + "test failed with error code: " + responseCode + ". Following URL was sent: " + url); - return responseCode; - } - } - } - } - - { - String[] ipAddresses = null; - // ----------------------------------------- - // LIST NAT IP ADDRESSES - // ----------------------------------------- - String encodedApiKey = URLEncoder.encode(s_apiKey.get(), "UTF-8"); - String requestToSign = "apikey=" + encodedApiKey + "&command=listPublicIpAddresses"; - requestToSign = requestToSign.toLowerCase(); - String signature = signRequest(requestToSign, s_secretKey.get()); - String encodedSignature = URLEncoder.encode(signature, "UTF-8"); - - url = developerServer + "?command=listPublicIpAddresses&apikey=" + encodedApiKey + "&signature=" + encodedSignature; - client = new HttpClient(); - method = new GetMethod(url); - responseCode = client.executeMethod(method); - s_logger.info("list ip addresses for user " + userId + " response code: " + responseCode); - if (responseCode == 200) { - - InputStream is = method.getResponseBodyAsStream(); - List ipAddressList = getNonSourceNatIPs(is); - ipAddresses = new String[ipAddressList.size()]; - ipAddressList.toArray(ipAddresses); - String ipAddrLogStr = ""; - if ((ipAddresses != null) && (ipAddresses.length > 0)) { - ipAddrLogStr = ipAddresses[0]; - for (int i = 1; i < ipAddresses.length; i++) { - ipAddrLogStr = ipAddrLogStr + "," + ipAddresses[i]; - } - } - s_logger.info("got ip addresses: " + ipAddrLogStr); - - } else { - s_logger.error("list nat ip addresses failed with error code: " + responseCode + ". Following URL was sent: " + url); - return responseCode; - } - - // ------------------------------------------------------------- - // Delete IP FORWARDING RULE -- Windows VM - // ------------------------------------------------------------- - String encodedIpFwdId = URLEncoder.encode(s_winipfwdid.get(), "UTF-8"); - - requestToSign = "apikey=" + encodedApiKey + "&command=deleteIpForwardingRule&id=" + encodedIpFwdId; - requestToSign = requestToSign.toLowerCase(); - signature = signRequest(requestToSign, s_secretKey.get()); - encodedSignature = URLEncoder.encode(signature, "UTF-8"); - - url = developerServer + "?command=deleteIpForwardingRule&apikey=" + encodedApiKey + "&id=" + encodedIpFwdId + "&signature=" + encodedSignature; - - s_logger.info("Delete Ip forwarding rule with " + url); - method = new GetMethod(url); - responseCode = client.executeMethod(method); - if (responseCode == 200) { - InputStream input = method.getResponseBodyAsStream(); - Element el = queryAsyncJobResult(server, input); - s_logger.info("IP forwarding rule was successfully deleted"); - - } else { - s_logger.error("IP forwarding rule creation failed with error code: " + responseCode + ". Following URL was sent: " + url); - return responseCode; - } - - //-------------------------------------------- - // Disable Static NAT for the Source NAT Ip - //-------------------------------------------- - encodedApiKey = URLEncoder.encode(s_apiKey.get(), "UTF-8"); - String encodedPublicIpId = URLEncoder.encode(s_publicIpId.get(), "UTF-8"); - requestToSign = "apikey=" + encodedApiKey + "&command=disableStaticNat" + "&id=" + encodedPublicIpId; - requestToSign = requestToSign.toLowerCase(); - signature = signRequest(requestToSign, s_secretKey.get()); - encodedSignature = URLEncoder.encode(signature, "UTF-8"); - - url = developerServer + "?command=disableStaticNat&apikey=" + encodedApiKey + "&id=" + encodedPublicIpId + "&signature=" + encodedSignature; - client = new HttpClient(); - method = new GetMethod(url); - responseCode = client.executeMethod(method); - s_logger.info("url is " + url); - s_logger.info("list ip addresses for user " + userId + " response code: " + responseCode); - if (responseCode == 200) { - InputStream is = method.getResponseBodyAsStream(); - Map success = getSingleValueFromXML(is, new String[] {"success"}); - s_logger.info("Disable Static NAT..success? " + success.get("success")); - } else { - s_logger.error("Disable Static NAT failed with error code: " + responseCode + ". Following URL was sent: " + url); - return responseCode; - } - - // ----------------------------------------- - // DISASSOCIATE IP ADDRESSES - // ----------------------------------------- - if (ipAddresses != null) { - for (String ipAddress : ipAddresses) { - requestToSign = "apikey=" + encodedApiKey + "&command=disassociateIpAddress&id=" + ipAddress; - requestToSign = requestToSign.toLowerCase(); - signature = signRequest(requestToSign, s_secretKey.get()); - encodedSignature = URLEncoder.encode(signature, "UTF-8"); - - url = developerServer + "?command=disassociateIpAddress&apikey=" + encodedApiKey + "&id=" + ipAddress + "&signature=" + encodedSignature; - client = new HttpClient(); - method = new GetMethod(url); - responseCode = client.executeMethod(method); - s_logger.info("disassociate ip address [" + userId + "/" + ipAddress + "] response code: " + responseCode); - if (responseCode == 200) { - InputStream input = method.getResponseBodyAsStream(); - Element disassocipel = queryAsyncJobResult(server, input); - Map success = getSingleValueFromXML(disassocipel, new String[] {"success"}); - // Map success = getSingleValueFromXML(input, new String[] { "success" }); - s_logger.info("disassociate ip address..success? " + success.get("success")); - } else { - s_logger.error("disassociate ip address failed with error code: " + responseCode + ". Following URL was sent: " + url); - return responseCode; - } - } - } - } - s_linuxIP.set(""); - s_linuxIpId.set(""); - s_linuxVmId.set(""); - s_linuxPassword.set(""); - s_windowsIP.set(""); - s_windowsIpId.set(""); - s_windowsVmId.set(""); - s_secretKey.set(""); - s_apiKey.set(""); - s_userId.set(Long.parseLong("0")); - s_account.set(""); - s_domainRouterId.set(""); - return responseCode; - } - - public static String signRequest(String request, String key) { - try { - Mac mac = Mac.getInstance("HmacSHA1"); - SecretKeySpec keySpec = new SecretKeySpec(key.getBytes(), "HmacSHA1"); - mac.init(keySpec); - mac.update(request.getBytes()); - byte[] encryptedBytes = mac.doFinal(); - return org.apache.commons.codec.binary.Base64.encodeBase64String(encryptedBytes); - } catch (Exception ex) { - s_logger.error("unable to sign request", ex); - } - return null; - } - - private static String sshWinTest(String host) { - if (host == null) { - s_logger.info("Did not receive a host back from test, ignoring win ssh test"); - return null; - } - - // We will retry 5 times before quitting - int retry = 1; - - while (true) { - try { - if (retry > 0) { - s_logger.info("Retry attempt : " + retry + " ...sleeping 300 seconds before next attempt. Account is " + s_account.get()); - Thread.sleep(300000); - } - - s_logger.info("Attempting to SSH into windows host " + host + " with retry attempt: " + retry + " for account " + s_account.get()); - - Connection conn = new Connection(host); - conn.connect(null, 60000, 60000); - - s_logger.info("User " + s_account.get() + " ssHed successfully into windows host " + host); - boolean success = false; - boolean isAuthenticated = conn.authenticateWithPassword("Administrator", "password"); - if (isAuthenticated == false) { - return "Authentication failed"; - } else { - s_logger.info("Authentication is successful"); - } - - try { - SCPClient scp = new SCPClient(conn); - scp.put("wget.exe", "wget.exe", "C:\\Users\\Administrator", "0777"); - s_logger.info("Successfully put wget.exe file"); - } catch (Exception ex) { - s_logger.error("Unable to put wget.exe " + ex); - } - - if (conn == null) { - s_logger.error("Connection is null"); - } - Session sess = conn.openSession(); - - s_logger.info("User + " + s_account.get() + " executing : wget http://" + downloadUrl); - String downloadCommand = "wget http://" + downloadUrl + " && dir dump.bin"; - sess.execCommand(downloadCommand); - - InputStream stdout = sess.getStdout(); - InputStream stderr = sess.getStderr(); - - byte[] buffer = new byte[8192]; - while (true) { - if ((stdout.available() == 0) && (stderr.available() == 0)) { - int conditions = sess.waitForCondition(ChannelCondition.STDOUT_DATA | ChannelCondition.STDERR_DATA | ChannelCondition.EOF, 120000); - - if ((conditions & ChannelCondition.TIMEOUT) != 0) { - s_logger.info("Timeout while waiting for data from peer."); - return null; - } - - if ((conditions & ChannelCondition.EOF) != 0) { - if ((conditions & (ChannelCondition.STDOUT_DATA | ChannelCondition.STDERR_DATA)) == 0) { - break; - } - } - } - - while (stdout.available() > 0) { - success = true; - int len = stdout.read(buffer); - if (len > 0) // this check is somewhat paranoid - s_logger.info(new String(buffer, 0, len)); - } - - while (stderr.available() > 0) { - /* int len = */stderr.read(buffer); - } - } - sess.close(); - conn.close(); - - if (success) { - return null; - } else { - retry++; - if (retry == MAX_RETRY_WIN) { - return "SSH Windows Network test fail for account " + s_account.get(); - } - } - } catch (Exception e) { - s_logger.error(e); - retry++; - if (retry == MAX_RETRY_WIN) { - return "SSH Windows Network test fail with error " + e.getMessage(); - } - } - } - } - - private static String sshTest(String host, String password, String snapshotTest) { - int i = 0; - if (host == null) { - s_logger.info("Did not receive a host back from test, ignoring ssh test"); - return null; - } - - if (password == null) { - s_logger.info("Did not receive a password back from test, ignoring ssh test"); - return null; - } - - // We will retry 5 times before quitting - String result = null; - int retry = 0; - - while (true) { - try { - if (retry > 0) { - s_logger.info("Retry attempt : " + retry + " ...sleeping 120 seconds before next attempt. Account is " + s_account.get()); - Thread.sleep(120000); - } - - s_logger.info("Attempting to SSH into linux host " + host + " with retry attempt: " + retry + ". Account is " + s_account.get()); - - Connection conn = new Connection(host); - conn.connect(null, 60000, 60000); - - s_logger.info("User + " + s_account.get() + " ssHed successfully into linux host " + host); - - boolean isAuthenticated = conn.authenticateWithPassword("root", password); - - if (isAuthenticated == false) { - s_logger.info("Authentication failed for root with password" + password); - return "Authentication failed"; - - } - - boolean success = false; - String linuxCommand = null; - - if (i % 10 == 0) - linuxCommand = "rm -rf *; wget http://" + downloadUrl + " && ls -al dump.bin"; - else - linuxCommand = "wget http://" + downloadUrl + " && ls -al dump.bin"; - - Session sess = conn.openSession(); - s_logger.info("User " + s_account.get() + " executing : " + linuxCommand); - sess.execCommand(linuxCommand); - - InputStream stdout = sess.getStdout(); - InputStream stderr = sess.getStderr(); - - byte[] buffer = new byte[8192]; - while (true) { - if ((stdout.available() == 0) && (stderr.available() == 0)) { - int conditions = sess.waitForCondition(ChannelCondition.STDOUT_DATA | ChannelCondition.STDERR_DATA | ChannelCondition.EOF, 120000); - - if ((conditions & ChannelCondition.TIMEOUT) != 0) { - s_logger.info("Timeout while waiting for data from peer."); - return null; - } - - if ((conditions & ChannelCondition.EOF) != 0) { - if ((conditions & (ChannelCondition.STDOUT_DATA | ChannelCondition.STDERR_DATA)) == 0) { - break; - } - } - } - - while (stdout.available() > 0) { - success = true; - int len = stdout.read(buffer); - if (len > 0) // this check is somewhat paranoid - s_logger.info(new String(buffer, 0, len)); - } - - while (stderr.available() > 0) { - /* int len = */stderr.read(buffer); - } - } - - sess.close(); - conn.close(); - - if (!success) { - retry++; - if (retry == MAX_RETRY_LINUX) { - result = "SSH Linux Network test fail"; - } - } - - if (snapshotTest.equals("no")) - return result; - else { - Long sleep = 300000L; - s_logger.info("Sleeping for " + sleep / 1000 / 60 + "minutes before executing next ssh test"); - Thread.sleep(sleep); - } - } catch (Exception e) { - retry++; - s_logger.error("SSH Linux Network test fail with error"); - if ((retry == MAX_RETRY_LINUX) && (snapshotTest.equals("no"))) { - return "SSH Linux Network test fail with error " + e.getMessage(); - } - } - i++; - } - } - - public static String createMD5Password(String password) { - MessageDigest md5; - - try { - md5 = MessageDigest.getInstance("MD5"); - } catch (NoSuchAlgorithmException e) { - throw new CloudRuntimeException("Error", e); - } - - md5.reset(); - BigInteger pwInt = new BigInteger(1, md5.digest(password.getBytes())); - - // make sure our MD5 hash value is 32 digits long... - StringBuffer sb = new StringBuffer(); - String pwStr = pwInt.toString(16); - int padding = 32 - pwStr.length(); - for (int i = 0; i < padding; i++) { - sb.append('0'); - } - sb.append(pwStr); - return sb.toString(); - } - - public static Element queryAsyncJobResult(String host, InputStream inputStream) { - Element returnBody = null; - - Map values = getSingleValueFromXML(inputStream, new String[] {"jobid"}); - String jobId = values.get("jobid"); - - if (jobId == null) { - s_logger.error("Unable to get a jobId"); - return null; - } - - // s_logger.info("Job id is " + jobId); - String resultUrl = host + "?command=queryAsyncJobResult&jobid=" + jobId; - HttpClient client = new HttpClient(); - HttpMethod method = new GetMethod(resultUrl); - while (true) { - try { - client.executeMethod(method); - // s_logger.info("Method is executed successfully. Following url was sent " + resultUrl); - InputStream is = method.getResponseBodyAsStream(); - DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance(); - DocumentBuilder builder = factory.newDocumentBuilder(); - Document doc = builder.parse(is); - returnBody = doc.getDocumentElement(); - doc.getDocumentElement().normalize(); - Element jobStatusTag = (Element)returnBody.getElementsByTagName("jobstatus").item(0); - String jobStatus = jobStatusTag.getTextContent(); - if (jobStatus.equals("0")) { - try { - Thread.sleep(1000); - } catch (InterruptedException e) { - s_logger.debug("[ignored] interrupted while during async job result query."); - } - } else { - break; - } - - } catch (Exception ex) { - s_logger.error(ex); - } - } - return returnBody; - } - -} diff --git a/test/src-not-used/main/java/com/cloud/test/stress/WgetTest.java b/test/src-not-used/main/java/com/cloud/test/stress/WgetTest.java deleted file mode 100644 index 91885568447..00000000000 --- a/test/src-not-used/main/java/com/cloud/test/stress/WgetTest.java +++ /dev/null @@ -1,150 +0,0 @@ -// 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.test.stress; - -import java.io.InputStream; -import java.util.Arrays; -import java.util.Iterator; -import java.util.List; - -import org.apache.log4j.Logger; - -import com.trilead.ssh2.ChannelCondition; -import com.trilead.ssh2.Connection; -import com.trilead.ssh2.Session; - -public class WgetTest { - - public static final int MAX_RETRY_LINUX = 1; - public static final Logger s_logger = Logger.getLogger(WgetTest.class.getName()); - public static String host = ""; - public static String password = "rs-ccb35ea5"; - - public static void main(String[] args) { - - // Parameters - List argsList = Arrays.asList(args); - Iterator iter = argsList.iterator(); - while (iter.hasNext()) { - String arg = iter.next(); - // host - if (arg.equals("-h")) { - host = iter.next(); - } - //password - - if (arg.equals("-p")) { - password = iter.next(); - } - - } - - int i = 0; - if (host == null || host.equals("")) { - s_logger.info("Did not receive a host back from test, ignoring ssh test"); - System.exit(2); - } - - if (password == null) { - s_logger.info("Did not receive a password back from test, ignoring ssh test"); - System.exit(2); - } - int retry = 0; - - try { - if (retry > 0) { - s_logger.info("Retry attempt : " + retry + " ...sleeping 120 seconds before next attempt"); - Thread.sleep(120000); - } - - s_logger.info("Attempting to SSH into linux host " + host + " with retry attempt: " + retry); - - Connection conn = new Connection(host); - conn.connect(null, 60000, 60000); - - s_logger.info("User + ssHed successfully into linux host " + host); - - boolean isAuthenticated = conn.authenticateWithPassword("root", password); - - if (isAuthenticated == false) { - s_logger.info("Authentication failed for root with password" + password); - System.exit(2); - } - - boolean success = false; - String linuxCommand = null; - - if (i % 10 == 0) - linuxCommand = "rm -rf *; wget http://192.168.1.250/dump.bin && ls -al dump.bin"; - else - linuxCommand = "wget http://192.168.1.250/dump.bin && ls -al dump.bin"; - - Session sess = conn.openSession(); - sess.execCommand(linuxCommand); - - InputStream stdout = sess.getStdout(); - InputStream stderr = sess.getStderr(); - - byte[] buffer = new byte[8192]; - while (true) { - if ((stdout.available() == 0) && (stderr.available() == 0)) { - int conditions = sess.waitForCondition(ChannelCondition.STDOUT_DATA | ChannelCondition.STDERR_DATA | ChannelCondition.EOF, 120000); - - if ((conditions & ChannelCondition.TIMEOUT) != 0) { - s_logger.info("Timeout while waiting for data from peer."); - System.exit(2); - } - - if ((conditions & ChannelCondition.EOF) != 0) { - if ((conditions & (ChannelCondition.STDOUT_DATA | ChannelCondition.STDERR_DATA)) == 0) { - break; - } - } - } - - while (stdout.available() > 0) { - success = true; - int len = stdout.read(buffer); - if (len > 0) // this check is somewhat paranoid - s_logger.info(new String(buffer, 0, len)); - } - - while (stderr.available() > 0) { - /* int len = */stderr.read(buffer); - } - } - - sess.close(); - conn.close(); - - if (!success) { - retry++; - if (retry == MAX_RETRY_LINUX) { - System.exit(2); - } - } - } catch (Exception e) { - retry++; - s_logger.error("SSH Linux Network test fail with error"); - if (retry == MAX_RETRY_LINUX) { - s_logger.error("Ssh test failed"); - System.exit(2); - } - } - } - -} diff --git a/test/src-not-used/main/java/com/cloud/test/ui/AbstractSeleniumTestCase.java b/test/src-not-used/main/java/com/cloud/test/ui/AbstractSeleniumTestCase.java deleted file mode 100644 index f9e678e2529..00000000000 --- a/test/src-not-used/main/java/com/cloud/test/ui/AbstractSeleniumTestCase.java +++ /dev/null @@ -1,55 +0,0 @@ -// 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.test.ui; - -import org.junit.AfterClass; -import org.junit.BeforeClass; -import org.junit.runner.RunWith; -import org.junit.runners.JUnit4; -import org.openqa.selenium.server.RemoteControlConfiguration; -import org.openqa.selenium.server.SeleniumServer; - -import com.thoughtworks.selenium.DefaultSelenium; - -@RunWith(JUnit4.class) -public abstract class AbstractSeleniumTestCase { - protected static DefaultSelenium selenium; - private static SeleniumServer seleniumServer; - - @BeforeClass - public static void setUp() throws Exception { - System.out.println("*** Starting selenium ... ***"); - RemoteControlConfiguration seleniumConfig = new RemoteControlConfiguration(); - seleniumConfig.setPort(4444); - seleniumServer = new SeleniumServer(seleniumConfig); - seleniumServer.start(); - - String host = System.getProperty("myParam", "localhost"); - selenium = createSeleniumClient("http://" + host + ":" + "8080/client/"); - selenium.start(); - System.out.println("*** Started selenium ***"); - } - - @AfterClass - public static void tearDown() throws Exception { - selenium.stop(); - } - - protected static DefaultSelenium createSeleniumClient(String url) throws Exception { - return new DefaultSelenium("localhost", 4444, "*firefox", url); - } -} diff --git a/test/src-not-used/main/java/com/cloud/test/ui/AddAndDeleteAISO.java b/test/src-not-used/main/java/com/cloud/test/ui/AddAndDeleteAISO.java deleted file mode 100644 index 1998ae7c8ef..00000000000 --- a/test/src-not-used/main/java/com/cloud/test/ui/AddAndDeleteAISO.java +++ /dev/null @@ -1,127 +0,0 @@ -// 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.test.ui; - -import static org.junit.Assert.assertTrue; -import static org.junit.Assert.fail; - -import org.junit.Test; - -import com.thoughtworks.selenium.SeleniumException; - -public class AddAndDeleteAISO extends AbstractSeleniumTestCase { - - @Test - public void testAddAndDeleteISO() throws Exception { - try { - selenium.open("/client/"); - selenium.type("account_username", "admin"); - selenium.type("account_password", "password"); - selenium.click("loginbutton"); - Thread.sleep(3000); - assertTrue(selenium.isTextPresent("admin")); - selenium.click("//div[@id='leftmenu_templates']/div"); - selenium.click("//div[@id='leftmenu_submenu_my_iso']/div/div[2]"); - Thread.sleep(3000); - selenium.click("label"); - - selenium.type("add_iso_name", "abc"); - selenium.type("add_iso_display_text", "abc"); - String iso_url = System.getProperty("add_iso_url", "http://10.91.28.6/ISO/Fedora-11-i386-DVD.iso"); - selenium.type("add_iso_url", iso_url); - String iso_zone = System.getProperty("add_iso_zone", "All Zones"); - selenium.select("add_iso_zone", "label=" + iso_zone); - String iso_os_type = System.getProperty("add_iso_os_type", "Fedora 11"); - selenium.select("add_iso_os_type", "label=" + iso_os_type); - selenium.click("//div[28]/div[11]/button[1]"); - Thread.sleep(3000); - int i = 1; - try { - for (;; i++) { - System.out.println("i= " + i); - selenium.click("//div[" + i + "]/div/div[2]/span/span"); - } - } catch (Exception ex) { - s_logger.info("[ignored]" - + "error during clicking test on iso: " + e.getLocalizedMessage()); - } - - for (int second = 0;; second++) { - if (second >= 60) - fail("timeout"); - try { - if (selenium.isVisible("//div[@id='after_action_info_container_on_top']")) - break; - } catch (Exception e) { - s_logger.info("[ignored]" - + "error during visibility test of iso: " + e.getLocalizedMessage()); - } - Thread.sleep(10000); - } - - assertTrue(selenium.isTextPresent("Adding succeeded")); - Thread.sleep(3000); - int status = 1; - while (!selenium.isTextPresent("Ready")) { - for (int j = 1; j <= i; j++) - - { - if (selenium.isTextPresent("Ready")) { - status = 0; - break; - } - selenium.click("//div[" + j + "]/div/div[2]/span/span"); - } - if (status == 0) { - break; - } else { - selenium.click("//div[@id='leftmenu_submenu_featured_iso']/div/div[2]"); - Thread.sleep(3000); - selenium.click("//div[@id='leftmenu_submenu_my_iso']/div/div[2]"); - Thread.sleep(3000); - } - - } - selenium.click("link=Delete ISO"); - selenium.click("//div[28]/div[11]/button[1]"); - for (int second = 0;; second++) { - if (second >= 60) - fail("timeout"); - try { - if (selenium.isVisible("after_action_info_container_on_top")) - break; - } catch (Exception e) { - s_logger.info("[ignored]" - + "error checking visibility after test completion for iso: " + e.getLocalizedMessage()); - } - Thread.sleep(1000); - } - - assertTrue(selenium.isTextPresent("Delete ISO action succeeded")); - selenium.click("main_logout"); - selenium.waitForPageToLoad("30000"); - assertTrue(selenium.isTextPresent("Welcome to Management Console")); - - } catch (SeleniumException ex) { - - System.err.println(ex.getMessage()); - fail(ex.getMessage()); - - throw ex; - } - } -} diff --git a/test/src-not-used/main/java/com/cloud/test/ui/AddAndDeleteATemplate.java b/test/src-not-used/main/java/com/cloud/test/ui/AddAndDeleteATemplate.java deleted file mode 100644 index 3a3264e9cf1..00000000000 --- a/test/src-not-used/main/java/com/cloud/test/ui/AddAndDeleteATemplate.java +++ /dev/null @@ -1,126 +0,0 @@ -// 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.test.ui; - -import static org.junit.Assert.assertTrue; -import static org.junit.Assert.fail; - -import org.junit.Test; - -import com.thoughtworks.selenium.SeleniumException; - -public class AddAndDeleteATemplate extends AbstractSeleniumTestCase { - - @Test - public void testAddAndDeleteTemplate() throws Exception { - try { - selenium.open("/client/"); - selenium.type("account_username", "admin"); - selenium.type("account_password", "password"); - selenium.click("loginbutton"); - Thread.sleep(3000); - assertTrue(selenium.isTextPresent("admin")); - selenium.click("//div[@id='leftmenu_templates']/div"); - selenium.click("//div[@id='leftmenu_submenu_my_template']/div/div[2]"); - Thread.sleep(3000); - selenium.click("label"); - selenium.type("add_template_name", "abc"); - selenium.type("add_template_display_text", "abc"); - String template_url = - System.getProperty("add_template_url", "http://10.91.28.6/templates/centos53-x86_64/latest/f59f18fb-ae94-4f97-afd2-f84755767aca.vhd.bz2"); - selenium.type("add_template_url", template_url); - String template_zone = System.getProperty("add_template_zone", "All Zones"); - selenium.select("add_template_zone", "label=" + template_zone); - String template_os_type = System.getProperty("add_template_os_type", "CentOS 5.3 (32-bit)"); - selenium.select("add_template_os_type", "label=" + template_os_type); - selenium.click("//div[28]/div[11]/button[1]"); - Thread.sleep(3000); - int i = 1; - try { - for (;; i++) { - System.out.println("i= " + i); - selenium.click("//div[" + i + "]/div/div[2]/span/span"); - } - } catch (Exception ex) { - s_logger.info("[ignored]" - + "error during clicking test on template: " + ex.getLocalizedMessage()); - } - - for (int second = 0;; second++) { - if (second >= 60) - fail("timeout"); - try { - if (selenium.isVisible("//div[@id='after_action_info_container_on_top']")) - break; - } catch (Exception e) { - s_logger.info("[ignored]" - + "error during visibility test of template: " + e.getLocalizedMessage()); - } - Thread.sleep(10000); - } - - assertTrue(selenium.isTextPresent("Adding succeeded")); - Thread.sleep(3000); - int status = 1; - while (!selenium.isTextPresent("Ready")) { - for (int j = 1; j <= i; j++) - - { - if (selenium.isTextPresent("Ready")) { - status = 0; - break; - } - selenium.click("//div[" + j + "]/div/div[2]/span/span"); - } - if (status == 0) { - break; - } else { - selenium.click("//div[@id='leftmenu_submenu_featured_template']/div/div[2]"); - Thread.sleep(3000); - selenium.click("//div[@id='leftmenu_submenu_my_template']/div/div[2]"); - Thread.sleep(3000); - } - - } - selenium.click("link=Delete Template"); - selenium.click("//div[28]/div[11]/button[1]"); - for (int second = 0;; second++) { - if (second >= 60) - fail("timeout"); - try { - if (selenium.isVisible("after_action_info_container_on_top")) - break; - } catch (Exception e) { - s_logger.info("[ignored]" - + "error checking visibility after test completion for template: " + e.getLocalizedMessage()); - } - Thread.sleep(1000); - } - - assertTrue(selenium.isTextPresent("Delete Template action succeeded")); - selenium.click("main_logout"); - selenium.waitForPageToLoad("30000"); - assertTrue(selenium.isTextPresent("Welcome to Management Console")); - } catch (SeleniumException ex) { - - System.err.println(ex.getMessage()); - fail(ex.getMessage()); - - throw ex; - } - } -} diff --git a/test/src-not-used/main/java/com/cloud/test/ui/UIScenarioTest.java b/test/src-not-used/main/java/com/cloud/test/ui/UIScenarioTest.java deleted file mode 100644 index 8fde7e37ea6..00000000000 --- a/test/src-not-used/main/java/com/cloud/test/ui/UIScenarioTest.java +++ /dev/null @@ -1,86 +0,0 @@ -// 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.test.ui; - -import static org.junit.Assert.assertTrue; -import static org.junit.Assert.fail; - -import org.junit.Test; - -import com.thoughtworks.selenium.SeleniumException; - -public class UIScenarioTest extends AbstractSeleniumTestCase { - - @Test - public void testLoginStartStopVMScenario() throws Exception { - try { - selenium.open("/client/"); - selenium.type("account_username", "admin"); - selenium.type("account_password", "password"); - selenium.click("loginbutton"); - Thread.sleep(3000); - assertTrue(selenium.isTextPresent("admin")); - selenium.click("//div[@id='leftmenu_instances']/div"); - selenium.click("//div[@id='leftmenu_instances_stopped_instances']/div/span"); - - Thread.sleep(3000); - selenium.click("//div[@id='midmenu_startvm_link']/div/div[2]"); - selenium.click("//div[39]/div[11]/button[1]"); - - for (int second = 0;; second++) { - if (second >= 60) - fail("timeout"); - try { - if (selenium.isVisible("//div/p[@id='after_action_info']")) - break; - } catch (Exception e) { - s_logger.info("[ignored]" - + "error during visibility test after start vm: " + e.getLocalizedMessage()); - } - Thread.sleep(10000); - } - assertTrue(selenium.isTextPresent("Start Instance action succeeded")); - selenium.click("//div[@id='leftmenu_instances_running_instances']/div/span"); - - Thread.sleep(3000); - selenium.click("//div[@id='midmenu_stopvm_link']/div/div[2]"); - selenium.click("//div[39]/div[11]/button[1]"); - for (int second = 0;; second++) { - if (second >= 60) - fail("timeout"); - try { - if (selenium.isVisible("//div/p[@id='after_action_info']")) - break; - } catch (Exception e) { - s_logger.info("[ignored]" - + "error during visibility test after stop vm: " + e.getLocalizedMessage()); - } - Thread.sleep(10000); - } - - assertTrue(selenium.isTextPresent("Stop Instance action succeeded")); - selenium.click("main_logout"); - selenium.waitForPageToLoad("30000"); - assertTrue(selenium.isTextPresent("Welcome to Management Console")); - - } catch (SeleniumException ex) { - fail(ex.getMessage()); - System.err.println(ex.getMessage()); - throw ex; - } - } -} diff --git a/test/src-not-used/main/java/com/cloud/test/utils/ConsoleProxy.java b/test/src-not-used/main/java/com/cloud/test/utils/ConsoleProxy.java deleted file mode 100644 index 8c10d75a5c1..00000000000 --- a/test/src-not-used/main/java/com/cloud/test/utils/ConsoleProxy.java +++ /dev/null @@ -1,110 +0,0 @@ -// 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.test.utils; - -import java.io.BufferedReader; -import java.io.IOException; - -import org.apache.log4j.Logger; - -import com.cloud.utils.script.OutputInterpreter; -import com.cloud.utils.script.Script; - -public class ConsoleProxy implements Runnable { - public static String proxyIp; - private String command; - private int connectionsMade; - private long responseTime; - public static final Logger s_logger = Logger.getLogger(ConsoleProxy.class.getClass()); - - public ConsoleProxy(String port, String sid, String host) { - this.command = "https://" + proxyIp + ".realhostip.com:8000/getscreen?w=100&h=75&host=" + host + "&port=" + port + "&sid=" + sid; - s_logger.info("Command for a console proxy is " + this.command); - this.connectionsMade = 0; - this.responseTime = 0; - } - - public int getConnectionsMade() { - return this.connectionsMade; - } - - public long getResponseTime() { - return this.responseTime; - } - - @Override - public void run() { - while (true) { - - Script myScript = new Script("wget"); - myScript.add(command); - myScript.execute(); - long begin = System.currentTimeMillis(); - WgetInt process = new WgetInt(); - String response = myScript.execute(process); - long end = process.getEnd(); - if (response != null) { - s_logger.info("Content lenght is incorrect: " + response); - } - - long duration = (end - begin); - this.connectionsMade++; - this.responseTime = this.responseTime + duration; - try { - Thread.sleep(1000); - } catch (InterruptedException e) { - s_logger.debug("[ignored] interrupted."); - } - - } - } - - public class WgetInt extends OutputInterpreter { - private long end; - - public long getEnd() { - return end; - } - - public void setEnd(long end) { - this.end = end; - } - - @Override - public String interpret(BufferedReader reader) throws IOException { - // TODO Auto-generated method stub - end = System.currentTimeMillis(); - String status = null; - String line = null; - while ((line = reader.readLine()) != null) { - int index = line.indexOf("Length:"); - if (index == -1) { - continue; - } else { - int index1 = line.indexOf("Length: 1827"); - if (index1 == -1) { - return status; - } else - status = line; - } - - } - return status; - } - - } -} diff --git a/test/src-not-used/main/java/com/cloud/test/utils/IpSqlGenerator.java b/test/src-not-used/main/java/com/cloud/test/utils/IpSqlGenerator.java deleted file mode 100644 index c37d08b86ff..00000000000 --- a/test/src-not-used/main/java/com/cloud/test/utils/IpSqlGenerator.java +++ /dev/null @@ -1,89 +0,0 @@ -// 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.test.utils; - -import java.io.DataOutputStream; -import java.io.File; -import java.io.FileOutputStream; -import java.util.StringTokenizer; - -public class IpSqlGenerator { - public static void main(String[] args) { - try { - if (args.length != 5) { - System.out.println("Usage -- generate-ip.sh "); - System.out.println("Example -- generate-ip.sh public 192.168.1.1 192.168.1.255 1 1"); - System.out.println(" will generate ips ranging from public ips 192.168.1.1 to 192.168.1.255 for dc 1 and pod 1"); - return; - } - - String type = args[0]; - - StringTokenizer st = new StringTokenizer(args[1], "."); - int ipS1 = Integer.parseInt(st.nextToken()); - int ipS2 = Integer.parseInt(st.nextToken()); - int ipS3 = Integer.parseInt(st.nextToken()); - int ipS4 = Integer.parseInt(st.nextToken()); - - st = new StringTokenizer(args[2], "."); - int ipE1 = Integer.parseInt(st.nextToken()); - int ipE2 = Integer.parseInt(st.nextToken()); - int ipE3 = Integer.parseInt(st.nextToken()); - int ipE4 = Integer.parseInt(st.nextToken()); - - String dcId = args[3]; - String podId = args[4]; - - if (type.equals("private")) { - FileOutputStream fs = new FileOutputStream(new File("private-ips.sql")); - DataOutputStream out = new DataOutputStream(fs); - for (int i = ipS1; i <= ipE1; i++) { - for (int j = ipS2; j <= ipE2; j++) { - for (int k = ipS3; k <= ipE3; k++) { - for (int l = ipS4; l <= ipE4; l++) { - out.writeBytes("INSERT INTO `vmops`.`dc_ip_address_alloc` (ip_address, data_center_id, pod_id) VALUES ('" + i + "." + j + "." + k + "." + - l + "'," + dcId + "," + podId + ");\r\n"); - } - } - } - } - out.writeBytes("\r\n"); - out.flush(); - out.close(); - } else { - FileOutputStream fs = new FileOutputStream(new File("public-ips.sql")); - DataOutputStream out = new DataOutputStream(fs); - for (int i = ipS1; i <= ipE1; i++) { - for (int j = ipS2; j <= ipE2; j++) { - for (int k = ipS3; k <= ipE3; k++) { - for (int l = ipS4; l <= ipE4; l++) { - out.writeBytes("INSERT INTO `vmops`.`user_ip_address` (ip_address, data_center_id) VALUES ('" + i + "." + j + "." + k + "." + l + "'," + - dcId + ");\r\n"); - } - } - } - } - out.writeBytes("\r\n"); - out.flush(); - out.close(); - } - } catch (Exception e) { - s_logger.info("[ignored]" - + "error during ip insert generator: " + e.getLocalizedMessage()); - } - } -} diff --git a/test/src-not-used/main/java/com/cloud/test/utils/ProxyLoadTemp.java b/test/src-not-used/main/java/com/cloud/test/utils/ProxyLoadTemp.java deleted file mode 100644 index f64b7d6b9e9..00000000000 --- a/test/src-not-used/main/java/com/cloud/test/utils/ProxyLoadTemp.java +++ /dev/null @@ -1,112 +0,0 @@ -// 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.test.utils; - -import java.io.BufferedReader; -import java.io.FileReader; -import java.util.ArrayList; - -import org.apache.log4j.Logger; - -public class ProxyLoadTemp { - public static final Logger s_logger = Logger.getLogger(ProxyLoadTemp.class.getClass()); - public static int numThreads = 0; - public static ArrayList proxyList = new ArrayList(); - public static long begin; - public static long end; - public static long sum = 0; - - public ProxyLoadTemp() { - } - - public static void main(String[] args) { - begin = System.currentTimeMillis(); - Runtime.getRuntime().addShutdownHook(new ShutdownThread(new ProxyLoadTemp())); - ConsoleProxy.proxyIp = "172-16-1-101"; - - try { - BufferedReader consoleInput = new BufferedReader(new FileReader("console.input")); - boolean eof = false; - s_logger.info("Started reading file"); - while (!eof) { - String line = consoleInput.readLine(); - s_logger.info("Line is " + line); - if (line == null) { - s_logger.info("Line " + numThreads + " is null"); - eof = true; - } else { - String[] result = null; - try { - s_logger.info("Starting parsing line " + line); - result = parseLine(line, "[,]"); - s_logger.info("Line retrieved from the file is " + result[0] + " " + result[1] + " " + result[2]); - ConsoleProxy proxy = new ConsoleProxy(result[0], result[1], result[2]); - proxyList.add(proxy); - new Thread(proxy).start(); - numThreads++; - - } catch (Exception ex) { - s_logger.warn(ex); - } - } - - } - } catch (Exception e) { - s_logger.warn(e); - } - - } - - public static class ShutdownThread extends Thread { - ProxyLoadTemp temp; - - public ShutdownThread(ProxyLoadTemp temp) { - this.temp = temp; - } - - @Override - public void run() { - s_logger.info("Program was running in " + numThreads + " threads"); - - for (int j = 0; j < proxyList.size(); j++) { - long av = 0; - if (proxyList.get(j).getConnectionsMade() != 0) { - av = proxyList.get(j).getResponseTime() / proxyList.get(j).getConnectionsMade(); - } - s_logger.info("Information for " + j + " thread: Number of requests sent is " + proxyList.get(j).getConnectionsMade() + ". Average response time is " + - av + " milliseconds"); - sum = sum + av; - - } - ProxyLoadTemp.end = System.currentTimeMillis(); - s_logger.info("Summary for all" + numThreads + " threads: Average response time is " + sum / numThreads + " milliseconds"); - s_logger.info("Test was running for " + (ProxyLoadTemp.end - ProxyLoadTemp.begin) / 1000 + " seconds"); - } - } - - public static String[] parseLine(String line, String del) throws Exception { - String del1 = del.substring(1, del.length() - 1); - if (line.contains(del1) != true) { - throw new Exception(); - } else { - String[] token = line.split(del); - return token; - } - - } - -} diff --git a/test/src-not-used/main/java/com/cloud/test/utils/SignEC2.java b/test/src-not-used/main/java/com/cloud/test/utils/SignEC2.java deleted file mode 100644 index 29e78c16c98..00000000000 --- a/test/src-not-used/main/java/com/cloud/test/utils/SignEC2.java +++ /dev/null @@ -1,143 +0,0 @@ -// 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.test.utils; - -import java.io.FileInputStream; -import java.io.IOException; -import java.net.URLEncoder; -import java.util.Arrays; -import java.util.Iterator; -import java.util.List; -import java.util.Map; -import java.util.Properties; -import java.util.Set; -import java.util.StringTokenizer; -import java.util.TreeMap; - -import org.apache.log4j.Logger; - -public class SignEC2 { - public static String url; - public static String secretkey; - public static String host; - public static String port; - public static String command; - public static String accessPoint; - public static final Logger s_logger = Logger.getLogger(SignEC2.class.getName()); - - public static void main(String[] args) { - // Parameters - List argsList = Arrays.asList(args); - Iterator iter = argsList.iterator(); - while (iter.hasNext()) { - String arg = iter.next(); - - if (arg.equals("-u")) { - url = iter.next(); - } - } - - Properties prop = new Properties(); - try { - prop.load(new FileInputStream("../conf/tool.properties")); - } catch (IOException ex) { - s_logger.error("Error reading from ../conf/tool.properties", ex); - System.exit(2); - } - - host = prop.getProperty("host"); - secretkey = prop.getProperty("secretkey"); - port = prop.getProperty("port"); - - if (host == null) { - s_logger.info("Please set host in tool.properties file"); - System.exit(1); - } - - if (port == null) { - s_logger.info("Please set port in tool.properties file"); - System.exit(1); - } - - if (url == null) { - s_logger.info("Please specify url with -u option"); - System.exit(1); - } - - if (secretkey == null) { - s_logger.info("Please set secretkey in tool.properties file"); - System.exit(1); - } - - if (prop.get("apikey") == null) { - s_logger.info("Please set apikey in tool.properties file"); - System.exit(1); - } - - if (prop.get("accesspoint") == null) { - s_logger.info("Please set apikey in tool.properties file"); - System.exit(1); - } - - TreeMap param = new TreeMap(); - - String req = "GET\n" + host + ":" + prop.getProperty("port") + "\n/" + prop.getProperty("accesspoint") + "\n"; - String temp = ""; - param.put("AWSAccessKeyId", prop.getProperty("apikey")); - param.put("Expires", prop.getProperty("expires")); - param.put("SignatureMethod", "HmacSHA1"); - param.put("SignatureVersion", "2"); - param.put("Version", prop.getProperty("version")); - param.put("id", "1"); - - StringTokenizer str1 = new StringTokenizer(url, "&"); - while (str1.hasMoreTokens()) { - String newEl = str1.nextToken(); - StringTokenizer str2 = new StringTokenizer(newEl, "="); - String name = str2.nextToken(); - String value = str2.nextToken(); - param.put(name, value); - } - - //sort url hash map by key - Set c = param.entrySet(); - Iterator it = c.iterator(); - while (it.hasNext()) { - Map.Entry me = (Map.Entry)it.next(); - String key = (String)me.getKey(); - String value = (String)me.getValue(); - try { - temp = temp + key + "=" + URLEncoder.encode(value, "UTF-8") + "&"; - } catch (Exception ex) { - s_logger.error("Unable to set parameter " + value + " for the command " + param.get("command")); - } - - } - temp = temp.substring(0, temp.length() - 1); - String requestToSign = req + temp; - String signature = UtilsForTest.signRequest(requestToSign, secretkey); - String encodedSignature = ""; - try { - encodedSignature = URLEncoder.encode(signature, "UTF-8"); - } catch (Exception ex) { - s_logger.error(ex); - } - String url = "http://" + host + ":" + prop.getProperty("port") + "/" + prop.getProperty("accesspoint") + "?" + temp + "&Signature=" + encodedSignature; - s_logger.info("Url is " + url); - - } -} diff --git a/test/src-not-used/main/java/com/cloud/test/utils/SignRequest.java b/test/src-not-used/main/java/com/cloud/test/utils/SignRequest.java deleted file mode 100644 index 95fd7b29374..00000000000 --- a/test/src-not-used/main/java/com/cloud/test/utils/SignRequest.java +++ /dev/null @@ -1,112 +0,0 @@ -// 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.test.utils; - -import java.net.URLEncoder; -import java.util.Arrays; -import java.util.Iterator; -import java.util.List; -import java.util.Map; -import java.util.Set; -import java.util.StringTokenizer; -import java.util.TreeMap; - -public class SignRequest { - public static String url; - public static String apikey; - public static String secretkey; - public static String command; - - public static void main(String[] args) { - // Parameters - List argsList = Arrays.asList(args); - Iterator iter = argsList.iterator(); - while (iter.hasNext()) { - String arg = iter.next(); - if (arg.equals("-a")) { - apikey = iter.next(); - - } - if (arg.equals("-u")) { - url = iter.next(); - } - - if (arg.equals("-s")) { - secretkey = iter.next(); - } - } - - if (url == null) { - System.out.println("Please specify url with -u option. Example: -u \"command=listZones&id=1\""); - System.exit(1); - } - - if (apikey == null) { - System.out.println("Please specify apikey with -a option"); - System.exit(1); - } - - if (secretkey == null) { - System.out.println("Please specify secretkey with -s option"); - System.exit(1); - } - - TreeMap param = new TreeMap(); - - String temp = ""; - param.put("apikey", apikey); - - StringTokenizer str1 = new StringTokenizer(url, "&"); - while (str1.hasMoreTokens()) { - String newEl = str1.nextToken(); - StringTokenizer str2 = new StringTokenizer(newEl, "="); - String name = str2.nextToken(); - String value = str2.nextToken(); - param.put(name, value); - } - - //sort url hash map by key - Set c = param.entrySet(); - Iterator it = c.iterator(); - while (it.hasNext()) { - Map.Entry me = (Map.Entry)it.next(); - String key = (String)me.getKey(); - String value = (String)me.getValue(); - try { - temp = temp + key + "=" + URLEncoder.encode(value, "UTF-8") + "&"; - } catch (Exception ex) { - System.out.println("Unable to set parameter " + value + " for the command " + param.get("command")); - } - - } - temp = temp.substring(0, temp.length() - 1); - String requestToSign = temp.toLowerCase(); - System.out.println("After sorting: " + requestToSign); - String signature = UtilsForTest.signRequest(requestToSign, secretkey); - System.out.println("After Base64 encoding: " + signature); - String encodedSignature = ""; - try { - encodedSignature = URLEncoder.encode(signature, "UTF-8"); - } catch (Exception ex) { - System.out.println(ex); - } - System.out.println("After UTF8 encoding: " + encodedSignature); - String url = temp + "&signature=" + encodedSignature; - System.out.println("After sort and add signature: " + url); - - } -} diff --git a/test/src-not-used/main/java/com/cloud/test/utils/SqlDataGenerator.java b/test/src-not-used/main/java/com/cloud/test/utils/SqlDataGenerator.java deleted file mode 100644 index 8b42b1f1237..00000000000 --- a/test/src-not-used/main/java/com/cloud/test/utils/SqlDataGenerator.java +++ /dev/null @@ -1,49 +0,0 @@ -// 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.test.utils; - -import java.io.DataOutputStream; -import java.io.File; -import java.io.FileOutputStream; -import java.util.Formatter; - -public class SqlDataGenerator { - public static void main(String[] args) { - try { - FileOutputStream fs = new FileOutputStream(new File("out.txt")); - - DataOutputStream out = new DataOutputStream(fs); - - for (int i = 20; i < 171; i++) { - out.writeBytes("INSERT INTO `vmops`.`dc_ip_address_alloc` (ip_address, data_center_id, pod_id) VALUES ('192.168.2." + i + "',1,1);\r\n"); - } - out.writeBytes("\r\n"); - for (int i = 1; i < 10000; i++) { - StringBuilder imagePath = new StringBuilder(); - Formatter formatter = new Formatter(imagePath); - formatter.format("%04x", i); - out.writeBytes("INSERT INTO `vmops`.`dc_vnet_alloc` (vnet, data_center_id) VALUES ('" + imagePath.toString() + "',1);\r\n"); - } - - out.flush(); - out.close(); - } catch (Exception e) { - s_logger.info("[ignored]" - + "error during sql generation: " + e.getLocalizedMessage()); - } - } -} diff --git a/test/src-not-used/main/java/com/cloud/test/utils/SubmitCert.java b/test/src-not-used/main/java/com/cloud/test/utils/SubmitCert.java deleted file mode 100644 index a130d6728ec..00000000000 --- a/test/src-not-used/main/java/com/cloud/test/utils/SubmitCert.java +++ /dev/null @@ -1,198 +0,0 @@ -// 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.test.utils; - -import java.io.BufferedReader; -import java.io.FileInputStream; -import java.io.FileReader; -import java.io.IOException; -import java.net.URLEncoder; -import java.util.Arrays; -import java.util.Iterator; -import java.util.List; -import java.util.Map; -import java.util.Properties; -import java.util.Set; -import java.util.StringTokenizer; -import java.util.TreeMap; - -import org.apache.commons.httpclient.HttpClient; -import org.apache.commons.httpclient.HttpMethod; -import org.apache.commons.httpclient.methods.GetMethod; -import org.apache.log4j.Logger; - -public class SubmitCert { - public static String url = "Action=SetCertificate"; - public static String secretKey; - public static String apiKey; - public static String host; - public static String port; - public static String command; - public static String accessPoint; - public static String signatureMethod; - public static String fileName = "tool.properties"; - public static String certFileName; - public static String cert; - public static final Logger s_logger = Logger.getLogger(SubmitCert.class.getName()); - - public static void main(String[] args) { - // Parameters - List argsList = Arrays.asList(args); - Iterator iter = argsList.iterator(); - while (iter.hasNext()) { - String arg = iter.next(); - - if (arg.equals("-c")) { - certFileName = iter.next(); - } - - if (arg.equals("-s")) { - secretKey = iter.next(); - } - - if (arg.equals("-a")) { - apiKey = iter.next(); - } - - if (arg.equals("-action")) { - url = "Action=" + iter.next(); - } - } - - Properties prop = new Properties(); - try { - prop.load(new FileInputStream("conf/tool.properties")); - } catch (IOException ex) { - s_logger.error("Error reading from conf/tool.properties", ex); - System.exit(2); - } - - host = prop.getProperty("host"); - port = prop.getProperty("port"); - - if (url.equals("Action=SetCertificate") && certFileName == null) { - s_logger.error("Please set path to certificate (including file name) with -c option"); - System.exit(1); - } - - if (secretKey == null) { - s_logger.error("Please set secretkey with -s option"); - System.exit(1); - } - - if (apiKey == null) { - s_logger.error("Please set apikey with -a option"); - System.exit(1); - } - - if (host == null) { - s_logger.error("Please set host in tool.properties file"); - System.exit(1); - } - - if (port == null) { - s_logger.error("Please set port in tool.properties file"); - System.exit(1); - } - - TreeMap param = new TreeMap(); - - String req = "GET\n" + host + ":" + prop.getProperty("port") + "\n/" + prop.getProperty("accesspoint") + "\n"; - String temp = ""; - - if (certFileName != null) { - cert = readCert(certFileName); - param.put("cert", cert); - } - - param.put("AWSAccessKeyId", apiKey); - param.put("Expires", prop.getProperty("expires")); - param.put("SignatureMethod", prop.getProperty("signaturemethod")); - param.put("SignatureVersion", "2"); - param.put("Version", prop.getProperty("version")); - - StringTokenizer str1 = new StringTokenizer(url, "&"); - while (str1.hasMoreTokens()) { - String newEl = str1.nextToken(); - StringTokenizer str2 = new StringTokenizer(newEl, "="); - String name = str2.nextToken(); - String value = str2.nextToken(); - param.put(name, value); - } - - //sort url hash map by key - Set c = param.entrySet(); - Iterator it = c.iterator(); - while (it.hasNext()) { - Map.Entry me = (Map.Entry)it.next(); - String key = (String)me.getKey(); - String value = (String)me.getValue(); - try { - temp = temp + key + "=" + URLEncoder.encode(value, "UTF-8") + "&"; - } catch (Exception ex) { - s_logger.error("Unable to set parameter " + value + " for the command " + param.get("command"), ex); - } - - } - temp = temp.substring(0, temp.length() - 1); - String requestToSign = req + temp; - String signature = UtilsForTest.signRequest(requestToSign, secretKey); - String encodedSignature = ""; - try { - encodedSignature = URLEncoder.encode(signature, "UTF-8"); - } catch (Exception ex) { - ex.printStackTrace(); - } - - String url = "http://" + host + ":" + prop.getProperty("port") + "/" + prop.getProperty("accesspoint") + "?" + temp + "&Signature=" + encodedSignature; - s_logger.info("Sending request with url: " + url + "\n"); - sendRequest(url); - } - - public static String readCert(String filePath) { - try { - StringBuffer fileData = new StringBuffer(1000); - BufferedReader reader = new BufferedReader(new FileReader(filePath)); - char[] buf = new char[1024]; - int numRead = 0; - while ((numRead = reader.read(buf)) != -1) { - String readData = String.valueOf(buf, 0, numRead); - fileData.append(readData); - buf = new char[1024]; - } - reader.close(); - return fileData.toString(); - } catch (Exception ex) { - s_logger.error(ex); - return null; - } - } - - public static void sendRequest(String url) { - try { - HttpClient client = new HttpClient(); - HttpMethod method = new GetMethod(url); - int responseCode = client.executeMethod(method); - String is = method.getResponseBodyAsString(); - s_logger.info("Response code " + responseCode + ": " + is); - } catch (Exception ex) { - ex.printStackTrace(); - } - - } - -} diff --git a/test/src-not-used/main/java/com/cloud/test/utils/TestClient.java b/test/src-not-used/main/java/com/cloud/test/utils/TestClient.java deleted file mode 100644 index 20df29147e3..00000000000 --- a/test/src-not-used/main/java/com/cloud/test/utils/TestClient.java +++ /dev/null @@ -1,385 +0,0 @@ -// 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.test.utils; - -import java.io.InputStream; -import java.util.Arrays; -import java.util.Iterator; -import java.util.List; -import java.util.Random; - -import org.apache.commons.httpclient.HttpClient; -import org.apache.commons.httpclient.HttpMethod; -import org.apache.commons.httpclient.methods.GetMethod; -import org.apache.log4j.Logger; -import org.apache.log4j.NDC; - -import com.trilead.ssh2.ChannelCondition; -import com.trilead.ssh2.Connection; -import com.trilead.ssh2.SCPClient; -import com.trilead.ssh2.Session; - -public class TestClient { - private static long sleepTime = 180000L; // default 0 - private static boolean cleanUp = true; - public static final Logger s_logger = Logger.getLogger(TestClient.class.getName()); - private static boolean repeat = true; - private static int numOfUsers = 0; - private static String[] users = null; - private static boolean internet = true; - - private static final int MAX_RETRY_LINUX = 5; - private static final int MAX_RETRY_WIN = 10; - - public static void main(String[] args) { - String host = "http://localhost"; - String port = "8080"; - String testUrl = "/client/test"; - int numThreads = 1; - - try { - // Parameters - List argsList = Arrays.asList(args); - Iterator iter = argsList.iterator(); - while (iter.hasNext()) { - String arg = iter.next(); - // host - if (arg.equals("-h")) { - host = "http://" + iter.next(); - } - - if (arg.equals("-p")) { - port = iter.next(); - } - - if (arg.equals("-t")) { - numThreads = Integer.parseInt(iter.next()); - } - - if (arg.equals("-s")) { - sleepTime = Long.parseLong(iter.next()); - } - - if (arg.equals("-c")) { - cleanUp = Boolean.parseBoolean(iter.next()); - if (!cleanUp) - sleepTime = 0L; // no need to wait if we don't ever cleanup - } - - if (arg.equals("-r")) { - repeat = Boolean.parseBoolean(iter.next()); - } - - if (arg.equals("-u")) { - numOfUsers = Integer.parseInt(iter.next()); - } - - if (arg.equals("-i")) { - internet = Boolean.parseBoolean(iter.next()); - } - } - - final String server = host + ":" + port + testUrl; - s_logger.info("Starting test against server: " + server + " with " + numThreads + " thread(s)"); - if (cleanUp) - s_logger.info("Clean up is enabled, each test will wait " + sleepTime + " ms before cleaning up"); - - if (numOfUsers > 0) { - s_logger.info("Pre-generating users for test of size : " + numOfUsers); - users = new String[numOfUsers]; - Random ran = new Random(); - for (int i = 0; i < numOfUsers; i++) { - users[i] = Math.abs(ran.nextInt()) + "-user"; - } - } - - for (int i = 0; i < numThreads; i++) { - new Thread(new Runnable() { - @Override - public void run() { - do { - String username = null; - try { - long now = System.currentTimeMillis(); - Random ran = new Random(); - if (users != null) { - username = users[Math.abs(ran.nextInt()) % numOfUsers]; - } else { - username = Math.abs(ran.nextInt()) + "-user"; - } - NDC.push(username); - - String url = server + "?email=" + username + "&password=" + username + "&command=deploy"; - s_logger.info("Launching test for user: " + username + " with url: " + url); - HttpClient client = new HttpClient(); - HttpMethod method = new GetMethod(url); - int responseCode = client.executeMethod(method); - boolean success = false; - String reason = null; - if (responseCode == 200) { - if (internet) { - s_logger.info("Deploy successful...waiting 5 minute before SSH tests"); - Thread.sleep(300000L); // Wait 60 seconds so the linux VM can boot up. - - s_logger.info("Begin Linux SSH test"); - reason = sshTest(method.getResponseHeader("linuxIP").getValue()); - - if (reason == null) { - s_logger.info("Linux SSH test successful"); - s_logger.info("Begin Windows SSH test"); - reason = sshWinTest(method.getResponseHeader("windowsIP").getValue()); - } - } - if (reason == null) { - if (internet) { - s_logger.info("Windows SSH test successful"); - } else { - s_logger.info("deploy test successful....now cleaning up"); - if (cleanUp) { - s_logger.info("Waiting " + sleepTime + " ms before cleaning up vms"); - Thread.sleep(sleepTime); - } else { - success = true; - } - } - if (users == null) { - s_logger.info("Sending cleanup command"); - url = server + "?email=" + username + "&password=" + username + "&command=cleanup"; - } else { - s_logger.info("Sending stop DomR / destroy VM command"); - url = server + "?email=" + username + "&password=" + username + "&command=stopDomR"; - } - method = new GetMethod(url); - responseCode = client.executeMethod(method); - if (responseCode == 200) { - success = true; - } else { - reason = method.getStatusText(); - } - } else { - // Just stop but don't destroy the VMs/Routers - s_logger.info("SSH test failed with reason '" + reason + "', stopping VMs"); - url = server + "?email=" + username + "&password=" + username + "&command=stop"; - responseCode = client.executeMethod(new GetMethod(url)); - } - } else { - // Just stop but don't destroy the VMs/Routers - reason = method.getStatusText(); - s_logger.info("Deploy test failed with reason '" + reason + "', stopping VMs"); - url = server + "?email=" + username + "&password=" + username + "&command=stop"; - client.executeMethod(new GetMethod(url)); - } - - if (success) { - s_logger.info("***** Completed test for user : " + username + " in " + ((System.currentTimeMillis() - now) / 1000L) + " seconds"); - } else { - s_logger.info("##### FAILED test for user : " + username + " in " + ((System.currentTimeMillis() - now) / 1000L) + - " seconds with reason : " + reason); - } - } catch (Exception e) { - s_logger.warn("Error in thread", e); - try { - HttpClient client = new HttpClient(); - String url = server + "?email=" + username + "&password=" + username + "&command=stop"; - client.executeMethod(new GetMethod(url)); - } catch (Exception e1) { - s_logger.info("[ignored]" - + "error while executing last resort stop attempt: " + e1.getLocalizedMessage()); - } - } finally { - NDC.clear(); - } - } while (repeat); - } - }).start(); - } - } catch (Exception e) { - s_logger.error(e); - } - } - - private static String sshWinTest(String host) { - if (host == null) { - s_logger.info("Did not receive a host back from test, ignoring win ssh test"); - return null; - } - - // We will retry 5 times before quitting - int retry = 0; - - while (true) { - try { - if (retry > 0) { - s_logger.info("Retry attempt : " + retry + " ...sleeping 300 seconds before next attempt"); - Thread.sleep(300000); - } - - s_logger.info("Attempting to SSH into windows host " + host + " with retry attempt: " + retry); - - Connection conn = new Connection(host); - conn.connect(null, 60000, 60000); - - s_logger.info("SSHed successfully into windows host " + host); - boolean success = false; - boolean isAuthenticated = conn.authenticateWithPassword("vmops", "vmops"); - if (isAuthenticated == false) { - return "Authentication failed"; - } - SCPClient scp = new SCPClient(conn); - - scp.put("wget.exe", ""); - - Session sess = conn.openSession(); - s_logger.info("Executing : wget http://172.16.0.220/dump.bin"); - sess.execCommand("wget http://172.16.0.220/dump.bin && dir dump.bin"); - - InputStream stdout = sess.getStdout(); - InputStream stderr = sess.getStderr(); - - byte[] buffer = new byte[8192]; - while (true) { - if ((stdout.available() == 0) && (stderr.available() == 0)) { - int conditions = sess.waitForCondition(ChannelCondition.STDOUT_DATA | ChannelCondition.STDERR_DATA | ChannelCondition.EOF, 120000); - - if ((conditions & ChannelCondition.TIMEOUT) != 0) { - s_logger.info("Timeout while waiting for data from peer."); - return null; - } - - if ((conditions & ChannelCondition.EOF) != 0) { - if ((conditions & (ChannelCondition.STDOUT_DATA | ChannelCondition.STDERR_DATA)) == 0) { - break; - } - } - } - - while (stdout.available() > 0) { - success = true; - int len = stdout.read(buffer); - if (len > 0) // this check is somewhat paranoid - s_logger.info(new String(buffer, 0, len)); - } - - while (stderr.available() > 0) { - int len = stderr.read(buffer); - } - } - sess.close(); - conn.close(); - - if (success) { - return null; - } else { - retry++; - if (retry == MAX_RETRY_WIN) { - return "SSH Windows Network test fail"; - } - } - } catch (Exception e) { - retry++; - if (retry == MAX_RETRY_WIN) { - return "SSH Windows Network test fail with error " + e.getMessage(); - } - } - } - } - - private static String sshTest(String host) { - if (host == null) { - s_logger.info("Did not receive a host back from test, ignoring ssh test"); - return null; - } - - // We will retry 5 times before quitting - int retry = 0; - - while (true) { - try { - if (retry > 0) { - s_logger.info("Retry attempt : " + retry + " ...sleeping 120 seconds before next attempt"); - Thread.sleep(120000); - } - - s_logger.info("Attempting to SSH into linux host " + host + " with retry attempt: " + retry); - - Connection conn = new Connection(host); - conn.connect(null, 60000, 60000); - - s_logger.info("SSHed successfully into linux host " + host); - - boolean isAuthenticated = conn.authenticateWithPassword("root", "password"); - - if (isAuthenticated == false) { - return "Authentication failed"; - } - boolean success = false; - Session sess = conn.openSession(); - s_logger.info("Executing : wget http://172.16.0.220/dump.bin"); - sess.execCommand("wget http://172.16.0.220/dump.bin && ls -al dump.bin"); - - InputStream stdout = sess.getStdout(); - InputStream stderr = sess.getStderr(); - - byte[] buffer = new byte[8192]; - while (true) { - if ((stdout.available() == 0) && (stderr.available() == 0)) { - int conditions = sess.waitForCondition(ChannelCondition.STDOUT_DATA | ChannelCondition.STDERR_DATA | ChannelCondition.EOF, 120000); - - if ((conditions & ChannelCondition.TIMEOUT) != 0) { - s_logger.info("Timeout while waiting for data from peer."); - return null; - } - - if ((conditions & ChannelCondition.EOF) != 0) { - if ((conditions & (ChannelCondition.STDOUT_DATA | ChannelCondition.STDERR_DATA)) == 0) { - break; - } - } - } - - while (stdout.available() > 0) { - success = true; - int len = stdout.read(buffer); - if (len > 0) // this check is somewhat paranoid - s_logger.info(new String(buffer, 0, len)); - } - - while (stderr.available() > 0) { - int len = stderr.read(buffer); - } - } - - sess.close(); - conn.close(); - - if (success) { - return null; - } else { - retry++; - if (retry == MAX_RETRY_LINUX) { - return "SSH Linux Network test fail"; - } - } - } catch (Exception e) { - retry++; - if (retry == MAX_RETRY_LINUX) { - return "SSH Linux Network test fail with error " + e.getMessage(); - } - } - } - } -} diff --git a/test/src-not-used/main/java/com/cloud/test/utils/UtilsForTest.java b/test/src-not-used/main/java/com/cloud/test/utils/UtilsForTest.java deleted file mode 100644 index 78ba001bb9c..00000000000 --- a/test/src-not-used/main/java/com/cloud/test/utils/UtilsForTest.java +++ /dev/null @@ -1,210 +0,0 @@ -// 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.test.utils; - -import java.io.InputStream; -import java.math.BigInteger; -import java.security.MessageDigest; -import java.security.NoSuchAlgorithmException; -import java.util.ArrayList; -import java.util.HashMap; -import java.util.List; -import java.util.Map; - -import javax.crypto.Mac; -import javax.crypto.spec.SecretKeySpec; -import javax.xml.parsers.DocumentBuilder; -import javax.xml.parsers.DocumentBuilderFactory; - -import org.apache.commons.codec.binary.Base64; -import org.w3c.dom.Document; -import org.w3c.dom.Element; -import org.w3c.dom.Node; -import org.w3c.dom.NodeList; - -import com.cloud.utils.exception.CloudRuntimeException; - -public class UtilsForTest { - - private static DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance(); - - public static boolean verifyTags(Map params) { - boolean result = true; - for (String value : params.keySet()) { - if (params.get(value) == null) { - result = false; - } - } - return result; - } - - public static boolean verifyTagValues(Map params, Map pattern) { - boolean result = true; - - if (pattern != null) { - for (String value : pattern.keySet()) { - if (!pattern.get(value).equals(params.get(value))) { - result = false; - System.out.println("Tag " + value + " has " + params.get(value) + " while expected value is: " + pattern.get(value)); - } - } - } - return result; - } - - public static Map parseXML(InputStream is, String[] tagNames) { - Map returnValues = new HashMap(); - try { - 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) { - System.out.println("no " + tagNames[i] + " tag in the response"); - returnValues.put(tagNames[i], null); - } else { - returnValues.put(tagNames[i], targetNodes.item(0).getTextContent()); - } - } - } catch (Exception ex) { - System.out.println("error processing XML"); - ex.printStackTrace(); - } - return returnValues; - } - - public static ArrayList> parseMulXML(InputStream is, String[] tagNames) { - ArrayList> returnValues = new ArrayList>(); - - try { - 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) { - System.out.println("no " + tagNames[i] + " tag in XML response...returning null"); - } else { - for (int j = 0; j < targetNodes.getLength(); j++) { - HashMap valueList = new HashMap(); - Node node = targetNodes.item(j); - //parse child nodes - NodeList child = node.getChildNodes(); - for (int c = 0; c < node.getChildNodes().getLength(); c++) { - child.item(c).getNodeName(); - valueList.put(child.item(c).getNodeName(), child.item(c).getTextContent()); - } - returnValues.add(valueList); - } - - } - } - } catch (Exception ex) { - System.out.println(ex); - } - - return returnValues; - } - - public static String createMD5String(String password) { - MessageDigest md5; - try { - md5 = MessageDigest.getInstance("MD5"); - } catch (NoSuchAlgorithmException e) { - throw new CloudRuntimeException("Error", e); - } - - md5.reset(); - BigInteger pwInt = new BigInteger(1, md5.digest(password.getBytes())); - - // make sure our MD5 hash value is 32 digits long... - StringBuffer sb = new StringBuffer(); - String pwStr = pwInt.toString(16); - int padding = 32 - pwStr.length(); - for (int i = 0; i < padding; i++) { - sb.append('0'); - } - sb.append(pwStr); - return sb.toString(); - } - - public static Map getSingleValueFromXML(InputStream is, String[] tagNames) { - Map returnValues = new HashMap(); - try { - 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) { - System.out.println("no " + tagNames[i] + " tag in XML response...returning null"); - } else { - returnValues.put(tagNames[i], targetNodes.item(0).getTextContent()); - } - } - } catch (Exception ex) { - System.out.println("error processing XML"); - ex.printStackTrace(); - } - return returnValues; - } - - public static Map> getMultipleValuesFromXML(InputStream is, String[] tagNames) { - Map> returnValues = new HashMap>(); - try { - 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) { - System.out.println("no " + tagNames[i] + " tag in XML response...returning null"); - } else { - List valueList = new ArrayList(); - for (int j = 0; j < targetNodes.getLength(); j++) { - Node node = targetNodes.item(j); - valueList.add(node.getTextContent()); - } - returnValues.put(tagNames[i], valueList); - } - } - } catch (Exception ex) { - System.out.println(ex); - } - return returnValues; - } - - public static String signRequest(String request, String key) { - try { - Mac mac = Mac.getInstance("HmacSHA1"); - SecretKeySpec keySpec = new SecretKeySpec(key.getBytes(), "HmacSHA1"); - mac.init(keySpec); - mac.update(request.getBytes()); - byte[] encryptedBytes = mac.doFinal(); - //System.out.println("HmacSHA1 hash: " + encryptedBytes); - return Base64.encodeBase64String(encryptedBytes); - } catch (Exception ex) { - System.out.println("unable to sign request"); - ex.printStackTrace(); - } - return null; - } - -} From c791c138e75649b604d42f56d7d185a34fe30ce3 Mon Sep 17 00:00:00 2001 From: Rohit Yadav Date: Fri, 10 May 2024 23:41:37 +0530 Subject: [PATCH 002/350] ui: change reporting link to Github Discussions (#9023) * ui: change reporting link to Github Discussions Many users are using the footer link to open questions about CloudStack that are usually discussed on the users@ mailing list. This fixes that behaviour by diverting them to Github Discussions which are linked with the user@ ML, smart users can still report actual bugs/issues via the issues tab. Signed-off-by: Rohit Yadav * Update en.json --------- Signed-off-by: Rohit Yadav --- ui/public/locales/en.json | 2 +- ui/src/components/page/GlobalFooter.vue | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/ui/public/locales/en.json b/ui/public/locales/en.json index b11a8f864fb..d7e3c51c1ea 100644 --- a/ui/public/locales/en.json +++ b/ui/public/locales/en.json @@ -1613,7 +1613,7 @@ "label.removing": "Removing", "label.replace.acl": "Replace ACL", "label.replace.acl.list": "Replace ACL list", -"label.report.bug": "Report issue", +"label.report.bug": "Ask a question or Report an issue", "label.required": "Required", "label.requireshvm": "HVM", "label.requiresupgrade": "Requires upgrade", diff --git a/ui/src/components/page/GlobalFooter.vue b/ui/src/components/page/GlobalFooter.vue index 86c8948e564..854cecc78ac 100644 --- a/ui/src/components/page/GlobalFooter.vue +++ b/ui/src/components/page/GlobalFooter.vue @@ -23,7 +23,7 @@
CloudStack {{ $store.getters.features.cloudstackversion }} - + {{ $t('label.report.bug') }} From 87b55af1979faa7020850842b1d72ecbdb77938a Mon Sep 17 00:00:00 2001 From: Vishesh Date: Thu, 30 May 2024 12:18:53 +0530 Subject: [PATCH 003/350] Fixup response code on incorrect credentials (#8671) --- .../oauth2/OAuth2UserAuthenticator.java | 26 +++++++++--- .../oauth2/OAuth2UserAuthenticatorTest.java | 40 ++++++++++++++----- .../com/cloud/user/AccountManagerImpl.java | 4 +- .../cloud/user/AccountManagerImplTest.java | 1 + 4 files changed, 53 insertions(+), 18 deletions(-) diff --git a/plugins/user-authenticators/oauth2/src/main/java/org/apache/cloudstack/oauth2/OAuth2UserAuthenticator.java b/plugins/user-authenticators/oauth2/src/main/java/org/apache/cloudstack/oauth2/OAuth2UserAuthenticator.java index 8484a5ef798..3a6d2bd6fae 100644 --- a/plugins/user-authenticators/oauth2/src/main/java/org/apache/cloudstack/oauth2/OAuth2UserAuthenticator.java +++ b/plugins/user-authenticators/oauth2/src/main/java/org/apache/cloudstack/oauth2/OAuth2UserAuthenticator.java @@ -32,16 +32,18 @@ import org.apache.log4j.Logger; import javax.inject.Inject; import java.util.Map; +import static org.apache.cloudstack.oauth2.OAuth2AuthManager.OAuth2IsPluginEnabled; + public class OAuth2UserAuthenticator extends AdapterBase implements UserAuthenticator { public static final Logger s_logger = Logger.getLogger(OAuth2UserAuthenticator.class); @Inject - private UserAccountDao _userAccountDao; + private UserAccountDao userAccountDao; @Inject - private UserDao _userDao; + private UserDao userDao; @Inject - private OAuth2AuthManager _userOAuth2mgr; + private OAuth2AuthManager userOAuth2mgr; @Override public Pair authenticate(String username, String password, Long domainId, Map requestParameters) { @@ -49,12 +51,20 @@ public class OAuth2UserAuthenticator extends AdapterBase implements UserAuthenti s_logger.debug("Trying OAuth2 auth for user: " + username); } - final UserAccount userAccount = _userAccountDao.getUserAccount(username, domainId); + if (!isOAuthPluginEnabled()) { + s_logger.debug("OAuth2 plugin is disabled"); + return new Pair(false, null); + } else if (requestParameters == null) { + s_logger.debug("Request parameters are null"); + return new Pair(false, null); + } + + final UserAccount userAccount = userAccountDao.getUserAccount(username, domainId); if (userAccount == null) { s_logger.debug("Unable to find user with " + username + " in domain " + domainId + ", or user source is not OAUTH2"); return new Pair(false, null); } else { - User user = _userDao.getUser(userAccount.getId()); + User user = userDao.getUser(userAccount.getId()); final String[] provider = (String[])requestParameters.get(ApiConstants.PROVIDER); final String[] emailArray = (String[])requestParameters.get(ApiConstants.EMAIL); final String[] secretCodeArray = (String[])requestParameters.get(ApiConstants.SECRET_CODE); @@ -62,7 +72,7 @@ public class OAuth2UserAuthenticator extends AdapterBase implements UserAuthenti String email = ((emailArray == null) ? null : emailArray[0]); String secretCode = ((secretCodeArray == null) ? null : secretCodeArray[0]); - UserOAuth2Authenticator authenticator = _userOAuth2mgr.getUserOAuth2AuthenticationProvider(oauthProvider); + UserOAuth2Authenticator authenticator = userOAuth2mgr.getUserOAuth2AuthenticationProvider(oauthProvider); if (user != null && authenticator.verifyUser(email, secretCode)) { return new Pair(true, null); } @@ -75,4 +85,8 @@ public class OAuth2UserAuthenticator extends AdapterBase implements UserAuthenti public String encode(String password) { return null; } + + protected boolean isOAuthPluginEnabled() { + return OAuth2IsPluginEnabled.value(); + } } diff --git a/plugins/user-authenticators/oauth2/src/test/java/org/apache/cloudstack/oauth2/OAuth2UserAuthenticatorTest.java b/plugins/user-authenticators/oauth2/src/test/java/org/apache/cloudstack/oauth2/OAuth2UserAuthenticatorTest.java index 06aa04d729c..9a6e8812679 100644 --- a/plugins/user-authenticators/oauth2/src/test/java/org/apache/cloudstack/oauth2/OAuth2UserAuthenticatorTest.java +++ b/plugins/user-authenticators/oauth2/src/test/java/org/apache/cloudstack/oauth2/OAuth2UserAuthenticatorTest.java @@ -24,23 +24,32 @@ import com.cloud.user.dao.UserAccountDao; import com.cloud.user.dao.UserDao; import com.cloud.utils.Pair; import org.apache.cloudstack.auth.UserOAuth2Authenticator; +import org.junit.After; import org.junit.Before; import org.junit.Test; +import org.junit.runner.RunWith; import org.mockito.InjectMocks; import org.mockito.Mock; import org.mockito.MockitoAnnotations; +import org.mockito.Spy; +import org.mockito.junit.MockitoJUnitRunner; import java.util.HashMap; import java.util.Map; import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNull; +import static org.junit.Assert.assertTrue; import static org.mockito.ArgumentMatchers.anyLong; import static org.mockito.ArgumentMatchers.anyString; +import static org.mockito.Mockito.doReturn; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.never; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; +@RunWith(MockitoJUnitRunner.class) public class OAuth2UserAuthenticatorTest { @Mock @@ -53,13 +62,22 @@ public class OAuth2UserAuthenticatorTest { private OAuth2AuthManager userOAuth2mgr; @InjectMocks + @Spy private OAuth2UserAuthenticator authenticator; + private AutoCloseable closeable; @Before public void setUp() { - MockitoAnnotations.initMocks(this); + closeable = MockitoAnnotations.openMocks(this); + doReturn(true).when(authenticator).isOAuthPluginEnabled(); } + @After + public void tearDown() throws Exception { + closeable.close(); + } + + @Test public void testAuthenticateWithValidCredentials() { String username = "testuser"; @@ -84,13 +102,13 @@ public class OAuth2UserAuthenticatorTest { Pair result = authenticator.authenticate(username, null, domainId, requestParameters); + assertTrue(result.first()); + assertNull(result.second()); + verify(userAccountDao).getUserAccount(username, domainId); verify(userDao).getUser(userAccount.getId()); verify(userOAuth2mgr).getUserOAuth2AuthenticationProvider(provider[0]); verify(userOAuth2Authenticator).verifyUser(email[0], secretCode[0]); - - assertEquals(true, result.first().booleanValue()); - assertEquals(null, result.second()); } @Test @@ -106,7 +124,7 @@ public class OAuth2UserAuthenticatorTest { UserOAuth2Authenticator userOAuth2Authenticator = mock(UserOAuth2Authenticator.class); when(userAccountDao.getUserAccount(username, domainId)).thenReturn(userAccount); - when(userDao.getUser(userAccount.getId())).thenReturn( user); + when(userDao.getUser(userAccount.getId())).thenReturn(user); when(userOAuth2mgr.getUserOAuth2AuthenticationProvider(provider[0])).thenReturn(userOAuth2Authenticator); when(userOAuth2Authenticator.verifyUser(email[0], secretCode[0])).thenReturn(false); @@ -117,13 +135,13 @@ public class OAuth2UserAuthenticatorTest { Pair result = authenticator.authenticate(username, null, domainId, requestParameters); + assertFalse(result.first()); + assertEquals(OAuth2UserAuthenticator.ActionOnFailedAuthentication.INCREMENT_INCORRECT_LOGIN_ATTEMPT_COUNT, result.second()); + verify(userAccountDao).getUserAccount(username, domainId); verify(userDao).getUser(userAccount.getId()); verify(userOAuth2mgr).getUserOAuth2AuthenticationProvider(provider[0]); verify(userOAuth2Authenticator).verifyUser(email[0], secretCode[0]); - - assertEquals(false, result.first().booleanValue()); - assertEquals(OAuth2UserAuthenticator.ActionOnFailedAuthentication.INCREMENT_INCORRECT_LOGIN_ATTEMPT_COUNT, result.second()); } @Test @@ -143,11 +161,11 @@ public class OAuth2UserAuthenticatorTest { Pair result = authenticator.authenticate(username, null, domainId, requestParameters); + assertFalse(result.first()); + assertNull(result.second()); + verify(userAccountDao).getUserAccount(username, domainId); verify(userDao, never()).getUser(anyLong()); verify(userOAuth2mgr, never()).getUserOAuth2AuthenticationProvider(anyString()); - - assertEquals(false, result.first().booleanValue()); - assertEquals(null, result.second()); } } diff --git a/server/src/main/java/com/cloud/user/AccountManagerImpl.java b/server/src/main/java/com/cloud/user/AccountManagerImpl.java index 86a359a3348..3eed429ed21 100644 --- a/server/src/main/java/com/cloud/user/AccountManagerImpl.java +++ b/server/src/main/java/com/cloud/user/AccountManagerImpl.java @@ -332,6 +332,7 @@ public class AccountManagerImpl extends ManagerBase implements AccountManager, M private List _securityCheckers; private int _cleanupInterval; + private static final String OAUTH2_PROVIDER_NAME = "oauth2"; private List apiNameList; protected static Map userTwoFactorAuthenticationProvidersMap = new HashMap<>(); @@ -2650,7 +2651,8 @@ public class AccountManagerImpl extends ManagerBase implements AccountManager, M continue; } } - if (secretCode != null && !authenticator.getName().equals("oauth2")) { + if ((secretCode != null && !authenticator.getName().equals(OAUTH2_PROVIDER_NAME)) + || (secretCode == null && authenticator.getName().equals(OAUTH2_PROVIDER_NAME))) { continue; } Pair result = authenticator.authenticate(username, password, domainId, requestParameters); diff --git a/server/src/test/java/com/cloud/user/AccountManagerImplTest.java b/server/src/test/java/com/cloud/user/AccountManagerImplTest.java index 6d9211dd526..9014af523fd 100644 --- a/server/src/test/java/com/cloud/user/AccountManagerImplTest.java +++ b/server/src/test/java/com/cloud/user/AccountManagerImplTest.java @@ -203,6 +203,7 @@ public class AccountManagerImplTest extends AccountManagetImplTestBase { Mockito.when(userAuthenticator.authenticate("test", "fail", 1L, new HashMap<>())).thenReturn(failureAuthenticationPair); Mockito.lenient().when(userAuthenticator.authenticate("test", null, 1L, new HashMap<>())).thenReturn(successAuthenticationPair); Mockito.lenient().when(userAuthenticator.authenticate("test", "", 1L, new HashMap<>())).thenReturn(successAuthenticationPair); + Mockito.when(userAuthenticator.getName()).thenReturn("test"); //Test for incorrect password. authentication should fail UserAccount userAccount = accountManagerImpl.authenticateUser("test", "fail", 1L, InetAddress.getByName("127.0.0.1"), new HashMap<>()); From d77998c2d44ac14f82cf52d54a3ba04996d63169 Mon Sep 17 00:00:00 2001 From: Abhishek Kumar Date: Mon, 3 Jun 2024 15:27:44 +0530 Subject: [PATCH 004/350] utils,ui: obfuscate sensitive log info, use POST for configureOutOfBandManagement (#9126) Signed-off-by: Abhishek Kumar --- ui/src/config/section/infra/hosts.js | 1 + .../utils/process/ProcessRunner.java | 51 ++++++++++----- .../utils/process/ProcessRunnerTest.java | 63 +++++++++++++++++++ 3 files changed, 100 insertions(+), 15 deletions(-) create mode 100644 utils/src/test/java/org/apache/cloudstack/utils/process/ProcessRunnerTest.java diff --git a/ui/src/config/section/infra/hosts.js b/ui/src/config/section/infra/hosts.js index 329b77fe2d7..81ca0c917b2 100644 --- a/ui/src/config/section/infra/hosts.js +++ b/ui/src/config/section/infra/hosts.js @@ -150,6 +150,7 @@ export default { message: 'label.outofbandmanagement.configure', docHelp: 'adminguide/hosts.html#out-of-band-management', dataView: true, + post: true, args: ['hostid', 'address', 'port', 'username', 'password', 'driver'], mapping: { hostid: { diff --git a/utils/src/main/java/org/apache/cloudstack/utils/process/ProcessRunner.java b/utils/src/main/java/org/apache/cloudstack/utils/process/ProcessRunner.java index b8b2555f4c9..6ca02b91263 100644 --- a/utils/src/main/java/org/apache/cloudstack/utils/process/ProcessRunner.java +++ b/utils/src/main/java/org/apache/cloudstack/utils/process/ProcessRunner.java @@ -19,13 +19,9 @@ package org.apache.cloudstack.utils.process; -import com.google.common.base.Preconditions; -import com.google.common.io.CharStreams; -import org.apache.log4j.Logger; -import org.joda.time.Duration; - import java.io.IOException; import java.io.InputStreamReader; +import java.util.ArrayList; import java.util.List; import java.util.concurrent.Callable; import java.util.concurrent.ExecutionException; @@ -33,7 +29,16 @@ import java.util.concurrent.ExecutorService; import java.util.concurrent.Future; import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeoutException; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + import org.apache.commons.lang3.StringUtils; +import org.apache.log4j.Logger; +import org.joda.time.Duration; + +import com.cloud.utils.Ternary; +import com.google.common.base.Preconditions; +import com.google.common.io.CharStreams; public final class ProcessRunner { public static final Logger LOG = Logger.getLogger(ProcessRunner.class); @@ -41,9 +46,26 @@ public final class ProcessRunner { // Default maximum timeout of 5 minutes for any command public static final Duration DEFAULT_MAX_TIMEOUT = new Duration(5 * 60 * 1000); private final ExecutorService executor; + private final List> commandLogReplacements = new ArrayList<>(); + + String removeCommandSensitiveInfoForLogging(String command) { + String commandLog = command.trim(); + + for (Ternary replacement : commandLogReplacements) { + if (commandLog.contains(replacement.first())) { + Pattern pattern = Pattern.compile(replacement.second()); + Matcher matcher = pattern.matcher(commandLog); + if (matcher.find()) { + commandLog = matcher.replaceAll(replacement.third()); + } + } + } + return commandLog; + } public ProcessRunner(ExecutorService executor) { this.executor = executor; + commandLogReplacements.add(new Ternary<>("ipmitool", "-P\\s+\\S+", "-P *****")); } /** @@ -72,14 +94,13 @@ public final class ProcessRunner { int retVal = -2; String stdOutput = null; String stdError = null; - - String oneLineCommand = StringUtils.join(commands, " "); + String commandLog = removeCommandSensitiveInfoForLogging(StringUtils.join(commands, " ")); try { - LOG.debug(String.format("Preparing command [%s] to execute.", oneLineCommand)); + LOG.debug(String.format("Preparing command [%s] to execute.", commandLog)); final Process process = new ProcessBuilder().command(commands).start(); - LOG.debug(String.format("Submitting command [%s].", oneLineCommand)); + LOG.debug(String.format("Submitting command [%s].", commandLog)); final Future processFuture = executor.submit(new Callable() { @Override public Integer call() throws Exception { @@ -87,14 +108,14 @@ public final class ProcessRunner { } }); try { - LOG.debug(String.format("Waiting for a response from command [%s]. Defined timeout: [%s].", oneLineCommand, timeOut.getStandardSeconds())); + LOG.debug(String.format("Waiting for a response from command [%s]. Defined timeout: [%s].", commandLog, timeOut.getStandardSeconds())); retVal = processFuture.get(timeOut.getStandardSeconds(), TimeUnit.SECONDS); } catch (ExecutionException e) { - LOG.warn(String.format("Failed to complete the requested command [%s] due to execution error.", oneLineCommand), e); + LOG.warn(String.format("Failed to complete the requested command [%s] due to execution error.", commands), e); retVal = -2; stdError = e.getMessage(); } catch (TimeoutException e) { - LOG.warn(String.format("Failed to complete the requested command [%s] within timeout. Defined timeout: [%s].", oneLineCommand, timeOut.getStandardSeconds()), e); + LOG.warn(String.format("Failed to complete the requested command [%s] within timeout. Defined timeout: [%s].", commandLog, timeOut.getStandardSeconds()), e); retVal = -1; stdError = "Operation timed out, aborted."; } finally { @@ -105,10 +126,10 @@ public final class ProcessRunner { process.destroy(); } - LOG.debug(String.format("Process standard output for command [%s]: [%s].", oneLineCommand, stdOutput)); - LOG.debug(String.format("Process standard error output command [%s]: [%s].", oneLineCommand, stdError)); + LOG.debug(String.format("Process standard output for command [%s]: [%s].", commandLog, stdOutput)); + LOG.debug(String.format("Process standard error output command [%s]: [%s].", commandLog, stdError)); } catch (IOException | InterruptedException e) { - LOG.error(String.format("Exception caught error running command [%s].", oneLineCommand), e); + LOG.error(String.format("Exception caught error running command [%s].", commandLog), e); stdError = e.getMessage(); } return new ProcessResult(stdOutput, stdError, retVal); diff --git a/utils/src/test/java/org/apache/cloudstack/utils/process/ProcessRunnerTest.java b/utils/src/test/java/org/apache/cloudstack/utils/process/ProcessRunnerTest.java new file mode 100644 index 00000000000..6fc34ded259 --- /dev/null +++ b/utils/src/test/java/org/apache/cloudstack/utils/process/ProcessRunnerTest.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.process; + +import java.util.concurrent.ExecutorService; + +import org.junit.Assert; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.InjectMocks; +import org.mockito.Mockito; +import org.mockito.junit.MockitoJUnitRunner; + +@RunWith(MockitoJUnitRunner.class) +public class ProcessRunnerTest { + + @InjectMocks + ProcessRunner processRunner = new ProcessRunner(Mockito.mock(ExecutorService.class)); + + private int countSubstringOccurrences(String mainString, String subString) { + int count = 0; + int index = 0; + while ((index = mainString.indexOf(subString, index)) != -1) { + count++; + index += subString.length(); + } + return count; + } + + @Test + public void testRemoveCommandSensitiveInfoForLoggingIpmi() { + String password = "R@ndomP@ss"; + String command = String.format("/usr/bin/ipmitool -H host -p 1234 -U Admin " + + "-P %s chassis power status", password); + String log = processRunner.removeCommandSensitiveInfoForLogging(command); + Assert.assertFalse(log.contains(password)); + + String command1 = String.format("%s -D %s", command, password); + log = processRunner.removeCommandSensitiveInfoForLogging(command1); + Assert.assertTrue(log.contains(password)); + Assert.assertEquals(1, countSubstringOccurrences(log, password)); + + String command2 = command.replace("ipmitool", "impit00l"); + log = processRunner.removeCommandSensitiveInfoForLogging(command2); + Assert.assertTrue(log.contains(password)); + Assert.assertEquals(1, countSubstringOccurrences(log, password)); + } +} From 875cd14919596e0c603c1aaad6e1feedeb419abf Mon Sep 17 00:00:00 2001 From: Wei Zhou Date: Tue, 4 Jun 2024 09:28:39 +0200 Subject: [PATCH 005/350] server: fix fail to list public ips of shared networks sometimes (#8624) --- server/src/main/java/com/cloud/api/ApiResponseHelper.java | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/server/src/main/java/com/cloud/api/ApiResponseHelper.java b/server/src/main/java/com/cloud/api/ApiResponseHelper.java index 6d66da43c37..b49a278de5b 100644 --- a/server/src/main/java/com/cloud/api/ApiResponseHelper.java +++ b/server/src/main/java/com/cloud/api/ApiResponseHelper.java @@ -992,6 +992,12 @@ public class ApiResponseHelper implements ResponseGenerator { if (!isAdmin) { return; } + try { + nic.getInstanceId(); + } catch (NullPointerException ex) { + return; + } + VirtualMachine vm = ApiDBUtils.findVMInstanceById(nic.getInstanceId()); if (vm == null) { return; From 68a231aaee0d77bfef9076436f14529ad96cfb03 Mon Sep 17 00:00:00 2001 From: Vishesh Date: Tue, 4 Jun 2024 18:13:41 +0530 Subject: [PATCH 006/350] Show site to site VPN connections in UI (#9066) From 631d6ad09bc5e175492973de9f2958456db4488e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jo=C3=A3o=20Jandre?= <48719461+JoaoJandre@users.noreply.github.com> Date: Wed, 5 Jun 2024 09:15:28 -0300 Subject: [PATCH 007/350] Do not retrieve VM's stats on normal VM listing (#8782) * Do not retrieve VM's stats on normal VM listing * Add config to control the behavior * address reviews --- .../api/command/user/vm/ListVMsCmd.java | 48 +++++++++++-------- .../api/response/UserVmResponse.java | 16 +++++++ .../apache/cloudstack/query/QueryService.java | 4 ++ .../cloudstack/api/ListVMsMetricsCmd.java | 13 +++-- .../metrics/MetricsServiceImpl.java | 1 - .../response/VmMetricsResponse.java | 11 ----- .../com/cloud/api/query/QueryManagerImpl.java | 2 +- .../cloud/api/query/ViewResponseHelper.java | 1 + ui/src/config/router.js | 1 + ui/src/config/section/compute.js | 3 +- ui/src/views/AutogenView.vue | 2 +- 11 files changed, 63 insertions(+), 39 deletions(-) diff --git a/api/src/main/java/org/apache/cloudstack/api/command/user/vm/ListVMsCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/user/vm/ListVMsCmd.java index 6a5ec28d1ba..4abccfd1da8 100644 --- a/api/src/main/java/org/apache/cloudstack/api/command/user/vm/ListVMsCmd.java +++ b/api/src/main/java/org/apache/cloudstack/api/command/user/vm/ListVMsCmd.java @@ -16,9 +16,10 @@ // under the License. package org.apache.cloudstack.api.command.user.vm; -import java.util.ArrayList; import java.util.EnumSet; +import java.util.HashSet; import java.util.List; +import java.util.Set; import org.apache.cloudstack.acl.RoleType; import org.apache.cloudstack.affinity.AffinityGroupResponse; @@ -45,6 +46,7 @@ import org.apache.cloudstack.api.response.UserVmResponse; import org.apache.cloudstack.api.response.VpcResponse; import org.apache.cloudstack.api.response.ZoneResponse; import org.apache.commons.lang3.BooleanUtils; +import org.apache.commons.collections.CollectionUtils; import org.apache.log4j.Logger; import com.cloud.exception.InvalidParameterValueException; @@ -58,7 +60,6 @@ import com.cloud.vm.VirtualMachine; public class ListVMsCmd extends BaseListRetrieveOnlyResourceCountCmd implements UserCmd { public static final Logger s_logger = Logger.getLogger(ListVMsCmd.class.getName()); - private static final String s_name = "listvirtualmachinesresponse"; ///////////////////////////////////////////////////// //////////////// API parameters ///////////////////// @@ -98,7 +99,8 @@ public class ListVMsCmd extends BaseListRetrieveOnlyResourceCountCmd implements collectionType = CommandType.STRING, description = "comma separated list of vm details requested, " + "value can be a list of [all, group, nics, stats, secgrp, tmpl, servoff, diskoff, backoff, iso, volume, min, affgrp]." - + " If no parameter is passed in, the details will be defaulted to all") + + " If no parameter is passed in, the details will be defaulted to all. When return.vm.stats.on.vm.list is true, the default" + + "details change to [group, nics, secgrp, tmpl, servoff, diskoff, backoff, iso, volume, min, affgrp], thus the stats will not be returned. ") private List viewDetails; @Parameter(name = ApiConstants.TEMPLATE_ID, type = CommandType.UUID, entityType = TemplateResponse.class, description = "list vms by template") @@ -239,22 +241,32 @@ public class ListVMsCmd extends BaseListRetrieveOnlyResourceCountCmd implements return autoScaleVmGroupId; } + protected boolean isViewDetailsEmpty() { + return CollectionUtils.isEmpty(viewDetails); + } + public EnumSet getDetails() throws InvalidParameterValueException { - EnumSet dv; - if (viewDetails == null || viewDetails.size() <= 0) { - dv = EnumSet.of(VMDetails.all); - } else { - try { - ArrayList dc = new ArrayList(); - for (String detail : viewDetails) { - dc.add(VMDetails.valueOf(detail)); - } - dv = EnumSet.copyOf(dc); - } catch (IllegalArgumentException e) { - throw new InvalidParameterValueException("The details parameter contains a non permitted value. The allowed values are " + EnumSet.allOf(VMDetails.class)); + if (isViewDetailsEmpty()) { + if (_queryService.ReturnVmStatsOnVmList.value()) { + return EnumSet.of(VMDetails.all); } + + Set allDetails = new HashSet<>(Set.of(VMDetails.values())); + allDetails.remove(VMDetails.stats); + allDetails.remove(VMDetails.all); + return EnumSet.copyOf(allDetails); + } + + try { + Set dc = new HashSet<>(); + for (String detail : viewDetails) { + dc.add(VMDetails.valueOf(detail)); + } + + return EnumSet.copyOf(dc); + } catch (IllegalArgumentException e) { + throw new InvalidParameterValueException("The details parameter contains a non permitted value. The allowed values are " + EnumSet.allOf(VMDetails.class)); } - return dv; } @Override @@ -277,10 +289,6 @@ public class ListVMsCmd extends BaseListRetrieveOnlyResourceCountCmd implements ///////////////////////////////////////////////////// /////////////// API Implementation/////////////////// ///////////////////////////////////////////////////// - @Override - public String getCommandName() { - return s_name; - } @Override public ApiCommandResourceType getApiResourceType() { diff --git a/api/src/main/java/org/apache/cloudstack/api/response/UserVmResponse.java b/api/src/main/java/org/apache/cloudstack/api/response/UserVmResponse.java index 5a0ea77a4e7..8deae7d80d3 100644 --- a/api/src/main/java/org/apache/cloudstack/api/response/UserVmResponse.java +++ b/api/src/main/java/org/apache/cloudstack/api/response/UserVmResponse.java @@ -37,6 +37,7 @@ import com.cloud.serializer.Param; import com.cloud.uservm.UserVm; import com.cloud.vm.VirtualMachine; import com.google.gson.annotations.SerializedName; +import org.apache.commons.collections.CollectionUtils; @SuppressWarnings("unused") @EntityReference(value = {VirtualMachine.class, UserVm.class, VirtualRouter.class}) @@ -273,6 +274,10 @@ public class UserVmResponse extends BaseResponseWithTagInformation implements Co @Param(description = "the hypervisor on which the template runs") private String hypervisor; + @SerializedName(ApiConstants.IP_ADDRESS) + @Param(description = "the VM's primary IP address") + private String ipAddress; + @SerializedName(ApiConstants.PUBLIC_IP_ID) @Param(description = "public IP address id associated with vm via Static nat rule") private String publicIpId; @@ -627,6 +632,10 @@ public class UserVmResponse extends BaseResponseWithTagInformation implements Co return hypervisor; } + public String getIpAddress() { + return ipAddress; + } + public String getPublicIpId() { return publicIpId; } @@ -863,6 +872,13 @@ public class UserVmResponse extends BaseResponseWithTagInformation implements Co public void setNics(Set nics) { this.nics = nics; + setIpAddress(nics); + } + + public void setIpAddress(final Set nics) { + if (CollectionUtils.isNotEmpty(nics)) { + this.ipAddress = nics.iterator().next().getIpaddress(); + } } public void addNic(NicResponse nic) { diff --git a/api/src/main/java/org/apache/cloudstack/query/QueryService.java b/api/src/main/java/org/apache/cloudstack/query/QueryService.java index 3299e7537a2..dd5aaf4e6d7 100644 --- a/api/src/main/java/org/apache/cloudstack/query/QueryService.java +++ b/api/src/main/java/org/apache/cloudstack/query/QueryService.java @@ -125,6 +125,10 @@ public interface QueryService { static final ConfigKey SharePublicTemplatesWithOtherDomains = new ConfigKey<>("Advanced", Boolean.class, "share.public.templates.with.other.domains", "true", "If false, templates of this domain will not show up in the list templates of other domains.", true, ConfigKey.Scope.Domain); + ConfigKey ReturnVmStatsOnVmList = new ConfigKey<>("Advanced", Boolean.class, "return.vm.stats.on.vm.list", "true", + "If false, changes the listVirtualMachines default details to [group, nics, secgrp, tmpl, servoff, diskoff, backoff, iso, volume, min, affgrp], so that the VMs' stats" + + " are not returned by default when listing VMs; only when the 'stats' or 'all' detail is informed.", true, ConfigKey.Scope.Global); + ListResponse searchForUsers(ListUsersCmd cmd) throws PermissionDeniedException; ListResponse searchForUsers(Long domainId, boolean recursive) throws PermissionDeniedException; diff --git a/plugins/metrics/src/main/java/org/apache/cloudstack/api/ListVMsMetricsCmd.java b/plugins/metrics/src/main/java/org/apache/cloudstack/api/ListVMsMetricsCmd.java index 55af69e623c..f5b2ee5bda7 100644 --- a/plugins/metrics/src/main/java/org/apache/cloudstack/api/ListVMsMetricsCmd.java +++ b/plugins/metrics/src/main/java/org/apache/cloudstack/api/ListVMsMetricsCmd.java @@ -17,10 +17,12 @@ package org.apache.cloudstack.api; +import java.util.EnumSet; import java.util.List; import javax.inject.Inject; +import com.cloud.exception.InvalidParameterValueException; import org.apache.cloudstack.acl.RoleType; import org.apache.cloudstack.api.command.user.UserCmd; import org.apache.cloudstack.api.command.user.vm.ListVMsCmd; @@ -42,18 +44,21 @@ import org.apache.cloudstack.response.VmMetricsResponse; * although most of it is not suitable/useful for the API purpose. * */ -@APICommand(name = ListVMsMetricsCmd.APINAME, description = "Lists VM metrics", responseObject = VmMetricsResponse.class, +@APICommand(name = "listVirtualMachinesMetrics", description = "Lists VM metrics", responseObject = VmMetricsResponse.class, requestHasSensitiveInfo = false, responseHasSensitiveInfo = false, responseView = ResponseObject.ResponseView.Restricted, since = "4.9.3", authorized = {RoleType.ResourceAdmin, RoleType.DomainAdmin, RoleType.User}) public class ListVMsMetricsCmd extends ListVMsCmd implements UserCmd { - public static final String APINAME = "listVirtualMachinesMetrics"; @Inject private MetricsService metricsService; @Override - public String getCommandName() { - return APINAME.toLowerCase() + BaseCmd.RESPONSE_SUFFIX; + public EnumSet getDetails() throws InvalidParameterValueException { + if (isViewDetailsEmpty()) { + return EnumSet.of(ApiConstants.VMDetails.all); + } + + return super.getDetails(); } @Override diff --git a/plugins/metrics/src/main/java/org/apache/cloudstack/metrics/MetricsServiceImpl.java b/plugins/metrics/src/main/java/org/apache/cloudstack/metrics/MetricsServiceImpl.java index 136976cafc2..86bf2e3e351 100644 --- a/plugins/metrics/src/main/java/org/apache/cloudstack/metrics/MetricsServiceImpl.java +++ b/plugins/metrics/src/main/java/org/apache/cloudstack/metrics/MetricsServiceImpl.java @@ -619,7 +619,6 @@ public class MetricsServiceImpl extends MutualExclusiveIdsManagerBase implements } metricsResponse.setHasAnnotation(vmResponse.hasAnnotation()); - metricsResponse.setIpAddress(vmResponse.getNics()); metricsResponse.setCpuTotal(vmResponse.getCpuNumber(), vmResponse.getCpuSpeed()); metricsResponse.setMemTotal(vmResponse.getMemory()); metricsResponse.setNetworkRead(vmResponse.getNetworkKbsRead()); diff --git a/plugins/metrics/src/main/java/org/apache/cloudstack/response/VmMetricsResponse.java b/plugins/metrics/src/main/java/org/apache/cloudstack/response/VmMetricsResponse.java index fb5e8e5e104..7e5db7939db 100644 --- a/plugins/metrics/src/main/java/org/apache/cloudstack/response/VmMetricsResponse.java +++ b/plugins/metrics/src/main/java/org/apache/cloudstack/response/VmMetricsResponse.java @@ -17,19 +17,13 @@ package org.apache.cloudstack.response; -import java.util.Set; - import org.apache.cloudstack.api.ApiConstants; -import org.apache.cloudstack.api.response.NicResponse; import org.apache.cloudstack.api.response.UserVmResponse; import com.cloud.serializer.Param; import com.google.gson.annotations.SerializedName; public class VmMetricsResponse extends UserVmResponse { - @SerializedName(ApiConstants.IP_ADDRESS) - @Param(description = "the VM's primary IP address") - private String ipAddress; @SerializedName("cputotal") @Param(description = "the total cpu capacity in Ghz") @@ -59,11 +53,6 @@ public class VmMetricsResponse extends UserVmResponse { @Param(description = "the total disk iops") private Long diskIopsTotal; - public void setIpAddress(final Set nics) { - if (nics != null && nics.size() > 0) { - this.ipAddress = nics.iterator().next().getIpaddress(); - } - } public void setCpuTotal(final Integer cpuNumber, final Integer cpuSpeed) { if (cpuNumber != null && cpuSpeed != null) { diff --git a/server/src/main/java/com/cloud/api/query/QueryManagerImpl.java b/server/src/main/java/com/cloud/api/query/QueryManagerImpl.java index 5248a5535a5..dc591e8f30c 100644 --- a/server/src/main/java/com/cloud/api/query/QueryManagerImpl.java +++ b/server/src/main/java/com/cloud/api/query/QueryManagerImpl.java @@ -5686,6 +5686,6 @@ public class QueryManagerImpl extends MutualExclusiveIdsManagerBase implements Q @Override public ConfigKey[] getConfigKeys() { return new ConfigKey[] {AllowUserViewDestroyedVM, UserVMDeniedDetails, UserVMReadOnlyDetails, SortKeyAscending, - AllowUserViewAllDomainAccounts, SharePublicTemplatesWithOtherDomains}; + AllowUserViewAllDomainAccounts, SharePublicTemplatesWithOtherDomains, ReturnVmStatsOnVmList}; } } diff --git a/server/src/main/java/com/cloud/api/query/ViewResponseHelper.java b/server/src/main/java/com/cloud/api/query/ViewResponseHelper.java index 623ba436d0e..934de8a2558 100644 --- a/server/src/main/java/com/cloud/api/query/ViewResponseHelper.java +++ b/server/src/main/java/com/cloud/api/query/ViewResponseHelper.java @@ -166,6 +166,7 @@ public class ViewResponseHelper { // update nics, securitygroups, tags, affinitygroups for 1 to many mapping fields userVmData = ApiDBUtils.fillVmDetails(view, userVmData, userVm); } + userVmData.setIpAddress(userVmData.getNics()); vmDataList.put(userVm.getId(), userVmData); } return new ArrayList(vmDataList.values()); diff --git a/ui/src/config/router.js b/ui/src/config/router.js index c3b69bde23a..ef9ae6c1130 100644 --- a/ui/src/config/router.js +++ b/ui/src/config/router.js @@ -75,6 +75,7 @@ function generateRouterMap (section) { icon: child.icon, docHelp: vueProps.$applyDocHelpMappings(child.docHelp), permission: child.permission, + getApiToCall: child.getApiToCall, resourceType: child.resourceType, filters: child.filters, params: child.params ? child.params : {}, diff --git a/ui/src/config/section/compute.js b/ui/src/config/section/compute.js index 969402a694c..4893cfc1053 100644 --- a/ui/src/config/section/compute.js +++ b/ui/src/config/section/compute.js @@ -28,7 +28,8 @@ export default { title: 'label.instances', icon: 'cloud-server-outlined', docHelp: 'adminguide/virtual_machines.html', - permission: ['listVirtualMachinesMetrics'], + permission: ['listVirtualMachines', 'listVirtualMachinesMetrics'], + getApiToCall: () => store.getters.metrics ? 'listVirtualMachinesMetrics' : 'listVirtualMachines', resourceType: 'UserVm', params: () => { var params = { details: 'servoff,tmpl,iso,nics,backoff' } diff --git a/ui/src/views/AutogenView.vue b/ui/src/views/AutogenView.vue index 6fa8a3e0848..f9c4edd36b0 100644 --- a/ui/src/views/AutogenView.vue +++ b/ui/src/views/AutogenView.vue @@ -819,7 +819,7 @@ export default { } if (this.$route && this.$route.meta && this.$route.meta.permission) { - this.apiName = this.$route.meta.permission[0] + this.apiName = (this.$route.meta.getApiToCall && this.$route.meta.getApiToCall()) || this.$route.meta.permission[0] if (this.$route.meta.columns) { const columns = this.$route.meta.columns if (columns && typeof columns === 'function') { From 0c615312f95aa3c0a23ad5baa7e3ad2b308133d7 Mon Sep 17 00:00:00 2001 From: dahn Date: Thu, 6 Jun 2024 09:11:11 +0200 Subject: [PATCH 008/350] log messages (#9093) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * log messages * Apply suggestions from code review Co-authored-by: João Jandre <48719461+JoaoJandre@users.noreply.github.com> * Update plugins/integrations/kubernetes-service/src/main/java/com/cloud/kubernetes/cluster/actionworkers/KubernetesClusterResourceModifierActionWorker.java Co-authored-by: Fabricio Duarte --------- Co-authored-by: João Jandre <48719461+JoaoJandre@users.noreply.github.com> Co-authored-by: Fabricio Duarte --- .../com/cloud/storage/resource/VmwareStorageProcessor.java | 2 +- .../kubernetes/cluster/KubernetesClusterManagerImpl.java | 4 +--- .../KubernetesClusterResourceModifierActionWorker.java | 4 +--- .../cluster/actionworkers/KubernetesClusterStartWorker.java | 2 +- .../kubernetes/cluster/utils/KubernetesClusterUtil.java | 6 ++---- 5 files changed, 6 insertions(+), 12 deletions(-) diff --git a/plugins/hypervisors/vmware/src/main/java/com/cloud/storage/resource/VmwareStorageProcessor.java b/plugins/hypervisors/vmware/src/main/java/com/cloud/storage/resource/VmwareStorageProcessor.java index d81fd028b10..7be0dd5862f 100644 --- a/plugins/hypervisors/vmware/src/main/java/com/cloud/storage/resource/VmwareStorageProcessor.java +++ b/plugins/hypervisors/vmware/src/main/java/com/cloud/storage/resource/VmwareStorageProcessor.java @@ -948,7 +948,7 @@ public class VmwareStorageProcessor implements StorageProcessor { dsMo.moveDatastoreFile(vmwareLayoutFilePair[i], dcMo.getMor(), dsMo.getMor(), legacyCloudStackLayoutFilePair[i], dcMo.getMor(), true); } - logger.info("detach disks from volume-wrapper VM and destroy" + vmdkName); + logger.info("detach disks from volume-wrapper VM and destroy {}", vmdkName); vmMo.detachAllDisksAndDestroy(); String srcFile = dsMo.getDatastorePath(vmdkName, true); diff --git a/plugins/integrations/kubernetes-service/src/main/java/com/cloud/kubernetes/cluster/KubernetesClusterManagerImpl.java b/plugins/integrations/kubernetes-service/src/main/java/com/cloud/kubernetes/cluster/KubernetesClusterManagerImpl.java index 834d6d33330..d9350a6b1be 100644 --- a/plugins/integrations/kubernetes-service/src/main/java/com/cloud/kubernetes/cluster/KubernetesClusterManagerImpl.java +++ b/plugins/integrations/kubernetes-service/src/main/java/com/cloud/kubernetes/cluster/KubernetesClusterManagerImpl.java @@ -515,9 +515,7 @@ public class KubernetesClusterManagerImpl extends ManagerBase implements Kuberne logger.debug(String.format("Checking host ID: %s for capacity already reserved %d", hostVO.getUuid(), reserved)); } if (capacityManager.checkIfHostHasCapacity(hostVO.getId(), cpu_requested * reserved, ram_requested * reserved, false, cpuOvercommitRatio, memoryOvercommitRatio, true)) { - if (logger.isDebugEnabled()) { - logger.debug(String.format("Found host ID: %s for with enough capacity, CPU=%d RAM=%s", hostVO.getUuid(), cpu_requested * reserved, toHumanReadableSize(ram_requested * reserved))); - } + logger.debug("Found host ID == '{}' to have enough capacity, CPU={} RAM={}", hostVO.getUuid(), cpu_requested * reserved, toHumanReadableSize(ram_requested * reserved)); hostEntry.setValue(new Pair(hostVO, reserved)); suitable_host_found = true; planCluster = cluster; diff --git a/plugins/integrations/kubernetes-service/src/main/java/com/cloud/kubernetes/cluster/actionworkers/KubernetesClusterResourceModifierActionWorker.java b/plugins/integrations/kubernetes-service/src/main/java/com/cloud/kubernetes/cluster/actionworkers/KubernetesClusterResourceModifierActionWorker.java index e8bc8e2851e..6604cda0adf 100644 --- a/plugins/integrations/kubernetes-service/src/main/java/com/cloud/kubernetes/cluster/actionworkers/KubernetesClusterResourceModifierActionWorker.java +++ b/plugins/integrations/kubernetes-service/src/main/java/com/cloud/kubernetes/cluster/actionworkers/KubernetesClusterResourceModifierActionWorker.java @@ -263,9 +263,7 @@ public class KubernetesClusterResourceModifierActionWorker extends KubernetesClu logger.debug(String.format("Checking host : %s for capacity already reserved %d", h.getName(), reserved)); } if (capacityManager.checkIfHostHasCapacity(h.getId(), cpu_requested * reserved, ram_requested * reserved, false, cpuOvercommitRatio, memoryOvercommitRatio, true)) { - if (logger.isDebugEnabled()) { - logger.debug(String.format("Found host : %s for with enough capacity, CPU=%d RAM=%s", h.getName(), cpu_requested * reserved, toHumanReadableSize(ram_requested * reserved))); - } + logger.debug("Found host {} with enough capacity: CPU={} RAM={}", h.getName(), cpu_requested * reserved, toHumanReadableSize(ram_requested * reserved)); hostEntry.setValue(new Pair(h, reserved)); suitable_host_found = true; break; diff --git a/plugins/integrations/kubernetes-service/src/main/java/com/cloud/kubernetes/cluster/actionworkers/KubernetesClusterStartWorker.java b/plugins/integrations/kubernetes-service/src/main/java/com/cloud/kubernetes/cluster/actionworkers/KubernetesClusterStartWorker.java index a7cea8093c8..b2fbbd31f6b 100644 --- a/plugins/integrations/kubernetes-service/src/main/java/com/cloud/kubernetes/cluster/actionworkers/KubernetesClusterStartWorker.java +++ b/plugins/integrations/kubernetes-service/src/main/java/com/cloud/kubernetes/cluster/actionworkers/KubernetesClusterStartWorker.java @@ -533,7 +533,7 @@ public class KubernetesClusterStartWorker extends KubernetesClusterResourceModif } attachIsoKubernetesVMs(clusterVMs); if (!KubernetesClusterUtil.isKubernetesClusterControlVmRunning(kubernetesCluster, publicIpAddress, publicIpSshPort.second(), startTimeoutTime)) { - String msg = String.format("Failed to setup Kubernetes cluster : %s in usable state as unable to access control node VMs of the cluster", kubernetesCluster.getName()); + String msg = String.format("Failed to setup Kubernetes cluster : %s is not in usable state as the system is unable to access control node VMs of the cluster", kubernetesCluster.getName()); if (kubernetesCluster.getControlNodeCount() > 1 && Network.GuestType.Shared.equals(network.getGuestType())) { msg = String.format("%s. Make sure external load-balancer has port forwarding rules for SSH access on ports %d-%d and API access on port %d", msg, diff --git a/plugins/integrations/kubernetes-service/src/main/java/com/cloud/kubernetes/cluster/utils/KubernetesClusterUtil.java b/plugins/integrations/kubernetes-service/src/main/java/com/cloud/kubernetes/cluster/utils/KubernetesClusterUtil.java index 77f785d2747..7a3268014fd 100644 --- a/plugins/integrations/kubernetes-service/src/main/java/com/cloud/kubernetes/cluster/utils/KubernetesClusterUtil.java +++ b/plugins/integrations/kubernetes-service/src/main/java/com/cloud/kubernetes/cluster/utils/KubernetesClusterUtil.java @@ -266,13 +266,11 @@ public class KubernetesClusterUtil { socket.connect(new InetSocketAddress(ipAddress, port), 10000); controlVmRunning = true; } catch (IOException e) { - if (LOGGER.isInfoEnabled()) { - LOGGER.info(String.format("Waiting for Kubernetes cluster : %s control node VMs to be accessible", kubernetesCluster.getName())); - } + LOGGER.info("Waiting for Kubernetes cluster : {} control node VMs to be accessible", kubernetesCluster.getName()); try { Thread.sleep(10000); } catch (InterruptedException ex) { - LOGGER.warn(String.format("Error while waiting for Kubernetes cluster : %s control node VMs to be accessible", kubernetesCluster.getName()), ex); + LOGGER.warn("Error while waiting for Kubernetes cluster : {} control node VMs to be accessible", kubernetesCluster.getName(), ex); } } } From 2339412f73465aa2664d929bc4bdbdf629a87c22 Mon Sep 17 00:00:00 2001 From: Rene Peinthor Date: Thu, 6 Jun 2024 09:49:03 +0200 Subject: [PATCH 009/350] linstor: make getDevicePath more robust (#9143) --- .../kvm/storage/LinstorStorageAdaptor.java | 24 ++++--------- .../LinstorPrimaryDataStoreDriverImpl.java | 27 ++------------- .../storage/datastore/util/LinstorUtil.java | 34 +++++++++++++++++++ 3 files changed, 43 insertions(+), 42 deletions(-) diff --git a/plugins/storage/volume/linstor/src/main/java/com/cloud/hypervisor/kvm/storage/LinstorStorageAdaptor.java b/plugins/storage/volume/linstor/src/main/java/com/cloud/hypervisor/kvm/storage/LinstorStorageAdaptor.java index 632b92c80fd..a3c283699a2 100644 --- a/plugins/storage/volume/linstor/src/main/java/com/cloud/hypervisor/kvm/storage/LinstorStorageAdaptor.java +++ b/plugins/storage/volume/linstor/src/main/java/com/cloud/hypervisor/kvm/storage/LinstorStorageAdaptor.java @@ -157,24 +157,12 @@ public class LinstorStorageAdaptor implements StorageAdaptor { List volumeDefs = api.volumeDefinitionList(rscName, null, null); final long size = volumeDefs.isEmpty() ? 0 : volumeDefs.get(0).getSizeKib() * 1024; - List resources = api.viewResources( - Collections.emptyList(), - Collections.singletonList(rscName), - Collections.emptyList(), - null, - null, - null); - if (!resources.isEmpty() && !resources.get(0).getVolumes().isEmpty()) { - final String devPath = resources.get(0).getVolumes().get(0).getDevicePath(); - final KVMPhysicalDisk kvmDisk = new KVMPhysicalDisk(devPath, name, pool); - kvmDisk.setFormat(QemuImg.PhysicalDiskFormat.RAW); - kvmDisk.setSize(size); - kvmDisk.setVirtualSize(size); - return kvmDisk; - } else { - s_logger.error("Linstor: viewResources didn't return resources or volumes for " + rscName); - throw new CloudRuntimeException("Linstor: viewResources didn't return resources or volumes."); - } + final String devicePath = LinstorUtil.getDevicePath(api, rscName); + final KVMPhysicalDisk kvmDisk = new KVMPhysicalDisk(devicePath, name, pool); + kvmDisk.setFormat(QemuImg.PhysicalDiskFormat.RAW); + kvmDisk.setSize(size); + kvmDisk.setVirtualSize(size); + return kvmDisk; } catch (ApiException apiEx) { s_logger.error(apiEx); throw new CloudRuntimeException(apiEx.getBestMessage(), apiEx); diff --git a/plugins/storage/volume/linstor/src/main/java/org/apache/cloudstack/storage/datastore/driver/LinstorPrimaryDataStoreDriverImpl.java b/plugins/storage/volume/linstor/src/main/java/org/apache/cloudstack/storage/datastore/driver/LinstorPrimaryDataStoreDriverImpl.java index d2d13eafc48..8b9b768d2a4 100644 --- a/plugins/storage/volume/linstor/src/main/java/org/apache/cloudstack/storage/datastore/driver/LinstorPrimaryDataStoreDriverImpl.java +++ b/plugins/storage/volume/linstor/src/main/java/org/apache/cloudstack/storage/datastore/driver/LinstorPrimaryDataStoreDriverImpl.java @@ -28,7 +28,6 @@ import com.linbit.linstor.api.model.ResourceDefinitionCloneStarted; import com.linbit.linstor.api.model.ResourceDefinitionCreate; import com.linbit.linstor.api.model.ResourceDefinitionModify; import com.linbit.linstor.api.model.ResourceGroupSpawn; -import com.linbit.linstor.api.model.ResourceWithVolumes; import com.linbit.linstor.api.model.Snapshot; import com.linbit.linstor.api.model.SnapshotRestore; import com.linbit.linstor.api.model.VolumeDefinition; @@ -38,7 +37,6 @@ import javax.annotation.Nonnull; import javax.inject.Inject; import java.util.Arrays; -import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Map; @@ -315,25 +313,6 @@ public class LinstorPrimaryDataStoreDriverImpl implements PrimaryDataStoreDriver return answers.stream().filter(ApiCallRc::isError).findFirst().map(ApiCallRc::getMessage).orElse(null); } - private String getDeviceName(DevelopersApi linstorApi, String rscName) throws ApiException { - List resources = linstorApi.viewResources( - Collections.emptyList(), - Collections.singletonList(rscName), - Collections.emptyList(), - null, - null, - null); - if (!resources.isEmpty() && !resources.get(0).getVolumes().isEmpty()) - { - s_logger.info("Linstor: Created drbd device: " + resources.get(0).getVolumes().get(0).getDevicePath()); - return resources.get(0).getVolumes().get(0).getDevicePath(); - } else - { - s_logger.error("Linstor: viewResources didn't return resources or volumes."); - throw new CloudRuntimeException("Linstor: viewResources didn't return resources or volumes."); - } - } - private void applyQoSSettings(StoragePoolVO storagePool, DevelopersApi api, String rscName, Long maxIops) throws ApiException { @@ -420,7 +399,7 @@ public class LinstorPrimaryDataStoreDriverImpl implements PrimaryDataStoreDriver applyAuxProps(linstorApi, rscName, vol.getName(), vol.getAttachedVmName()); applyQoSSettings(storagePoolVO, linstorApi, rscName, vol.getMaxIops()); - return getDeviceName(linstorApi, rscName); + return LinstorUtil.getDevicePath(linstorApi, rscName); } catch (ApiException apiEx) { s_logger.error("Linstor: ApiEx - " + apiEx.getMessage()); @@ -473,7 +452,7 @@ public class LinstorPrimaryDataStoreDriverImpl implements PrimaryDataStoreDriver applyAuxProps(linstorApi, rscName, volumeInfo.getName(), volumeInfo.getAttachedVmName()); applyQoSSettings(storagePoolVO, linstorApi, rscName, volumeInfo.getMaxIops()); - return getDeviceName(linstorApi, rscName); + return LinstorUtil.getDevicePath(linstorApi, rscName); } catch (ApiException apiEx) { s_logger.error("Linstor: ApiEx - " + apiEx.getMessage()); throw new CloudRuntimeException(apiEx.getBestMessage(), apiEx); @@ -520,7 +499,7 @@ public class LinstorPrimaryDataStoreDriverImpl implements PrimaryDataStoreDriver applyAuxProps(linstorApi, rscName, volumeVO.getName(), null); applyQoSSettings(storagePoolVO, linstorApi, rscName, volumeVO.getMaxIops()); - return getDeviceName(linstorApi, rscName); + return LinstorUtil.getDevicePath(linstorApi, rscName); } catch (ApiException apiEx) { s_logger.error("Linstor: ApiEx - " + apiEx.getMessage()); throw new CloudRuntimeException(apiEx.getBestMessage(), apiEx); diff --git a/plugins/storage/volume/linstor/src/main/java/org/apache/cloudstack/storage/datastore/util/LinstorUtil.java b/plugins/storage/volume/linstor/src/main/java/org/apache/cloudstack/storage/datastore/util/LinstorUtil.java index c8544fd3e3e..a23a26bd9dc 100644 --- a/plugins/storage/volume/linstor/src/main/java/org/apache/cloudstack/storage/datastore/util/LinstorUtil.java +++ b/plugins/storage/volume/linstor/src/main/java/org/apache/cloudstack/storage/datastore/util/LinstorUtil.java @@ -25,6 +25,7 @@ import com.linbit.linstor.api.model.ApiCallRcList; import com.linbit.linstor.api.model.ProviderKind; import com.linbit.linstor.api.model.Resource; import com.linbit.linstor.api.model.ResourceGroup; +import com.linbit.linstor.api.model.ResourceWithVolumes; import com.linbit.linstor.api.model.StoragePool; import java.util.Collections; @@ -109,4 +110,37 @@ public class LinstorUtil { s_logger.error("isResourceInUse: null returned from resourceList"); return false; } + + /** + * Try to get the device path for the given resource name. + * This could be made a bit more direct after java-linstor api is fixed for layer data subtypes. + * @param api developer api object to use + * @param rscName resource name to get the device path + * @return The device path of the resource. + * @throws ApiException if Linstor API call failed. + * @throws CloudRuntimeException if no device path could be found. + */ + public static String getDevicePath(DevelopersApi api, String rscName) throws ApiException, CloudRuntimeException { + List resources = api.viewResources( + Collections.emptyList(), + Collections.singletonList(rscName), + Collections.emptyList(), + null, + null, + null); + for (ResourceWithVolumes rsc : resources) { + if (!rsc.getVolumes().isEmpty()) { + // CloudStack resource always only have 1 volume + String devicePath = rsc.getVolumes().get(0).getDevicePath(); + if (devicePath != null && !devicePath.isEmpty()) { + s_logger.debug(String.format("getDevicePath: %s -> %s", rscName, devicePath)); + return devicePath; + } + } + } + + final String errMsg = "viewResources didn't return resources or volumes for " + rscName; + s_logger.error(errMsg); + throw new CloudRuntimeException("Linstor: " + errMsg); + } } From 91c7bc722f21625a4879aea1dffcd293712cbbad Mon Sep 17 00:00:00 2001 From: Abhishek Kumar Date: Thu, 6 Jun 2024 14:24:02 +0530 Subject: [PATCH 010/350] server,cks: check if vm is cks node during vm destroy (#9057) Signed-off-by: Abhishek Kumar --- .../cluster/KubernetesClusterHelper.java | 5 +- .../main/java/com/cloud/uservm/UserVm.java | 2 + .../src/main/java/com/cloud/vm/UserVmVO.java | 1 + .../cluster/KubernetesClusterHelperImpl.java | 35 ++++++++- .../dao/KubernetesClusterVmMapDao.java | 2 + .../dao/KubernetesClusterVmMapDaoImpl.java | 10 +++ .../KubernetesClusterHelperImplTest.java | 71 +++++++++++++++++++ .../java/com/cloud/vm/UserVmManagerImpl.java | 19 ++++- utils/pom.xml | 4 ++ .../utils/component/ComponentContext.java | 19 +++++ 10 files changed, 163 insertions(+), 5 deletions(-) create mode 100644 plugins/integrations/kubernetes-service/src/test/java/com/cloud/kubernetes/cluster/KubernetesClusterHelperImplTest.java diff --git a/api/src/main/java/com/cloud/kubernetes/cluster/KubernetesClusterHelper.java b/api/src/main/java/com/cloud/kubernetes/cluster/KubernetesClusterHelper.java index e445e50f82c..e67e5277cee 100644 --- a/api/src/main/java/com/cloud/kubernetes/cluster/KubernetesClusterHelper.java +++ b/api/src/main/java/com/cloud/kubernetes/cluster/KubernetesClusterHelper.java @@ -16,10 +16,13 @@ // under the License. package com.cloud.kubernetes.cluster; -import com.cloud.utils.component.Adapter; import org.apache.cloudstack.acl.ControlledEntity; +import com.cloud.uservm.UserVm; +import com.cloud.utils.component.Adapter; + public interface KubernetesClusterHelper extends Adapter { ControlledEntity findByUuid(String uuid); + void checkVmCanBeDestroyed(UserVm userVm); } diff --git a/api/src/main/java/com/cloud/uservm/UserVm.java b/api/src/main/java/com/cloud/uservm/UserVm.java index e30f5e03054..9035d2903c9 100644 --- a/api/src/main/java/com/cloud/uservm/UserVm.java +++ b/api/src/main/java/com/cloud/uservm/UserVm.java @@ -48,4 +48,6 @@ public interface UserVm extends VirtualMachine, ControlledEntity { void setAccountId(long accountId); public boolean isDisplayVm(); + + String getUserVmType(); } diff --git a/engine/schema/src/main/java/com/cloud/vm/UserVmVO.java b/engine/schema/src/main/java/com/cloud/vm/UserVmVO.java index ce0bd2d5717..ce3a9a84a34 100644 --- a/engine/schema/src/main/java/com/cloud/vm/UserVmVO.java +++ b/engine/schema/src/main/java/com/cloud/vm/UserVmVO.java @@ -148,6 +148,7 @@ public class UserVmVO extends VMInstanceVO implements UserVm { return updateParameters; } + @Override public String getUserVmType() { return userVmType; } diff --git a/plugins/integrations/kubernetes-service/src/main/java/com/cloud/kubernetes/cluster/KubernetesClusterHelperImpl.java b/plugins/integrations/kubernetes-service/src/main/java/com/cloud/kubernetes/cluster/KubernetesClusterHelperImpl.java index 0ef916ab959..74f97426d85 100644 --- a/plugins/integrations/kubernetes-service/src/main/java/com/cloud/kubernetes/cluster/KubernetesClusterHelperImpl.java +++ b/plugins/integrations/kubernetes-service/src/main/java/com/cloud/kubernetes/cluster/KubernetesClusterHelperImpl.java @@ -16,26 +16,55 @@ // under the License. package com.cloud.kubernetes.cluster; -import com.cloud.kubernetes.cluster.dao.KubernetesClusterDao; -import com.cloud.utils.component.AdapterBase; +import javax.inject.Inject; + import org.apache.cloudstack.acl.ControlledEntity; import org.apache.cloudstack.framework.config.ConfigKey; import org.apache.cloudstack.framework.config.Configurable; +import org.apache.log4j.Logger; import org.springframework.stereotype.Component; -import javax.inject.Inject; +import com.cloud.kubernetes.cluster.dao.KubernetesClusterDao; +import com.cloud.kubernetes.cluster.dao.KubernetesClusterVmMapDao; +import com.cloud.uservm.UserVm; +import com.cloud.utils.component.AdapterBase; +import com.cloud.utils.exception.CloudRuntimeException; +import com.cloud.vm.UserVmManager; @Component public class KubernetesClusterHelperImpl extends AdapterBase implements KubernetesClusterHelper, Configurable { + private static final Logger logger = Logger.getLogger(KubernetesClusterHelperImpl.class); @Inject private KubernetesClusterDao kubernetesClusterDao; + @Inject + private KubernetesClusterVmMapDao kubernetesClusterVmMapDao; @Override public ControlledEntity findByUuid(String uuid) { return kubernetesClusterDao.findByUuid(uuid); } + @Override + public void checkVmCanBeDestroyed(UserVm userVm) { + if (!UserVmManager.CKS_NODE.equals(userVm.getUserVmType())) { + return; + } + KubernetesClusterVmMapVO vmMapVO = kubernetesClusterVmMapDao.findByVmId(userVm.getId()); + if (vmMapVO == null) { + return; + } + logger.error(String.format("VM ID: %s is a part of Kubernetes cluster ID: %d", userVm.getId(), vmMapVO.getClusterId())); + KubernetesCluster kubernetesCluster = kubernetesClusterDao.findById(vmMapVO.getClusterId()); + String msg = "Instance is a part of a Kubernetes cluster"; + if (kubernetesCluster != null) { + msg += String.format(": %s", kubernetesCluster.getName()); + } + msg += ". Use Instance delete option from Kubernetes cluster details or scale API for " + + "Kubernetes clusters with 'nodeids' to destroy the instance."; + throw new CloudRuntimeException(msg); + } + @Override public String getConfigComponentName() { return KubernetesClusterHelper.class.getSimpleName(); diff --git a/plugins/integrations/kubernetes-service/src/main/java/com/cloud/kubernetes/cluster/dao/KubernetesClusterVmMapDao.java b/plugins/integrations/kubernetes-service/src/main/java/com/cloud/kubernetes/cluster/dao/KubernetesClusterVmMapDao.java index 688a611ac99..abb7c3b5d01 100644 --- a/plugins/integrations/kubernetes-service/src/main/java/com/cloud/kubernetes/cluster/dao/KubernetesClusterVmMapDao.java +++ b/plugins/integrations/kubernetes-service/src/main/java/com/cloud/kubernetes/cluster/dao/KubernetesClusterVmMapDao.java @@ -28,4 +28,6 @@ public interface KubernetesClusterVmMapDao extends GenericDao vmIds); public int removeByClusterId(long clusterId); + + KubernetesClusterVmMapVO findByVmId(long vmId); } diff --git a/plugins/integrations/kubernetes-service/src/main/java/com/cloud/kubernetes/cluster/dao/KubernetesClusterVmMapDaoImpl.java b/plugins/integrations/kubernetes-service/src/main/java/com/cloud/kubernetes/cluster/dao/KubernetesClusterVmMapDaoImpl.java index b9f2ec917b2..3625873d029 100644 --- a/plugins/integrations/kubernetes-service/src/main/java/com/cloud/kubernetes/cluster/dao/KubernetesClusterVmMapDaoImpl.java +++ b/plugins/integrations/kubernetes-service/src/main/java/com/cloud/kubernetes/cluster/dao/KubernetesClusterVmMapDaoImpl.java @@ -69,4 +69,14 @@ public class KubernetesClusterVmMapDaoImpl extends GenericDaoBase sb = createSearchBuilder(); + sb.and("vmId", sb.entity().getVmId(), SearchCriteria.Op.EQ); + sb.done(); + SearchCriteria sc = sb.create(); + sc.setParameters("vmId", vmId); + return findOneBy(sc); + } } diff --git a/plugins/integrations/kubernetes-service/src/test/java/com/cloud/kubernetes/cluster/KubernetesClusterHelperImplTest.java b/plugins/integrations/kubernetes-service/src/test/java/com/cloud/kubernetes/cluster/KubernetesClusterHelperImplTest.java new file mode 100644 index 00000000000..167ade67cfb --- /dev/null +++ b/plugins/integrations/kubernetes-service/src/test/java/com/cloud/kubernetes/cluster/KubernetesClusterHelperImplTest.java @@ -0,0 +1,71 @@ +// 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.kubernetes.cluster; + + +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.Mockito; +import org.mockito.junit.MockitoJUnitRunner; + +import com.cloud.kubernetes.cluster.dao.KubernetesClusterDao; +import com.cloud.kubernetes.cluster.dao.KubernetesClusterVmMapDao; +import com.cloud.uservm.UserVm; +import com.cloud.utils.exception.CloudRuntimeException; +import com.cloud.vm.UserVmManager; + +@RunWith(MockitoJUnitRunner.class) +public class KubernetesClusterHelperImplTest { + @Mock + KubernetesClusterVmMapDao kubernetesClusterVmMapDao; + @Mock + KubernetesClusterDao kubernetesClusterDao; + + @InjectMocks + KubernetesClusterHelperImpl kubernetesClusterHelper = new KubernetesClusterHelperImpl(); + + @Test + public void testCheckVmCanBeDestroyedNotCKSNode() { + UserVm vm = Mockito.mock(UserVm.class); + Mockito.when(vm.getUserVmType()).thenReturn(""); + kubernetesClusterHelper.checkVmCanBeDestroyed(vm); + Mockito.verify(kubernetesClusterVmMapDao, Mockito.never()).findByVmId(Mockito.anyLong()); + } + + @Test + public void testCheckVmCanBeDestroyedNotInCluster() { + UserVm vm = Mockito.mock(UserVm.class); + Mockito.when(vm.getId()).thenReturn(1L); + Mockito.when(vm.getUserVmType()).thenReturn(UserVmManager.CKS_NODE); + Mockito.when(kubernetesClusterVmMapDao.findByVmId(1L)).thenReturn(null); + kubernetesClusterHelper.checkVmCanBeDestroyed(vm); + } + + @Test(expected = CloudRuntimeException.class) + public void testCheckVmCanBeDestroyedInCluster() { + UserVm vm = Mockito.mock(UserVm.class); + Mockito.when(vm.getId()).thenReturn(1L); + Mockito.when(vm.getUserVmType()).thenReturn(UserVmManager.CKS_NODE); + KubernetesClusterVmMapVO map = Mockito.mock(KubernetesClusterVmMapVO.class); + Mockito.when(map.getClusterId()).thenReturn(1L); + Mockito.when(kubernetesClusterVmMapDao.findByVmId(1L)).thenReturn(map); + Mockito.when(kubernetesClusterDao.findById(1L)).thenReturn(Mockito.mock(KubernetesClusterVO.class)); + kubernetesClusterHelper.checkVmCanBeDestroyed(vm); + } +} diff --git a/server/src/main/java/com/cloud/vm/UserVmManagerImpl.java b/server/src/main/java/com/cloud/vm/UserVmManagerImpl.java index 3c172051463..d9484761998 100644 --- a/server/src/main/java/com/cloud/vm/UserVmManagerImpl.java +++ b/server/src/main/java/com/cloud/vm/UserVmManagerImpl.java @@ -130,8 +130,8 @@ import org.apache.cloudstack.storage.template.VnfTemplateManager; import org.apache.cloudstack.userdata.UserDataManager; import org.apache.cloudstack.utils.bytescale.ByteScaleUtils; import org.apache.cloudstack.utils.security.ParserUtils; -import org.apache.cloudstack.vm.schedule.VMScheduleManager; import org.apache.cloudstack.vm.UnmanagedVMsManager; +import org.apache.cloudstack.vm.schedule.VMScheduleManager; import org.apache.commons.codec.binary.Base64; import org.apache.commons.collections.CollectionUtils; import org.apache.commons.collections.MapUtils; @@ -141,6 +141,7 @@ import org.apache.commons.lang3.builder.ToStringBuilder; import org.apache.commons.lang3.builder.ToStringStyle; import org.apache.log4j.Logger; import org.jetbrains.annotations.NotNull; +import org.springframework.beans.factory.NoSuchBeanDefinitionException; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Qualifier; import org.w3c.dom.Document; @@ -241,6 +242,7 @@ import com.cloud.host.dao.HostDao; import com.cloud.hypervisor.Hypervisor.HypervisorType; import com.cloud.hypervisor.dao.HypervisorCapabilitiesDao; import com.cloud.hypervisor.kvm.dpdk.DpdkHelper; +import com.cloud.kubernetes.cluster.KubernetesClusterHelper; import com.cloud.network.IpAddressManager; import com.cloud.network.Network; import com.cloud.network.Network.GuestType; @@ -346,6 +348,7 @@ import com.cloud.utils.DateUtil; import com.cloud.utils.Journal; import com.cloud.utils.NumbersUtil; import com.cloud.utils.Pair; +import com.cloud.utils.component.ComponentContext; import com.cloud.utils.component.ManagerBase; import com.cloud.utils.concurrency.NamedThreadFactory; import com.cloud.utils.crypt.DBEncryptionUtil; @@ -595,6 +598,7 @@ public class UserVmManagerImpl extends ManagerBase implements UserVmManager, Vir @Inject VMScheduleManager vmScheduleManager; + private ScheduledExecutorService _executor = null; private ScheduledExecutorService _vmIpFetchExecutor = null; private int _expungeInterval; @@ -3280,6 +3284,16 @@ public class UserVmManagerImpl extends ManagerBase implements UserVmManager, Vir return null; } + protected void checkPluginsIfVmCanBeDestroyed(UserVm vm) { + try { + KubernetesClusterHelper kubernetesClusterHelper = + ComponentContext.getDelegateComponentOfType(KubernetesClusterHelper.class); + kubernetesClusterHelper.checkVmCanBeDestroyed(vm); + } catch (NoSuchBeanDefinitionException ignored) { + s_logger.debug("No KubernetesClusterHelper bean found"); + } + } + @Override @ActionEvent(eventType = EventTypes.EVENT_VM_DESTROY, eventDescription = "destroying Vm", async = true) public UserVm destroyVm(DestroyVMCmd cmd) throws ResourceUnavailableException, ConcurrentOperationException { @@ -3306,6 +3320,9 @@ public class UserVmManagerImpl extends ManagerBase implements UserVmManager, Vir // check if vm belongs to AutoScale vm group in Disabled state autoScaleManager.checkIfVmActionAllowed(vmId); + // check if vm belongs to any plugin resources + checkPluginsIfVmCanBeDestroyed(vm); + // check if there are active volume snapshots tasks s_logger.debug("Checking if there are any ongoing snapshots on the ROOT volumes associated with VM with ID " + vmId); if (checkStatusOfVolumeSnapshots(vmId, Volume.Type.ROOT)) { diff --git a/utils/pom.xml b/utils/pom.xml index d52903a1df9..bf0d2bc346b 100755 --- a/utils/pom.xml +++ b/utils/pom.xml @@ -226,6 +226,10 @@ tink ${cs.tink.version} + + org.apache.commons + commons-collections4 + diff --git a/utils/src/main/java/com/cloud/utils/component/ComponentContext.java b/utils/src/main/java/com/cloud/utils/component/ComponentContext.java index 8486dbf4bd4..0a086809a9d 100644 --- a/utils/src/main/java/com/cloud/utils/component/ComponentContext.java +++ b/utils/src/main/java/com/cloud/utils/component/ComponentContext.java @@ -29,6 +29,7 @@ import javax.management.MalformedObjectNameException; import javax.management.NotCompliantMBeanException; import javax.naming.ConfigurationException; +import org.apache.commons.collections4.MapUtils; import org.apache.log4j.Logger; import org.springframework.aop.framework.Advised; import org.springframework.beans.BeansException; @@ -286,4 +287,22 @@ public class ComponentContext implements ApplicationContextAware { private static synchronized void initInitializeBeans(boolean initializeBeans) { s_initializeBeans = initializeBeans; } + + public static T getDelegateComponentOfType(Class beanType) { + if (s_appContextDelegates == null) { + throw new NoSuchBeanDefinitionException(beanType.getName()); + } + T bean = null; + for (ApplicationContext context : s_appContextDelegates.values()) { + Map map = context.getBeansOfType(beanType); + if (MapUtils.isNotEmpty(map)) { + bean = (T)map.values().toArray()[0]; + break; + } + } + if (bean == null) { + throw new NoSuchBeanDefinitionException(beanType.getName()); + } + return bean; + } } From 81cdf3cfbfe17a216a6619a85c26dad7e89971f8 Mon Sep 17 00:00:00 2001 From: Henrique Sato Date: Fri, 7 Jun 2024 04:55:54 -0300 Subject: [PATCH 011/350] Fix instance & backup resource table columns (#9147) Co-authored-by: Henrique Sato --- ui/src/components/view/ListResourceTable.vue | 25 ++++++++++---------- 1 file changed, 12 insertions(+), 13 deletions(-) diff --git a/ui/src/components/view/ListResourceTable.vue b/ui/src/components/view/ListResourceTable.vue index 16c1b388c5d..a7e805b5443 100644 --- a/ui/src/components/view/ListResourceTable.vue +++ b/ui/src/components/view/ListResourceTable.vue @@ -41,21 +41,20 @@ + + + + + +
- - - - - - -
From 2542582c1e022a74453cc5fdd9989cdd33abdce1 Mon Sep 17 00:00:00 2001 From: Daan Hoogland Date: Fri, 7 Jun 2024 11:31:36 +0200 Subject: [PATCH 012/350] logging migration in merge missing --- .../kubernetes/cluster/KubernetesClusterHelperImpl.java | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/plugins/integrations/kubernetes-service/src/main/java/com/cloud/kubernetes/cluster/KubernetesClusterHelperImpl.java b/plugins/integrations/kubernetes-service/src/main/java/com/cloud/kubernetes/cluster/KubernetesClusterHelperImpl.java index 846337c3d39..9d8bb48dc94 100644 --- a/plugins/integrations/kubernetes-service/src/main/java/com/cloud/kubernetes/cluster/KubernetesClusterHelperImpl.java +++ b/plugins/integrations/kubernetes-service/src/main/java/com/cloud/kubernetes/cluster/KubernetesClusterHelperImpl.java @@ -28,14 +28,15 @@ import javax.inject.Inject; import org.apache.cloudstack.acl.ControlledEntity; import org.apache.cloudstack.framework.config.ConfigKey; import org.apache.cloudstack.framework.config.Configurable; -import org.apache.log4j.Logger; import org.springframework.stereotype.Component; import java.util.Objects; +import org.apache.logging.log4j.Logger; +import org.apache.logging.log4j.LogManager; @Component public class KubernetesClusterHelperImpl extends AdapterBase implements KubernetesClusterHelper, Configurable { - private static final Logger logger = Logger.getLogger(KubernetesClusterHelperImpl.class); + private static final Logger logger = LogManager.getLogger(KubernetesClusterHelperImpl.class); @Inject private KubernetesClusterDao kubernetesClusterDao; From be552fdce9c1f1766c16b5fcb0ddcfac528f90c0 Mon Sep 17 00:00:00 2001 From: Abhishek Kumar Date: Mon, 10 Jun 2024 10:40:12 +0530 Subject: [PATCH 013/350] feature: webhooks (#8674) * api,server,ui: weebhoks feature Signed-off-by: Abhishek Kumar * fix Signed-off-by: Abhishek Kumar * fix Signed-off-by: Abhishek Kumar * changes Signed-off-by: Abhishek Kumar * registry of message busses * test bus Signed-off-by: Abhishek Kumar * refactor Signed-off-by: Abhishek Kumar * test Signed-off-by: Abhishek Kumar * fix and refactor Signed-off-by: Abhishek Kumar * changes for webhook dispatch history Signed-off-by: Abhishek Kumar * changes, initial ui Signed-off-by: Abhishek Kumar * improvements Signed-off-by: Abhishek Kumar * changes for account webhook cleanup Signed-off-by: Abhishek Kumar * fix remaining event bus usage Signed-off-by: Abhishek Kumar * changes for testing webhook dispatch Signed-off-by: Abhishek Kumar * wip Signed-off-by: Abhishek Kumar * fix test Signed-off-by: Abhishek Kumar * make element Signed-off-by: Abhishek Kumar * missing Signed-off-by: Abhishek Kumar * buid fix * fix lint Signed-off-by: Abhishek Kumar * changes for project delete check Signed-off-by: Abhishek Kumar * fix Signed-off-by: Abhishek Kumar * add collapse in create Signed-off-by: Abhishek Kumar * ui fix and refactor for eventditributor publish Signed-off-by: Abhishek Kumar * update org.json and add json validation Signed-off-by: Abhishek Kumar * schema fixes Signed-off-by: Abhishek Kumar * wordings Signed-off-by: Abhishek Kumar * ui: improve progress button Signed-off-by: Abhishek Kumar * ui improvements Signed-off-by: Abhishek Kumar * remove unrelated change Signed-off-by: Abhishek Kumar * search and count Signed-off-by: Abhishek Kumar * add payloadurl in info Signed-off-by: Abhishek Kumar * positive progress Signed-off-by: Abhishek Kumar * fix hmac key Signed-off-by: Abhishek Kumar * create webhook form fixes Signed-off-by: Abhishek Kumar * refactor, address feedback Signed-off-by: Abhishek Kumar * indentation Signed-off-by: Abhishek Kumar * fix filters Signed-off-by: Abhishek Kumar * remove test eventbus Signed-off-by: Abhishek Kumar * default scope be Local Signed-off-by: Abhishek Kumar * add lifecycle smoke test Signed-off-by: Abhishek Kumar * add test for webhook deliveries Signed-off-by: Abhishek Kumar * refactor Signed-off-by: Abhishek Kumar * fix lint Signed-off-by: Abhishek Kumar * refactor - losgs and others Signed-off-by: Abhishek Kumar * unit tests Signed-off-by: Abhishek Kumar * fix lint Signed-off-by: Abhishek Kumar * build fix Signed-off-by: Abhishek Kumar * smoke test fix, log refactor Signed-off-by: Abhishek Kumar * get bean from all components Signed-off-by: Abhishek Kumar * ui: missing label Signed-off-by: Abhishek Kumar * address review comments Signed-off-by: Abhishek Kumar * add some more tests Signed-off-by: Abhishek Kumar * lint Signed-off-by: Abhishek Kumar * rename setting Signed-off-by: Abhishek Kumar * upgrade: move 4.19.0->4.20.0 to 4.19.1->4.20.0 * fix test delivery layout Signed-off-by: Abhishek Kumar * fix webhook secret display Signed-off-by: Abhishek Kumar * add http to payloadurl when no scheme Signed-off-by: Abhishek Kumar * allow removing secretkey for webhook Signed-off-by: Abhishek Kumar * fix update sslverification Signed-off-by: Abhishek Kumar * disallow same payload url for same account Signed-off-by: Abhishek Kumar * fix delivery with url w/o scheme Signed-off-by: Abhishek Kumar * api: listApis should return params based on caller Signed-off-by: Abhishek Kumar * wip changes Signed-off-by: Abhishek Kumar * Update engine/schema/src/main/resources/META-INF/db/schema-41900to42000.sql * remove unique constraint for now Constraint is present in Java code validations Signed-off-by: Abhishek Kumar * fixes Signed-off-by: Abhishek Kumar * ui: add option to delete multiple deliveries Signed-off-by: Abhishek Kumar * add filter for deliveries, delete api start/endtime support Signed-off-by: Abhishek Kumar * do not throw error when no deliveries removed Signed-off-by: Abhishek Kumar * ui: fix deliveries table column sorting, time filter cancel Signed-off-by: Abhishek Kumar * remove isDebugEnabled wrapping * merge fix Signed-off-by: Abhishek Kumar --------- Signed-off-by: Abhishek Kumar Co-authored-by: Daan Hoogland Co-authored-by: Wei Zhou --- .github/workflows/ci.yml | 3 +- .../apache/cloudstack/api/ApiConstants.java | 23 +- .../api/command/admin/user/UpdateUserCmd.java | 2 +- .../api/response/BucketResponse.java | 2 +- client/pom.xml | 5 + .../spring-core-registry-core-context.xml | 9 +- .../cloudstack/event/module.properties | 21 + ...re-lifecycle-event-context-inheritable.xml | 31 + .../java/com/cloud/event/UsageEventUtils.java | 22 +- .../cloud/network/NetworkStateListener.java | 32 +- .../network/NetworkStateListenerTest.java | 40 ++ .../META-INF/db/schema-41910to42000.sql | 42 +- .../db/views/cloud.webhook_delivery_view.sql | 48 ++ .../META-INF/db/views/cloud.webhook_view.sql | 52 ++ .../cloudstack/framework/events/Event.java | 63 +- .../cloudstack/framework/events/EventBus.java | 2 + .../framework/events/EventDistributor.java | 35 ++ .../events/EventDistributorImpl.java | 68 +++ .../events/EventDistributorImplTest.java | 67 ++ .../api/response/ApiDiscoveryResponse.java | 25 +- .../api/response/ApiParameterResponse.java | 13 +- .../discovery/ApiDiscoveryServiceImpl.java | 38 +- .../mom/inmemory/InMemoryEventBus.java | 5 + .../cloudstack/mom/kafka/KafkaEventBus.java | 8 +- .../mom/rabbitmq/RabbitMQEventBus.java | 5 +- plugins/event-bus/webhook/pom.xml | 46 ++ .../cloudstack/mom/webhook/Webhook.java | 48 ++ .../mom/webhook/WebhookApiService.java | 44 ++ .../mom/webhook/WebhookApiServiceImpl.java | 574 ++++++++++++++++++ .../mom/webhook/WebhookDelivery.java | 39 ++ .../mom/webhook/WebhookDeliveryThread.java | 287 +++++++++ .../mom/webhook/WebhookEventBus.java | 88 +++ .../mom/webhook/WebhookService.java | 63 ++ .../mom/webhook/WebhookServiceImpl.java | 354 +++++++++++ .../api/command/user/CreateWebhookCmd.java | 167 +++++ .../api/command/user/DeleteWebhookCmd.java | 84 +++ .../user/DeleteWebhookDeliveryCmd.java | 126 ++++ .../user/ExecuteWebhookDeliveryCmd.java | 132 ++++ .../user/ListWebhookDeliveriesCmd.java | 125 ++++ .../api/command/user/ListWebhooksCmd.java | 95 +++ .../api/command/user/UpdateWebhookCmd.java | 136 +++++ .../api/response/WebhookDeliveryResponse.java | 136 +++++ .../webhook/api/response/WebhookResponse.java | 149 +++++ .../mom/webhook/dao/WebhookDao.java | 31 + .../mom/webhook/dao/WebhookDaoImpl.java | 99 +++ .../mom/webhook/dao/WebhookDeliveryDao.java | 29 + .../webhook/dao/WebhookDeliveryDaoImpl.java | 73 +++ .../webhook/dao/WebhookDeliveryJoinDao.java | 33 + .../dao/WebhookDeliveryJoinDaoImpl.java | 71 +++ .../mom/webhook/dao/WebhookJoinDao.java | 28 + .../mom/webhook/dao/WebhookJoinDaoImpl.java | 45 ++ .../mom/webhook/vo/WebhookDeliveryJoinVO.java | 182 ++++++ .../mom/webhook/vo/WebhookDeliveryVO.java | 174 ++++++ .../mom/webhook/vo/WebhookJoinVO.java | 234 +++++++ .../cloudstack/mom/webhook/vo/WebhookVO.java | 232 +++++++ .../cloudstack/webhook/module.properties | 18 + .../webhook/spring-event-webhook-context.xml | 41 ++ .../webhook/WebhookApiServiceImplTest.java | 253 ++++++++ .../webhook/WebhookDeliveryThreadTest.java | 62 ++ .../mom/webhook/WebhookEventBusTest.java | 106 ++++ .../command/user/CreateWebhookCmdTest.java | 173 ++++++ .../command/user/DeleteWebhookCmdTest.java | 106 ++++ .../user/DeleteWebhookDeliveryCmdTest.java | 108 ++++ .../user/ExecuteWebhookDeliveryCmdTest.java | 153 +++++ .../user/ListWebhookDeliveriesCmdTest.java | 141 +++++ .../api/command/user/ListWebhooksCmdTest.java | 105 ++++ .../command/user/UpdateWebhookCmdTest.java | 163 +++++ .../contrail/management/EventUtils.java | 39 +- plugins/pom.xml | 1 + .../main/java/com/cloud/api/ApiServer.java | 47 +- .../com/cloud/event/ActionEventUtils.java | 63 +- .../java/com/cloud/event/AlertGenerator.java | 20 +- .../cloud/projects/ProjectManagerImpl.java | 19 +- .../listener/SnapshotStateListener.java | 26 +- .../storage/listener/VolumeStateListener.java | 43 +- .../com/cloud/user/AccountManagerImpl.java | 15 + .../com/cloud/vm/UserVmStateListener.java | 31 +- .../cloudstack/webhook/WebhookHelper.java | 28 + .../spring-server-core-managers-context.xml | 4 + .../com/cloud/event/ActionEventUtilsTest.java | 20 +- .../projects/ProjectManagerImplTest.java | 40 +- .../HypervisorTemplateAdapterTest.java | 100 ++- .../cloud/user/AccountManagerImplTest.java | 84 ++- .../smoke/test_webhook_delivery.py | 212 +++++++ .../smoke/test_webhook_lifecycle.py | 392 ++++++++++++ tools/apidoc/gen_toc.py | 4 +- tools/marvin/marvin/lib/base.py | 62 ++ ui/public/locales/en.json | 40 ++ .../FilterStats.vue => DateTimeFilter.vue} | 14 +- ui/src/components/view/DetailsTab.vue | 20 +- ui/src/components/view/InfoCard.vue | 22 + ui/src/components/view/ListView.vue | 61 +- ui/src/components/view/SearchView.vue | 100 ++- ui/src/components/view/StatsTab.vue | 7 +- .../view/TestWebhookDeliveryView.vue | 278 +++++++++ .../components/view/WebhookDeliveriesTab.vue | 526 ++++++++++++++++ ui/src/config/section/tools.js | 149 +++++ ui/src/core/lazy_lib/icons_use.js | 6 + ui/src/views/AutogenView.vue | 9 +- ui/src/views/tools/CreateWebhook.vue | 357 +++++++++++ ui/src/views/tools/TestWebhookDelivery.vue | 202 ++++++ .../utils/component/ComponentContext.java | 13 +- 102 files changed, 8739 insertions(+), 329 deletions(-) create mode 100644 core/src/main/resources/META-INF/cloudstack/event/module.properties create mode 100644 core/src/main/resources/META-INF/cloudstack/event/spring-core-lifecycle-event-context-inheritable.xml create mode 100644 engine/components-api/src/test/java/com/cloud/network/NetworkStateListenerTest.java create mode 100644 engine/schema/src/main/resources/META-INF/db/views/cloud.webhook_delivery_view.sql create mode 100644 engine/schema/src/main/resources/META-INF/db/views/cloud.webhook_view.sql create mode 100644 framework/events/src/main/java/org/apache/cloudstack/framework/events/EventDistributor.java create mode 100644 framework/events/src/main/java/org/apache/cloudstack/framework/events/EventDistributorImpl.java create mode 100644 framework/events/src/test/java/org/apache/cloudstack/framework/events/EventDistributorImplTest.java create mode 100644 plugins/event-bus/webhook/pom.xml create mode 100644 plugins/event-bus/webhook/src/main/java/org/apache/cloudstack/mom/webhook/Webhook.java create mode 100644 plugins/event-bus/webhook/src/main/java/org/apache/cloudstack/mom/webhook/WebhookApiService.java create mode 100644 plugins/event-bus/webhook/src/main/java/org/apache/cloudstack/mom/webhook/WebhookApiServiceImpl.java create mode 100644 plugins/event-bus/webhook/src/main/java/org/apache/cloudstack/mom/webhook/WebhookDelivery.java create mode 100644 plugins/event-bus/webhook/src/main/java/org/apache/cloudstack/mom/webhook/WebhookDeliveryThread.java create mode 100644 plugins/event-bus/webhook/src/main/java/org/apache/cloudstack/mom/webhook/WebhookEventBus.java create mode 100644 plugins/event-bus/webhook/src/main/java/org/apache/cloudstack/mom/webhook/WebhookService.java create mode 100644 plugins/event-bus/webhook/src/main/java/org/apache/cloudstack/mom/webhook/WebhookServiceImpl.java create mode 100644 plugins/event-bus/webhook/src/main/java/org/apache/cloudstack/mom/webhook/api/command/user/CreateWebhookCmd.java create mode 100644 plugins/event-bus/webhook/src/main/java/org/apache/cloudstack/mom/webhook/api/command/user/DeleteWebhookCmd.java create mode 100644 plugins/event-bus/webhook/src/main/java/org/apache/cloudstack/mom/webhook/api/command/user/DeleteWebhookDeliveryCmd.java create mode 100644 plugins/event-bus/webhook/src/main/java/org/apache/cloudstack/mom/webhook/api/command/user/ExecuteWebhookDeliveryCmd.java create mode 100644 plugins/event-bus/webhook/src/main/java/org/apache/cloudstack/mom/webhook/api/command/user/ListWebhookDeliveriesCmd.java create mode 100644 plugins/event-bus/webhook/src/main/java/org/apache/cloudstack/mom/webhook/api/command/user/ListWebhooksCmd.java create mode 100644 plugins/event-bus/webhook/src/main/java/org/apache/cloudstack/mom/webhook/api/command/user/UpdateWebhookCmd.java create mode 100644 plugins/event-bus/webhook/src/main/java/org/apache/cloudstack/mom/webhook/api/response/WebhookDeliveryResponse.java create mode 100644 plugins/event-bus/webhook/src/main/java/org/apache/cloudstack/mom/webhook/api/response/WebhookResponse.java create mode 100644 plugins/event-bus/webhook/src/main/java/org/apache/cloudstack/mom/webhook/dao/WebhookDao.java create mode 100644 plugins/event-bus/webhook/src/main/java/org/apache/cloudstack/mom/webhook/dao/WebhookDaoImpl.java create mode 100644 plugins/event-bus/webhook/src/main/java/org/apache/cloudstack/mom/webhook/dao/WebhookDeliveryDao.java create mode 100644 plugins/event-bus/webhook/src/main/java/org/apache/cloudstack/mom/webhook/dao/WebhookDeliveryDaoImpl.java create mode 100644 plugins/event-bus/webhook/src/main/java/org/apache/cloudstack/mom/webhook/dao/WebhookDeliveryJoinDao.java create mode 100644 plugins/event-bus/webhook/src/main/java/org/apache/cloudstack/mom/webhook/dao/WebhookDeliveryJoinDaoImpl.java create mode 100644 plugins/event-bus/webhook/src/main/java/org/apache/cloudstack/mom/webhook/dao/WebhookJoinDao.java create mode 100644 plugins/event-bus/webhook/src/main/java/org/apache/cloudstack/mom/webhook/dao/WebhookJoinDaoImpl.java create mode 100644 plugins/event-bus/webhook/src/main/java/org/apache/cloudstack/mom/webhook/vo/WebhookDeliveryJoinVO.java create mode 100644 plugins/event-bus/webhook/src/main/java/org/apache/cloudstack/mom/webhook/vo/WebhookDeliveryVO.java create mode 100644 plugins/event-bus/webhook/src/main/java/org/apache/cloudstack/mom/webhook/vo/WebhookJoinVO.java create mode 100644 plugins/event-bus/webhook/src/main/java/org/apache/cloudstack/mom/webhook/vo/WebhookVO.java create mode 100644 plugins/event-bus/webhook/src/main/resources/META-INF/cloudstack/webhook/module.properties create mode 100644 plugins/event-bus/webhook/src/main/resources/META-INF/cloudstack/webhook/spring-event-webhook-context.xml create mode 100644 plugins/event-bus/webhook/src/test/java/org/apache/cloudstack/mom/webhook/WebhookApiServiceImplTest.java create mode 100644 plugins/event-bus/webhook/src/test/java/org/apache/cloudstack/mom/webhook/WebhookDeliveryThreadTest.java create mode 100644 plugins/event-bus/webhook/src/test/java/org/apache/cloudstack/mom/webhook/WebhookEventBusTest.java create mode 100644 plugins/event-bus/webhook/src/test/java/org/apache/cloudstack/mom/webhook/api/command/user/CreateWebhookCmdTest.java create mode 100644 plugins/event-bus/webhook/src/test/java/org/apache/cloudstack/mom/webhook/api/command/user/DeleteWebhookCmdTest.java create mode 100644 plugins/event-bus/webhook/src/test/java/org/apache/cloudstack/mom/webhook/api/command/user/DeleteWebhookDeliveryCmdTest.java create mode 100644 plugins/event-bus/webhook/src/test/java/org/apache/cloudstack/mom/webhook/api/command/user/ExecuteWebhookDeliveryCmdTest.java create mode 100644 plugins/event-bus/webhook/src/test/java/org/apache/cloudstack/mom/webhook/api/command/user/ListWebhookDeliveriesCmdTest.java create mode 100644 plugins/event-bus/webhook/src/test/java/org/apache/cloudstack/mom/webhook/api/command/user/ListWebhooksCmdTest.java create mode 100644 plugins/event-bus/webhook/src/test/java/org/apache/cloudstack/mom/webhook/api/command/user/UpdateWebhookCmdTest.java create mode 100644 server/src/main/java/org/apache/cloudstack/webhook/WebhookHelper.java create mode 100644 test/integration/smoke/test_webhook_delivery.py create mode 100644 test/integration/smoke/test_webhook_lifecycle.py rename ui/src/components/view/{stats/FilterStats.vue => DateTimeFilter.vue} (93%) create mode 100644 ui/src/components/view/TestWebhookDeliveryView.vue create mode 100644 ui/src/components/view/WebhookDeliveriesTab.vue create mode 100644 ui/src/views/tools/CreateWebhook.vue create mode 100644 ui/src/views/tools/TestWebhookDelivery.vue diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index fac2d6266fa..51c0b006c4a 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -86,7 +86,8 @@ jobs: smoke/test_migration smoke/test_multipleips_per_nic smoke/test_nested_virtualization - smoke/test_set_sourcenat", + smoke/test_set_sourcenat + smoke/test_webhook_lifecycle", "smoke/test_network smoke/test_network_acl smoke/test_network_ipv6 diff --git a/api/src/main/java/org/apache/cloudstack/api/ApiConstants.java b/api/src/main/java/org/apache/cloudstack/api/ApiConstants.java index c5a059c39be..81217b8406a 100644 --- a/api/src/main/java/org/apache/cloudstack/api/ApiConstants.java +++ b/api/src/main/java/org/apache/cloudstack/api/ApiConstants.java @@ -175,6 +175,8 @@ public class ApiConstants { public static final String END_IPV6 = "endipv6"; public static final String END_PORT = "endport"; public static final String ENTRY_TIME = "entrytime"; + public static final String EVENT_ID = "eventid"; + public static final String EVENT_TYPE = "eventtype"; public static final String EXPIRES = "expires"; public static final String EXTRA_CONFIG = "extraconfig"; public static final String EXTRA_DHCP_OPTION = "extradhcpoption"; @@ -209,6 +211,7 @@ public class ApiConstants { public static final String HA_PROVIDER = "haprovider"; public static final String HA_STATE = "hastate"; public static final String HEALTH = "health"; + public static final String HEADERS = "headers"; public static final String HIDE_IP_ADDRESS_USAGE = "hideipaddressusage"; public static final String HOST_ID = "hostid"; public static final String HOST_IDS = "hostids"; @@ -281,6 +284,7 @@ public class ApiConstants { public static final String JOB_STATUS = "jobstatus"; public static final String KEEPALIVE_ENABLED = "keepaliveenabled"; public static final String KERNEL_VERSION = "kernelversion"; + public static final String KEY = "key"; public static final String LABEL = "label"; public static final String LASTNAME = "lastname"; public static final String LAST_BOOT = "lastboottime"; @@ -355,6 +359,7 @@ public class ApiConstants { public static final String SSHKEY_ENABLED = "sshkeyenabled"; public static final String PATH = "path"; public static final String PAYLOAD = "payload"; + public static final String PAYLOAD_URL = "payloadurl"; public static final String POD_ID = "podid"; public static final String POD_NAME = "podname"; public static final String POD_IDS = "podids"; @@ -400,11 +405,9 @@ public class ApiConstants { public static final String QUERY_FILTER = "queryfilter"; public static final String SCHEDULE = "schedule"; public static final String SCOPE = "scope"; - public static final String SECRET_KEY = "usersecretkey"; - public static final String SECONDARY_IP = "secondaryip"; - public static final String SINCE = "since"; - public static final String KEY = "key"; public static final String SEARCH_BASE = "searchbase"; + public static final String SECONDARY_IP = "secondaryip"; + public static final String SECRET_KEY = "secretkey"; public static final String SECURITY_GROUP_IDS = "securitygroupids"; public static final String SECURITY_GROUP_NAMES = "securitygroupnames"; public static final String SECURITY_GROUP_NAME = "securitygroupname"; @@ -422,6 +425,7 @@ public class ApiConstants { public static final String SHOW_UNIQUE = "showunique"; public static final String SIGNATURE = "signature"; public static final String SIGNATURE_VERSION = "signatureversion"; + public static final String SINCE = "since"; public static final String SIZE = "size"; public static final String SNAPSHOT = "snapshot"; public static final String SNAPSHOT_ID = "snapshotid"; @@ -429,8 +433,7 @@ public class ApiConstants { public static final String SNAPSHOT_TYPE = "snapshottype"; public static final String SNAPSHOT_QUIESCEVM = "quiescevm"; public static final String SOURCE_ZONE_ID = "sourcezoneid"; - public static final String SUITABLE_FOR_VM = "suitableforvirtualmachine"; - public static final String SUPPORTS_STORAGE_SNAPSHOT = "supportsstoragesnapshot"; + public static final String SSL_VERIFICATION = "sslverification"; public static final String START_DATE = "startdate"; public static final String START_ID = "startid"; public static final String START_IP = "startip"; @@ -449,6 +452,9 @@ public class ApiConstants { public static final String SYSTEM_VM_TYPE = "systemvmtype"; public static final String TAGS = "tags"; public static final String STORAGE_TAGS = "storagetags"; + public static final String SUCCESS = "success"; + public static final String SUITABLE_FOR_VM = "suitableforvirtualmachine"; + public static final String SUPPORTS_STORAGE_SNAPSHOT = "supportsstoragesnapshot"; public static final String TARGET_IQN = "targetiqn"; public static final String TEMPLATE_FILTER = "templatefilter"; public static final String TEMPLATE_ID = "templateid"; @@ -482,6 +488,7 @@ public class ApiConstants { public static final String USERNAME = "username"; public static final String USER_CONFIGURABLE = "userconfigurable"; public static final String USER_SECURITY_GROUP_LIST = "usersecuritygrouplist"; + public static final String USER_SECRET_KEY = "usersecretkey"; public static final String USE_VIRTUAL_NETWORK = "usevirtualnetwork"; public static final String UPDATE_IN_SEQUENCE = "updateinsequence"; public static final String VALUE = "value"; @@ -561,6 +568,7 @@ public class ApiConstants { public static final String ALLOCATION_STATE = "allocationstate"; public static final String MANAGED_STATE = "managedstate"; public static final String MANAGEMENT_SERVER_ID = "managementserverid"; + public static final String MANAGEMENT_SERVER_NAME = "managementservername"; public static final String STORAGE = "storage"; public static final String STORAGE_ID = "storageid"; public static final String PING_STORAGE_SERVER_IP = "pingstorageserverip"; @@ -1121,6 +1129,9 @@ public class ApiConstants { public static final String PARAMETER_DESCRIPTION_IS_TAG_A_RULE = "Whether the informed tag is a JS interpretable rule or not."; + public static final String WEBHOOK_ID = "webhookid"; + public static final String WEBHOOK_NAME = "webhookname"; + /** * This enum specifies IO Drivers, each option controls specific policies on I/O. * Qemu guests support "threads" and "native" options Since 0.8.8 ; "io_uring" is supported Since 6.3.0 (QEMU 5.0). diff --git a/api/src/main/java/org/apache/cloudstack/api/command/admin/user/UpdateUserCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/admin/user/UpdateUserCmd.java index 3f8d386d266..c9e1e934152 100644 --- a/api/src/main/java/org/apache/cloudstack/api/command/admin/user/UpdateUserCmd.java +++ b/api/src/main/java/org/apache/cloudstack/api/command/admin/user/UpdateUserCmd.java @@ -66,7 +66,7 @@ public class UpdateUserCmd extends BaseCmd { @Parameter(name = ApiConstants.CURRENT_PASSWORD, type = CommandType.STRING, description = "Current password that was being used by the user. You must inform the current password when updating the password.", acceptedOnAdminPort = false) private String currentPassword; - @Parameter(name = ApiConstants.SECRET_KEY, type = CommandType.STRING, description = "The secret key for the user. Must be specified with userApiKey") + @Parameter(name = ApiConstants.USER_SECRET_KEY, type = CommandType.STRING, description = "The secret key for the user. Must be specified with userApiKey") private String secretKey; @Parameter(name = ApiConstants.TIMEZONE, diff --git a/api/src/main/java/org/apache/cloudstack/api/response/BucketResponse.java b/api/src/main/java/org/apache/cloudstack/api/response/BucketResponse.java index b75f3604324..a1cce6e43d7 100644 --- a/api/src/main/java/org/apache/cloudstack/api/response/BucketResponse.java +++ b/api/src/main/java/org/apache/cloudstack/api/response/BucketResponse.java @@ -98,7 +98,7 @@ public class BucketResponse extends BaseResponseWithTagInformation implements Co @Param(description = "Bucket Access Key") private String accessKey; - @SerializedName(ApiConstants.SECRET_KEY) + @SerializedName(ApiConstants.USER_SECRET_KEY) @Param(description = "Bucket Secret Key") private String secretKey; diff --git a/client/pom.xml b/client/pom.xml index 1d11fa74650..23e0f1886bd 100644 --- a/client/pom.xml +++ b/client/pom.xml @@ -438,6 +438,11 @@ cloud-mom-kafka ${project.version} + + org.apache.cloudstack + cloud-mom-webhook + ${project.version} + org.apache.cloudstack cloud-framework-agent-lb diff --git a/core/src/main/resources/META-INF/cloudstack/core/spring-core-registry-core-context.xml b/core/src/main/resources/META-INF/cloudstack/core/spring-core-registry-core-context.xml index 49775fe41e1..2d6632def40 100644 --- a/core/src/main/resources/META-INF/cloudstack/core/spring-core-registry-core-context.xml +++ b/core/src/main/resources/META-INF/cloudstack/core/spring-core-registry-core-context.xml @@ -288,10 +288,10 @@ + class="org.apache.cloudstack.spring.lifecycle.registry.ExtensionRegistry"> - + @@ -358,4 +358,9 @@ + + + + diff --git a/core/src/main/resources/META-INF/cloudstack/event/module.properties b/core/src/main/resources/META-INF/cloudstack/event/module.properties new file mode 100644 index 00000000000..ab1f88e9844 --- /dev/null +++ b/core/src/main/resources/META-INF/cloudstack/event/module.properties @@ -0,0 +1,21 @@ +# +# 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. +# + +name=event +parent=core diff --git a/core/src/main/resources/META-INF/cloudstack/event/spring-core-lifecycle-event-context-inheritable.xml b/core/src/main/resources/META-INF/cloudstack/event/spring-core-lifecycle-event-context-inheritable.xml new file mode 100644 index 00000000000..63d11c65bac --- /dev/null +++ b/core/src/main/resources/META-INF/cloudstack/event/spring-core-lifecycle-event-context-inheritable.xml @@ -0,0 +1,31 @@ + + + + + + + + diff --git a/engine/components-api/src/main/java/com/cloud/event/UsageEventUtils.java b/engine/components-api/src/main/java/com/cloud/event/UsageEventUtils.java index 27f63c8c64b..51d0846fafb 100644 --- a/engine/components-api/src/main/java/com/cloud/event/UsageEventUtils.java +++ b/engine/components-api/src/main/java/com/cloud/event/UsageEventUtils.java @@ -25,15 +25,14 @@ import java.util.Map; import javax.annotation.PostConstruct; import javax.inject.Inject; -import org.apache.commons.collections.MapUtils; -import org.apache.logging.log4j.Logger; -import org.apache.logging.log4j.LogManager; -import org.springframework.beans.factory.NoSuchBeanDefinitionException; - import org.apache.cloudstack.framework.config.dao.ConfigurationDao; import org.apache.cloudstack.framework.events.Event; import org.apache.cloudstack.framework.events.EventBus; -import org.apache.cloudstack.framework.events.EventBusException; +import org.apache.cloudstack.framework.events.EventDistributor; +import org.apache.commons.collections.MapUtils; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; +import org.springframework.beans.factory.NoSuchBeanDefinitionException; import com.cloud.dc.DataCenterVO; import com.cloud.dc.dao.DataCenterDao; @@ -50,6 +49,7 @@ public class UsageEventUtils { protected static Logger LOGGER = LogManager.getLogger(UsageEventUtils.class); protected static EventBus s_eventBus = null; protected static ConfigurationDao s_configDao; + private static EventDistributor eventDistributor; @Inject UsageEventDao usageEventDao; @@ -207,9 +207,9 @@ public class UsageEventUtils { if( !configValue) return; try { - s_eventBus = ComponentContext.getComponent(EventBus.class); + eventDistributor = ComponentContext.getComponent(EventDistributor.class); } catch (NoSuchBeanDefinitionException nbe) { - return; // no provider is configured to provide events bus, so just return + return; // no provider is configured to provide events distributor, so just return } Account account = s_accountDao.findById(accountId); @@ -238,11 +238,7 @@ public class UsageEventUtils { event.setDescription(eventDescription); - try { - s_eventBus.publish(event); - } catch (EventBusException e) { - LOGGER.warn("Failed to publish usage event on the event bus."); - } + eventDistributor.publish(event); } static final String Name = "management-server"; diff --git a/engine/components-api/src/main/java/com/cloud/network/NetworkStateListener.java b/engine/components-api/src/main/java/com/cloud/network/NetworkStateListener.java index 24be76e4d3b..107e177ef57 100644 --- a/engine/components-api/src/main/java/com/cloud/network/NetworkStateListener.java +++ b/engine/components-api/src/main/java/com/cloud/network/NetworkStateListener.java @@ -25,11 +25,9 @@ import java.util.Map; import javax.inject.Inject; import org.apache.cloudstack.framework.config.dao.ConfigurationDao; -import org.apache.cloudstack.framework.events.EventBus; -import org.apache.cloudstack.framework.events.EventBusException; -import org.apache.logging.log4j.Logger; +import org.apache.cloudstack.framework.events.EventDistributor; import org.apache.logging.log4j.LogManager; -import org.springframework.beans.factory.NoSuchBeanDefinitionException; +import org.apache.logging.log4j.Logger; import com.cloud.event.EventCategory; import com.cloud.network.Network.Event; @@ -43,7 +41,7 @@ public class NetworkStateListener implements StateListener eventDescription = new HashMap(); + new org.apache.cloudstack.framework.events.Event("management-server", EventCategory.RESOURCE_STATE_CHANGE_EVENT.getName(), event, resourceName, vo.getUuid()); + Map eventDescription = new HashMap<>(); eventDescription.put("resource", resourceName); eventDescription.put("id", vo.getUuid()); eventDescription.put("old-state", oldState.name()); @@ -92,11 +91,8 @@ public class NetworkStateListener implements StateListener publish(Event event); +} diff --git a/framework/events/src/main/java/org/apache/cloudstack/framework/events/EventDistributorImpl.java b/framework/events/src/main/java/org/apache/cloudstack/framework/events/EventDistributorImpl.java new file mode 100644 index 00000000000..a67ff5cc926 --- /dev/null +++ b/framework/events/src/main/java/org/apache/cloudstack/framework/events/EventDistributorImpl.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 org.apache.cloudstack.framework.events; + +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import javax.annotation.PostConstruct; + +import org.apache.commons.lang3.StringUtils; + +import com.cloud.utils.component.ManagerBase; + +public class EventDistributorImpl extends ManagerBase implements EventDistributor { + + List eventBuses; + + public void setEventBuses(List eventBuses) { + this.eventBuses = eventBuses; + } + + @PostConstruct + public void init() { + logger.trace("Found {} event buses : {}", () -> eventBuses.size(), + () -> StringUtils.join(eventBuses.stream().map(x->x.getClass().getName()).toArray())); + } + + @Override + public Map publish(Event event) { + Map exceptions = new HashMap<>(); + if (event == null) { + return exceptions; + } + logger.trace("Publishing event [category: {}, type: {}]: {} to {} event buses", + event.getEventCategory(), event.getEventType(), + event.getDescription(), eventBuses.size()); + for (EventBus bus : eventBuses) { + try { + bus.publish(event); + } catch (EventBusException e) { + logger.warn("Failed to publish event [category: {}, type: {}] on bus {}", + event.getEventCategory(), event.getEventType(), bus.getName()); + logger.trace(event.getDescription()); + exceptions.put(bus.getName(), e); + } + } + return exceptions; + } + +} diff --git a/framework/events/src/test/java/org/apache/cloudstack/framework/events/EventDistributorImplTest.java b/framework/events/src/test/java/org/apache/cloudstack/framework/events/EventDistributorImplTest.java new file mode 100644 index 00000000000..8a8dd91b9d8 --- /dev/null +++ b/framework/events/src/test/java/org/apache/cloudstack/framework/events/EventDistributorImplTest.java @@ -0,0 +1,67 @@ +// 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.framework.events; + +import java.util.List; +import java.util.Map; + +import org.apache.commons.collections.MapUtils; +import org.junit.Assert; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.InjectMocks; +import org.mockito.Mockito; +import org.mockito.junit.MockitoJUnitRunner; +import org.springframework.test.util.ReflectionTestUtils; + +@RunWith(MockitoJUnitRunner.class) +public class EventDistributorImplTest { + + @InjectMocks + EventDistributorImpl eventDistributor = new EventDistributorImpl(); + + @Test + public void testSetEventBuses() { + Assert.assertNull(ReflectionTestUtils.getField(eventDistributor, "eventBuses")); + EventBus eventBus = Mockito.mock(EventBus.class); + EventBus eventBus1 = Mockito.mock(EventBus.class); + eventDistributor.setEventBuses(List.of(eventBus, eventBus1)); + Assert.assertNotNull(ReflectionTestUtils.getField(eventDistributor, "eventBuses")); + } + + @Test + public void testPublishNullEvent() { + Map exceptionMap = eventDistributor.publish(null); + Assert.assertTrue(MapUtils.isEmpty(exceptionMap)); + } + + @Test + public void testPublishOneReturnsException() throws EventBusException { + String busName = "Test"; + EventBus eventBus = Mockito.mock(EventBus.class); + Mockito.doReturn(busName).when(eventBus).getName(); + Mockito.doThrow(EventBusException.class).when(eventBus).publish(Mockito.any(Event.class)); + EventBus eventBus1 = Mockito.mock(EventBus.class); + Mockito.doNothing().when(eventBus1).publish(Mockito.any(Event.class)); + eventDistributor.eventBuses = List.of(eventBus, eventBus1); + Map exceptionMap = eventDistributor.publish(Mockito.mock(Event.class)); + Assert.assertTrue(MapUtils.isNotEmpty(exceptionMap)); + Assert.assertEquals(1, exceptionMap.size()); + Assert.assertTrue(exceptionMap.containsKey(busName)); + } +} diff --git a/plugins/api/discovery/src/main/java/org/apache/cloudstack/api/response/ApiDiscoveryResponse.java b/plugins/api/discovery/src/main/java/org/apache/cloudstack/api/response/ApiDiscoveryResponse.java index dccf5a68e11..81a9df750cb 100644 --- a/plugins/api/discovery/src/main/java/org/apache/cloudstack/api/response/ApiDiscoveryResponse.java +++ b/plugins/api/discovery/src/main/java/org/apache/cloudstack/api/response/ApiDiscoveryResponse.java @@ -16,13 +16,14 @@ // under the License. package org.apache.cloudstack.api.response; -import com.cloud.serializer.Param; -import com.google.gson.annotations.SerializedName; +import java.util.HashSet; +import java.util.Set; + import org.apache.cloudstack.api.ApiConstants; import org.apache.cloudstack.api.BaseResponse; -import java.util.HashSet; -import java.util.Set; +import com.cloud.serializer.Param; +import com.google.gson.annotations.SerializedName; @SuppressWarnings("unused") public class ApiDiscoveryResponse extends BaseResponse { @@ -64,6 +65,18 @@ public class ApiDiscoveryResponse extends BaseResponse { isAsync = false; } + public ApiDiscoveryResponse(ApiDiscoveryResponse another) { + this.name = another.getName(); + this.description = another.getDescription(); + this.since = another.getSince(); + this.isAsync = another.getAsync(); + this.related = another.getRelated(); + this.params = new HashSet<>(another.getParams()); + this.apiResponse = new HashSet<>(another.getApiResponse()); + this.type = another.getType(); + this.setObjectName(another.getObjectName()); + } + public void setName(String name) { this.name = name; } @@ -123,4 +136,8 @@ public class ApiDiscoveryResponse extends BaseResponse { public Set getApiResponse() { return apiResponse; } + + public String getType() { + return type; + } } diff --git a/plugins/api/discovery/src/main/java/org/apache/cloudstack/api/response/ApiParameterResponse.java b/plugins/api/discovery/src/main/java/org/apache/cloudstack/api/response/ApiParameterResponse.java index 7713f6b5d69..75f0aacd504 100644 --- a/plugins/api/discovery/src/main/java/org/apache/cloudstack/api/response/ApiParameterResponse.java +++ b/plugins/api/discovery/src/main/java/org/apache/cloudstack/api/response/ApiParameterResponse.java @@ -16,12 +16,14 @@ // under the License. package org.apache.cloudstack.api.response; -import com.google.gson.annotations.SerializedName; +import java.util.List; +import org.apache.cloudstack.acl.RoleType; import org.apache.cloudstack.api.ApiConstants; import org.apache.cloudstack.api.BaseResponse; import com.cloud.serializer.Param; +import com.google.gson.annotations.SerializedName; public class ApiParameterResponse extends BaseResponse { @SerializedName(ApiConstants.NAME) @@ -52,6 +54,8 @@ public class ApiParameterResponse extends BaseResponse { @Param(description = "comma separated related apis to get the parameter") private String related; + private transient List authorizedRoleTypes = null; + public ApiParameterResponse() { } @@ -87,4 +91,11 @@ public class ApiParameterResponse extends BaseResponse { this.related = related; } + public void setAuthorizedRoleTypes(List authorizedRoleTypes) { + this.authorizedRoleTypes = authorizedRoleTypes; + } + + public List getAuthorizedRoleTypes() { + return authorizedRoleTypes; + } } diff --git a/plugins/api/discovery/src/main/java/org/apache/cloudstack/discovery/ApiDiscoveryServiceImpl.java b/plugins/api/discovery/src/main/java/org/apache/cloudstack/discovery/ApiDiscoveryServiceImpl.java index 239bc49a65a..452b95cf2c0 100644 --- a/plugins/api/discovery/src/main/java/org/apache/cloudstack/discovery/ApiDiscoveryServiceImpl.java +++ b/plugins/api/discovery/src/main/java/org/apache/cloudstack/discovery/ApiDiscoveryServiceImpl.java @@ -18,8 +18,10 @@ package org.apache.cloudstack.discovery; import java.lang.reflect.Field; import java.util.ArrayList; +import java.util.Arrays; import java.util.HashMap; import java.util.HashSet; +import java.util.Iterator; import java.util.LinkedHashSet; import java.util.List; import java.util.Map; @@ -28,21 +30,22 @@ import java.util.Set; import javax.inject.Inject; import org.apache.cloudstack.acl.APIChecker; +import org.apache.cloudstack.acl.Role; +import org.apache.cloudstack.acl.RoleService; +import org.apache.cloudstack.acl.RoleType; import org.apache.cloudstack.api.APICommand; import org.apache.cloudstack.api.BaseAsyncCmd; import org.apache.cloudstack.api.BaseAsyncCreateCmd; import org.apache.cloudstack.api.BaseCmd; import org.apache.cloudstack.api.BaseResponse; import org.apache.cloudstack.api.Parameter; -import org.apache.cloudstack.acl.Role; -import org.apache.cloudstack.acl.RoleService; -import org.apache.cloudstack.acl.RoleType; import org.apache.cloudstack.api.command.user.discovery.ListApisCmd; import org.apache.cloudstack.api.response.ApiDiscoveryResponse; import org.apache.cloudstack.api.response.ApiParameterResponse; import org.apache.cloudstack.api.response.ApiResponseResponse; import org.apache.cloudstack.api.response.ListResponse; import org.apache.cloudstack.utils.reflectiontostringbuilderutils.ReflectionToStringBuilderUtils; +import org.apache.commons.collections.CollectionUtils; import org.apache.commons.lang3.StringUtils; import org.reflections.ReflectionUtils; import org.springframework.stereotype.Component; @@ -215,6 +218,9 @@ public class ApiDiscoveryServiceImpl extends ComponentLifecycleBase implements A paramResponse.setSince(parameterAnnotation.since()); } paramResponse.setRelated(parameterAnnotation.entityType()[0].getName()); + if (parameterAnnotation.authorized() != null) { + paramResponse.setAuthorizedRoleTypes(Arrays.asList(parameterAnnotation.authorized())); + } response.addParam(paramResponse); } } @@ -247,6 +253,7 @@ public class ApiDiscoveryServiceImpl extends ComponentLifecycleBase implements A if (user == null) return null; + Account account = accountService.getAccount(user.getAccountId()); if (name != null) { if (!s_apiNameDiscoveryResponseMap.containsKey(name)) @@ -260,10 +267,9 @@ public class ApiDiscoveryServiceImpl extends ComponentLifecycleBase implements A return null; } } - responseList.add(s_apiNameDiscoveryResponseMap.get(name)); + responseList.add(getApiDiscoveryResponseWithAccessibleParams(name, account)); } else { - Account account = accountService.getAccount(user.getAccountId()); if (account == null) { throw new PermissionDeniedException(String.format("The account with id [%s] for user [%s] is null.", user.getAccountId(), user)); } @@ -284,13 +290,33 @@ public class ApiDiscoveryServiceImpl extends ComponentLifecycleBase implements A } for (String apiName: apisAllowed) { - responseList.add(s_apiNameDiscoveryResponseMap.get(apiName)); + responseList.add(getApiDiscoveryResponseWithAccessibleParams(apiName, account)); } } response.setResponses(responseList); return response; } + private static ApiDiscoveryResponse getApiDiscoveryResponseWithAccessibleParams(String name, Account account) { + if (Account.Type.ADMIN.equals(account.getType())) { + return s_apiNameDiscoveryResponseMap.get(name); + } + ApiDiscoveryResponse apiDiscoveryResponse = + new ApiDiscoveryResponse(s_apiNameDiscoveryResponseMap.get(name)); + Iterator iterator = apiDiscoveryResponse.getParams().iterator(); + while (iterator.hasNext()) { + ApiParameterResponse parameterResponse = iterator.next(); + List authorizedRoleTypes = parameterResponse.getAuthorizedRoleTypes(); + RoleType accountRoleType = RoleType.getByAccountType(account.getType()); + if (CollectionUtils.isNotEmpty(parameterResponse.getAuthorizedRoleTypes()) && + accountRoleType != null && + !authorizedRoleTypes.contains(accountRoleType)) { + iterator.remove(); + } + } + return apiDiscoveryResponse; + } + @Override public List> getCommands() { List> cmdList = new ArrayList>(); diff --git a/plugins/event-bus/inmemory/src/main/java/org/apache/cloudstack/mom/inmemory/InMemoryEventBus.java b/plugins/event-bus/inmemory/src/main/java/org/apache/cloudstack/mom/inmemory/InMemoryEventBus.java index d5d36278192..0c00c0639fd 100644 --- a/plugins/event-bus/inmemory/src/main/java/org/apache/cloudstack/mom/inmemory/InMemoryEventBus.java +++ b/plugins/event-bus/inmemory/src/main/java/org/apache/cloudstack/mom/inmemory/InMemoryEventBus.java @@ -60,6 +60,8 @@ public class InMemoryEventBus extends ManagerBase implements EventBus { if (subscriber == null || topic == null) { throw new EventBusException("Invalid EventSubscriber/EventTopic object passed."); } + logger.debug("subscribing '{}' to events of type '{}' from '{}'", subscriber.toString(), topic.getEventType(), topic.getEventSource()); + UUID subscriberId = UUID.randomUUID(); subscribers.put(subscriberId, new Pair(topic, subscriber)); @@ -68,6 +70,7 @@ public class InMemoryEventBus extends ManagerBase implements EventBus { @Override public void unsubscribe(UUID subscriberId, EventSubscriber subscriber) throws EventBusException { + logger.debug("unsubscribing '{}'", subscriberId); if (subscriberId == null) { throw new EventBusException("Cannot unregister a null subscriberId."); } @@ -85,7 +88,9 @@ public class InMemoryEventBus extends ManagerBase implements EventBus { @Override public void publish(Event event) throws EventBusException { + logger.trace("publish '{}'", event.getDescription()); if (subscribers == null || subscribers.isEmpty()) { + logger.trace("no subscribers, no publish"); return; // no subscriber to publish to, so just return } diff --git a/plugins/event-bus/kafka/src/main/java/org/apache/cloudstack/mom/kafka/KafkaEventBus.java b/plugins/event-bus/kafka/src/main/java/org/apache/cloudstack/mom/kafka/KafkaEventBus.java index 01888779fc6..f2589d2d7d0 100644 --- a/plugins/event-bus/kafka/src/main/java/org/apache/cloudstack/mom/kafka/KafkaEventBus.java +++ b/plugins/event-bus/kafka/src/main/java/org/apache/cloudstack/mom/kafka/KafkaEventBus.java @@ -87,19 +87,23 @@ public class KafkaEventBus extends ManagerBase implements EventBus { @Override public UUID subscribe(EventTopic topic, EventSubscriber subscriber) throws EventBusException { + logger.debug("subscribing '{}' to events of type '{}' from '{}'", subscriber.toString(), topic.getEventType(), topic.getEventSource()); + /* NOOP */ return UUID.randomUUID(); } @Override public void unsubscribe(UUID subscriberId, EventSubscriber subscriber) throws EventBusException { + logger.debug("unsubscribing '{}'", subscriberId); /* NOOP */ } @Override public void publish(Event event) throws EventBusException { - ProducerRecord record = new ProducerRecord(_topic, event.getResourceUUID(), event.getDescription()); - _producer.send(record); + logger.trace("publish '{}'", event.getDescription()); + ProducerRecord newRecord = new ProducerRecord<>(_topic, event.getResourceUUID(), event.getDescription()); + _producer.send(newRecord); } @Override diff --git a/plugins/event-bus/rabbitmq/src/main/java/org/apache/cloudstack/mom/rabbitmq/RabbitMQEventBus.java b/plugins/event-bus/rabbitmq/src/main/java/org/apache/cloudstack/mom/rabbitmq/RabbitMQEventBus.java index 8cd2289f9f3..e8067e75b40 100644 --- a/plugins/event-bus/rabbitmq/src/main/java/org/apache/cloudstack/mom/rabbitmq/RabbitMQEventBus.java +++ b/plugins/event-bus/rabbitmq/src/main/java/org/apache/cloudstack/mom/rabbitmq/RabbitMQEventBus.java @@ -185,11 +185,12 @@ public class RabbitMQEventBus extends ManagerBase implements EventBus { */ @Override public UUID subscribe(EventTopic topic, EventSubscriber subscriber) throws EventBusException { - if (subscriber == null || topic == null) { throw new EventBusException("Invalid EventSubscriber/EventTopic object passed."); } + logger.debug("subscribing '{}' to events of type '{}' from '{}'", subscriber.toString(), topic.getEventType(), topic.getEventSource()); + // create a UUID, that will be used for managing subscriptions and also used as queue name // for on the queue used for the subscriber on the AMQP broker UUID queueId = UUID.randomUUID(); @@ -250,6 +251,7 @@ public class RabbitMQEventBus extends ManagerBase implements EventBus { @Override public void unsubscribe(UUID subscriberId, EventSubscriber subscriber) throws EventBusException { + logger.debug("unsubscribing '{}'", subscriberId); try { String classname = subscriber.getClass().getName(); String queueName = UUID.nameUUIDFromBytes(classname.getBytes()).toString(); @@ -265,6 +267,7 @@ public class RabbitMQEventBus extends ManagerBase implements EventBus { // publish event on to the exchange created on AMQP server @Override public void publish(Event event) throws EventBusException { + logger.trace("publish '{}'", event.getDescription()); String routingKey = createRoutingKey(event); String eventDescription = event.getDescription(); diff --git a/plugins/event-bus/webhook/pom.xml b/plugins/event-bus/webhook/pom.xml new file mode 100644 index 00000000000..278f4dc0ec5 --- /dev/null +++ b/plugins/event-bus/webhook/pom.xml @@ -0,0 +1,46 @@ + + + 4.0.0 + cloud-mom-webhook + Apache CloudStack Plugin - Webhook Event Bus + + org.apache.cloudstack + cloudstack-plugins + 4.20.0.0-SNAPSHOT + ../../pom.xml + + + + org.apache.cloudstack + cloud-framework-events + ${project.version} + + + org.apache.cloudstack + cloud-engine-api + ${project.version} + + + org.json + json + + + diff --git a/plugins/event-bus/webhook/src/main/java/org/apache/cloudstack/mom/webhook/Webhook.java b/plugins/event-bus/webhook/src/main/java/org/apache/cloudstack/mom/webhook/Webhook.java new file mode 100644 index 00000000000..1cc73ae31df --- /dev/null +++ b/plugins/event-bus/webhook/src/main/java/org/apache/cloudstack/mom/webhook/Webhook.java @@ -0,0 +1,48 @@ +// 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.mom.webhook; + +import java.util.Date; + +import org.apache.cloudstack.acl.ControlledEntity; +import org.apache.cloudstack.api.Identity; +import org.apache.cloudstack.api.InternalIdentity; + +public interface Webhook extends ControlledEntity, Identity, InternalIdentity { + public static final long ID_DUMMY = 0L; + public static final String NAME_DUMMY = "Test"; + enum State { + Enabled, Disabled; + }; + + enum Scope { + Local, Domain, Global; + }; + + long getId(); + String getName(); + String getDescription(); + State getState(); + long getDomainId(); + long getAccountId(); + String getPayloadUrl(); + String getSecretKey(); + boolean isSslVerification(); + Scope getScope(); + Date getCreated(); +} diff --git a/plugins/event-bus/webhook/src/main/java/org/apache/cloudstack/mom/webhook/WebhookApiService.java b/plugins/event-bus/webhook/src/main/java/org/apache/cloudstack/mom/webhook/WebhookApiService.java new file mode 100644 index 00000000000..edd77e5b414 --- /dev/null +++ b/plugins/event-bus/webhook/src/main/java/org/apache/cloudstack/mom/webhook/WebhookApiService.java @@ -0,0 +1,44 @@ +// 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.mom.webhook; + +import org.apache.cloudstack.api.response.ListResponse; +import org.apache.cloudstack.mom.webhook.api.command.user.CreateWebhookCmd; +import org.apache.cloudstack.mom.webhook.api.command.user.DeleteWebhookCmd; +import org.apache.cloudstack.mom.webhook.api.command.user.DeleteWebhookDeliveryCmd; +import org.apache.cloudstack.mom.webhook.api.command.user.ExecuteWebhookDeliveryCmd; +import org.apache.cloudstack.mom.webhook.api.command.user.ListWebhookDeliveriesCmd; +import org.apache.cloudstack.mom.webhook.api.command.user.ListWebhooksCmd; +import org.apache.cloudstack.mom.webhook.api.command.user.UpdateWebhookCmd; +import org.apache.cloudstack.mom.webhook.api.response.WebhookDeliveryResponse; +import org.apache.cloudstack.mom.webhook.api.response.WebhookResponse; + +import com.cloud.utils.component.PluggableService; +import com.cloud.utils.exception.CloudRuntimeException; + +public interface WebhookApiService extends PluggableService { + + ListResponse listWebhooks(ListWebhooksCmd cmd); + WebhookResponse createWebhook(CreateWebhookCmd cmd) throws CloudRuntimeException; + boolean deleteWebhook(DeleteWebhookCmd cmd) throws CloudRuntimeException; + WebhookResponse updateWebhook(UpdateWebhookCmd cmd) throws CloudRuntimeException; + WebhookResponse createWebhookResponse(long webhookId); + ListResponse listWebhookDeliveries(ListWebhookDeliveriesCmd cmd); + int deleteWebhookDelivery(DeleteWebhookDeliveryCmd cmd) throws CloudRuntimeException; + WebhookDeliveryResponse executeWebhookDelivery(ExecuteWebhookDeliveryCmd cmd) throws CloudRuntimeException; +} diff --git a/plugins/event-bus/webhook/src/main/java/org/apache/cloudstack/mom/webhook/WebhookApiServiceImpl.java b/plugins/event-bus/webhook/src/main/java/org/apache/cloudstack/mom/webhook/WebhookApiServiceImpl.java new file mode 100644 index 00000000000..187b140d5d8 --- /dev/null +++ b/plugins/event-bus/webhook/src/main/java/org/apache/cloudstack/mom/webhook/WebhookApiServiceImpl.java @@ -0,0 +1,574 @@ +// 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.mom.webhook; + +import java.net.URI; +import java.util.ArrayList; +import java.util.Date; +import java.util.List; +import java.util.stream.Collectors; + +import javax.inject.Inject; + +import org.apache.cloudstack.acl.SecurityChecker; +import org.apache.cloudstack.api.ApiConstants; +import org.apache.cloudstack.api.response.ListResponse; +import org.apache.cloudstack.context.CallContext; +import org.apache.cloudstack.mom.webhook.api.command.user.CreateWebhookCmd; +import org.apache.cloudstack.mom.webhook.api.command.user.DeleteWebhookCmd; +import org.apache.cloudstack.mom.webhook.api.command.user.DeleteWebhookDeliveryCmd; +import org.apache.cloudstack.mom.webhook.api.command.user.ExecuteWebhookDeliveryCmd; +import org.apache.cloudstack.mom.webhook.api.command.user.ListWebhookDeliveriesCmd; +import org.apache.cloudstack.mom.webhook.api.command.user.ListWebhooksCmd; +import org.apache.cloudstack.mom.webhook.api.command.user.UpdateWebhookCmd; +import org.apache.cloudstack.mom.webhook.api.response.WebhookDeliveryResponse; +import org.apache.cloudstack.mom.webhook.api.response.WebhookResponse; +import org.apache.cloudstack.mom.webhook.dao.WebhookDao; +import org.apache.cloudstack.mom.webhook.dao.WebhookDeliveryDao; +import org.apache.cloudstack.mom.webhook.dao.WebhookDeliveryJoinDao; +import org.apache.cloudstack.mom.webhook.dao.WebhookJoinDao; +import org.apache.cloudstack.mom.webhook.vo.WebhookDeliveryJoinVO; +import org.apache.cloudstack.mom.webhook.vo.WebhookDeliveryVO; +import org.apache.cloudstack.mom.webhook.vo.WebhookJoinVO; +import org.apache.cloudstack.mom.webhook.vo.WebhookVO; +import org.apache.commons.lang3.ObjectUtils; +import org.apache.commons.lang3.StringUtils; + +import com.cloud.api.ApiResponseHelper; +import com.cloud.cluster.ManagementServerHostVO; +import com.cloud.cluster.dao.ManagementServerHostDao; +import com.cloud.domain.Domain; +import com.cloud.domain.dao.DomainDao; +import com.cloud.exception.InvalidParameterValueException; +import com.cloud.exception.PermissionDeniedException; +import com.cloud.projects.Project; +import com.cloud.user.Account; +import com.cloud.user.AccountManager; +import com.cloud.utils.Pair; +import com.cloud.utils.Ternary; +import com.cloud.utils.UriUtils; +import com.cloud.utils.component.ManagerBase; +import com.cloud.utils.db.Filter; +import com.cloud.utils.db.SearchBuilder; +import com.cloud.utils.db.SearchCriteria; +import com.cloud.utils.exception.CloudRuntimeException; +import com.cloud.utils.rest.HttpConstants; + +public class WebhookApiServiceImpl extends ManagerBase implements WebhookApiService { + + @Inject + AccountManager accountManager; + @Inject + DomainDao domainDao; + @Inject + WebhookDao webhookDao; + @Inject + WebhookJoinDao webhookJoinDao; + @Inject + WebhookDeliveryDao webhookDeliveryDao; + @Inject + WebhookDeliveryJoinDao webhookDeliveryJoinDao; + @Inject + ManagementServerHostDao managementServerHostDao; + @Inject + WebhookService webhookService; + + protected WebhookResponse createWebhookResponse(WebhookJoinVO webhookVO) { + WebhookResponse response = new WebhookResponse(); + response.setObjectName("webhook"); + response.setId(webhookVO.getUuid()); + response.setName(webhookVO.getName()); + response.setDescription(webhookVO.getDescription()); + ApiResponseHelper.populateOwner(response, webhookVO); + response.setState(webhookVO.getState().toString()); + response.setPayloadUrl(webhookVO.getPayloadUrl()); + response.setSecretKey(webhookVO.getSecretKey()); + response.setSslVerification(webhookVO.isSslVerification()); + response.setScope(webhookVO.getScope().toString()); + response.setCreated(webhookVO.getCreated()); + return response; + } + + protected List getIdsOfAccessibleWebhooks(Account caller) { + if (Account.Type.ADMIN.equals(caller.getType())) { + return new ArrayList<>(); + } + String domainPath = null; + if (Account.Type.DOMAIN_ADMIN.equals(caller.getType())) { + Domain domain = domainDao.findById(caller.getDomainId()); + domainPath = domain.getPath(); + } + List webhooks = webhookJoinDao.listByAccountOrDomain(caller.getId(), domainPath); + return webhooks.stream().map(WebhookJoinVO::getId).collect(Collectors.toList()); + } + + protected ManagementServerHostVO basicWebhookDeliveryApiCheck(Account caller, final Long id, final Long webhookId, + final Long managementServerId, final Date startDate, final Date endDate) { + if (id != null) { + WebhookDeliveryVO webhookDeliveryVO = webhookDeliveryDao.findById(id); + if (webhookDeliveryVO == null) { + throw new InvalidParameterValueException("Invalid ID specified"); + } + WebhookVO webhookVO = webhookDao.findById(webhookDeliveryVO.getWebhookId()); + if (webhookVO != null) { + accountManager.checkAccess(caller, SecurityChecker.AccessType.OperateEntry, false, webhookVO); + } + } + if (webhookId != null) { + WebhookVO webhookVO = webhookDao.findById(webhookId); + if (webhookVO == null) { + throw new InvalidParameterValueException("Invalid Webhook specified"); + } + accountManager.checkAccess(caller, SecurityChecker.AccessType.OperateEntry, false, webhookVO); + } + if (endDate != null && startDate != null && endDate.before(startDate)) { + throw new InvalidParameterValueException(String.format("Invalid %s specified", ApiConstants.END_DATE)); + } + ManagementServerHostVO managementServerHostVO = null; + if (managementServerId != null) { + if (!Account.Type.ADMIN.equals(caller.getType())) { + throw new PermissionDeniedException("Invalid parameter specified"); + } + managementServerHostVO = managementServerHostDao.findById(managementServerId); + if (managementServerHostVO == null) { + throw new InvalidParameterValueException("Invalid management server specified"); + } + } + return managementServerHostVO; + } + + protected WebhookDeliveryResponse createWebhookDeliveryResponse(WebhookDeliveryJoinVO webhookDeliveryVO) { + WebhookDeliveryResponse response = new WebhookDeliveryResponse(); + response.setObjectName(WebhookDelivery.class.getSimpleName().toLowerCase()); + response.setId(webhookDeliveryVO.getUuid()); + response.setEventId(webhookDeliveryVO.getEventUuid()); + response.setEventType(webhookDeliveryVO.getEventType()); + response.setWebhookId(webhookDeliveryVO.getWebhookUuId()); + response.setWebhookName(webhookDeliveryVO.getWebhookName()); + response.setManagementServerId(webhookDeliveryVO.getManagementServerUuId()); + response.setManagementServerName(webhookDeliveryVO.getManagementServerName()); + response.setHeaders(webhookDeliveryVO.getHeaders()); + response.setPayload(webhookDeliveryVO.getPayload()); + response.setSuccess(webhookDeliveryVO.isSuccess()); + response.setResponse(webhookDeliveryVO.getResponse()); + response.setStartTime(webhookDeliveryVO.getStartTime()); + response.setEndTime(webhookDeliveryVO.getEndTime()); + return response; + } + + protected WebhookDeliveryResponse createTestWebhookDeliveryResponse(WebhookDelivery webhookDelivery, + Webhook webhook) { + WebhookDeliveryResponse response = new WebhookDeliveryResponse(); + response.setObjectName(WebhookDelivery.class.getSimpleName().toLowerCase()); + response.setId(webhookDelivery.getUuid()); + response.setEventType(WebhookDelivery.TEST_EVENT_TYPE); + if (webhook != null) { + response.setWebhookId(webhook.getUuid()); + response.setWebhookName(webhook.getName()); + } + ManagementServerHostVO msHost = + managementServerHostDao.findByMsid(webhookDelivery.getManagementServerId()); + if (msHost != null) { + response.setManagementServerId(msHost.getUuid()); + response.setManagementServerName(msHost.getName()); + } + response.setHeaders(webhookDelivery.getHeaders()); + response.setPayload(webhookDelivery.getPayload()); + response.setSuccess(webhookDelivery.isSuccess()); + response.setResponse(webhookDelivery.getResponse()); + response.setStartTime(webhookDelivery.getStartTime()); + response.setEndTime(webhookDelivery.getEndTime()); + return response; + } + + /** + * @param cmd + * @return Account + */ + protected Account getOwner(final CreateWebhookCmd cmd) { + final Account caller = CallContext.current().getCallingAccount(); + return accountManager.finalizeOwner(caller, cmd.getAccountName(), cmd.getDomainId(), cmd.getProjectId()); + } + + protected String getNormalizedPayloadUrl(String payloadUrl) { + if (StringUtils.isBlank(payloadUrl) || payloadUrl.startsWith("http://") || payloadUrl.startsWith("https://")) { + return payloadUrl; + } + return String.format("http://%s", payloadUrl); + } + + protected void validateWebhookOwnerPayloadUrl(Account owner, String payloadUrl, Webhook currentWebhook) { + WebhookVO webhookVO = webhookDao.findByAccountAndPayloadUrl(owner.getId(), payloadUrl); + if (webhookVO == null) { + return; + } + if (currentWebhook != null && webhookVO.getId() == currentWebhook.getId()) { + return; + } + String error = String.format("Payload URL: %s is already in use by another webhook", payloadUrl); + logger.error(String.format("%s: %s for Account [%s]", error, webhookVO, owner)); + throw new InvalidParameterValueException(error); + } + + @Override + public ListResponse listWebhooks(ListWebhooksCmd cmd) { + final CallContext ctx = CallContext.current(); + final Account caller = ctx.getCallingAccount(); + final Long clusterId = cmd.getId(); + final String stateStr = cmd.getState(); + final String name = cmd.getName(); + final String keyword = cmd.getKeyword(); + final String scopeStr = cmd.getScope(); + List responsesList = new ArrayList<>(); + List permittedAccounts = new ArrayList<>(); + Ternary domainIdRecursiveListProject = + new Ternary<>(cmd.getDomainId(), cmd.isRecursive(), null); + accountManager.buildACLSearchParameters(caller, clusterId, cmd.getAccountName(), cmd.getProjectId(), + permittedAccounts, domainIdRecursiveListProject, cmd.listAll(), false); + Long domainId = domainIdRecursiveListProject.first(); + Boolean isRecursive = domainIdRecursiveListProject.second(); + Project.ListProjectResourcesCriteria listProjectResourcesCriteria = domainIdRecursiveListProject.third(); + + + Filter searchFilter = new Filter(WebhookJoinVO.class, "id", true, cmd.getStartIndex(), + cmd.getPageSizeVal()); + SearchBuilder sb = webhookJoinDao.createSearchBuilder(); + accountManager.buildACLSearchBuilder(sb, domainId, isRecursive, permittedAccounts, + listProjectResourcesCriteria); + sb.and("id", sb.entity().getId(), SearchCriteria.Op.EQ); + sb.and("name", sb.entity().getName(), SearchCriteria.Op.EQ); + sb.and("keyword", sb.entity().getName(), SearchCriteria.Op.LIKE); + sb.and("state", sb.entity().getState(), SearchCriteria.Op.EQ); + sb.and("scope", sb.entity().getScope(), SearchCriteria.Op.EQ); + SearchCriteria sc = sb.create(); + accountManager.buildACLSearchCriteria(sc, domainId, isRecursive, permittedAccounts, + listProjectResourcesCriteria); + Webhook.Scope scope = null; + if (StringUtils.isNotEmpty(scopeStr)) { + try { + scope = Webhook.Scope.valueOf(scopeStr); + } catch (IllegalArgumentException iae) { + throw new InvalidParameterValueException("Invalid scope specified"); + } + } + if ((Webhook.Scope.Global.equals(scope) && !Account.Type.ADMIN.equals(caller.getType())) || + (Webhook.Scope.Domain.equals(scope) && + !List.of(Account.Type.ADMIN, Account.Type.DOMAIN_ADMIN).contains(caller.getType()))) { + throw new InvalidParameterValueException(String.format("Scope %s can not be specified", scope)); + } + Webhook.State state = null; + if (StringUtils.isNotEmpty(stateStr)) { + try { + state = Webhook.State.valueOf(stateStr); + } catch (IllegalArgumentException iae) { + throw new InvalidParameterValueException("Invalid state specified"); + } + } + if (scope != null) { + sc.setParameters("scope", scope.name()); + } + if (state != null) { + sc.setParameters("state", state.name()); + } + if(keyword != null){ + sc.setParameters("keyword", "%" + keyword + "%"); + } + if (clusterId != null) { + sc.setParameters("id", clusterId); + } + if (name != null) { + sc.setParameters("name", name); + } + Pair, Integer> webhooksAndCount = webhookJoinDao.searchAndCount(sc, searchFilter); + for (WebhookJoinVO webhook : webhooksAndCount.first()) { + WebhookResponse response = createWebhookResponse(webhook); + responsesList.add(response); + } + ListResponse response = new ListResponse<>(); + response.setResponses(responsesList, webhooksAndCount.second()); + return response; + } + + @Override + public WebhookResponse createWebhook(CreateWebhookCmd cmd) throws CloudRuntimeException { + final Account owner = getOwner(cmd); + final String name = cmd.getName(); + final String description = cmd.getDescription(); + final String payloadUrl = getNormalizedPayloadUrl(cmd.getPayloadUrl()); + final String secretKey = cmd.getSecretKey(); + final boolean sslVerification = cmd.isSslVerification(); + final String scopeStr = cmd.getScope(); + final String stateStr = cmd.getState(); + Webhook.Scope scope = Webhook.Scope.Local; + if (StringUtils.isNotEmpty(scopeStr)) { + try { + scope = Webhook.Scope.valueOf(scopeStr); + } catch (IllegalArgumentException iae) { + throw new InvalidParameterValueException("Invalid scope specified"); + } + } + if ((Webhook.Scope.Global.equals(scope) && !Account.Type.ADMIN.equals(owner.getType())) || + (Webhook.Scope.Domain.equals(scope) && + !List.of(Account.Type.ADMIN, Account.Type.DOMAIN_ADMIN).contains(owner.getType()))) { + throw new InvalidParameterValueException( + String.format("Scope %s can not be specified for owner %s", scope, owner.getName())); + } + Webhook.State state = Webhook.State.Enabled; + if (StringUtils.isNotEmpty(stateStr)) { + try { + state = Webhook.State.valueOf(stateStr); + } catch (IllegalArgumentException iae) { + throw new InvalidParameterValueException("Invalid state specified"); + } + } + UriUtils.validateUrl(payloadUrl); + validateWebhookOwnerPayloadUrl(owner, payloadUrl, null); + URI uri = URI.create(payloadUrl); + if (sslVerification && !HttpConstants.HTTPS.equalsIgnoreCase(uri.getScheme())) { + throw new InvalidParameterValueException( + String.format("SSL verification can be specified only for HTTPS URLs, %s", payloadUrl)); + } + long domainId = owner.getDomainId(); + Long cmdDomainId = cmd.getDomainId(); + if (cmdDomainId != null && + List.of(Account.Type.ADMIN, Account.Type.DOMAIN_ADMIN).contains(owner.getType()) && + Webhook.Scope.Domain.equals(scope)) { + domainId = cmdDomainId; + } + WebhookVO webhook = new WebhookVO(name, description, state, domainId, owner.getId(), payloadUrl, secretKey, + sslVerification, scope); + webhook = webhookDao.persist(webhook); + return createWebhookResponse(webhook.getId()); + } + + @Override + public boolean deleteWebhook(DeleteWebhookCmd cmd) throws CloudRuntimeException { + final Account caller = CallContext.current().getCallingAccount(); + final long id = cmd.getId(); + Webhook webhook = webhookDao.findById(id); + if (webhook == null) { + throw new InvalidParameterValueException("Unable to find the webhook with the specified ID"); + } + accountManager.checkAccess(caller, SecurityChecker.AccessType.OperateEntry, false, webhook); + return webhookDao.remove(id); + } + + @Override + public WebhookResponse updateWebhook(UpdateWebhookCmd cmd) throws CloudRuntimeException { + final Account caller = CallContext.current().getCallingAccount(); + final long id = cmd.getId(); + final String name = cmd.getName(); + final String description = cmd.getDescription(); + final String payloadUrl = getNormalizedPayloadUrl(cmd.getPayloadUrl()); + String secretKey = cmd.getSecretKey(); + final Boolean sslVerification = cmd.isSslVerification(); + final String scopeStr = cmd.getScope(); + final String stateStr = cmd.getState(); + WebhookVO webhook = webhookDao.findById(id); + if (webhook == null) { + throw new InvalidParameterValueException("Unable to find the webhook with the specified ID"); + } + accountManager.checkAccess(caller, SecurityChecker.AccessType.OperateEntry, false, webhook); + boolean updateNeeded = false; + if (StringUtils.isNotBlank(name)) { + webhook.setName(name); + updateNeeded = true; + } + if (description != null) { + webhook.setDescription(description); + updateNeeded = true; + } + if (StringUtils.isNotEmpty(stateStr)) { + try { + Webhook.State state = Webhook.State.valueOf(stateStr); + webhook.setState(state); + updateNeeded = true; + } catch (IllegalArgumentException iae) { + throw new InvalidParameterValueException("Invalid state specified"); + } + } + Account owner = accountManager.getAccount(webhook.getAccountId()); + if (StringUtils.isNotEmpty(scopeStr)) { + try { + Webhook.Scope scope = Webhook.Scope.valueOf(scopeStr); + if ((Webhook.Scope.Global.equals(scope) && !Account.Type.ADMIN.equals(owner.getType())) || + (Webhook.Scope.Domain.equals(scope) && + !List.of(Account.Type.ADMIN, Account.Type.DOMAIN_ADMIN).contains(owner.getType()))) { + throw new InvalidParameterValueException( + String.format("Scope %s can not be specified for owner %s", scope, owner.getName())); + } + webhook.setScope(scope); + updateNeeded = true; + } catch (IllegalArgumentException iae) { + throw new InvalidParameterValueException("Invalid scope specified"); + } + } + URI uri = URI.create(webhook.getPayloadUrl()); + if (StringUtils.isNotEmpty(payloadUrl)) { + UriUtils.validateUrl(payloadUrl); + validateWebhookOwnerPayloadUrl(owner, payloadUrl, webhook); + uri = URI.create(payloadUrl); + webhook.setPayloadUrl(payloadUrl); + updateNeeded = true; + } + if (sslVerification != null) { + if (Boolean.TRUE.equals(sslVerification) && !HttpConstants.HTTPS.equalsIgnoreCase(uri.getScheme())) { + throw new InvalidParameterValueException( + String.format("SSL verification can be specified only for HTTPS URLs, %s", payloadUrl)); + } + webhook.setSslVerification(sslVerification); + updateNeeded = true; + } + if (secretKey != null) { + if (StringUtils.isBlank(secretKey)) { + secretKey = null; + } + webhook.setSecretKey(secretKey); + updateNeeded = true; + } + if (updateNeeded && !webhookDao.update(id, webhook)) { + return null; + } + return createWebhookResponse(webhook.getId()); + } + + @Override + public WebhookResponse createWebhookResponse(long webhookId) { + WebhookJoinVO webhookVO = webhookJoinDao.findById(webhookId); + return createWebhookResponse(webhookVO); + } + + @Override + public ListResponse listWebhookDeliveries(ListWebhookDeliveriesCmd cmd) { + final CallContext ctx = CallContext.current(); + final Account caller = ctx.getCallingAccount(); + final Long id = cmd.getId(); + final Long webhookId = cmd.getWebhookId(); + final Long managementServerId = cmd.getManagementServerId(); + final String keyword = cmd.getKeyword(); + final Date startDate = cmd.getStartDate(); + final Date endDate = cmd.getEndDate(); + final String eventType = cmd.getEventType(); + List responsesList = new ArrayList<>(); + ManagementServerHostVO host = basicWebhookDeliveryApiCheck(caller, id, webhookId, managementServerId, + startDate, endDate); + + Filter searchFilter = new Filter(WebhookDeliveryJoinVO.class, "id", false, cmd.getStartIndex(), + cmd.getPageSizeVal()); + List webhookIds = new ArrayList<>(); + if (webhookId != null) { + webhookIds.add(webhookId); + } else { + webhookIds.addAll(getIdsOfAccessibleWebhooks(caller)); + } + Pair, Integer> deliveriesAndCount = + webhookDeliveryJoinDao.searchAndCountByListApiParameters(id, webhookIds, + (host != null ? host.getMsid() : null), keyword, startDate, endDate, eventType, searchFilter); + for (WebhookDeliveryJoinVO delivery : deliveriesAndCount.first()) { + WebhookDeliveryResponse response = createWebhookDeliveryResponse(delivery); + responsesList.add(response); + } + ListResponse response = new ListResponse<>(); + response.setResponses(responsesList, deliveriesAndCount.second()); + return response; + } + + @Override + public int deleteWebhookDelivery(DeleteWebhookDeliveryCmd cmd) throws CloudRuntimeException { + final CallContext ctx = CallContext.current(); + final Account caller = ctx.getCallingAccount(); + final Long id = cmd.getId(); + final Long webhookId = cmd.getWebhookId(); + final Long managementServerId = cmd.getManagementServerId(); + final Date startDate = cmd.getStartDate(); + final Date endDate = cmd.getEndDate(); + ManagementServerHostVO host = basicWebhookDeliveryApiCheck(caller, id, webhookId, managementServerId, + startDate, endDate); + int removed = webhookDeliveryDao.deleteByDeleteApiParams(id, webhookId, + (host != null ? host.getMsid() : null), startDate, endDate); + logger.info("{} webhook deliveries removed", removed); + return removed; + } + + @Override + public WebhookDeliveryResponse executeWebhookDelivery(ExecuteWebhookDeliveryCmd cmd) throws CloudRuntimeException { + final CallContext ctx = CallContext.current(); + final Account caller = ctx.getCallingAccount(); + final Long deliveryId = cmd.getId(); + final Long webhookId = cmd.getWebhookId(); + final String payloadUrl = getNormalizedPayloadUrl(cmd.getPayloadUrl()); + final String secretKey = cmd.getSecretKey(); + final Boolean sslVerification = cmd.isSslVerification(); + final String payload = cmd.getPayload(); + final Account owner = accountManager.finalizeOwner(caller, null, null, null); + + if (ObjectUtils.allNull(deliveryId, webhookId) && StringUtils.isBlank(payloadUrl)) { + throw new InvalidParameterValueException(String.format("One of the %s, %s or %s must be specified", + ApiConstants.ID, ApiConstants.WEBHOOK_ID, ApiConstants.PAYLOAD_URL)); + } + WebhookDeliveryVO existingDelivery = null; + WebhookVO webhook = null; + if (deliveryId != null) { + existingDelivery = webhookDeliveryDao.findById(deliveryId); + if (existingDelivery == null) { + throw new InvalidParameterValueException("Invalid webhook delivery specified"); + } + webhook = webhookDao.findById(existingDelivery.getWebhookId()); + } + if (StringUtils.isNotBlank(payloadUrl)) { + UriUtils.validateUrl(payloadUrl); + } + if (webhookId != null) { + webhook = webhookDao.findById(webhookId); + if (webhook == null) { + throw new InvalidParameterValueException("Invalid webhook specified"); + } + if (StringUtils.isNotBlank(payloadUrl)) { + webhook.setPayloadUrl(payloadUrl); + } + if (StringUtils.isNotBlank(secretKey)) { + webhook.setSecretKey(secretKey); + } + if (sslVerification != null) { + webhook.setSslVerification(Boolean.TRUE.equals(sslVerification)); + } + } + if (ObjectUtils.allNull(deliveryId, webhookId)) { + webhook = new WebhookVO(owner.getDomainId(), owner.getId(), payloadUrl, secretKey, + Boolean.TRUE.equals(sslVerification)); + } + WebhookDelivery webhookDelivery = webhookService.executeWebhookDelivery(existingDelivery, webhook, payload); + if (webhookDelivery.getId() != WebhookDelivery.ID_DUMMY) { + return createWebhookDeliveryResponse(webhookDeliveryJoinDao.findById(webhookDelivery.getId())); + } + return createTestWebhookDeliveryResponse(webhookDelivery, webhook); + } + + @Override + public List> getCommands() { + List> cmdList = new ArrayList<>(); + cmdList.add(CreateWebhookCmd.class); + cmdList.add(ListWebhooksCmd.class); + cmdList.add(UpdateWebhookCmd.class); + cmdList.add(DeleteWebhookCmd.class); + cmdList.add(ListWebhookDeliveriesCmd.class); + cmdList.add(DeleteWebhookDeliveryCmd.class); + cmdList.add(ExecuteWebhookDeliveryCmd.class); + return cmdList; + } +} diff --git a/plugins/event-bus/webhook/src/main/java/org/apache/cloudstack/mom/webhook/WebhookDelivery.java b/plugins/event-bus/webhook/src/main/java/org/apache/cloudstack/mom/webhook/WebhookDelivery.java new file mode 100644 index 00000000000..b24891539f9 --- /dev/null +++ b/plugins/event-bus/webhook/src/main/java/org/apache/cloudstack/mom/webhook/WebhookDelivery.java @@ -0,0 +1,39 @@ +// 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.mom.webhook; + +import java.util.Date; + +import org.apache.cloudstack.api.Identity; +import org.apache.cloudstack.api.InternalIdentity; + +public interface WebhookDelivery extends Identity, InternalIdentity { + public static final long ID_DUMMY = 0L; + public static final String TEST_EVENT_TYPE = "TEST.WEBHOOK"; + + long getId(); + long getEventId(); + long getWebhookId(); + long getManagementServerId(); + String getHeaders(); + String getPayload(); + boolean isSuccess(); + String getResponse(); + Date getStartTime(); + Date getEndTime(); +} diff --git a/plugins/event-bus/webhook/src/main/java/org/apache/cloudstack/mom/webhook/WebhookDeliveryThread.java b/plugins/event-bus/webhook/src/main/java/org/apache/cloudstack/mom/webhook/WebhookDeliveryThread.java new file mode 100644 index 00000000000..ac840c00be3 --- /dev/null +++ b/plugins/event-bus/webhook/src/main/java/org/apache/cloudstack/mom/webhook/WebhookDeliveryThread.java @@ -0,0 +1,287 @@ +// 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.mom.webhook; + +import java.io.IOException; +import java.net.URI; +import java.net.URISyntaxException; +import java.nio.charset.StandardCharsets; +import java.security.InvalidKeyException; +import java.security.KeyManagementException; +import java.security.KeyStoreException; +import java.security.NoSuchAlgorithmException; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Date; +import java.util.List; + +import javax.crypto.Mac; +import javax.crypto.SecretKey; +import javax.crypto.spec.SecretKeySpec; + +import org.apache.cloudstack.framework.async.AsyncCompletionCallback; +import org.apache.cloudstack.framework.async.AsyncRpcContext; +import org.apache.cloudstack.framework.events.Event; +import org.apache.cloudstack.storage.command.CommandResult; +import org.apache.commons.codec.DecoderException; +import org.apache.commons.codec.binary.Base64; +import org.apache.commons.httpclient.HttpStatus; +import org.apache.commons.lang3.StringUtils; +import org.apache.http.Header; +import org.apache.http.HttpEntity; +import org.apache.http.HttpHeaders; +import org.apache.http.client.config.RequestConfig; +import org.apache.http.client.methods.CloseableHttpResponse; +import org.apache.http.client.methods.HttpPost; +import org.apache.http.conn.ssl.NoopHostnameVerifier; +import org.apache.http.conn.ssl.TrustAllStrategy; +import org.apache.http.entity.ContentType; +import org.apache.http.entity.StringEntity; +import org.apache.http.impl.client.CloseableHttpClient; +import org.apache.http.impl.client.HttpClients; +import org.apache.http.message.BasicHeader; +import org.apache.http.ssl.SSLContextBuilder; +import org.apache.http.util.EntityUtils; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; +import org.json.JSONArray; +import org.json.JSONException; +import org.json.JSONObject; + +public class WebhookDeliveryThread implements Runnable { + protected static Logger LOGGER = LogManager.getLogger(WebhookDeliveryThread.class); + + private static final String HEADER_X_CS_EVENT_ID = "X-CS-Event-ID"; + private static final String HEADER_X_CS_EVENT = "X-CS-Event"; + private static final String HEADER_X_CS_SIGNATURE = "X-CS-Signature"; + private static final String PREFIX_HEADER_USER_AGENT = "CS-Hookshot/"; + private final Webhook webhook; + private final Event event; + private CloseableHttpClient httpClient; + private String headers; + private String payload; + private String response; + private Date startTime; + private int deliveryTries = 3; + private int deliveryTimeout = 10; + + AsyncCompletionCallback callback; + + protected boolean isValidJson(String json) { + try { + new JSONObject(json); + } catch (JSONException ex) { + try { + new JSONArray(json); + } catch (JSONException ex1) { + return false; + } + } + return true; + } + + protected void setHttpClient() throws NoSuchAlgorithmException, KeyStoreException, KeyManagementException { + if (webhook.isSslVerification()) { + httpClient = HttpClients.createDefault(); + return; + } + httpClient = HttpClients + .custom() + .setSSLContext(new SSLContextBuilder().loadTrustMaterial(null, + TrustAllStrategy.INSTANCE).build()) + .setSSLHostnameVerifier(NoopHostnameVerifier.INSTANCE) + .build(); + } + + protected HttpPost getBasicHttpPostRequest() throws URISyntaxException { + final URI uri = new URI(webhook.getPayloadUrl()); + HttpPost request = new HttpPost(); + RequestConfig.Builder requestConfig = RequestConfig.custom(); + requestConfig.setConnectTimeout(deliveryTimeout * 1000); + requestConfig.setConnectionRequestTimeout(deliveryTimeout * 1000); + requestConfig.setSocketTimeout(deliveryTimeout * 1000); + request.setConfig(requestConfig.build()); + request.setURI(uri); + return request; + } + + protected void updateRequestHeaders(HttpPost request) throws DecoderException, NoSuchAlgorithmException, + InvalidKeyException { + request.addHeader(HEADER_X_CS_EVENT_ID, event.getEventUuid()); + request.addHeader(HEADER_X_CS_EVENT, event.getEventType()); + request.setHeader(HttpHeaders.USER_AGENT, String.format("%s%s", PREFIX_HEADER_USER_AGENT, + event.getResourceAccountUuid())); + if (StringUtils.isNotBlank(webhook.getSecretKey())) { + request.addHeader(HEADER_X_CS_SIGNATURE, generateHMACSignature(payload, webhook.getSecretKey())); + } + List
headers = new ArrayList<>(Arrays.asList(request.getAllHeaders())); + HttpEntity entity = request.getEntity(); + if (entity.getContentLength() > 0 && !request.containsHeader(HttpHeaders.CONTENT_LENGTH)) { + headers.add(new BasicHeader(HttpHeaders.CONTENT_LENGTH, Long.toString(entity.getContentLength()))); + } + if (entity.getContentType() != null && !request.containsHeader(HttpHeaders.CONTENT_TYPE)) { + headers.add(entity.getContentType()); + } + if (entity.getContentEncoding() != null && !request.containsHeader(HttpHeaders.CONTENT_ENCODING)) { + headers.add(entity.getContentEncoding()); + } + this.headers = StringUtils.join(headers, "\n"); + } + + public WebhookDeliveryThread(Webhook webhook, Event event, + AsyncCompletionCallback callback) { + this.webhook = webhook; + this.event = event; + this.callback = callback; + } + + public void setDeliveryTries(int deliveryTries) { + this.deliveryTries = deliveryTries; + } + + public void setDeliveryTimeout(int deliveryTimeout) { + this.deliveryTimeout = deliveryTimeout; + } + + @Override + public void run() { + LOGGER.debug("Delivering event: {} for {}", event.getEventType(), webhook); + if (event == null) { + LOGGER.warn("Invalid event received for delivering to {}", webhook); + return; + } + payload = event.getDescription(); + LOGGER.trace("Payload: {}", payload); + int attempt = 0; + boolean success = false; + try { + setHttpClient(); + } catch (NoSuchAlgorithmException | KeyManagementException | KeyStoreException e) { + response = String.format("Failed to initiate delivery due to : %s", e.getMessage()); + callback.complete(new WebhookDeliveryResult(headers, payload, success, response, new Date())); + return; + } + while (attempt < deliveryTries) { + attempt++; + if (delivery(attempt)) { + success = true; + break; + } + } + callback.complete(new WebhookDeliveryResult(headers, payload, success, response, startTime)); + } + + protected void updateResponseFromRequest(HttpEntity entity) { + try { + this.response = EntityUtils.toString(entity, StandardCharsets.UTF_8); + } catch (IOException e) { + LOGGER.error("Failed to parse response for event: {} for {}", + event.getEventType(), webhook); + this.response = ""; + } + } + + protected boolean delivery(int attempt) { + startTime = new Date(); + try { + HttpPost request = getBasicHttpPostRequest(); + StringEntity input = new StringEntity(payload, + isValidJson(payload) ? ContentType.APPLICATION_JSON : ContentType.TEXT_PLAIN); + request.setEntity(input); + updateRequestHeaders(request); + LOGGER.trace("Delivering event: {} for {} with timeout: {}, " + + "attempt #{}", event.getEventType(), webhook, + deliveryTimeout, attempt); + final CloseableHttpResponse response = httpClient.execute(request); + updateResponseFromRequest(response.getEntity()); + if (response.getStatusLine().getStatusCode() == HttpStatus.SC_OK) { + LOGGER.trace("Successfully delivered event: {} for {}", + event.getEventType(), webhook); + return true; + } + } catch (URISyntaxException | IOException | DecoderException | NoSuchAlgorithmException | + InvalidKeyException e) { + LOGGER.warn("Failed to deliver {}, in attempt #{} due to: {}", + webhook, attempt, e.getMessage()); + response = String.format("Failed due to : %s", e.getMessage()); + } + return false; + } + + public static String generateHMACSignature(String data, String key) + throws InvalidKeyException, NoSuchAlgorithmException, DecoderException { + Mac mac = Mac.getInstance("HMACSHA256"); + SecretKey secretKey = new SecretKeySpec(key.getBytes(StandardCharsets.UTF_8), mac.getAlgorithm()); + mac.init(secretKey); + byte[] dataAsBytes = data.getBytes(StandardCharsets.UTF_8); + byte[] encodedText = mac.doFinal(dataAsBytes); + return new String(Base64.encodeBase64(encodedText)).trim(); + } + + public static class WebhookDeliveryContext extends AsyncRpcContext { + private final Long eventId; + private final Long ruleId; + + public WebhookDeliveryContext(AsyncCompletionCallback callback, Long eventId, Long ruleId) { + super(callback); + this.eventId = eventId; + this.ruleId = ruleId; + } + + public Long getEventId() { + return eventId; + } + + public Long getRuleId() { + return ruleId; + } + } + + public static class WebhookDeliveryResult extends CommandResult { + private final String headers; + private final String payload; + private final Date starTime; + private final Date endTime; + + public WebhookDeliveryResult(String headers, String payload, boolean success, String response, Date starTime) { + super(); + this.headers = headers; + this.payload = payload; + this.setResult(response); + this.setSuccess(success); + this.starTime = starTime; + this.endTime = new Date(); + } + + public String getHeaders() { + return headers; + } + + public String getPayload() { + return payload; + } + + public Date getStarTime() { + return starTime; + } + + public Date getEndTime() { + return endTime; + } + } +} diff --git a/plugins/event-bus/webhook/src/main/java/org/apache/cloudstack/mom/webhook/WebhookEventBus.java b/plugins/event-bus/webhook/src/main/java/org/apache/cloudstack/mom/webhook/WebhookEventBus.java new file mode 100644 index 00000000000..c2dade84361 --- /dev/null +++ b/plugins/event-bus/webhook/src/main/java/org/apache/cloudstack/mom/webhook/WebhookEventBus.java @@ -0,0 +1,88 @@ +/* + * 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.mom.webhook; + +import java.util.Map; +import java.util.UUID; + +import javax.inject.Inject; +import javax.naming.ConfigurationException; + +import org.apache.cloudstack.framework.events.Event; +import org.apache.cloudstack.framework.events.EventBus; +import org.apache.cloudstack.framework.events.EventBusException; +import org.apache.cloudstack.framework.events.EventSubscriber; +import org.apache.cloudstack.framework.events.EventTopic; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; + +import com.cloud.utils.component.ManagerBase; +import com.google.gson.Gson; + +public class WebhookEventBus extends ManagerBase implements EventBus { + + protected static Logger LOGGER = LogManager.getLogger(WebhookEventBus.class); + private static Gson gson; + + @Inject + WebhookService webhookService; + + @Override + public boolean configure(String name, Map params) throws ConfigurationException { + _name = name; + return true; + } + + @Override + public void setName(String name) { + _name = name; + } + + @Override + public UUID subscribe(EventTopic topic, EventSubscriber subscriber) throws EventBusException { + /* NOOP */ + return UUID.randomUUID(); + } + + @Override + public void unsubscribe(UUID subscriberId, EventSubscriber subscriber) throws EventBusException { + /* NOOP */ + } + + @Override + public void publish(Event event) throws EventBusException { + webhookService.handleEvent(event); + } + + @Override + public String getName() { + return _name; + } + + @Override + public boolean start() { + return true; + } + + @Override + public boolean stop() { + return true; + } +} diff --git a/plugins/event-bus/webhook/src/main/java/org/apache/cloudstack/mom/webhook/WebhookService.java b/plugins/event-bus/webhook/src/main/java/org/apache/cloudstack/mom/webhook/WebhookService.java new file mode 100644 index 00000000000..5a5aced288d --- /dev/null +++ b/plugins/event-bus/webhook/src/main/java/org/apache/cloudstack/mom/webhook/WebhookService.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.mom.webhook; + +import org.apache.cloudstack.framework.config.ConfigKey; +import org.apache.cloudstack.framework.config.Configurable; +import org.apache.cloudstack.framework.events.Event; +import org.apache.cloudstack.framework.events.EventBusException; + +import com.cloud.utils.component.PluggableService; +import com.cloud.utils.exception.CloudRuntimeException; + +public interface WebhookService extends PluggableService, Configurable { + + ConfigKey WebhookDeliveryTimeout = new ConfigKey<>("Advanced", Integer.class, + "webhook.delivery.timeout", "10", + "Wait timeout (in seconds) for a webhook delivery delivery", + true, ConfigKey.Scope.Domain); + + ConfigKey WebhookDeliveryTries = new ConfigKey<>("Advanced", Integer.class, + "webhook.delivery.tries", "3", + "Number of tries to be made for a webhook delivery", + true, ConfigKey.Scope.Domain); + + ConfigKey WebhookDeliveryThreadPoolSize = new ConfigKey<>("Advanced", Integer.class, + "webhook.delivery.thread.pool.size", "5", + "Size of the thread pool for webhook deliveries", + false, ConfigKey.Scope.Global); + + ConfigKey WebhookDeliveriesLimit = new ConfigKey<>("Advanced", Integer.class, + "webhook.deliveries.limit", "10", + "Limit for the number of deliveries to keep in DB per webhook", + true, ConfigKey.Scope.Global); + + ConfigKey WebhookDeliveriesCleanupInitialDelay = new ConfigKey<>("Advanced", Integer.class, + "webhook.deliveries.cleanup.initial.delay", "180", + "Initial delay (in seconds) for webhook deliveries cleanup task", + false, ConfigKey.Scope.Global); + + ConfigKey WebhookDeliveriesCleanupInterval = new ConfigKey<>("Advanced", Integer.class, + "webhook.deliveries.cleanup.interval", "3600", + "Interval (in seconds) for cleaning up webhook deliveries", + false, ConfigKey.Scope.Global); + + void handleEvent(Event event) throws EventBusException; + WebhookDelivery executeWebhookDelivery(WebhookDelivery delivery, Webhook webhook, String payload) + throws CloudRuntimeException; +} diff --git a/plugins/event-bus/webhook/src/main/java/org/apache/cloudstack/mom/webhook/WebhookServiceImpl.java b/plugins/event-bus/webhook/src/main/java/org/apache/cloudstack/mom/webhook/WebhookServiceImpl.java new file mode 100644 index 00000000000..58b265a99c0 --- /dev/null +++ b/plugins/event-bus/webhook/src/main/java/org/apache/cloudstack/mom/webhook/WebhookServiceImpl.java @@ -0,0 +1,354 @@ +// 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.mom.webhook; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.UUID; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.concurrent.ScheduledExecutorService; +import java.util.concurrent.TimeUnit; + +import javax.inject.Inject; +import javax.naming.ConfigurationException; + +import org.apache.cloudstack.acl.ControlledEntity; +import org.apache.cloudstack.framework.async.AsyncCallFuture; +import org.apache.cloudstack.framework.async.AsyncCallbackDispatcher; +import org.apache.cloudstack.framework.async.AsyncCompletionCallback; +import org.apache.cloudstack.framework.async.AsyncRpcContext; +import org.apache.cloudstack.framework.config.ConfigKey; +import org.apache.cloudstack.framework.events.Event; +import org.apache.cloudstack.framework.events.EventBusException; +import org.apache.cloudstack.managed.context.ManagedContextRunnable; +import org.apache.cloudstack.mom.webhook.dao.WebhookDao; +import org.apache.cloudstack.mom.webhook.dao.WebhookDeliveryDao; +import org.apache.cloudstack.mom.webhook.vo.WebhookDeliveryVO; +import org.apache.cloudstack.mom.webhook.vo.WebhookVO; +import org.apache.cloudstack.utils.identity.ManagementServerNode; +import org.apache.cloudstack.webhook.WebhookHelper; +import org.apache.commons.lang3.StringUtils; + +import com.cloud.api.query.vo.EventJoinVO; +import com.cloud.cluster.ManagementServerHostVO; +import com.cloud.cluster.dao.ManagementServerHostDao; +import com.cloud.domain.dao.DomainDao; +import com.cloud.event.EventCategory; +import com.cloud.event.dao.EventJoinDao; +import com.cloud.server.ManagementService; +import com.cloud.user.Account; +import com.cloud.user.AccountManager; +import com.cloud.utils.Pair; +import com.cloud.utils.component.ComponentContext; +import com.cloud.utils.component.ManagerBase; +import com.cloud.utils.concurrency.NamedThreadFactory; +import com.cloud.utils.db.Filter; +import com.cloud.utils.db.GlobalLock; +import com.cloud.utils.exception.CloudRuntimeException; + +public class WebhookServiceImpl extends ManagerBase implements WebhookService, WebhookHelper { + public static final String WEBHOOK_JOB_POOL_THREAD_PREFIX = "Webhook-Job-Executor"; + private ExecutorService webhookJobExecutor; + private ScheduledExecutorService webhookDeliveriesCleanupExecutor; + + @Inject + EventJoinDao eventJoinDao; + @Inject + WebhookDao webhookDao; + @Inject + protected WebhookDeliveryDao webhookDeliveryDao; + @Inject + ManagementServerHostDao managementServerHostDao; + @Inject + DomainDao domainDao; + @Inject + AccountManager accountManager; + + protected WebhookDeliveryThread getDeliveryJob(Event event, Webhook webhook, Pair configs) { + WebhookDeliveryThread.WebhookDeliveryContext context = + new WebhookDeliveryThread.WebhookDeliveryContext<>(null, event.getEventId(), webhook.getId()); + AsyncCallbackDispatcher caller = + AsyncCallbackDispatcher.create(this); + caller.setCallback(caller.getTarget().deliveryCompleteCallback(null, null)) + .setContext(context); + WebhookDeliveryThread job = new WebhookDeliveryThread(webhook, event, caller); + job = ComponentContext.inject(job); + job.setDeliveryTries(configs.first()); + job.setDeliveryTimeout(configs.second()); + return job; + } + + protected List getDeliveryJobs(Event event) throws EventBusException { + List jobs = new ArrayList<>(); + if (!EventCategory.ACTION_EVENT.getName().equals(event.getEventCategory())) { + return jobs; + } + if (event.getResourceAccountId() == null) { + logger.warn("Skipping delivering event [ID: {}, description: {}] to any webhook as account ID is missing", + event.getEventId(), event.getDescription()); + throw new EventBusException(String.format("Account missing for the event ID: %s", event.getEventUuid())); + } + List domainIds = new ArrayList<>(); + if (event.getResourceDomainId() != null) { + domainIds.add(event.getResourceDomainId()); + domainIds.addAll(domainDao.getDomainParentIds(event.getResourceDomainId())); + } + List webhooks = + webhookDao.listByEnabledForDelivery(event.getResourceAccountId(), domainIds); + Map> domainConfigs = new HashMap<>(); + for (WebhookVO webhook : webhooks) { + if (!domainConfigs.containsKey(webhook.getDomainId())) { + domainConfigs.put(webhook.getDomainId(), + new Pair<>(WebhookDeliveryTries.valueIn(webhook.getDomainId()), + WebhookDeliveryTimeout.valueIn(webhook.getDomainId()))); + } + Pair configs = domainConfigs.get(webhook.getDomainId()); + WebhookDeliveryThread job = getDeliveryJob(event, webhook, configs); + jobs.add(job); + } + return jobs; + } + + protected Runnable getManualDeliveryJob(WebhookDelivery existingDelivery, Webhook webhook, String payload, + AsyncCallFuture future) { + if (StringUtils.isBlank(payload)) { + payload = "{ \"CloudStack\": \"works!\" }"; + } + long eventId = Webhook.ID_DUMMY; + String eventType = WebhookDelivery.TEST_EVENT_TYPE; + String eventUuid = UUID.randomUUID().toString(); + String description = payload; + String resourceAccountUuid = null; + if (existingDelivery != null) { + EventJoinVO eventJoinVO = eventJoinDao.findById(existingDelivery.getEventId()); + eventId = eventJoinVO.getId(); + eventType = eventJoinVO.getType(); + eventUuid = eventJoinVO.getUuid(); + description = existingDelivery.getPayload(); + resourceAccountUuid = eventJoinVO.getAccountUuid(); + } else { + Account account = accountManager.getAccount(webhook.getAccountId()); + resourceAccountUuid = account.getUuid(); + } + Event event = new Event(ManagementService.Name, EventCategory.ACTION_EVENT.toString(), + eventType, null, null); + event.setEventId(eventId); + event.setEventUuid(eventUuid); + event.setDescription(description); + event.setResourceAccountUuid(resourceAccountUuid); + ManualDeliveryContext context = + new ManualDeliveryContext<>(null, webhook, future); + AsyncCallbackDispatcher caller = + AsyncCallbackDispatcher.create(this); + caller.setCallback(caller.getTarget().manualDeliveryCompleteCallback(null, null)) + .setContext(context); + WebhookDeliveryThread job = new WebhookDeliveryThread(webhook, event, caller); + job.setDeliveryTries(WebhookDeliveryTries.valueIn(webhook.getDomainId())); + job.setDeliveryTimeout(WebhookDeliveryTimeout.valueIn(webhook.getDomainId())); + return job; + } + + protected Void deliveryCompleteCallback( + AsyncCallbackDispatcher callback, + WebhookDeliveryThread.WebhookDeliveryContext context) { + WebhookDeliveryThread.WebhookDeliveryResult result = callback.getResult(); + WebhookDeliveryVO deliveryVO = new WebhookDeliveryVO(context.getEventId(), context.getRuleId(), + ManagementServerNode.getManagementServerId(), result.getHeaders(), result.getPayload(), + result.isSuccess(), result.getResult(), result.getStarTime(), result.getEndTime()); + webhookDeliveryDao.persist(deliveryVO); + return null; + } + + protected Void manualDeliveryCompleteCallback( + AsyncCallbackDispatcher callback, + ManualDeliveryContext context) { + WebhookDeliveryThread.WebhookDeliveryResult result = callback.getResult(); + context.future.complete(result); + return null; + } + + protected long cleanupOldWebhookDeliveries(long deliveriesLimit) { + Filter filter = new Filter(WebhookVO.class, "id", true, 0L, 50L); + Pair, Integer> webhooksAndCount = + webhookDao.searchAndCount(webhookDao.createSearchCriteria(), filter); + List webhooks = webhooksAndCount.first(); + long count = webhooksAndCount.second(); + long processed = 0; + do { + for (WebhookVO webhook : webhooks) { + webhookDeliveryDao.removeOlderDeliveries(webhook.getId(), deliveriesLimit); + processed++; + } + if (processed < count) { + filter.setOffset(processed); + webhooks = webhookDao.listAll(filter); + } + } while (processed < count); + return processed; + } + + @Override + public boolean configure(String name, Map params) throws ConfigurationException { + try { + webhookJobExecutor = Executors.newFixedThreadPool(WebhookDeliveryThreadPoolSize.value(), + new NamedThreadFactory(WEBHOOK_JOB_POOL_THREAD_PREFIX)); + webhookDeliveriesCleanupExecutor = Executors.newScheduledThreadPool(1, + new NamedThreadFactory("Webhook-Deliveries-Cleanup-Worker")); + } catch (final Exception e) { + throw new ConfigurationException("Unable to to configure WebhookServiceImpl"); + } + return true; + } + + @Override + public boolean start() { + long webhookDeliveriesCleanupInitialDelay = WebhookDeliveriesCleanupInitialDelay.value(); + long webhookDeliveriesCleanupInterval = WebhookDeliveriesCleanupInterval.value(); + logger.debug("Scheduling webhook deliveries cleanup task with initial delay={}s and interval={}s", + webhookDeliveriesCleanupInitialDelay, webhookDeliveriesCleanupInterval); + webhookDeliveriesCleanupExecutor.scheduleWithFixedDelay(new WebhookDeliveryCleanupWorker(), + webhookDeliveriesCleanupInitialDelay, webhookDeliveriesCleanupInterval, TimeUnit.SECONDS); + return true; + } + + @Override + public boolean stop() { + webhookJobExecutor.shutdown(); + return true; + } + + @Override + public String getConfigComponentName() { + return WebhookService.class.getName(); + } + + @Override + public ConfigKey[] getConfigKeys() { + return new ConfigKey[]{ + WebhookDeliveryTimeout, + WebhookDeliveryTries, + WebhookDeliveryThreadPoolSize, + WebhookDeliveriesLimit, + WebhookDeliveriesCleanupInitialDelay, + WebhookDeliveriesCleanupInterval + }; + } + + @Override + public void deleteWebhooksForAccount(long accountId) { + webhookDao.deleteByAccount(accountId); + } + + @Override + public List listWebhooksByAccount(long accountId) { + return webhookDao.listByAccount(accountId); + } + + @Override + public void handleEvent(Event event) throws EventBusException { + List jobs = getDeliveryJobs(event); + for(Runnable job : jobs) { + webhookJobExecutor.submit(job); + } + } + + @Override + public WebhookDelivery executeWebhookDelivery(WebhookDelivery delivery, Webhook webhook, String payload) + throws CloudRuntimeException { + AsyncCallFuture future = new AsyncCallFuture<>(); + Runnable job = getManualDeliveryJob(delivery, webhook, payload, future); + webhookJobExecutor.submit(job); + WebhookDeliveryThread.WebhookDeliveryResult result = null; + WebhookDeliveryVO webhookDeliveryVO; + try { + result = future.get(); + if (delivery != null) { + webhookDeliveryVO = new WebhookDeliveryVO(delivery.getEventId(), delivery.getWebhookId(), + ManagementServerNode.getManagementServerId(), result.getHeaders(), result.getPayload(), + result.isSuccess(), result.getResult(), result.getStarTime(), result.getEndTime()); + webhookDeliveryVO = webhookDeliveryDao.persist(webhookDeliveryVO); + } else { + webhookDeliveryVO = new WebhookDeliveryVO(ManagementServerNode.getManagementServerId(), + result.getHeaders(), result.getPayload(), result.isSuccess(), result.getResult(), + result.getStarTime(), result.getEndTime()); + } + } catch (InterruptedException | ExecutionException e) { + logger.error(String.format("Failed to execute test webhook delivery due to: %s", e.getMessage()), e); + throw new CloudRuntimeException("Failed to execute test webhook delivery"); + } + return webhookDeliveryVO; + } + + @Override + public List> getCommands() { + return new ArrayList<>(); + } + + static public class ManualDeliveryContext extends AsyncRpcContext { + final Webhook webhook; + final AsyncCallFuture future; + + public ManualDeliveryContext(AsyncCompletionCallback callback, Webhook webhook, + AsyncCallFuture future) { + super(callback); + this.webhook = webhook; + this.future = future; + } + + } + + public class WebhookDeliveryCleanupWorker extends ManagedContextRunnable { + + protected void runCleanupForLongestRunningManagementServer() { + try { + ManagementServerHostVO msHost = managementServerHostDao.findOneByLongestRuntime(); + if (msHost == null || (msHost.getMsid() != ManagementServerNode.getManagementServerId())) { + logger.debug("Skipping the webhook delivery cleanup task on this management server"); + return; + } + long deliveriesLimit = WebhookDeliveriesLimit.value(); + logger.debug("Clearing old deliveries for webhooks with limit={} using management server {}", + deliveriesLimit, msHost.getMsid()); + long processed = cleanupOldWebhookDeliveries(deliveriesLimit); + logger.debug("Cleared old deliveries with limit={} for {} webhooks", deliveriesLimit, processed); + } catch (Exception e) { + logger.warn("Cleanup task failed to cleanup old webhook deliveries", e); + } + } + + @Override + protected void runInContext() { + GlobalLock gcLock = GlobalLock.getInternLock("WebhookDeliveriesCleanup"); + try { + if (gcLock.lock(3)) { + try { + runCleanupForLongestRunningManagementServer(); + } finally { + gcLock.unlock(); + } + } + } finally { + gcLock.releaseRef(); + } + } + } +} diff --git a/plugins/event-bus/webhook/src/main/java/org/apache/cloudstack/mom/webhook/api/command/user/CreateWebhookCmd.java b/plugins/event-bus/webhook/src/main/java/org/apache/cloudstack/mom/webhook/api/command/user/CreateWebhookCmd.java new file mode 100644 index 00000000000..d3d2cf18e1f --- /dev/null +++ b/plugins/event-bus/webhook/src/main/java/org/apache/cloudstack/mom/webhook/api/command/user/CreateWebhookCmd.java @@ -0,0 +1,167 @@ +// 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.mom.webhook.api.command.user; + + +import javax.inject.Inject; + +import org.apache.cloudstack.acl.RoleType; +import org.apache.cloudstack.acl.SecurityChecker; +import org.apache.cloudstack.api.ACL; +import org.apache.cloudstack.api.APICommand; +import org.apache.cloudstack.api.ApiConstants; +import org.apache.cloudstack.api.ApiErrorCode; +import org.apache.cloudstack.api.BaseCmd; +import org.apache.cloudstack.api.Parameter; +import org.apache.cloudstack.api.ResponseObject; +import org.apache.cloudstack.api.ServerApiException; +import org.apache.cloudstack.api.response.DomainResponse; +import org.apache.cloudstack.api.response.ProjectResponse; +import org.apache.cloudstack.context.CallContext; +import org.apache.cloudstack.mom.webhook.WebhookApiService; +import org.apache.cloudstack.mom.webhook.Webhook; +import org.apache.cloudstack.mom.webhook.api.response.WebhookResponse; + +import com.cloud.utils.exception.CloudRuntimeException; + +@APICommand(name = "createWebhook", + description = "Creates a Webhook", + responseObject = WebhookResponse.class, + responseView = ResponseObject.ResponseView.Restricted, + entityType = {Webhook.class}, + requestHasSensitiveInfo = false, + responseHasSensitiveInfo = true, + authorized = {RoleType.Admin, RoleType.ResourceAdmin, RoleType.DomainAdmin, RoleType.User}, + since = "4.20.0") +public class CreateWebhookCmd extends BaseCmd { + + @Inject + WebhookApiService webhookApiService; + + ///////////////////////////////////////////////////// + //////////////// API parameters ///////////////////// + ///////////////////////////////////////////////////// + + @Parameter(name = ApiConstants.NAME, type = BaseCmd.CommandType.STRING, required = true, description = "Name for the Webhook") + private String name; + + @Parameter(name = ApiConstants.DESCRIPTION, type = BaseCmd.CommandType.STRING, description = "Description for the Webhook") + private String description; + + @Parameter(name = ApiConstants.STATE, type = BaseCmd.CommandType.STRING, description = "State of the Webhook") + private String state; + + @ACL(accessType = SecurityChecker.AccessType.UseEntry) + @Parameter(name = ApiConstants.ACCOUNT, type = BaseCmd.CommandType.STRING, description = "An optional account for the" + + " Webhook. Must be used with domainId.") + private String accountName; + + @ACL(accessType = SecurityChecker.AccessType.UseEntry) + @Parameter(name = ApiConstants.DOMAIN_ID, type = BaseCmd.CommandType.UUID, entityType = DomainResponse.class, + description = "an optional domainId for the Webhook. If the account parameter is used, domainId must also be used.") + private Long domainId; + + @ACL(accessType = SecurityChecker.AccessType.UseEntry) + @Parameter(name = ApiConstants.PROJECT_ID, type = BaseCmd.CommandType.UUID, entityType = ProjectResponse.class, + description = "Project for the Webhook") + private Long projectId; + + @Parameter(name = ApiConstants.PAYLOAD_URL, + type = BaseCmd.CommandType.STRING, + required = true, + description = "Payload URL of the Webhook") + private String payloadUrl; + + @Parameter(name = ApiConstants.SECRET_KEY, type = BaseCmd.CommandType.STRING, description = "Secret key of the Webhook") + private String secretKey; + + @Parameter(name = ApiConstants.SSL_VERIFICATION, type = BaseCmd.CommandType.BOOLEAN, description = "If set to true then SSL verification will be done for the Webhook otherwise not") + private Boolean sslVerification; + + @Parameter(name = ApiConstants.SCOPE, type = BaseCmd.CommandType.STRING, description = "Scope of the Webhook", + authorized = {RoleType.Admin, RoleType.DomainAdmin}) + private String scope; + + ///////////////////////////////////////////////////// + /////////////////// Accessors /////////////////////// + ///////////////////////////////////////////////////// + + + public String getName() { + return name; + } + + public String getDescription() { + return description; + } + + public String getState() { + return state; + } + + public String getAccountName() { + return accountName; + } + + public Long getDomainId() { + return domainId; + } + + public Long getProjectId() { + return projectId; + } + + public String getPayloadUrl() { + return payloadUrl; + } + + public String getSecretKey() { + return secretKey; + } + + public boolean isSslVerification() { + return Boolean.TRUE.equals(sslVerification); + } + + public String getScope() { + return scope; + } + + @Override + public long getEntityOwnerId() { + return CallContext.current().getCallingAccountId(); + } + + ///////////////////////////////////////////////////// + /////////////// API Implementation/////////////////// + ///////////////////////////////////////////////////// + + @Override + public void execute() throws ServerApiException { + try { + WebhookResponse response = webhookApiService.createWebhook(this); + if (response == null) { + throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, "Failed to create webhook"); + } + response.setResponseName(getCommandName()); + setResponseObject(response); + } catch (CloudRuntimeException ex) { + throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, ex.getMessage()); + } + } +} diff --git a/plugins/event-bus/webhook/src/main/java/org/apache/cloudstack/mom/webhook/api/command/user/DeleteWebhookCmd.java b/plugins/event-bus/webhook/src/main/java/org/apache/cloudstack/mom/webhook/api/command/user/DeleteWebhookCmd.java new file mode 100644 index 00000000000..c9fb01580c2 --- /dev/null +++ b/plugins/event-bus/webhook/src/main/java/org/apache/cloudstack/mom/webhook/api/command/user/DeleteWebhookCmd.java @@ -0,0 +1,84 @@ +// 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.mom.webhook.api.command.user; + +import javax.inject.Inject; + +import org.apache.cloudstack.acl.RoleType; +import org.apache.cloudstack.api.APICommand; +import org.apache.cloudstack.api.ApiConstants; +import org.apache.cloudstack.api.ApiErrorCode; +import org.apache.cloudstack.api.BaseCmd; +import org.apache.cloudstack.api.Parameter; +import org.apache.cloudstack.api.ServerApiException; +import org.apache.cloudstack.api.response.SuccessResponse; +import org.apache.cloudstack.context.CallContext; +import org.apache.cloudstack.mom.webhook.WebhookApiService; +import org.apache.cloudstack.mom.webhook.Webhook; +import org.apache.cloudstack.mom.webhook.api.response.WebhookResponse; + +import com.cloud.utils.exception.CloudRuntimeException; + +@APICommand(name = "deleteWebhook", + description = "Deletes a Webhook", + responseObject = SuccessResponse.class, + entityType = {Webhook.class}, + authorized = {RoleType.Admin, RoleType.ResourceAdmin, RoleType.DomainAdmin, RoleType.User}, + since = "4.20.0") +public class DeleteWebhookCmd extends BaseCmd { + + @Inject + WebhookApiService webhookApiService; + + ///////////////////////////////////////////////////// + //////////////// API parameters ///////////////////// + ///////////////////////////////////////////////////// + @Parameter(name = ApiConstants.ID, type = CommandType.UUID, + entityType = WebhookResponse.class, + required = true, + description = "The ID of the Webhook") + private Long id; + + ///////////////////////////////////////////////////// + /////////////////// Accessors /////////////////////// + ///////////////////////////////////////////////////// + public Long getId() { + return id; + } + + @Override + public long getEntityOwnerId() { + return CallContext.current().getCallingAccountId(); + } + + ///////////////////////////////////////////////////// + /////////////// API Implementation/////////////////// + ///////////////////////////////////////////////////// + @Override + public void execute() throws ServerApiException { + try { + if (!webhookApiService.deleteWebhook(this)) { + throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, String.format("Failed to delete webhook ID: %d", getId())); + } + SuccessResponse response = new SuccessResponse(getCommandName()); + setResponseObject(response); + } catch (CloudRuntimeException ex) { + throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, ex.getMessage()); + } + } +} diff --git a/plugins/event-bus/webhook/src/main/java/org/apache/cloudstack/mom/webhook/api/command/user/DeleteWebhookDeliveryCmd.java b/plugins/event-bus/webhook/src/main/java/org/apache/cloudstack/mom/webhook/api/command/user/DeleteWebhookDeliveryCmd.java new file mode 100644 index 00000000000..dcfe71bf171 --- /dev/null +++ b/plugins/event-bus/webhook/src/main/java/org/apache/cloudstack/mom/webhook/api/command/user/DeleteWebhookDeliveryCmd.java @@ -0,0 +1,126 @@ +// 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.mom.webhook.api.command.user; + +import java.util.Date; + +import javax.inject.Inject; + +import org.apache.cloudstack.acl.RoleType; +import org.apache.cloudstack.api.APICommand; +import org.apache.cloudstack.api.ApiConstants; +import org.apache.cloudstack.api.ApiErrorCode; +import org.apache.cloudstack.api.BaseCmd; +import org.apache.cloudstack.api.Parameter; +import org.apache.cloudstack.api.ServerApiException; +import org.apache.cloudstack.api.response.ManagementServerResponse; +import org.apache.cloudstack.api.response.SuccessResponse; +import org.apache.cloudstack.context.CallContext; +import org.apache.cloudstack.mom.webhook.WebhookApiService; +import org.apache.cloudstack.mom.webhook.WebhookDelivery; +import org.apache.cloudstack.mom.webhook.api.response.WebhookDeliveryResponse; +import org.apache.cloudstack.mom.webhook.api.response.WebhookResponse; + +import com.cloud.utils.exception.CloudRuntimeException; + +@APICommand(name = "deleteWebhookDelivery", + description = "Deletes Webhook delivery", + responseObject = SuccessResponse.class, + entityType = {WebhookDelivery.class}, + authorized = {RoleType.Admin, RoleType.ResourceAdmin, RoleType.DomainAdmin, RoleType.User}, + since = "4.20.0") +public class DeleteWebhookDeliveryCmd extends BaseCmd { + + @Inject + WebhookApiService webhookApiService; + + ///////////////////////////////////////////////////// + //////////////// API parameters ///////////////////// + ///////////////////////////////////////////////////// + @Parameter(name = ApiConstants.ID, type = BaseCmd.CommandType.UUID, + entityType = WebhookDeliveryResponse.class, + description = "The ID of the Webhook delivery") + private Long id; + + @Parameter(name = ApiConstants.WEBHOOK_ID, type = BaseCmd.CommandType.UUID, + entityType = WebhookResponse.class, + description = "The ID of the Webhook") + private Long webhookId; + + @Parameter(name = ApiConstants.MANAGEMENT_SERVER_ID, type = BaseCmd.CommandType.UUID, + entityType = ManagementServerResponse.class, + description = "The ID of the management server", + authorized = {RoleType.Admin}) + private Long managementServerId; + + @Parameter(name = ApiConstants.START_DATE, + type = CommandType.DATE, + description = "The start date range for the Webhook delivery " + + "(use format \"yyyy-MM-dd\" or \"yyyy-MM-dd HH:mm:ss\"). " + + "All deliveries having start date equal to or after the specified date will be considered.") + private Date startDate; + + @Parameter(name = ApiConstants.END_DATE, + type = CommandType.DATE, + description = "The end date range for the Webhook delivery " + + "(use format \"yyyy-MM-dd\" or \"yyyy-MM-dd HH:mm:ss\"). " + + "All deliveries having end date equal to or before the specified date will be considered.") + private Date endDate; + + ///////////////////////////////////////////////////// + /////////////////// Accessors /////////////////////// + ///////////////////////////////////////////////////// + public Long getId() { + return id; + } + + public Long getWebhookId() { + return webhookId; + } + + public Long getManagementServerId() { + return managementServerId; + } + + public Date getStartDate() { + return startDate; + } + + public Date getEndDate() { + return endDate; + } + + @Override + public long getEntityOwnerId() { + return CallContext.current().getCallingAccountId(); + } + + ///////////////////////////////////////////////////// + /////////////// API Implementation/////////////////// + ///////////////////////////////////////////////////// + @Override + public void execute() throws ServerApiException { + try { + webhookApiService.deleteWebhookDelivery(this); + SuccessResponse response = new SuccessResponse(getCommandName()); + setResponseObject(response); + } catch (CloudRuntimeException ex) { + throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, ex.getMessage()); + } + } +} diff --git a/plugins/event-bus/webhook/src/main/java/org/apache/cloudstack/mom/webhook/api/command/user/ExecuteWebhookDeliveryCmd.java b/plugins/event-bus/webhook/src/main/java/org/apache/cloudstack/mom/webhook/api/command/user/ExecuteWebhookDeliveryCmd.java new file mode 100644 index 00000000000..f31a5481376 --- /dev/null +++ b/plugins/event-bus/webhook/src/main/java/org/apache/cloudstack/mom/webhook/api/command/user/ExecuteWebhookDeliveryCmd.java @@ -0,0 +1,132 @@ +// 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.mom.webhook.api.command.user; + +import javax.inject.Inject; + +import org.apache.cloudstack.acl.RoleType; +import org.apache.cloudstack.api.APICommand; +import org.apache.cloudstack.api.ApiConstants; +import org.apache.cloudstack.api.ApiErrorCode; +import org.apache.cloudstack.api.BaseCmd; +import org.apache.cloudstack.api.Parameter; +import org.apache.cloudstack.api.ServerApiException; +import org.apache.cloudstack.context.CallContext; +import org.apache.cloudstack.mom.webhook.WebhookApiService; +import org.apache.cloudstack.mom.webhook.WebhookDelivery; +import org.apache.cloudstack.mom.webhook.api.response.WebhookDeliveryResponse; +import org.apache.cloudstack.mom.webhook.api.response.WebhookResponse; + +import com.cloud.utils.exception.CloudRuntimeException; + + +@APICommand(name = "executeWebhookDelivery", + description = "Executes a Webhook delivery", + responseObject = WebhookDeliveryResponse.class, + entityType = {WebhookDelivery.class}, + requestHasSensitiveInfo = false, + responseHasSensitiveInfo = false, + authorized = {RoleType.Admin, RoleType.ResourceAdmin, RoleType.DomainAdmin, RoleType.User}, + since = "4.20.0") +public class ExecuteWebhookDeliveryCmd extends BaseCmd { + + @Inject + WebhookApiService webhookApiService; + + ///////////////////////////////////////////////////// + //////////////// API parameters ///////////////////// + ///////////////////////////////////////////////////// + @Parameter(name = ApiConstants.ID, type = CommandType.UUID, + entityType = WebhookDeliveryResponse.class, + description = "The ID of the Webhook delivery for redelivery") + private Long id; + + @Parameter(name = ApiConstants.WEBHOOK_ID, type = CommandType.UUID, + entityType = WebhookResponse.class, + description = "The ID of the Webhook") + private Long webhookId; + + @Parameter(name = ApiConstants.PAYLOAD_URL, + type = BaseCmd.CommandType.STRING, + description = "Payload URL of the Webhook delivery") + private String payloadUrl; + + @Parameter(name = ApiConstants.SECRET_KEY, type = BaseCmd.CommandType.STRING, description = "Secret key of the Webhook delivery") + private String secretKey; + + @Parameter(name = ApiConstants.SSL_VERIFICATION, type = BaseCmd.CommandType.BOOLEAN, description = "If set to true then SSL verification will be done for the Webhook delivery otherwise not") + private Boolean sslVerification; + + @Parameter(name = ApiConstants.PAYLOAD, + type = BaseCmd.CommandType.STRING, + description = "Payload of the Webhook delivery") + private String payload; + + ///////////////////////////////////////////////////// + /////////////////// Accessors /////////////////////// + ///////////////////////////////////////////////////// + + + public Long getId() { + return id; + } + + public Long getWebhookId() { + return webhookId; + } + + public String getPayloadUrl() { + return payloadUrl; + } + + public String getSecretKey() { + return secretKey; + } + + public Boolean isSslVerification() { + return sslVerification; + } + + public String getPayload() { + return payload; + } + + @Override + public long getEntityOwnerId() { + return CallContext.current().getCallingAccountId(); + } + + ///////////////////////////////////////////////////// + /////////////// API Implementation/////////////////// + ///////////////////////////////////////////////////// + + @Override + public void execute() throws ServerApiException { + try { + WebhookDeliveryResponse response = webhookApiService.executeWebhookDelivery(this); + if (response == null) { + throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, "Failed to test Webhook delivery"); + } + response.setResponseName(getCommandName()); + setResponseObject(response); + } catch (CloudRuntimeException ex) { + throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, ex.getMessage()); + } + + } +} diff --git a/plugins/event-bus/webhook/src/main/java/org/apache/cloudstack/mom/webhook/api/command/user/ListWebhookDeliveriesCmd.java b/plugins/event-bus/webhook/src/main/java/org/apache/cloudstack/mom/webhook/api/command/user/ListWebhookDeliveriesCmd.java new file mode 100644 index 00000000000..466dad0d122 --- /dev/null +++ b/plugins/event-bus/webhook/src/main/java/org/apache/cloudstack/mom/webhook/api/command/user/ListWebhookDeliveriesCmd.java @@ -0,0 +1,125 @@ +// 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.mom.webhook.api.command.user; + +import java.util.Date; + +import javax.inject.Inject; + +import org.apache.cloudstack.acl.RoleType; +import org.apache.cloudstack.api.APICommand; +import org.apache.cloudstack.api.ApiConstants; +import org.apache.cloudstack.api.BaseCmd; +import org.apache.cloudstack.api.BaseListCmd; +import org.apache.cloudstack.api.Parameter; +import org.apache.cloudstack.api.ResponseObject; +import org.apache.cloudstack.api.ServerApiException; +import org.apache.cloudstack.api.response.ListResponse; +import org.apache.cloudstack.api.response.ManagementServerResponse; +import org.apache.cloudstack.mom.webhook.WebhookApiService; +import org.apache.cloudstack.mom.webhook.WebhookDelivery; +import org.apache.cloudstack.mom.webhook.api.response.WebhookDeliveryResponse; +import org.apache.cloudstack.mom.webhook.api.response.WebhookResponse; + +@APICommand(name = "listWebhookDeliveries", + description = "Lists Webhook deliveries", + responseObject = WebhookResponse.class, + responseView = ResponseObject.ResponseView.Restricted, + entityType = {WebhookDelivery.class}, + authorized = {RoleType.Admin, RoleType.ResourceAdmin, RoleType.DomainAdmin, RoleType.User}, + since = "4.20.0") +public class ListWebhookDeliveriesCmd extends BaseListCmd { + + @Inject + WebhookApiService webhookApiService; + + ///////////////////////////////////////////////////// + //////////////// API parameters ///////////////////// + ///////////////////////////////////////////////////// + @Parameter(name = ApiConstants.ID, type = BaseCmd.CommandType.UUID, + entityType = WebhookDeliveryResponse.class, + description = "The ID of the Webhook delivery") + private Long id; + + @Parameter(name = ApiConstants.WEBHOOK_ID, type = BaseCmd.CommandType.UUID, + entityType = WebhookResponse.class, + description = "The ID of the Webhook") + private Long webhookId; + + @Parameter(name = ApiConstants.MANAGEMENT_SERVER_ID, type = BaseCmd.CommandType.UUID, + entityType = ManagementServerResponse.class, + description = "The ID of the management server", + authorized = {RoleType.Admin}) + private Long managementServerId; + + @Parameter(name = ApiConstants.START_DATE, + type = CommandType.DATE, + description = "The start date range for the Webhook delivery " + + "(use format \"yyyy-MM-dd\" or \"yyyy-MM-dd HH:mm:ss\"). " + + "All deliveries having start date equal to or after the specified date will be listed.") + private Date startDate; + + @Parameter(name = ApiConstants.END_DATE, + type = CommandType.DATE, + description = "The end date range for the Webhook delivery " + + "(use format \"yyyy-MM-dd\" or \"yyyy-MM-dd HH:mm:ss\"). " + + "All deliveries having end date equal to or before the specified date will be listed.") + private Date endDate; + + @Parameter(name = ApiConstants.EVENT_TYPE, + type = CommandType.STRING, + description = "The event type of the Webhook delivery") + private String eventType; + + ///////////////////////////////////////////////////// + /////////////////// Accessors /////////////////////// + ///////////////////////////////////////////////////// + public Long getId() { + return id; + } + + public Long getWebhookId() { + return webhookId; + } + + public Long getManagementServerId() { + return managementServerId; + } + + public Date getStartDate() { + return startDate; + } + + public Date getEndDate() { + return endDate; + } + + public String getEventType() { + return eventType; + } + + ///////////////////////////////////////////////////// + /////////////// API Implementation/////////////////// + ///////////////////////////////////////////////////// + @Override + public void execute() throws ServerApiException { + ListResponse response = webhookApiService.listWebhookDeliveries(this); + response.setResponseName(getCommandName()); + setResponseObject(response); + } +} diff --git a/plugins/event-bus/webhook/src/main/java/org/apache/cloudstack/mom/webhook/api/command/user/ListWebhooksCmd.java b/plugins/event-bus/webhook/src/main/java/org/apache/cloudstack/mom/webhook/api/command/user/ListWebhooksCmd.java new file mode 100644 index 00000000000..6510c308f6e --- /dev/null +++ b/plugins/event-bus/webhook/src/main/java/org/apache/cloudstack/mom/webhook/api/command/user/ListWebhooksCmd.java @@ -0,0 +1,95 @@ +// 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.mom.webhook.api.command.user; + + +import javax.inject.Inject; + +import org.apache.cloudstack.acl.RoleType; +import org.apache.cloudstack.api.APICommand; +import org.apache.cloudstack.api.ApiConstants; +import org.apache.cloudstack.api.BaseListProjectAndAccountResourcesCmd; +import org.apache.cloudstack.api.Parameter; +import org.apache.cloudstack.api.ResponseObject; +import org.apache.cloudstack.api.ServerApiException; +import org.apache.cloudstack.api.response.ListResponse; +import org.apache.cloudstack.mom.webhook.WebhookApiService; +import org.apache.cloudstack.mom.webhook.Webhook; +import org.apache.cloudstack.mom.webhook.api.response.WebhookResponse; + +@APICommand(name = "listWebhooks", + description = "Lists Webhooks", + responseObject = WebhookResponse.class, + responseView = ResponseObject.ResponseView.Restricted, + entityType = {Webhook.class}, + authorized = {RoleType.Admin, RoleType.ResourceAdmin, RoleType.DomainAdmin, RoleType.User}, + since = "4.20.0") +public class ListWebhooksCmd extends BaseListProjectAndAccountResourcesCmd { + + @Inject + WebhookApiService webhookApiService; + + ///////////////////////////////////////////////////// + //////////////// API parameters ///////////////////// + ///////////////////////////////////////////////////// + @Parameter(name = ApiConstants.ID, type = CommandType.UUID, + entityType = WebhookResponse.class, + description = "The ID of the Webhook") + private Long id; + + @Parameter(name = ApiConstants.STATE, type = CommandType.STRING, description = "The state of the Webhook") + private String state; + + @Parameter(name = ApiConstants.NAME, type = CommandType.STRING, description = "The name of the Webhook") + private String name; + + @Parameter(name = ApiConstants.SCOPE, + type = CommandType.STRING, + description = "The scope of the Webhook", + authorized = {RoleType.Admin, RoleType.DomainAdmin}) + private String scope; + + ///////////////////////////////////////////////////// + /////////////////// Accessors /////////////////////// + ///////////////////////////////////////////////////// + public Long getId() { + return id; + } + + public String getState() { + return state; + } + + public String getName() { + return name; + } + + public String getScope() { + return scope; + } + + ///////////////////////////////////////////////////// + /////////////// API Implementation/////////////////// + ///////////////////////////////////////////////////// + @Override + public void execute() throws ServerApiException { + ListResponse response = webhookApiService.listWebhooks(this); + response.setResponseName(getCommandName()); + setResponseObject(response); + } +} diff --git a/plugins/event-bus/webhook/src/main/java/org/apache/cloudstack/mom/webhook/api/command/user/UpdateWebhookCmd.java b/plugins/event-bus/webhook/src/main/java/org/apache/cloudstack/mom/webhook/api/command/user/UpdateWebhookCmd.java new file mode 100644 index 00000000000..c2be1d3f4fa --- /dev/null +++ b/plugins/event-bus/webhook/src/main/java/org/apache/cloudstack/mom/webhook/api/command/user/UpdateWebhookCmd.java @@ -0,0 +1,136 @@ +// 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.mom.webhook.api.command.user; + +import javax.inject.Inject; + +import org.apache.cloudstack.acl.RoleType; +import org.apache.cloudstack.api.APICommand; +import org.apache.cloudstack.api.ApiConstants; +import org.apache.cloudstack.api.ApiErrorCode; +import org.apache.cloudstack.api.BaseCmd; +import org.apache.cloudstack.api.Parameter; +import org.apache.cloudstack.api.ServerApiException; +import org.apache.cloudstack.api.response.SuccessResponse; +import org.apache.cloudstack.context.CallContext; +import org.apache.cloudstack.mom.webhook.WebhookApiService; +import org.apache.cloudstack.mom.webhook.Webhook; +import org.apache.cloudstack.mom.webhook.api.response.WebhookResponse; + +import com.cloud.utils.exception.CloudRuntimeException; + +@APICommand(name = "updateWebhook", + description = "Updates a Webhook", + responseObject = SuccessResponse.class, + entityType = {Webhook.class}, + authorized = {RoleType.Admin, RoleType.ResourceAdmin, RoleType.DomainAdmin, RoleType.User}, + since = "4.20.0") +public class UpdateWebhookCmd extends BaseCmd { + + @Inject + WebhookApiService webhookApiService; + + ///////////////////////////////////////////////////// + //////////////// API parameters ///////////////////// + ///////////////////////////////////////////////////// + @Parameter(name = ApiConstants.ID, type = CommandType.UUID, + entityType = WebhookResponse.class, + required = true, + description = "The ID of the Webhook") + private Long id; + @Parameter(name = ApiConstants.NAME, type = BaseCmd.CommandType.STRING, description = "Name for the Webhook") + private String name; + + @Parameter(name = ApiConstants.DESCRIPTION, type = BaseCmd.CommandType.STRING, description = "Description for the Webhook") + private String description; + + @Parameter(name = ApiConstants.STATE, type = BaseCmd.CommandType.STRING, description = "State of the Webhook") + private String state; + + @Parameter(name = ApiConstants.PAYLOAD_URL, + type = BaseCmd.CommandType.STRING, + description = "Payload URL of the Webhook") + private String payloadUrl; + + @Parameter(name = ApiConstants.SECRET_KEY, type = BaseCmd.CommandType.STRING, description = "Secret key of the Webhook") + private String secretKey; + + @Parameter(name = ApiConstants.SSL_VERIFICATION, type = BaseCmd.CommandType.BOOLEAN, description = "If set to true then SSL verification will be done for the Webhook otherwise not") + private Boolean sslVerification; + + @Parameter(name = ApiConstants.SCOPE, type = BaseCmd.CommandType.STRING, description = "Scope of the Webhook", + authorized = {RoleType.Admin, RoleType.DomainAdmin}) + private String scope; + + ///////////////////////////////////////////////////// + /////////////////// Accessors /////////////////////// + ///////////////////////////////////////////////////// + public Long getId() { + return id; + } + + public String getName() { + return name; + } + + public String getDescription() { + return description; + } + + public String getState() { + return state; + } + + public String getPayloadUrl() { + return payloadUrl; + } + + public String getSecretKey() { + return secretKey; + } + + public Boolean isSslVerification() { + return sslVerification; + } + + public String getScope() { + return scope; + } + + @Override + public long getEntityOwnerId() { + return CallContext.current().getCallingAccountId(); + } + + ///////////////////////////////////////////////////// + /////////////// API Implementation/////////////////// + ///////////////////////////////////////////////////// + @Override + public void execute() throws ServerApiException { + try { + WebhookResponse response = webhookApiService.updateWebhook(this); + if (response == null) { + throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, "Failed to update Webhook"); + } + response.setResponseName(getCommandName()); + setResponseObject(response); + } catch (CloudRuntimeException ex) { + throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, ex.getMessage()); + } + } +} diff --git a/plugins/event-bus/webhook/src/main/java/org/apache/cloudstack/mom/webhook/api/response/WebhookDeliveryResponse.java b/plugins/event-bus/webhook/src/main/java/org/apache/cloudstack/mom/webhook/api/response/WebhookDeliveryResponse.java new file mode 100644 index 00000000000..6463fe9b48b --- /dev/null +++ b/plugins/event-bus/webhook/src/main/java/org/apache/cloudstack/mom/webhook/api/response/WebhookDeliveryResponse.java @@ -0,0 +1,136 @@ +// 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.mom.webhook.api.response; + + +import java.util.Date; + +import org.apache.cloudstack.api.ApiConstants; +import org.apache.cloudstack.api.BaseResponse; +import org.apache.cloudstack.api.EntityReference; +import org.apache.cloudstack.mom.webhook.WebhookDelivery; + +import com.cloud.serializer.Param; +import com.google.gson.annotations.SerializedName; + +@EntityReference(value = {WebhookDelivery.class}) +public class WebhookDeliveryResponse extends BaseResponse { + @SerializedName(ApiConstants.ID) + @Param(description = "The ID of the Webhook delivery") + private String id; + + @SerializedName(ApiConstants.EVENT_ID) + @Param(description = "The ID of the event") + private String eventId; + + @SerializedName(ApiConstants.EVENT_TYPE) + @Param(description = "The type of the event") + private String eventType; + + @SerializedName(ApiConstants.WEBHOOK_ID) + @Param(description = "The ID of the Webhook") + private String webhookId; + + @SerializedName(ApiConstants.WEBHOOK_NAME) + @Param(description = "The name of the Webhook") + private String webhookName; + + @SerializedName(ApiConstants.MANAGEMENT_SERVER_ID) + @Param(description = "The ID of the management server which executed delivery") + private String managementServerId; + + @SerializedName(ApiConstants.MANAGEMENT_SERVER_NAME) + @Param(description = "The name of the management server which executed delivery") + private String managementServerName; + + @SerializedName(ApiConstants.HEADERS) + @Param(description = "The headers of the webhook delivery") + private String headers; + + @SerializedName(ApiConstants.PAYLOAD) + @Param(description = "The payload of the webhook delivery") + private String payload; + + @SerializedName(ApiConstants.SUCCESS) + @Param(description = "Whether Webhook delivery succeeded or not") + private boolean success; + + @SerializedName(ApiConstants.RESPONSE) + @Param(description = "The response of the webhook delivery") + private String response; + + @SerializedName(ApiConstants.START_DATE) + @Param(description = "The start time of the Webhook delivery") + private Date startTime; + + @SerializedName(ApiConstants.END_DATE) + @Param(description = "The end time of the Webhook delivery") + private Date endTime; + + public void setId(String id) { + this.id = id; + } + + public void setEventId(String eventId) { + this.eventId = eventId; + } + + public void setEventType(String eventType) { + this.eventType = eventType; + } + + public void setWebhookId(String webhookId) { + this.webhookId = webhookId; + } + + public void setWebhookName(String webhookName) { + this.webhookName = webhookName; + } + + public void setManagementServerId(String managementServerId) { + this.managementServerId = managementServerId; + } + + public void setManagementServerName(String managementServerName) { + this.managementServerName = managementServerName; + } + + public void setHeaders(String headers) { + this.headers = headers; + } + + public void setPayload(String payload) { + this.payload = payload; + } + + public void setSuccess(boolean success) { + this.success = success; + } + + public void setResponse(String response) { + this.response = response; + } + + public void setStartTime(Date startTime) { + this.startTime = startTime; + } + + public void setEndTime(Date endTime) { + this.endTime = endTime; + } +} diff --git a/plugins/event-bus/webhook/src/main/java/org/apache/cloudstack/mom/webhook/api/response/WebhookResponse.java b/plugins/event-bus/webhook/src/main/java/org/apache/cloudstack/mom/webhook/api/response/WebhookResponse.java new file mode 100644 index 00000000000..161b8c5796a --- /dev/null +++ b/plugins/event-bus/webhook/src/main/java/org/apache/cloudstack/mom/webhook/api/response/WebhookResponse.java @@ -0,0 +1,149 @@ +// 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.mom.webhook.api.response; + +import java.util.Date; + +import org.apache.cloudstack.api.ApiConstants; +import org.apache.cloudstack.api.BaseResponse; +import org.apache.cloudstack.api.EntityReference; +import org.apache.cloudstack.api.response.ControlledViewEntityResponse; +import org.apache.cloudstack.mom.webhook.Webhook; + +import com.cloud.serializer.Param; +import com.google.gson.annotations.SerializedName; + +@EntityReference(value = {Webhook.class}) +public class WebhookResponse extends BaseResponse implements ControlledViewEntityResponse { + @SerializedName(ApiConstants.ID) + @Param(description = "The ID of the Webhook") + private String id; + + @SerializedName(ApiConstants.NAME) + @Param(description = "The name of the Webhook") + private String name; + + @SerializedName(ApiConstants.DESCRIPTION) + @Param(description = "The description of the Webhook") + private String description; + + @SerializedName(ApiConstants.STATE) + @Param(description = "The state of the Webhook") + private String state; + + @SerializedName(ApiConstants.DOMAIN_ID) + @Param(description = "The ID of the domain in which the Webhook exists") + private String domainId; + + @SerializedName(ApiConstants.DOMAIN) + @Param(description = "The name of the domain in which the Webhook exists") + private String domainName; + + @SerializedName(ApiConstants.ACCOUNT) + @Param(description = "The account associated with the Webhook") + private String accountName; + + @SerializedName(ApiConstants.PROJECT_ID) + @Param(description = "the project id of the Kubernetes cluster") + private String projectId; + + @SerializedName(ApiConstants.PROJECT) + @Param(description = "the project name of the Kubernetes cluster") + private String projectName; + + @SerializedName(ApiConstants.PAYLOAD_URL) + @Param(description = "The payload URL end point for the Webhook") + private String payloadUrl; + + @SerializedName(ApiConstants.SECRET_KEY) + @Param(description = "The secret key for the Webhook") + private String secretKey; + + @SerializedName(ApiConstants.SSL_VERIFICATION) + @Param(description = "Whether SSL verification is enabled for the Webhook") + private boolean sslVerification; + + @SerializedName(ApiConstants.SCOPE) + @Param(description = "The scope of the Webhook") + private String scope; + + @SerializedName(ApiConstants.CREATED) + @Param(description = "The date when this Webhook was created") + private Date created; + + public void setId(String id) { + this.id = id; + } + + public void setName(String name) { + this.name = name; + } + + public void setDescription(String description) { + this.description = description; + } + + public void setState(String state) { + this.state = state; + } + + @Override + public void setDomainId(String domainId) { + this.domainId = domainId; + } + + @Override + public void setDomainName(String domainName) { + this.domainName = domainName; + } + + @Override + public void setAccountName(String accountName) { + this.accountName = accountName; + } + + @Override + public void setProjectId(String projectId) { + this.projectId = projectId; + } + + @Override + public void setProjectName(String projectName) { + this.projectName = projectName; + } + + public void setPayloadUrl(String payloadUrl) { + this.payloadUrl = payloadUrl; + } + + public void setSecretKey(String secretKey) { + this.secretKey = secretKey; + } + + public void setSslVerification(boolean sslVerification) { + this.sslVerification = sslVerification; + } + + public void setScope(String scope) { + this.scope = scope; + } + + public void setCreated(Date created) { + this.created = created; + } +} diff --git a/plugins/event-bus/webhook/src/main/java/org/apache/cloudstack/mom/webhook/dao/WebhookDao.java b/plugins/event-bus/webhook/src/main/java/org/apache/cloudstack/mom/webhook/dao/WebhookDao.java new file mode 100644 index 00000000000..d26e5db7dba --- /dev/null +++ b/plugins/event-bus/webhook/src/main/java/org/apache/cloudstack/mom/webhook/dao/WebhookDao.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.mom.webhook.dao; + +import java.util.List; + +import org.apache.cloudstack.mom.webhook.vo.WebhookVO; + +import com.cloud.utils.db.GenericDao; + +public interface WebhookDao extends GenericDao { + List listByEnabledForDelivery(Long accountId, List domainIds); + void deleteByAccount(long accountId); + List listByAccount(long accountId); + WebhookVO findByAccountAndPayloadUrl(long accountId, String payloadUrl); +} diff --git a/plugins/event-bus/webhook/src/main/java/org/apache/cloudstack/mom/webhook/dao/WebhookDaoImpl.java b/plugins/event-bus/webhook/src/main/java/org/apache/cloudstack/mom/webhook/dao/WebhookDaoImpl.java new file mode 100644 index 00000000000..2ef2269a9b9 --- /dev/null +++ b/plugins/event-bus/webhook/src/main/java/org/apache/cloudstack/mom/webhook/dao/WebhookDaoImpl.java @@ -0,0 +1,99 @@ +// 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.mom.webhook.dao; + +import java.util.List; +import java.util.Map; + +import javax.naming.ConfigurationException; + +import org.apache.cloudstack.mom.webhook.Webhook; +import org.apache.cloudstack.mom.webhook.vo.WebhookVO; +import org.apache.commons.collections.CollectionUtils; + +import com.cloud.utils.db.GenericDaoBase; +import com.cloud.utils.db.SearchBuilder; +import com.cloud.utils.db.SearchCriteria; + +public class WebhookDaoImpl extends GenericDaoBase implements WebhookDao { + SearchBuilder accountIdSearch; + + @Override + public boolean configure(String name, Map params) throws ConfigurationException { + super.configure(name, params); + + accountIdSearch = createSearchBuilder(); + accountIdSearch.and("accountId", accountIdSearch.entity().getAccountId(), SearchCriteria.Op.EQ); + + return true; + } + @Override + public List listByEnabledForDelivery(Long accountId, List domainIds) { + SearchBuilder sb = createSearchBuilder(); + sb.and("state", sb.entity().getState(), SearchCriteria.Op.EQ); + sb.and().op("scopeGlobal", sb.entity().getScope(), SearchCriteria.Op.EQ); + if (accountId != null) { + sb.or().op("scopeLocal", sb.entity().getScope(), SearchCriteria.Op.EQ); + sb.and("accountId", sb.entity().getAccountId(), SearchCriteria.Op.EQ); + sb.cp(); + } + if (CollectionUtils.isNotEmpty(domainIds)) { + sb.or().op("scopeDomain", sb.entity().getScope(), SearchCriteria.Op.EQ); + sb.and("domainId", sb.entity().getDomainId(), SearchCriteria.Op.IN); + sb.cp(); + } + sb.cp(); + SearchCriteria sc = sb.create(); + sc.setParameters("state", Webhook.State.Enabled.name()); + sc.setParameters("scopeGlobal", Webhook.Scope.Global.name()); + if (accountId != null) { + sc.setParameters("scopeLocal", Webhook.Scope.Local.name()); + sc.setParameters("accountId", accountId); + } + if (CollectionUtils.isNotEmpty(domainIds)) { + sc.setParameters("scopeDomain", Webhook.Scope.Domain.name()); + sc.setParameters("domainId", domainIds.toArray()); + } + return listBy(sc); + } + + @Override + public void deleteByAccount(long accountId) { + SearchCriteria sc = accountIdSearch.create(); + sc.setParameters("accountId", accountId); + remove(sc); + } + + @Override + public List listByAccount(long accountId) { + SearchCriteria sc = accountIdSearch.create(); + sc.setParameters("accountId", accountId); + return listBy(sc); + } + + @Override + public WebhookVO findByAccountAndPayloadUrl(long accountId, String payloadUrl) { + SearchBuilder sb = createSearchBuilder(); + sb.and("accountId", sb.entity().getAccountId(), SearchCriteria.Op.EQ); + sb.and("payloadUrl", sb.entity().getPayloadUrl(), SearchCriteria.Op.EQ); + SearchCriteria sc = sb.create(); + sc.setParameters("accountId", accountId); + sc.setParameters("payloadUrl", payloadUrl); + return findOneBy(sc); + } +} diff --git a/plugins/event-bus/webhook/src/main/java/org/apache/cloudstack/mom/webhook/dao/WebhookDeliveryDao.java b/plugins/event-bus/webhook/src/main/java/org/apache/cloudstack/mom/webhook/dao/WebhookDeliveryDao.java new file mode 100644 index 00000000000..0fe76d2904e --- /dev/null +++ b/plugins/event-bus/webhook/src/main/java/org/apache/cloudstack/mom/webhook/dao/WebhookDeliveryDao.java @@ -0,0 +1,29 @@ +// 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.mom.webhook.dao; + +import java.util.Date; + +import org.apache.cloudstack.mom.webhook.vo.WebhookDeliveryVO; + +import com.cloud.utils.db.GenericDao; + +public interface WebhookDeliveryDao extends GenericDao { + int deleteByDeleteApiParams(Long id, Long webhookId, Long managementServerId, Date startDate, Date endDate); + void removeOlderDeliveries(long webhookId, long limit); +} diff --git a/plugins/event-bus/webhook/src/main/java/org/apache/cloudstack/mom/webhook/dao/WebhookDeliveryDaoImpl.java b/plugins/event-bus/webhook/src/main/java/org/apache/cloudstack/mom/webhook/dao/WebhookDeliveryDaoImpl.java new file mode 100644 index 00000000000..088ed53772a --- /dev/null +++ b/plugins/event-bus/webhook/src/main/java/org/apache/cloudstack/mom/webhook/dao/WebhookDeliveryDaoImpl.java @@ -0,0 +1,73 @@ +// 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.mom.webhook.dao; + +import java.util.Date; +import java.util.List; + +import org.apache.cloudstack.mom.webhook.vo.WebhookDeliveryVO; + +import com.cloud.utils.db.Filter; +import com.cloud.utils.db.GenericDaoBase; +import com.cloud.utils.db.SearchBuilder; +import com.cloud.utils.db.SearchCriteria; + +public class WebhookDeliveryDaoImpl extends GenericDaoBase implements WebhookDeliveryDao { + @Override + public int deleteByDeleteApiParams(Long id, Long webhookId, Long managementServerId, Date startDate, + Date endDate) { + SearchBuilder sb = createSearchBuilder(); + sb.and("id", sb.entity().getId(), SearchCriteria.Op.EQ); + sb.and("webhookId", sb.entity().getWebhookId(), SearchCriteria.Op.EQ); + sb.and("managementServerId", sb.entity().getManagementServerId(), SearchCriteria.Op.EQ); + sb.and("startDate", sb.entity().getStartTime(), SearchCriteria.Op.GTEQ); + sb.and("endDate", sb.entity().getEndTime(), SearchCriteria.Op.LTEQ); + SearchCriteria sc = sb.create(); + if (id != null) { + sc.setParameters("id", id); + } + if (webhookId != null) { + sc.setParameters("webhookId", webhookId); + } + if (managementServerId != null) { + sc.setParameters("managementServerId", managementServerId); + } + if (startDate != null) { + sc.setParameters("startDate", startDate); + } + if (endDate != null) { + sc.setParameters("endDate", endDate); + } + return remove(sc); + } + + @Override + public void removeOlderDeliveries(long webhookId, long limit) { + Filter searchFilter = new Filter(WebhookDeliveryVO.class, "id", false, 0L, limit); + SearchBuilder sb = createSearchBuilder(); + sb.and("webhookId", sb.entity().getWebhookId(), SearchCriteria.Op.EQ); + SearchCriteria sc = sb.create(); + sc.setParameters("webhookId", webhookId); + List keep = listBy(sc, searchFilter); + SearchBuilder sbDelete = createSearchBuilder(); + sbDelete.and("id", sbDelete.entity().getId(), SearchCriteria.Op.NOTIN); + SearchCriteria scDelete = sbDelete.create(); + scDelete.setParameters("id", keep.stream().map(WebhookDeliveryVO::getId).toArray()); + remove(scDelete); + } +} diff --git a/plugins/event-bus/webhook/src/main/java/org/apache/cloudstack/mom/webhook/dao/WebhookDeliveryJoinDao.java b/plugins/event-bus/webhook/src/main/java/org/apache/cloudstack/mom/webhook/dao/WebhookDeliveryJoinDao.java new file mode 100644 index 00000000000..70fec4d7cbf --- /dev/null +++ b/plugins/event-bus/webhook/src/main/java/org/apache/cloudstack/mom/webhook/dao/WebhookDeliveryJoinDao.java @@ -0,0 +1,33 @@ +// 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.mom.webhook.dao; + +import java.util.Date; +import java.util.List; + +import org.apache.cloudstack.mom.webhook.vo.WebhookDeliveryJoinVO; + +import com.cloud.utils.Pair; +import com.cloud.utils.db.Filter; +import com.cloud.utils.db.GenericDao; + +public interface WebhookDeliveryJoinDao extends GenericDao { + Pair, Integer> searchAndCountByListApiParameters(Long id, + List webhookIds, Long managementServerId, final String keyword, final Date startDate, + final Date endDate, final String eventType, Filter searchFilter); +} diff --git a/plugins/event-bus/webhook/src/main/java/org/apache/cloudstack/mom/webhook/dao/WebhookDeliveryJoinDaoImpl.java b/plugins/event-bus/webhook/src/main/java/org/apache/cloudstack/mom/webhook/dao/WebhookDeliveryJoinDaoImpl.java new file mode 100644 index 00000000000..db84010fbc4 --- /dev/null +++ b/plugins/event-bus/webhook/src/main/java/org/apache/cloudstack/mom/webhook/dao/WebhookDeliveryJoinDaoImpl.java @@ -0,0 +1,71 @@ +// 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.mom.webhook.dao; + +import java.util.Date; +import java.util.List; + +import org.apache.cloudstack.mom.webhook.vo.WebhookDeliveryJoinVO; +import org.apache.commons.collections.CollectionUtils; + +import com.cloud.utils.Pair; +import com.cloud.utils.StringUtils; +import com.cloud.utils.db.Filter; +import com.cloud.utils.db.GenericDaoBase; +import com.cloud.utils.db.SearchBuilder; +import com.cloud.utils.db.SearchCriteria; + +public class WebhookDeliveryJoinDaoImpl extends GenericDaoBase + implements WebhookDeliveryJoinDao { + @Override + public Pair, Integer> searchAndCountByListApiParameters(Long id, + List webhookIds, Long managementServerId, String keyword, final Date startDate, + final Date endDate, final String eventType, Filter searchFilter) { + SearchBuilder sb = createSearchBuilder(); + sb.and("id", sb.entity().getId(), SearchCriteria.Op.EQ); + sb.and("webhookId", sb.entity().getWebhookId(), SearchCriteria.Op.IN); + sb.and("managementServerId", sb.entity().getManagementServerMsId(), SearchCriteria.Op.EQ); + sb.and("keyword", sb.entity().getPayload(), SearchCriteria.Op.LIKE); + sb.and("startDate", sb.entity().getStartTime(), SearchCriteria.Op.GTEQ); + sb.and("endDate", sb.entity().getEndTime(), SearchCriteria.Op.LTEQ); + sb.and("eventType", sb.entity().getEventType(), SearchCriteria.Op.EQ); + SearchCriteria sc = sb.create(); + if (id != null) { + sc.setParameters("id", id); + } + if (CollectionUtils.isNotEmpty(webhookIds)) { + sc.setParameters("webhookId", webhookIds.toArray()); + } + if (managementServerId != null) { + sc.setParameters("managementServerId", managementServerId); + } + if (keyword != null) { + sc.setParameters("keyword", "%" + keyword + "%"); + } + if (startDate != null) { + sc.setParameters("startDate", startDate); + } + if (endDate != null) { + sc.setParameters("endDate", endDate); + } + if (StringUtils.isNotBlank(eventType)) { + sc.setParameters("eventType", eventType); + } + return searchAndCount(sc, searchFilter); + } +} diff --git a/plugins/event-bus/webhook/src/main/java/org/apache/cloudstack/mom/webhook/dao/WebhookJoinDao.java b/plugins/event-bus/webhook/src/main/java/org/apache/cloudstack/mom/webhook/dao/WebhookJoinDao.java new file mode 100644 index 00000000000..87b4871a14b --- /dev/null +++ b/plugins/event-bus/webhook/src/main/java/org/apache/cloudstack/mom/webhook/dao/WebhookJoinDao.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 org.apache.cloudstack.mom.webhook.dao; + +import java.util.List; + +import org.apache.cloudstack.mom.webhook.vo.WebhookJoinVO; + +import com.cloud.utils.db.GenericDao; + +public interface WebhookJoinDao extends GenericDao { + List listByAccountOrDomain(long accountId, String domainPath); +} diff --git a/plugins/event-bus/webhook/src/main/java/org/apache/cloudstack/mom/webhook/dao/WebhookJoinDaoImpl.java b/plugins/event-bus/webhook/src/main/java/org/apache/cloudstack/mom/webhook/dao/WebhookJoinDaoImpl.java new file mode 100644 index 00000000000..986e8bc2f19 --- /dev/null +++ b/plugins/event-bus/webhook/src/main/java/org/apache/cloudstack/mom/webhook/dao/WebhookJoinDaoImpl.java @@ -0,0 +1,45 @@ +// 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.mom.webhook.dao; + +import java.util.List; + +import org.apache.cloudstack.mom.webhook.vo.WebhookJoinVO; +import org.apache.commons.lang3.StringUtils; + +import com.cloud.utils.db.GenericDaoBase; +import com.cloud.utils.db.SearchBuilder; +import com.cloud.utils.db.SearchCriteria; + +public class WebhookJoinDaoImpl extends GenericDaoBase implements WebhookJoinDao { + @Override + public List listByAccountOrDomain(long accountId, String domainPath) { + SearchBuilder sb = createSearchBuilder(); + sb.and().op("accountId", sb.entity().getAccountId(), SearchCriteria.Op.EQ); + if (StringUtils.isNotBlank(domainPath)) { + sb.or("domainPath", sb.entity().getDomainPath(), SearchCriteria.Op.LIKE); + } + sb.cp(); + SearchCriteria sc = sb.create(); + sc.setParameters("accountId", accountId); + if (StringUtils.isNotBlank(domainPath)) { + sc.setParameters("domainPath", domainPath); + } + return listBy(sc); + } +} diff --git a/plugins/event-bus/webhook/src/main/java/org/apache/cloudstack/mom/webhook/vo/WebhookDeliveryJoinVO.java b/plugins/event-bus/webhook/src/main/java/org/apache/cloudstack/mom/webhook/vo/WebhookDeliveryJoinVO.java new file mode 100644 index 00000000000..e36f870c8d9 --- /dev/null +++ b/plugins/event-bus/webhook/src/main/java/org/apache/cloudstack/mom/webhook/vo/WebhookDeliveryJoinVO.java @@ -0,0 +1,182 @@ +// 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.mom.webhook.vo; + + +import java.util.Date; + +import javax.persistence.Column; +import javax.persistence.Entity; +import javax.persistence.GeneratedValue; +import javax.persistence.GenerationType; +import javax.persistence.Id; +import javax.persistence.Table; +import javax.persistence.Temporal; +import javax.persistence.TemporalType; + +import org.apache.cloudstack.api.Identity; +import org.apache.cloudstack.api.InternalIdentity; +import org.apache.cloudstack.utils.reflectiontostringbuilderutils.ReflectionToStringBuilderUtils; + +import com.cloud.api.query.vo.BaseViewVO; + +@Entity +@Table(name = "webhook_delivery_view") +public class WebhookDeliveryJoinVO extends BaseViewVO implements InternalIdentity, Identity { + + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + @Column(name = "id") + private long id; + + @Column(name = "uuid") + private String uuid; + + @Column(name = "event_id") + private long eventId; + + @Column(name = "event_uuid") + private String eventUuid; + + @Column(name = "event_type") + private String eventType; + + @Column(name = "webhook_id") + private long webhookId; + + @Column(name = "webhook_uuid") + private String webhookUuId; + + @Column(name = "webhook_name") + private String webhookName; + + @Column(name = "mshost_id") + private long managementServerId; + + @Column(name = "mshost_uuid") + private String managementServerUuId; + + @Column(name = "mshost_msid") + private long managementServerMsId; + + @Column(name = "mshost_name") + private String managementServerName; + + @Column(name = "headers", length = 65535) + private String headers; + + @Column(name = "payload", length = 65535) + private String payload; + + @Column(name = "success") + private boolean success; + + @Column(name = "response", length = 65535) + private String response; + + @Column(name = "start_time") + @Temporal(value = TemporalType.TIMESTAMP) + private Date startTime; + + @Column(name = "end_time") + @Temporal(value = TemporalType.TIMESTAMP) + private Date endTime; + + @Override + public long getId() { + return 0; + } + + @Override + public String getUuid() { + return uuid; + } + + public long getEventId() { + return eventId; + } + + public String getEventUuid() { + return eventUuid; + } + + public String getEventType() { + return eventType; + } + + public long getWebhookId() { + return webhookId; + } + + public String getWebhookUuId() { + return webhookUuId; + } + + public String getWebhookName() { + return webhookName; + } + + public long getManagementServerId() { + return managementServerId; + } + + public String getManagementServerUuId() { + return managementServerUuId; + } + + public long getManagementServerMsId() { + return managementServerMsId; + } + + public String getManagementServerName() { + return managementServerName; + } + + public String getHeaders() { + return headers; + } + + public String getPayload() { + return payload; + } + + public boolean isSuccess() { + return success; + } + + public String getResponse() { + return response; + } + + public Date getStartTime() { + return startTime; + } + + public Date getEndTime() { + return endTime; + } + + @Override + public String toString() { + return String.format("WebhookDelivery [%s]", ReflectionToStringBuilderUtils.reflectOnlySelectedFields( + this, "id", "uuid", "webhookId", "startTime", "success")); + } + + public WebhookDeliveryJoinVO() { + } +} diff --git a/plugins/event-bus/webhook/src/main/java/org/apache/cloudstack/mom/webhook/vo/WebhookDeliveryVO.java b/plugins/event-bus/webhook/src/main/java/org/apache/cloudstack/mom/webhook/vo/WebhookDeliveryVO.java new file mode 100644 index 00000000000..e39f57a2663 --- /dev/null +++ b/plugins/event-bus/webhook/src/main/java/org/apache/cloudstack/mom/webhook/vo/WebhookDeliveryVO.java @@ -0,0 +1,174 @@ +// 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.mom.webhook.vo; + + +import java.util.Date; +import java.util.UUID; + +import javax.persistence.Column; +import javax.persistence.Entity; +import javax.persistence.GeneratedValue; +import javax.persistence.GenerationType; +import javax.persistence.Id; +import javax.persistence.Table; +import javax.persistence.Temporal; +import javax.persistence.TemporalType; + +import org.apache.cloudstack.mom.webhook.WebhookDelivery; +import org.apache.cloudstack.utils.reflectiontostringbuilderutils.ReflectionToStringBuilderUtils; + +@Entity +@Table(name = "webhook_delivery") +public class WebhookDeliveryVO implements WebhookDelivery { + + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + @Column(name = "id") + private long id; + + @Column(name = "uuid") + private String uuid; + + @Column(name = "event_id") + private long eventId; + + @Column(name = "webhook_id") + private long webhookId; + + @Column(name = "mshost_msid") + private long mangementServerId; + + @Column(name = "headers", length = 65535) + private String headers; + + @Column(name = "payload", length = 65535) + private String payload; + + @Column(name = "success") + private boolean success; + + @Column(name = "response", length = 65535) + private String response; + + @Column(name = "start_time") + @Temporal(value = TemporalType.TIMESTAMP) + private Date startTime; + + @Column(name = "end_time") + @Temporal(value = TemporalType.TIMESTAMP) + private Date endTime; + + @Override + public long getId() { + return id; + } + + @Override + public String getUuid() { + return uuid; + } + + @Override + public long getEventId() { + return eventId; + } + + @Override + public long getWebhookId() { + return webhookId; + } + + @Override + public long getManagementServerId() { + return mangementServerId; + } + + public String getHeaders() { + return headers; + } + + @Override + public String getPayload() { + return payload; + } + + @Override + public boolean isSuccess() { + return success; + } + + @Override + public String getResponse() { + return response; + } + + @Override + public Date getStartTime() { + return startTime; + } + + @Override + public Date getEndTime() { + return endTime; + } + + @Override + public String toString() { + return String.format("WebhookDelivery [%s]", ReflectionToStringBuilderUtils.reflectOnlySelectedFields( + this, "id", "uuid", "webhookId", "startTime", "success")); + } + + public WebhookDeliveryVO() { + this.uuid = UUID.randomUUID().toString(); + } + + public WebhookDeliveryVO(long eventId, long webhookId, long managementServerId, String headers, String payload, + boolean success, String response, Date startTime, Date endTime) { + this.uuid = UUID.randomUUID().toString(); + this.eventId = eventId; + this.webhookId = webhookId; + this.mangementServerId = managementServerId; + this.headers = headers; + this.payload = payload; + this.success = success; + this.response = response; + this.startTime = startTime; + this.endTime = endTime; + } + + + + /* + * For creating a dummy object for testing delivery + */ + public WebhookDeliveryVO(long managementServerId, String headers, String payload, boolean success, + String response, Date startTime, Date endTime) { + this.id = WebhookDelivery.ID_DUMMY; + this.uuid = UUID.randomUUID().toString(); + this.eventId = WebhookDelivery.ID_DUMMY; + this.webhookId = WebhookDelivery.ID_DUMMY; + this.mangementServerId = managementServerId; + this.headers = headers; + this.payload = payload; + this.success = success; + this.response = response; + this.startTime = startTime; + this.endTime = endTime; + } +} diff --git a/plugins/event-bus/webhook/src/main/java/org/apache/cloudstack/mom/webhook/vo/WebhookJoinVO.java b/plugins/event-bus/webhook/src/main/java/org/apache/cloudstack/mom/webhook/vo/WebhookJoinVO.java new file mode 100644 index 00000000000..f1708609587 --- /dev/null +++ b/plugins/event-bus/webhook/src/main/java/org/apache/cloudstack/mom/webhook/vo/WebhookJoinVO.java @@ -0,0 +1,234 @@ +// 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.mom.webhook.vo; + + +import java.util.Date; + +import javax.persistence.Column; +import javax.persistence.Entity; +import javax.persistence.EnumType; +import javax.persistence.Enumerated; +import javax.persistence.Id; +import javax.persistence.Table; + +import org.apache.cloudstack.mom.webhook.Webhook; +import org.apache.cloudstack.utils.reflectiontostringbuilderutils.ReflectionToStringBuilderUtils; + +import com.cloud.api.query.vo.ControlledViewEntity; +import com.cloud.user.Account; +import com.cloud.utils.db.Encrypt; +import com.cloud.utils.db.GenericDao; + +@Entity +@Table(name = "webhook_view") +public class WebhookJoinVO implements ControlledViewEntity { + + @Id + @Column(name = "id", updatable = false, nullable = false) + private long id; + + @Column(name = "uuid") + private String uuid; + + @Column(name = "name") + private String name; + + @Column(name = "description", length = 4096) + private String description; + + @Column(name = "state") + @Enumerated(value = EnumType.STRING) + private Webhook.State state; + + @Column(name = "payload_url") + private String payloadUrl; + + @Column(name = "secret_key") + @Encrypt + private String secretKey; + + @Column(name = "ssl_verification") + private boolean sslVerification; + + @Column(name = "scope") + @Enumerated(value = EnumType.STRING) + private Webhook.Scope scope; + + @Column(name = GenericDao.CREATED_COLUMN) + private Date created; + + @Column(name = GenericDao.REMOVED_COLUMN) + private Date removed; + + @Column(name = "account_id") + private long accountId; + + @Column(name = "account_uuid") + private String accountUuid; + + @Column(name = "account_name") + private String accountName; + + @Column(name = "account_type") + @Enumerated(value = EnumType.STRING) + private Account.Type accountType; + + @Column(name = "domain_id") + private long domainId; + + @Column(name = "domain_uuid") + private String domainUuid; + + @Column(name = "domain_name") + private String domainName; + + @Column(name = "domain_path") + private String domainPath; + + @Column(name = "project_id") + private long projectId; + + @Column(name = "project_uuid") + private String projectUuid; + + @Column(name = "project_name") + private String projectName; + + @Override + public long getId() { + return id; + } + + @Override + public String getUuid() { + return uuid; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public String getDescription() { + return description; + } + + public void setDescription(String description) { + this.description = description; + } + + public Webhook.State getState() { + return state; + } + + public String getPayloadUrl() { + return payloadUrl; + } + + public void setPayloadUrl(String payloadUrl) { + this.payloadUrl = payloadUrl; + } + + public String getSecretKey() { + return secretKey; + } + + public Webhook.Scope getScope() { + return scope; + } + + public boolean isSslVerification() { + return sslVerification; + } + + public Date getCreated() { + return created; + } + + public Date getRemoved() { + return removed; + } + + @Override + public long getDomainId() { + return domainId; + } + + @Override + public String getDomainPath() { + return domainPath; + } + + @Override + public String getDomainUuid() { + return domainUuid; + } + + @Override + public String getDomainName() { + return domainName; + } + + @Override + public Account.Type getAccountType() { + return accountType; + } + + @Override + public long getAccountId() { + return accountId; + } + + @Override + public String getAccountUuid() { + return accountUuid; + } + + @Override + public String getAccountName() { + return accountName; + } + + @Override + public String getProjectUuid() { + return projectUuid; + } + + @Override + public String getProjectName() { + return projectName; + } + + @Override + public Class getEntityType() { + return Webhook.class; + } + + @Override + public String toString() { + return String.format("Webhook [%s]", ReflectionToStringBuilderUtils.reflectOnlySelectedFields( + this, "id", "uuid", "name")); + } + + public WebhookJoinVO() { + } +} diff --git a/plugins/event-bus/webhook/src/main/java/org/apache/cloudstack/mom/webhook/vo/WebhookVO.java b/plugins/event-bus/webhook/src/main/java/org/apache/cloudstack/mom/webhook/vo/WebhookVO.java new file mode 100644 index 00000000000..93e3e801423 --- /dev/null +++ b/plugins/event-bus/webhook/src/main/java/org/apache/cloudstack/mom/webhook/vo/WebhookVO.java @@ -0,0 +1,232 @@ +// 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.mom.webhook.vo; + + +import java.util.Date; +import java.util.UUID; + +import javax.persistence.Column; +import javax.persistence.Entity; +import javax.persistence.EnumType; +import javax.persistence.Enumerated; +import javax.persistence.GeneratedValue; +import javax.persistence.GenerationType; +import javax.persistence.Id; +import javax.persistence.Table; + +import org.apache.cloudstack.mom.webhook.Webhook; +import org.apache.cloudstack.utils.reflectiontostringbuilderutils.ReflectionToStringBuilderUtils; + +import com.cloud.utils.db.Encrypt; +import com.cloud.utils.db.GenericDao; + +@Entity +@Table(name = "webhook") +public class WebhookVO implements Webhook { + + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + @Column(name = "id") + private long id; + + @Column(name = "uuid") + private String uuid; + + @Column(name = "name") + private String name; + + @Column(name = "description", length = 4096) + private String description; + + @Column(name = "state") + @Enumerated(value = EnumType.STRING) + private State state; + + @Column(name = "domain_id") + private long domainId; + + @Column(name = "account_id") + private long accountId; + + @Column(name = "payload_url") + private String payloadUrl; + + @Column(name = "secret_key") + @Encrypt + private String secretKey; + + @Column(name = "ssl_verification") + private boolean sslVerification; + + @Column(name = "scope") + @Enumerated(value = EnumType.STRING) + private Scope scope; + + @Column(name = GenericDao.CREATED_COLUMN) + private Date created; + + @Column(name = GenericDao.REMOVED_COLUMN) + private Date removed; + + @Override + public long getId() { + return id; + } + + @Override + public String getUuid() { + return uuid; + } + + @Override + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + @Override + public String getDescription() { + return description; + } + + public void setDescription(String description) { + this.description = description; + } + + @Override + public State getState() { + return state; + } + + public void setState(State state) { + this.state = state; + } + + @Override + public long getDomainId() { + return domainId; + } + + public void setDomainId(long domainId) { + this.domainId = domainId; + } + + @Override + public long getAccountId() { + return accountId; + } + + public void setAccountId(long accountId) { + this.accountId = accountId; + } + + @Override + public String getPayloadUrl() { + return payloadUrl; + } + + public void setPayloadUrl(String payloadUrl) { + this.payloadUrl = payloadUrl; + } + + @Override + public String getSecretKey() { + return secretKey; + } + + public void setSecretKey(String secretKey) { + this.secretKey = secretKey; + } + + @Override + public Scope getScope() { + return scope; + } + + public void setScope(Scope scope) { + this.scope = scope; + } + + @Override + public boolean isSslVerification() { + return sslVerification; + } + + public void setSslVerification(boolean sslVerification) { + this.sslVerification = sslVerification; + } + + @Override + public Date getCreated() { + return created; + } + + public Date getRemoved() { + return removed; + } + + @Override + public Class getEntityType() { + return Webhook.class; + } + + @Override + public String toString() { + return String.format("Webhook [%s]",ReflectionToStringBuilderUtils.reflectOnlySelectedFields( + this, "id", "uuid", "name", "payloadUrl")); + } + + public WebhookVO() { + this.uuid = UUID.randomUUID().toString(); + } + + public WebhookVO(String name, String description, State state, long domainId, long accountId, + String payloadUrl, String secretKey, boolean sslVerification, Scope scope) { + this.uuid = UUID.randomUUID().toString(); + this.name = name; + this.description = description; + this.state = state; + this.domainId = domainId; + this.accountId = accountId; + this.payloadUrl = payloadUrl; + this.secretKey = secretKey; + this.sslVerification = sslVerification; + this.scope = scope; + } + + /* + * For creating a dummy rule for testing delivery + */ + public WebhookVO(long domainId, long accountId, String payloadUrl, String secretKey, boolean sslVerification) { + this.uuid = UUID.randomUUID().toString(); + this.id = ID_DUMMY; + this.name = NAME_DUMMY; + this.description = NAME_DUMMY; + this.state = State.Enabled; + this.domainId = domainId; + this.accountId = accountId; + this.payloadUrl = payloadUrl; + this.secretKey = secretKey; + this.sslVerification = sslVerification; + this.scope = Scope.Local; + } +} diff --git a/plugins/event-bus/webhook/src/main/resources/META-INF/cloudstack/webhook/module.properties b/plugins/event-bus/webhook/src/main/resources/META-INF/cloudstack/webhook/module.properties new file mode 100644 index 00000000000..299144ff82a --- /dev/null +++ b/plugins/event-bus/webhook/src/main/resources/META-INF/cloudstack/webhook/module.properties @@ -0,0 +1,18 @@ +# 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. +name=webhook +parent=event diff --git a/plugins/event-bus/webhook/src/main/resources/META-INF/cloudstack/webhook/spring-event-webhook-context.xml b/plugins/event-bus/webhook/src/main/resources/META-INF/cloudstack/webhook/spring-event-webhook-context.xml new file mode 100644 index 00000000000..22f688c781f --- /dev/null +++ b/plugins/event-bus/webhook/src/main/resources/META-INF/cloudstack/webhook/spring-event-webhook-context.xml @@ -0,0 +1,41 @@ + + + + + + + + + + + + + + + diff --git a/plugins/event-bus/webhook/src/test/java/org/apache/cloudstack/mom/webhook/WebhookApiServiceImplTest.java b/plugins/event-bus/webhook/src/test/java/org/apache/cloudstack/mom/webhook/WebhookApiServiceImplTest.java new file mode 100644 index 00000000000..dff35806984 --- /dev/null +++ b/plugins/event-bus/webhook/src/test/java/org/apache/cloudstack/mom/webhook/WebhookApiServiceImplTest.java @@ -0,0 +1,253 @@ +// 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.mom.webhook; + +import java.util.List; + +import org.apache.cloudstack.acl.SecurityChecker; +import org.apache.cloudstack.context.CallContext; +import org.apache.cloudstack.mom.webhook.api.command.user.DeleteWebhookCmd; +import org.apache.cloudstack.mom.webhook.api.response.WebhookResponse; +import org.apache.cloudstack.mom.webhook.dao.WebhookDao; +import org.apache.cloudstack.mom.webhook.dao.WebhookJoinDao; +import org.apache.cloudstack.mom.webhook.vo.WebhookJoinVO; +import org.apache.cloudstack.mom.webhook.vo.WebhookVO; +import org.apache.commons.collections.CollectionUtils; +import org.junit.Assert; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.MockedStatic; +import org.mockito.Mockito; +import org.mockito.junit.MockitoJUnitRunner; +import org.springframework.test.util.ReflectionTestUtils; + +import com.cloud.api.ApiResponseHelper; +import com.cloud.domain.DomainVO; +import com.cloud.domain.dao.DomainDao; +import com.cloud.exception.InvalidParameterValueException; +import com.cloud.exception.PermissionDeniedException; +import com.cloud.user.Account; +import com.cloud.user.AccountManager; + +@RunWith(MockitoJUnitRunner.class) +public class WebhookApiServiceImplTest { + + @Mock + WebhookDao webhookDao; + @Mock + WebhookJoinDao webhookJoinDao; + @Mock + AccountManager accountManager; + + @Mock + DomainDao domainDao; + + @InjectMocks + WebhookApiServiceImpl webhookApiServiceImpl = new WebhookApiServiceImpl(); + + private WebhookJoinVO prepareTestWebhookJoinVO() { + String name = "webhook"; + String description = "webhook-description"; + Webhook.State state = Webhook.State.Enabled; + String payloadUrl = "url"; + String secretKey = "key"; + boolean sslVerification = false; + Webhook.Scope scope = Webhook.Scope.Local; + WebhookJoinVO webhookJoinVO = new WebhookJoinVO(); + ReflectionTestUtils.setField(webhookJoinVO, "name", name); + ReflectionTestUtils.setField(webhookJoinVO, "description", description); + ReflectionTestUtils.setField(webhookJoinVO, "state", state); + ReflectionTestUtils.setField(webhookJoinVO, "payloadUrl", payloadUrl); + ReflectionTestUtils.setField(webhookJoinVO, "secretKey", secretKey); + ReflectionTestUtils.setField(webhookJoinVO, "sslVerification", sslVerification); + ReflectionTestUtils.setField(webhookJoinVO, "scope", scope); + return webhookJoinVO; + } + + private void validateWebhookResponseWithWebhookJoinVO(WebhookResponse response, WebhookJoinVO webhookJoinVO) { + Assert.assertEquals(webhookJoinVO.getName(), ReflectionTestUtils.getField(response, "name")); + Assert.assertEquals(webhookJoinVO.getDescription(), ReflectionTestUtils.getField(response, "description")); + Assert.assertEquals(webhookJoinVO.getState().toString(), ReflectionTestUtils.getField(response, "state")); + Assert.assertEquals(webhookJoinVO.getPayloadUrl(), ReflectionTestUtils.getField(response, "payloadUrl")); + Assert.assertEquals(webhookJoinVO.getSecretKey(), ReflectionTestUtils.getField(response, "secretKey")); + Assert.assertEquals(webhookJoinVO.isSslVerification(), ReflectionTestUtils.getField(response, "sslVerification")); + Assert.assertEquals(webhookJoinVO.getScope().toString(), ReflectionTestUtils.getField(response, "scope")); + } + + @Test + public void testCreateWebhookResponse() { + WebhookJoinVO webhookJoinVO = prepareTestWebhookJoinVO(); + try (MockedStatic mockedApiResponseHelper = Mockito.mockStatic(ApiResponseHelper.class)) { + WebhookResponse response = webhookApiServiceImpl.createWebhookResponse(webhookJoinVO); + validateWebhookResponseWithWebhookJoinVO(response, webhookJoinVO); + } + } + + @Test + public void testCreateWebhookResponseId() { + WebhookJoinVO webhookJoinVO = prepareTestWebhookJoinVO(); + long id = 1L; + Mockito.when(webhookJoinDao.findById(id)).thenReturn(webhookJoinVO); + try (MockedStatic mockedApiResponseHelper = Mockito.mockStatic(ApiResponseHelper.class)) { + WebhookResponse response = webhookApiServiceImpl.createWebhookResponse(id); + validateWebhookResponseWithWebhookJoinVO(response, webhookJoinVO); + } + } + + @Test + public void testGetIdsOfAccessibleWebhooksAdmin() { + Account account = Mockito.mock(Account.class); + Mockito.when(account.getType()).thenReturn(Account.Type.ADMIN); + Assert.assertTrue(CollectionUtils.isEmpty(webhookApiServiceImpl.getIdsOfAccessibleWebhooks(account))); + } + + @Test + public void testGetIdsOfAccessibleWebhooksDomainAdmin() { + Long accountId = 1L; + Account account = Mockito.mock(Account.class); + Mockito.when(account.getType()).thenReturn(Account.Type.DOMAIN_ADMIN); + Mockito.when(account.getDomainId()).thenReturn(1L); + Mockito.when(account.getId()).thenReturn(accountId); + String domainPath = "d1"; + DomainVO domain = Mockito.mock(DomainVO.class); + Mockito.when(domain.getPath()).thenReturn(domainPath); + Mockito.when(domainDao.findById(1L)).thenReturn(domain); + WebhookJoinVO webhookJoinVO = Mockito.mock(WebhookJoinVO.class); + Mockito.when(webhookJoinVO.getId()).thenReturn(1L); + Mockito.when(webhookJoinDao.listByAccountOrDomain(accountId, domainPath)).thenReturn(List.of(webhookJoinVO)); + List result = webhookApiServiceImpl.getIdsOfAccessibleWebhooks(account); + Assert.assertTrue(CollectionUtils.isNotEmpty(result)); + Assert.assertEquals(1, result.size()); + } + + @Test + public void testGetIdsOfAccessibleWebhooksNormalUser() { + Long accountId = 1L; + Account account = Mockito.mock(Account.class); + Mockito.when(account.getType()).thenReturn(Account.Type.NORMAL); + Mockito.when(account.getId()).thenReturn(accountId); + WebhookJoinVO webhookJoinVO = Mockito.mock(WebhookJoinVO.class); + Mockito.when(webhookJoinVO.getId()).thenReturn(1L); + Mockito.when(webhookJoinDao.listByAccountOrDomain(accountId, null)).thenReturn(List.of(webhookJoinVO)); + List result = webhookApiServiceImpl.getIdsOfAccessibleWebhooks(account); + Assert.assertTrue(CollectionUtils.isNotEmpty(result)); + Assert.assertEquals(1, result.size()); + } + + @Test(expected = InvalidParameterValueException.class) + public void testDeleteWebhookInvalidWebhook() { + try (MockedStatic callContextMocked = Mockito.mockStatic(CallContext.class)) { + DeleteWebhookCmd cmd = Mockito.mock(DeleteWebhookCmd.class); + Mockito.when(cmd.getId()).thenReturn(1L); + CallContext callContextMock = Mockito.mock(CallContext.class); + callContextMocked.when(CallContext::current).thenReturn(callContextMock); + webhookApiServiceImpl.deleteWebhook(cmd); + } + } + + @Test(expected = PermissionDeniedException.class) + public void testDeleteWebhookNoPermission() { + try (MockedStatic callContextMocked = Mockito.mockStatic(CallContext.class)) { + DeleteWebhookCmd cmd = Mockito.mock(DeleteWebhookCmd.class); + Mockito.when(cmd.getId()).thenReturn(1L); + WebhookVO webhookVO = Mockito.mock(WebhookVO.class); + Mockito.when(webhookDao.findById(1L)).thenReturn(webhookVO); + CallContext callContextMock = Mockito.mock(CallContext.class); + Account account = Mockito.mock(Account.class); + Mockito.when(callContextMock.getCallingAccount()).thenReturn(account); + callContextMocked.when(CallContext::current).thenReturn(callContextMock); + Mockito.doThrow(PermissionDeniedException.class).when(accountManager).checkAccess(account, + SecurityChecker.AccessType.OperateEntry, false, webhookVO); + webhookApiServiceImpl.deleteWebhook(cmd); + } + } + + @Test + public void testDeleteWebhook() { + try (MockedStatic callContextMocked = Mockito.mockStatic(CallContext.class)) { + DeleteWebhookCmd cmd = Mockito.mock(DeleteWebhookCmd.class); + Mockito.when(cmd.getId()).thenReturn(1L); + WebhookVO webhookVO = Mockito.mock(WebhookVO.class); + Mockito.when(webhookDao.findById(1L)).thenReturn(webhookVO); + CallContext callContextMock = Mockito.mock(CallContext.class); + Account account = Mockito.mock(Account.class); + Mockito.when(callContextMock.getCallingAccount()).thenReturn(account); + callContextMocked.when(CallContext::current).thenReturn(callContextMock); + Mockito.doNothing().when(accountManager).checkAccess(account, + SecurityChecker.AccessType.OperateEntry, false, webhookVO); + Mockito.doReturn(true).when(webhookDao).remove(Mockito.anyLong()); + Assert.assertTrue(webhookApiServiceImpl.deleteWebhook(cmd)); + } + } + + @Test + public void testValidateWebhookOwnerPayloadUrlNonExistent() { + Mockito.when(webhookDao.findByAccountAndPayloadUrl(Mockito.anyLong(), Mockito.anyString())).thenReturn(null); + Account account = Mockito.mock(Account.class); + String url = "url"; + webhookApiServiceImpl.validateWebhookOwnerPayloadUrl(account, url, null); + webhookApiServiceImpl.validateWebhookOwnerPayloadUrl(account, url, Mockito.mock(Webhook.class)); + } + + @Test(expected = InvalidParameterValueException.class) + public void testValidateWebhookOwnerPayloadUrlCreateExist() { + Mockito.when(webhookDao.findByAccountAndPayloadUrl(Mockito.anyLong(), Mockito.anyString())) + .thenReturn(Mockito.mock(WebhookVO.class)); + webhookApiServiceImpl.validateWebhookOwnerPayloadUrl(Mockito.mock(Account.class), "url", + null); + } + + private Webhook mockWebhook(long id) { + Webhook webhook = Mockito.mock(Webhook.class); + Mockito.when(webhook.getId()).thenReturn(id); + return webhook; + } + + private WebhookVO mockWebhookVO(long id) { + WebhookVO webhook = Mockito.mock(WebhookVO.class); + Mockito.when(webhook.getId()).thenReturn(id); + return webhook; + } + + @Test + public void testValidateWebhookOwnerPayloadUrlUpdateSameExist() { + WebhookVO webhookVO = mockWebhookVO(1L); + Mockito.when(webhookDao.findByAccountAndPayloadUrl(Mockito.anyLong(), Mockito.anyString())) + .thenReturn(webhookVO); + webhookApiServiceImpl.validateWebhookOwnerPayloadUrl(Mockito.mock(Account.class), "url", + mockWebhook(1L)); + } + + @Test(expected = InvalidParameterValueException.class) + public void testValidateWebhookOwnerPayloadUrlUpdateDifferentExist() { + WebhookVO webhookVO = mockWebhookVO(2L); + Mockito.when(webhookDao.findByAccountAndPayloadUrl(Mockito.anyLong(), Mockito.anyString())) + .thenReturn(webhookVO); + webhookApiServiceImpl.validateWebhookOwnerPayloadUrl(Mockito.mock(Account.class), "url", + mockWebhook(1L)); + } + + @Test + public void testGetNormalizedPayloadUrl() { + Assert.assertEquals("http://abc.com", webhookApiServiceImpl.getNormalizedPayloadUrl("abc.com")); + Assert.assertEquals("http://abc.com", webhookApiServiceImpl.getNormalizedPayloadUrl("http://abc.com")); + Assert.assertEquals("https://abc.com", + webhookApiServiceImpl.getNormalizedPayloadUrl("https://abc.com")); + } +} diff --git a/plugins/event-bus/webhook/src/test/java/org/apache/cloudstack/mom/webhook/WebhookDeliveryThreadTest.java b/plugins/event-bus/webhook/src/test/java/org/apache/cloudstack/mom/webhook/WebhookDeliveryThreadTest.java new file mode 100644 index 00000000000..3be8dee5c2e --- /dev/null +++ b/plugins/event-bus/webhook/src/test/java/org/apache/cloudstack/mom/webhook/WebhookDeliveryThreadTest.java @@ -0,0 +1,62 @@ +// 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.mom.webhook; + +import java.security.InvalidKeyException; +import java.security.NoSuchAlgorithmException; + +import org.apache.commons.codec.DecoderException; +import org.junit.Assert; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.InjectMocks; +import org.mockito.junit.MockitoJUnitRunner; +import org.springframework.test.util.ReflectionTestUtils; + +@RunWith(MockitoJUnitRunner.class) +public class WebhookDeliveryThreadTest { + @InjectMocks + WebhookDeliveryThread webhookDeliveryThread; + + @Test + public void testIsValidJson() { + Assert.assertFalse(webhookDeliveryThread.isValidJson("text")); + Assert.assertTrue(webhookDeliveryThread.isValidJson("{ \"CloudStack\": \"works!\" }")); + Assert.assertTrue(webhookDeliveryThread.isValidJson("[{ \"CloudStack\": \"works!\" }]")); + } + + @Test + public void testGenerateHMACSignature() { + String data = "CloudStack works!"; + String key = "Pj4pnwSUBZ4wQFXw2zWdVY1k5Ku9bIy70wCNG1DmS8keO7QapCLw2Axtgc2nEPYzfFCfB38ATNLt6caDqU2dSw"; + String result = "HYLWSII5Ap23WeSaykNsIo6mOhmV3d18s5p2cq2ebCA="; + try { + String sign = WebhookDeliveryThread.generateHMACSignature(data, key); + Assert.assertEquals(result, sign); + } catch (InvalidKeyException | NoSuchAlgorithmException | DecoderException e) { + Assert.fail(e.getMessage()); + } + } + + @Test + public void testSetDeliveryTries() { + int tries = 2; + webhookDeliveryThread.setDeliveryTries(tries); + Assert.assertEquals(tries, ReflectionTestUtils.getField(webhookDeliveryThread, "deliveryTries")); + } +} diff --git a/plugins/event-bus/webhook/src/test/java/org/apache/cloudstack/mom/webhook/WebhookEventBusTest.java b/plugins/event-bus/webhook/src/test/java/org/apache/cloudstack/mom/webhook/WebhookEventBusTest.java new file mode 100644 index 00000000000..ebd3f9e828c --- /dev/null +++ b/plugins/event-bus/webhook/src/test/java/org/apache/cloudstack/mom/webhook/WebhookEventBusTest.java @@ -0,0 +1,106 @@ +// 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.mom.webhook; + +import java.util.HashMap; +import java.util.UUID; + +import javax.naming.ConfigurationException; + +import org.apache.cloudstack.framework.events.Event; +import org.apache.cloudstack.framework.events.EventBusException; +import org.apache.cloudstack.framework.events.EventSubscriber; +import org.apache.cloudstack.framework.events.EventTopic; +import org.junit.Assert; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.Mockito; +import org.mockito.junit.MockitoJUnitRunner; +import org.springframework.test.util.ReflectionTestUtils; + +@RunWith(MockitoJUnitRunner.class) +public class WebhookEventBusTest { + + @Mock + WebhookService webhookService; + @InjectMocks + WebhookEventBus eventBus = new WebhookEventBus(); + + @Test + public void testConfigure() { + String name = "name"; + try { + Assert.assertTrue(eventBus.configure(name, new HashMap<>())); + String result = (String)ReflectionTestUtils.getField(eventBus, "_name"); + Assert.assertEquals(name, result); + } catch (ConfigurationException e) { + Assert.fail("Error configuring"); + } + } + + @Test + public void testSetName() { + String name = "name"; + eventBus.setName(name); + String result = (String)ReflectionTestUtils.getField(eventBus, "_name"); + Assert.assertEquals(name, result); + } + + @Test + public void testGetName() { + String name = "name"; + ReflectionTestUtils.setField(eventBus, "_name", name); + Assert.assertEquals(name, eventBus.getName()); + } + + @Test + public void testStart() { + Assert.assertTrue(eventBus.start()); + } + + @Test + public void testStop() { + Assert.assertTrue(eventBus.stop()); + } + + @Test + public void testSubscribe() { + try { + Assert.assertNotNull(eventBus.subscribe(Mockito.mock(EventTopic.class), Mockito.mock(EventSubscriber.class))); + } catch (EventBusException e) { + Assert.fail("Error subscribing"); + } + } + + @Test + public void testUnsubscribe() { + try { + eventBus.unsubscribe(Mockito.mock(UUID.class), Mockito.mock(EventSubscriber.class)); + } catch (EventBusException e) { + Assert.fail("Error unsubscribing"); + } + } + + @Test(expected = EventBusException.class) + public void testPublishException() throws EventBusException { + Mockito.doThrow(EventBusException.class).when(webhookService).handleEvent(Mockito.any(Event.class)); + eventBus.publish(Mockito.mock(Event.class)); + } +} diff --git a/plugins/event-bus/webhook/src/test/java/org/apache/cloudstack/mom/webhook/api/command/user/CreateWebhookCmdTest.java b/plugins/event-bus/webhook/src/test/java/org/apache/cloudstack/mom/webhook/api/command/user/CreateWebhookCmdTest.java new file mode 100644 index 00000000000..7736a42af04 --- /dev/null +++ b/plugins/event-bus/webhook/src/test/java/org/apache/cloudstack/mom/webhook/api/command/user/CreateWebhookCmdTest.java @@ -0,0 +1,173 @@ +// 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.mom.webhook.api.command.user; + +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.util.UUID; + +import org.apache.cloudstack.api.ServerApiException; +import org.apache.cloudstack.context.CallContext; +import org.apache.cloudstack.mom.webhook.WebhookApiService; +import org.apache.cloudstack.mom.webhook.api.response.WebhookResponse; +import org.junit.Assert; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mock; +import org.mockito.Mockito; +import org.mockito.junit.MockitoJUnitRunner; +import org.springframework.test.util.ReflectionTestUtils; + +import com.cloud.user.Account; +import com.cloud.user.AccountVO; +import com.cloud.user.User; +import com.cloud.user.UserVO; +import com.cloud.utils.exception.CloudRuntimeException; + +@RunWith(MockitoJUnitRunner.class) +public class CreateWebhookCmdTest { + + @Mock + WebhookApiService webhookApiService; + + private Object getCommandMethodValue(Object obj, String methodName) { + Object result = null; + try { + Method method = obj.getClass().getMethod(methodName); + result = method.invoke(obj); + } catch (NoSuchMethodException | InvocationTargetException | IllegalAccessException e) { + Assert.fail(String.format("Failed to get method %s value", methodName)); + } + return result; + } + + private void runStringMemberTest(String memberName) { + String methodName = "get" + memberName.substring(0, 1).toUpperCase() + memberName.substring(1); + CreateWebhookCmd cmd = new CreateWebhookCmd(); + ReflectionTestUtils.setField(cmd, memberName, null); + Assert.assertNull(getCommandMethodValue(cmd, methodName)); + String value = UUID.randomUUID().toString(); + ReflectionTestUtils.setField(cmd, memberName, value); + Assert.assertEquals(value, getCommandMethodValue(cmd, methodName)); + } + + private void runLongMemberTest(String memberName) { + String methodName = "get" + memberName.substring(0, 1).toUpperCase() + memberName.substring(1); + CreateWebhookCmd cmd = new CreateWebhookCmd(); + ReflectionTestUtils.setField(cmd, memberName, null); + Assert.assertNull(getCommandMethodValue(cmd, methodName)); + Long value = 100L; + ReflectionTestUtils.setField(cmd, memberName, value); + Assert.assertEquals(value, getCommandMethodValue(cmd, methodName)); + } + + private void runBooleanMemberTest(String memberName) { + String methodName = "is" + memberName.substring(0, 1).toUpperCase() + memberName.substring(1); + CreateWebhookCmd cmd = new CreateWebhookCmd(); + ReflectionTestUtils.setField(cmd, memberName, null); + Assert.assertFalse((boolean)getCommandMethodValue(cmd, methodName)); + Boolean value = true; + ReflectionTestUtils.setField(cmd, memberName, value); + Assert.assertEquals(value, getCommandMethodValue(cmd, methodName)); + } + + @Test + public void testGetName() { + runStringMemberTest("name"); + } + + @Test + public void testGetDescription() { + runStringMemberTest("description"); + } + + @Test + public void testGetPayloadUrl() { + runStringMemberTest("payloadUrl"); + } + + @Test + public void testGetSecretKey() { + runStringMemberTest("secretKey"); + } + + @Test + public void testGetScope() { + runStringMemberTest("scope"); + } + + @Test + public void testGetState() { + runStringMemberTest("state"); + } + + @Test + public void testGetAccount() { + runStringMemberTest("accountName"); + } + + @Test + public void testGetDomainId() { + runLongMemberTest("domainId"); + } + + @Test + public void testGetProjectId() { + runLongMemberTest("projectId"); + } + + @Test + public void testIsSslVerification() { + runBooleanMemberTest("sslVerification"); + } + + @Test + public void testGetEntityOwnerId() { + Account account = new AccountVO("testaccount", 1L, "networkdomain", Account.Type.NORMAL, "uuid"); + UserVO user = new UserVO(1, "testuser", "password", "firstname", "lastName", "email", "timezone", UUID.randomUUID().toString(), User.Source.UNKNOWN); + CallContext.register(user, account); + CreateWebhookCmd cmd = new CreateWebhookCmd(); + Assert.assertEquals(account.getId(), cmd.getEntityOwnerId()); + } + + @Test(expected = ServerApiException.class) + public void testExecuteNullResponse() { + CreateWebhookCmd cmd = new CreateWebhookCmd(); + cmd.webhookApiService = webhookApiService; + Mockito.when(webhookApiService.createWebhook(cmd)).thenReturn(null); + cmd.execute(); + } + + @Test(expected = ServerApiException.class) + public void testExecuteCRE() { + CreateWebhookCmd cmd = new CreateWebhookCmd(); + cmd.webhookApiService = webhookApiService; + Mockito.when(webhookApiService.createWebhook(cmd)).thenThrow(CloudRuntimeException.class); + cmd.execute(); + } + + @Test + public void testExecute() { + CreateWebhookCmd cmd = new CreateWebhookCmd(); + cmd.webhookApiService = webhookApiService; + WebhookResponse response = new WebhookResponse(); + Mockito.when(webhookApiService.createWebhook(cmd)).thenReturn(response); + cmd.execute(); + Assert.assertEquals(cmd.getCommandName(), response.getResponseName()); + } +} diff --git a/plugins/event-bus/webhook/src/test/java/org/apache/cloudstack/mom/webhook/api/command/user/DeleteWebhookCmdTest.java b/plugins/event-bus/webhook/src/test/java/org/apache/cloudstack/mom/webhook/api/command/user/DeleteWebhookCmdTest.java new file mode 100644 index 00000000000..e9aa61aabb8 --- /dev/null +++ b/plugins/event-bus/webhook/src/test/java/org/apache/cloudstack/mom/webhook/api/command/user/DeleteWebhookCmdTest.java @@ -0,0 +1,106 @@ +// 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.mom.webhook.api.command.user; + +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.util.UUID; + +import org.apache.cloudstack.api.ServerApiException; +import org.apache.cloudstack.context.CallContext; +import org.apache.cloudstack.mom.webhook.WebhookApiService; +import org.junit.Assert; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mock; +import org.mockito.Mockito; +import org.mockito.junit.MockitoJUnitRunner; +import org.springframework.test.util.ReflectionTestUtils; + +import com.cloud.user.Account; +import com.cloud.user.AccountVO; +import com.cloud.user.User; +import com.cloud.user.UserVO; +import com.cloud.utils.exception.CloudRuntimeException; + +@RunWith(MockitoJUnitRunner.class) +public class DeleteWebhookCmdTest { + + @Mock + WebhookApiService webhookApiService; + + private Object getCommandMethodValue(Object obj, String methodName) { + Object result = null; + try { + Method method = obj.getClass().getMethod(methodName); + result = method.invoke(obj); + } catch (NoSuchMethodException | InvocationTargetException | IllegalAccessException e) { + Assert.fail(String.format("Failed to get method %s value", methodName)); + } + return result; + } + + private void runLongMemberTest(String memberName) { + String methodName = "get" + memberName.substring(0, 1).toUpperCase() + memberName.substring(1); + DeleteWebhookCmd cmd = new DeleteWebhookCmd(); + ReflectionTestUtils.setField(cmd, memberName, null); + Assert.assertNull(getCommandMethodValue(cmd, methodName)); + Long value = 100L; + ReflectionTestUtils.setField(cmd, memberName, value); + Assert.assertEquals(value, getCommandMethodValue(cmd, methodName)); + } + + @Test + public void testGetId() { + runLongMemberTest("id"); + } + + @Test + public void testGetEntityOwnerId() { + Account account = new AccountVO("testaccount", 1L, "networkdomain", Account.Type.NORMAL, "uuid"); + UserVO user = new UserVO(1, "testuser", "password", "firstname", "lastName", "email", "timezone", UUID.randomUUID().toString(), User.Source.UNKNOWN); + CallContext.register(user, account); + DeleteWebhookCmd cmd = new DeleteWebhookCmd(); + Assert.assertEquals(account.getId(), cmd.getEntityOwnerId()); + } + + @Test(expected = ServerApiException.class) + public void testExecuteFalseResponse() { + DeleteWebhookCmd cmd = new DeleteWebhookCmd(); + cmd.webhookApiService = webhookApiService; + Mockito.when(webhookApiService.deleteWebhook(cmd)).thenReturn(false); + cmd.execute(); + } + + @Test(expected = ServerApiException.class) + public void testExecuteCRE() { + DeleteWebhookCmd cmd = new DeleteWebhookCmd(); + cmd.webhookApiService = webhookApiService; + Mockito.when(webhookApiService.deleteWebhook(cmd)).thenThrow(CloudRuntimeException.class); + cmd.execute(); + } + + @Test + public void testExecute() { + DeleteWebhookCmd cmd = new DeleteWebhookCmd(); + cmd.webhookApiService = webhookApiService; + Mockito.when(webhookApiService.deleteWebhook(cmd)).thenReturn(true); + cmd.execute(); + Assert.assertNotNull(cmd.getResponseObject()); + } +} diff --git a/plugins/event-bus/webhook/src/test/java/org/apache/cloudstack/mom/webhook/api/command/user/DeleteWebhookDeliveryCmdTest.java b/plugins/event-bus/webhook/src/test/java/org/apache/cloudstack/mom/webhook/api/command/user/DeleteWebhookDeliveryCmdTest.java new file mode 100644 index 00000000000..2a090eb7fb1 --- /dev/null +++ b/plugins/event-bus/webhook/src/test/java/org/apache/cloudstack/mom/webhook/api/command/user/DeleteWebhookDeliveryCmdTest.java @@ -0,0 +1,108 @@ +// 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.mom.webhook.api.command.user; + +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.util.UUID; + +import org.apache.cloudstack.api.ServerApiException; +import org.apache.cloudstack.context.CallContext; +import org.apache.cloudstack.mom.webhook.WebhookApiService; +import org.junit.Assert; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mock; +import org.mockito.Mockito; +import org.mockito.junit.MockitoJUnitRunner; +import org.springframework.test.util.ReflectionTestUtils; + +import com.cloud.user.Account; +import com.cloud.user.AccountVO; +import com.cloud.user.User; +import com.cloud.user.UserVO; +import com.cloud.utils.exception.CloudRuntimeException; + +@RunWith(MockitoJUnitRunner.class) +public class DeleteWebhookDeliveryCmdTest { + + @Mock + WebhookApiService webhookApiService; + + private Object getCommandMethodValue(Object obj, String methodName) { + Object result = null; + try { + Method method = obj.getClass().getMethod(methodName); + result = method.invoke(obj); + } catch (NoSuchMethodException | InvocationTargetException | IllegalAccessException e) { + Assert.fail(String.format("Failed to get method %s value", methodName)); + } + return result; + } + + private void runLongMemberTest(String memberName) { + String methodName = "get" + memberName.substring(0, 1).toUpperCase() + memberName.substring(1); + DeleteWebhookDeliveryCmd cmd = new DeleteWebhookDeliveryCmd(); + ReflectionTestUtils.setField(cmd, memberName, null); + Assert.assertNull(getCommandMethodValue(cmd, methodName)); + Long value = 100L; + ReflectionTestUtils.setField(cmd, memberName, value); + Assert.assertEquals(value, getCommandMethodValue(cmd, methodName)); + } + + @Test + public void testGetId() { + runLongMemberTest("id"); + } + + @Test + public void testGetWebhookId() { + runLongMemberTest("webhookId"); + } + + @Test + public void testGetManagementServerId() { + runLongMemberTest("managementServerId"); + } + + @Test + public void testGetEntityOwnerId() { + Account account = new AccountVO("testaccount", 1L, "networkdomain", Account.Type.NORMAL, "uuid"); + UserVO user = new UserVO(1, "testuser", "password", "firstname", "lastName", "email", "timezone", UUID.randomUUID().toString(), User.Source.UNKNOWN); + CallContext.register(user, account); + DeleteWebhookDeliveryCmd cmd = new DeleteWebhookDeliveryCmd(); + Assert.assertEquals(account.getId(), cmd.getEntityOwnerId()); + } + + @Test(expected = ServerApiException.class) + public void testExecuteCRE() { + DeleteWebhookDeliveryCmd cmd = new DeleteWebhookDeliveryCmd(); + cmd.webhookApiService = webhookApiService; + Mockito.when(webhookApiService.deleteWebhookDelivery(cmd)).thenThrow(CloudRuntimeException.class); + cmd.execute(); + } + + @Test + public void testExecute() { + DeleteWebhookDeliveryCmd cmd = new DeleteWebhookDeliveryCmd(); + cmd.webhookApiService = webhookApiService; + Mockito.when(webhookApiService.deleteWebhookDelivery(cmd)).thenReturn(10); + cmd.execute(); + Assert.assertNotNull(cmd.getResponseObject()); + } +} diff --git a/plugins/event-bus/webhook/src/test/java/org/apache/cloudstack/mom/webhook/api/command/user/ExecuteWebhookDeliveryCmdTest.java b/plugins/event-bus/webhook/src/test/java/org/apache/cloudstack/mom/webhook/api/command/user/ExecuteWebhookDeliveryCmdTest.java new file mode 100644 index 00000000000..84d51a1e18d --- /dev/null +++ b/plugins/event-bus/webhook/src/test/java/org/apache/cloudstack/mom/webhook/api/command/user/ExecuteWebhookDeliveryCmdTest.java @@ -0,0 +1,153 @@ +// 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.mom.webhook.api.command.user; + +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.util.UUID; + +import org.apache.cloudstack.api.ServerApiException; +import org.apache.cloudstack.context.CallContext; +import org.apache.cloudstack.mom.webhook.WebhookApiService; +import org.apache.cloudstack.mom.webhook.api.response.WebhookDeliveryResponse; +import org.junit.Assert; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mock; +import org.mockito.Mockito; +import org.mockito.junit.MockitoJUnitRunner; +import org.springframework.test.util.ReflectionTestUtils; + +import com.cloud.user.Account; +import com.cloud.user.AccountVO; +import com.cloud.user.User; +import com.cloud.user.UserVO; +import com.cloud.utils.exception.CloudRuntimeException; + +@RunWith(MockitoJUnitRunner.class) +public class ExecuteWebhookDeliveryCmdTest { + + @Mock + WebhookApiService webhookApiService; + + private Object getCommandMethodValue(Object obj, String methodName) { + Object result = null; + try { + Method method = obj.getClass().getMethod(methodName); + result = method.invoke(obj); + } catch (NoSuchMethodException | InvocationTargetException | IllegalAccessException e) { + Assert.fail(String.format("Failed to get method %s value", methodName)); + } + return result; + } + + private void runStringMemberTest(String memberName) { + String methodName = "get" + memberName.substring(0, 1).toUpperCase() + memberName.substring(1); + ExecuteWebhookDeliveryCmd cmd = new ExecuteWebhookDeliveryCmd(); + ReflectionTestUtils.setField(cmd, memberName, null); + Assert.assertNull(getCommandMethodValue(cmd, methodName)); + String value = UUID.randomUUID().toString(); + ReflectionTestUtils.setField(cmd, memberName, value); + Assert.assertEquals(value, getCommandMethodValue(cmd, methodName)); + } + + private void runLongMemberTest(String memberName) { + String methodName = "get" + memberName.substring(0, 1).toUpperCase() + memberName.substring(1); + ExecuteWebhookDeliveryCmd cmd = new ExecuteWebhookDeliveryCmd(); + ReflectionTestUtils.setField(cmd, memberName, null); + Assert.assertNull(getCommandMethodValue(cmd, methodName)); + Long value = 100L; + ReflectionTestUtils.setField(cmd, memberName, value); + Assert.assertEquals(value, getCommandMethodValue(cmd, methodName)); + } + + private void runBooleanMemberTest(String memberName) { + String methodName = "is" + memberName.substring(0, 1).toUpperCase() + memberName.substring(1); + ExecuteWebhookDeliveryCmd cmd = new ExecuteWebhookDeliveryCmd(); + ReflectionTestUtils.setField(cmd, memberName, null); + Assert.assertNull(getCommandMethodValue(cmd, methodName)); + Boolean value = true; + ReflectionTestUtils.setField(cmd, memberName, value); + Assert.assertEquals(value, getCommandMethodValue(cmd, methodName)); + } + + @Test + public void testGetId() { + runLongMemberTest("id"); + } + + @Test + public void testGetWebhookId() { + runLongMemberTest("webhookId"); + } + + @Test + public void testGetPayloadUrl() { + runStringMemberTest("payloadUrl"); + } + + @Test + public void testGetSecretKey() { + runStringMemberTest("secretKey"); + } + + @Test + public void testIsSslVerification() { + runBooleanMemberTest("sslVerification"); + } + + @Test + public void testGetPayload() { + runStringMemberTest("payload"); + } + + @Test + public void testGetEntityOwnerId() { + Account account = new AccountVO("testaccount", 1L, "networkdomain", Account.Type.NORMAL, "uuid"); + UserVO user = new UserVO(1, "testuser", "password", "firstname", "lastName", "email", "timezone", UUID.randomUUID().toString(), User.Source.UNKNOWN); + CallContext.register(user, account); + ExecuteWebhookDeliveryCmd cmd = new ExecuteWebhookDeliveryCmd(); + Assert.assertEquals(account.getId(), cmd.getEntityOwnerId()); + } + + @Test(expected = ServerApiException.class) + public void testExecuteNullResponse() { + ExecuteWebhookDeliveryCmd cmd = new ExecuteWebhookDeliveryCmd(); + cmd.webhookApiService = webhookApiService; + Mockito.when(webhookApiService.executeWebhookDelivery(cmd)).thenReturn(null); + cmd.execute(); + } + + @Test(expected = ServerApiException.class) + public void testExecuteCRE() { + ExecuteWebhookDeliveryCmd cmd = new ExecuteWebhookDeliveryCmd(); + cmd.webhookApiService = webhookApiService; + Mockito.when(webhookApiService.executeWebhookDelivery(cmd)).thenThrow(CloudRuntimeException.class); + cmd.execute(); + } + + @Test + public void testExecute() { + ExecuteWebhookDeliveryCmd cmd = new ExecuteWebhookDeliveryCmd(); + cmd.webhookApiService = webhookApiService; + WebhookDeliveryResponse response = new WebhookDeliveryResponse(); + Mockito.when(webhookApiService.executeWebhookDelivery(cmd)).thenReturn(response); + cmd.execute(); + Assert.assertEquals(cmd.getCommandName(), response.getResponseName()); + } +} diff --git a/plugins/event-bus/webhook/src/test/java/org/apache/cloudstack/mom/webhook/api/command/user/ListWebhookDeliveriesCmdTest.java b/plugins/event-bus/webhook/src/test/java/org/apache/cloudstack/mom/webhook/api/command/user/ListWebhookDeliveriesCmdTest.java new file mode 100644 index 00000000000..6359b042c40 --- /dev/null +++ b/plugins/event-bus/webhook/src/test/java/org/apache/cloudstack/mom/webhook/api/command/user/ListWebhookDeliveriesCmdTest.java @@ -0,0 +1,141 @@ +// 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.mom.webhook.api.command.user; + +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.util.ArrayList; +import java.util.Date; +import java.util.List; +import java.util.UUID; + +import org.apache.cloudstack.api.response.ListResponse; +import org.apache.cloudstack.context.CallContext; +import org.apache.cloudstack.mom.webhook.WebhookApiService; +import org.apache.cloudstack.mom.webhook.api.response.WebhookDeliveryResponse; +import org.junit.Assert; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mock; +import org.mockito.Mockito; +import org.mockito.junit.MockitoJUnitRunner; +import org.springframework.test.util.ReflectionTestUtils; + +import com.cloud.user.Account; +import com.cloud.user.AccountVO; +import com.cloud.user.User; +import com.cloud.user.UserVO; + +@RunWith(MockitoJUnitRunner.class) +public class ListWebhookDeliveriesCmdTest { + + @Mock + WebhookApiService webhookApiService; + + private Object getCommandMethodValue(Object obj, String methodName) { + Object result = null; + try { + Method method = obj.getClass().getMethod(methodName); + result = method.invoke(obj); + } catch (NoSuchMethodException | InvocationTargetException | IllegalAccessException e) { + Assert.fail(String.format("Failed to get method %s value", methodName)); + } + return result; + } + + private void runLongMemberTest(String memberName) { + String methodName = "get" + memberName.substring(0, 1).toUpperCase() + memberName.substring(1); + ListWebhookDeliveriesCmd cmd = new ListWebhookDeliveriesCmd(); + ReflectionTestUtils.setField(cmd, memberName, null); + Assert.assertNull(getCommandMethodValue(cmd, methodName)); + Long value = 100L; + ReflectionTestUtils.setField(cmd, memberName, value); + Assert.assertEquals(value, getCommandMethodValue(cmd, methodName)); + } + + private void runStringMemberTest(String memberName) { + String methodName = "get" + memberName.substring(0, 1).toUpperCase() + memberName.substring(1); + ListWebhookDeliveriesCmd cmd = new ListWebhookDeliveriesCmd(); + ReflectionTestUtils.setField(cmd, memberName, null); + Assert.assertNull(getCommandMethodValue(cmd, methodName)); + String value = UUID.randomUUID().toString(); + ReflectionTestUtils.setField(cmd, memberName, value); + Assert.assertEquals(value, getCommandMethodValue(cmd, methodName)); + } + + private void runDateMemberTest(String memberName) { + String methodName = "get" + memberName.substring(0, 1).toUpperCase() + memberName.substring(1); + ListWebhookDeliveriesCmd cmd = new ListWebhookDeliveriesCmd(); + ReflectionTestUtils.setField(cmd, memberName, null); + Assert.assertNull(getCommandMethodValue(cmd, methodName)); + Date value = new Date(); + ReflectionTestUtils.setField(cmd, memberName, value); + Assert.assertEquals(value, getCommandMethodValue(cmd, methodName)); + } + + @Test + public void testGetId() { + runLongMemberTest("id"); + } + + @Test + public void testGetWebhookId() { + runLongMemberTest("webhookId"); + } + + @Test + public void testGetManagementServerId() { + runLongMemberTest("managementServerId"); + } + + @Test + public void testStartDate() { + runDateMemberTest("startDate"); + } + + @Test + public void testEndDate() { + runDateMemberTest("endDate"); + } + + @Test + public void testEventType() { + runStringMemberTest("eventType"); + } + + @Test + public void testGetEntityOwnerId() { + Account account = new AccountVO("testaccount", 1L, "networkdomain", Account.Type.NORMAL, "uuid"); + UserVO user = new UserVO(1, "testuser", "password", "firstname", "lastName", "email", "timezone", UUID.randomUUID().toString(), User.Source.UNKNOWN); + CallContext.register(user, account); + ListWebhookDeliveriesCmd cmd = new ListWebhookDeliveriesCmd(); + Assert.assertEquals(account.getId(), cmd.getEntityOwnerId()); + } + + @Test + public void testExecute() { + ListWebhookDeliveriesCmd cmd = new ListWebhookDeliveriesCmd(); + cmd.webhookApiService = webhookApiService; + List responseList = new ArrayList<>(); + ListResponse listResponse = new ListResponse<>(); + listResponse.setResponses(responseList); + Mockito.when(webhookApiService.listWebhookDeliveries(cmd)).thenReturn(listResponse); + cmd.execute(); + Assert.assertNotNull(cmd.getResponseObject()); + } +} diff --git a/plugins/event-bus/webhook/src/test/java/org/apache/cloudstack/mom/webhook/api/command/user/ListWebhooksCmdTest.java b/plugins/event-bus/webhook/src/test/java/org/apache/cloudstack/mom/webhook/api/command/user/ListWebhooksCmdTest.java new file mode 100644 index 00000000000..1cbf9d1e836 --- /dev/null +++ b/plugins/event-bus/webhook/src/test/java/org/apache/cloudstack/mom/webhook/api/command/user/ListWebhooksCmdTest.java @@ -0,0 +1,105 @@ +// 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.mom.webhook.api.command.user; + +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.util.ArrayList; +import java.util.List; +import java.util.UUID; + +import org.apache.cloudstack.api.response.ListResponse; +import org.apache.cloudstack.mom.webhook.WebhookApiService; +import org.apache.cloudstack.mom.webhook.api.response.WebhookResponse; +import org.junit.Assert; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mock; +import org.mockito.Mockito; +import org.mockito.junit.MockitoJUnitRunner; +import org.springframework.test.util.ReflectionTestUtils; + +@RunWith(MockitoJUnitRunner.class) +public class ListWebhooksCmdTest { + + @Mock + WebhookApiService webhookApiService; + + private Object getCommandMethodValue(Object obj, String methodName) { + Object result = null; + try { + Method method = obj.getClass().getMethod(methodName); + result = method.invoke(obj); + } catch (NoSuchMethodException | InvocationTargetException | IllegalAccessException e) { + Assert.fail(String.format("Failed to get method %s value", methodName)); + } + return result; + } + + private void runStringMemberTest(String memberName) { + String methodName = "get" + memberName.substring(0, 1).toUpperCase() + memberName.substring(1); + ListWebhooksCmd cmd = new ListWebhooksCmd(); + ReflectionTestUtils.setField(cmd, memberName, null); + Assert.assertNull(getCommandMethodValue(cmd, methodName)); + String value = UUID.randomUUID().toString(); + ReflectionTestUtils.setField(cmd, memberName, value); + Assert.assertEquals(value, getCommandMethodValue(cmd, methodName)); + } + + private void runLongMemberTest(String memberName) { + String methodName = "get" + memberName.substring(0, 1).toUpperCase() + memberName.substring(1); + ListWebhooksCmd cmd = new ListWebhooksCmd(); + ReflectionTestUtils.setField(cmd, memberName, null); + Assert.assertNull(getCommandMethodValue(cmd, methodName)); + Long value = 100L; + ReflectionTestUtils.setField(cmd, memberName, value); + Assert.assertEquals(value, getCommandMethodValue(cmd, methodName)); + } + + @Test + public void testGetId() { + runLongMemberTest("id"); + } + + @Test + public void testGetName() { + runStringMemberTest("name"); + } + + @Test + public void testGetState() { + runStringMemberTest("state"); + } + + @Test + public void testGetScope() { + runStringMemberTest("scope"); + } + + @Test + public void testExecute() { + ListWebhooksCmd cmd = new ListWebhooksCmd(); + cmd.webhookApiService = webhookApiService; + List responseList = new ArrayList<>(); + ListResponse listResponse = new ListResponse<>(); + listResponse.setResponses(responseList); + Mockito.when(webhookApiService.listWebhooks(cmd)).thenReturn(listResponse); + cmd.execute(); + Assert.assertNotNull(cmd.getResponseObject()); + } +} diff --git a/plugins/event-bus/webhook/src/test/java/org/apache/cloudstack/mom/webhook/api/command/user/UpdateWebhookCmdTest.java b/plugins/event-bus/webhook/src/test/java/org/apache/cloudstack/mom/webhook/api/command/user/UpdateWebhookCmdTest.java new file mode 100644 index 00000000000..719e63cadf2 --- /dev/null +++ b/plugins/event-bus/webhook/src/test/java/org/apache/cloudstack/mom/webhook/api/command/user/UpdateWebhookCmdTest.java @@ -0,0 +1,163 @@ +// 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.mom.webhook.api.command.user; + +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.util.UUID; + +import org.apache.cloudstack.api.ServerApiException; +import org.apache.cloudstack.context.CallContext; +import org.apache.cloudstack.mom.webhook.WebhookApiService; +import org.apache.cloudstack.mom.webhook.api.response.WebhookResponse; +import org.junit.Assert; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mock; +import org.mockito.Mockito; +import org.mockito.junit.MockitoJUnitRunner; +import org.springframework.test.util.ReflectionTestUtils; + +import com.cloud.user.Account; +import com.cloud.user.AccountVO; +import com.cloud.user.User; +import com.cloud.user.UserVO; +import com.cloud.utils.exception.CloudRuntimeException; + +@RunWith(MockitoJUnitRunner.class) +public class UpdateWebhookCmdTest { + + @Mock + WebhookApiService webhookApiService; + + private Object getCommandMethodValue(Object obj, String methodName) { + Object result = null; + try { + Method method = obj.getClass().getMethod(methodName); + result = method.invoke(obj); + } catch (NoSuchMethodException | InvocationTargetException | IllegalAccessException e) { + Assert.fail(String.format("Failed to get method %s value", methodName)); + } + return result; + } + + private void runStringMemberTest(String memberName) { + String methodName = "get" + memberName.substring(0, 1).toUpperCase() + memberName.substring(1); + UpdateWebhookCmd cmd = new UpdateWebhookCmd(); + ReflectionTestUtils.setField(cmd, memberName, null); + Assert.assertNull(getCommandMethodValue(cmd, methodName)); + String value = UUID.randomUUID().toString(); + ReflectionTestUtils.setField(cmd, memberName, value); + Assert.assertEquals(value, getCommandMethodValue(cmd, methodName)); + } + + private void runLongMemberTest(String memberName) { + String methodName = "get" + memberName.substring(0, 1).toUpperCase() + memberName.substring(1); + UpdateWebhookCmd cmd = new UpdateWebhookCmd(); + ReflectionTestUtils.setField(cmd, memberName, null); + Assert.assertNull(getCommandMethodValue(cmd, methodName)); + Long value = 100L; + ReflectionTestUtils.setField(cmd, memberName, value); + Assert.assertEquals(value, getCommandMethodValue(cmd, methodName)); + } + + private void runBooleanMemberTest(String memberName) { + String methodName = "is" + memberName.substring(0, 1).toUpperCase() + memberName.substring(1); + UpdateWebhookCmd cmd = new UpdateWebhookCmd(); + ReflectionTestUtils.setField(cmd, memberName, null); + Assert.assertNull(getCommandMethodValue(cmd, methodName)); + Boolean value = true; + ReflectionTestUtils.setField(cmd, memberName, value); + Assert.assertEquals(value, getCommandMethodValue(cmd, methodName)); + } + + @Test + public void testGetId() { + runLongMemberTest("id"); + } + + @Test + public void testGetName() { + runStringMemberTest("name"); + } + + @Test + public void testGetDescription() { + runStringMemberTest("description"); + } + + @Test + public void testGetPayloadUrl() { + runStringMemberTest("payloadUrl"); + } + + @Test + public void testGetSecretKey() { + runStringMemberTest("secretKey"); + } + + @Test + public void testGetScope() { + runStringMemberTest("scope"); + } + + @Test + public void testGetState() { + runStringMemberTest("state"); + } + + @Test + public void testIsSslVerification() { + runBooleanMemberTest("sslVerification"); + } + + @Test + public void testGetEntityOwnerId() { + Account account = new AccountVO("testaccount", 1L, "networkdomain", Account.Type.NORMAL, "uuid"); + UserVO user = new UserVO(1, "testuser", "password", "firstname", "lastName", "email", "timezone", UUID.randomUUID().toString(), User.Source.UNKNOWN); + CallContext.register(user, account); + UpdateWebhookCmd cmd = new UpdateWebhookCmd(); + Assert.assertEquals(account.getId(), cmd.getEntityOwnerId()); + } + + @Test(expected = ServerApiException.class) + public void testExecuteNullResponse() { + UpdateWebhookCmd cmd = new UpdateWebhookCmd(); + cmd.webhookApiService = webhookApiService; + Mockito.when(webhookApiService.updateWebhook(cmd)).thenReturn(null); + cmd.execute(); + } + + @Test(expected = ServerApiException.class) + public void testExecuteCRE() { + UpdateWebhookCmd cmd = new UpdateWebhookCmd(); + cmd.webhookApiService = webhookApiService; + Mockito.when(webhookApiService.updateWebhook(cmd)).thenThrow(CloudRuntimeException.class); + cmd.execute(); + } + + @Test + public void testExecute() { + UpdateWebhookCmd cmd = new UpdateWebhookCmd(); + cmd.webhookApiService = webhookApiService; + WebhookResponse response = new WebhookResponse(); + Mockito.when(webhookApiService.updateWebhook(cmd)).thenReturn(response); + cmd.execute(); + Assert.assertEquals(cmd.getCommandName(), response.getResponseName()); + } +} diff --git a/plugins/network-elements/juniper-contrail/src/main/java/org/apache/cloudstack/network/contrail/management/EventUtils.java b/plugins/network-elements/juniper-contrail/src/main/java/org/apache/cloudstack/network/contrail/management/EventUtils.java index 6f1a98846c5..d47bf6eceeb 100644 --- a/plugins/network-elements/juniper-contrail/src/main/java/org/apache/cloudstack/network/contrail/management/EventUtils.java +++ b/plugins/network-elements/juniper-contrail/src/main/java/org/apache/cloudstack/network/contrail/management/EventUtils.java @@ -18,21 +18,19 @@ package org.apache.cloudstack.network.contrail.management; import java.lang.reflect.Method; -import java.util.HashMap; -import java.util.Map; import java.util.ArrayList; +import java.util.HashMap; import java.util.List; +import java.util.Map; -import org.apache.logging.log4j.Logger; -import org.apache.logging.log4j.LogManager; -import org.springframework.beans.factory.NoSuchBeanDefinitionException; -import org.springframework.stereotype.Component; - -import org.apache.cloudstack.context.CallContext; -import org.apache.cloudstack.framework.events.EventBus; -import org.apache.cloudstack.framework.events.EventBusException; import org.aopalliance.intercept.MethodInterceptor; import org.aopalliance.intercept.MethodInvocation; +import org.apache.cloudstack.context.CallContext; +import org.apache.cloudstack.framework.events.EventDistributor; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; +import org.springframework.beans.factory.NoSuchBeanDefinitionException; +import org.springframework.stereotype.Component; import com.cloud.event.ActionEvent; import com.cloud.event.ActionEvents; @@ -43,15 +41,20 @@ import com.cloud.server.ManagementService; import com.cloud.utils.component.ComponentContext; import com.cloud.utils.component.ComponentMethodInterceptor; + @Component public class EventUtils { protected static Logger LOGGER = LogManager.getLogger(EventUtils.class); - protected static EventBus s_eventBus = null; + private static EventDistributor eventDistributor; public EventUtils() { } + public static void setEventDistributor(EventDistributor eventDistributorImpl) { + eventDistributor = eventDistributorImpl; + } + private static void publishOnMessageBus(String eventCategory, String eventType, String details, Event.State state) { if (state != com.cloud.event.Event.State.Completed) { @@ -59,7 +62,7 @@ public class EventUtils { } try { - s_eventBus = ComponentContext.getComponent(EventBus.class); + setEventDistributor(ComponentContext.getComponent(EventDistributor.class)); } catch (NoSuchBeanDefinitionException nbe) { return; // no provider is configured to provide events bus, so just return } @@ -67,18 +70,12 @@ public class EventUtils { org.apache.cloudstack.framework.events.Event event = new org.apache.cloudstack.framework.events.Event(ManagementService.Name, eventCategory, eventType, EventTypes.getEntityForEvent(eventType), null); - Map eventDescription = new HashMap(); + Map eventDescription = new HashMap<>(); eventDescription.put("event", eventType); eventDescription.put("status", state.toString()); eventDescription.put("details", details); event.setDescription(eventDescription); - try { - s_eventBus.publish(event); - } catch (EventBusException evx) { - String errMsg = "Failed to publish contrail event."; - LOGGER.warn(errMsg, evx); - } - + eventDistributor.publish(event); } public static class EventInterceptor implements ComponentMethodInterceptor, MethodInterceptor { @@ -119,7 +116,7 @@ public class EventUtils { } protected List getActionEvents(Method m) { - List result = new ArrayList(); + List result = new ArrayList<>(); ActionEvents events = m.getAnnotation(ActionEvents.class); diff --git a/plugins/pom.xml b/plugins/pom.xml index e29f385dc37..279067e2c97 100755 --- a/plugins/pom.xml +++ b/plugins/pom.xml @@ -79,6 +79,7 @@ event-bus/inmemory event-bus/kafka event-bus/rabbitmq + event-bus/webhook ha-planners/skip-heurestics diff --git a/server/src/main/java/com/cloud/api/ApiServer.java b/server/src/main/java/com/cloud/api/ApiServer.java index fd57b43080a..6a03b23bcd2 100644 --- a/server/src/main/java/com/cloud/api/ApiServer.java +++ b/server/src/main/java/com/cloud/api/ApiServer.java @@ -95,8 +95,7 @@ import org.apache.cloudstack.config.ApiServiceConfiguration; import org.apache.cloudstack.context.CallContext; import org.apache.cloudstack.framework.config.ConfigKey; import org.apache.cloudstack.framework.config.Configurable; -import org.apache.cloudstack.framework.events.EventBus; -import org.apache.cloudstack.framework.events.EventBusException; +import org.apache.cloudstack.framework.events.EventDistributor; import org.apache.cloudstack.framework.jobs.AsyncJob; import org.apache.cloudstack.framework.jobs.AsyncJobManager; import org.apache.cloudstack.framework.jobs.impl.AsyncJobVO; @@ -132,10 +131,9 @@ import org.apache.http.protocol.ResponseConnControl; import org.apache.http.protocol.ResponseContent; import org.apache.http.protocol.ResponseDate; import org.apache.http.protocol.ResponseServer; -import org.apache.logging.log4j.Logger; import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; import org.bouncycastle.jce.provider.BouncyCastleProvider; -import org.springframework.beans.factory.NoSuchBeanDefinitionException; import org.springframework.stereotype.Component; import com.cloud.api.dispatch.DispatchChainFactory; @@ -196,26 +194,26 @@ public class ApiServer extends ManagerBase implements HttpRequestHandler, ApiSer */ private static final String CONTROL_CHARACTERS = "[\000-\011\013-\014\016-\037\177]"; + @Inject + private AccountManager accountMgr; + @Inject + private APIAuthenticationManager authManager; @Inject private ApiDispatcher dispatcher; @Inject - private DispatchChainFactory dispatchChainFactory; + private AsyncJobManager asyncMgr; @Inject - private AccountManager accountMgr; + private DispatchChainFactory dispatchChainFactory; @Inject private DomainManager domainMgr; @Inject private DomainDao domainDao; @Inject - private UUIDManager uuidMgr; - @Inject - private AsyncJobManager asyncMgr; - @Inject private EntityManager entityMgr; @Inject - private APIAuthenticationManager authManager; - @Inject private ProjectDao projectDao; + @Inject + private UUIDManager uuidMgr; private List pluggableServices; @@ -224,6 +222,7 @@ public class ApiServer extends ManagerBase implements HttpRequestHandler, ApiSer @Inject private ApiAsyncJobDispatcher asyncDispatcher; + private EventDistributor eventDistributor = null; private static int s_workerCount = 0; private static Map>> s_apiNameCmdClassMap = new HashMap>>(); @@ -311,6 +310,10 @@ public class ApiServer extends ManagerBase implements HttpRequestHandler, ApiSer return true; } + public void setEventDistributor(EventDistributor eventDistributor) { + this.eventDistributor = eventDistributor; + } + @MessageHandler(topic = AsyncJob.Topics.JOB_EVENT_PUBLISH) public void handleAsyncJobPublishEvent(String subject, String senderAddress, Object args) { assert (args != null); @@ -322,12 +325,8 @@ public class ApiServer extends ManagerBase implements HttpRequestHandler, ApiSer if (logger.isTraceEnabled()) logger.trace("Handle asyjob publish event " + jobEvent); - - EventBus eventBus = null; - try { - eventBus = ComponentContext.getComponent(EventBus.class); - } catch (NoSuchBeanDefinitionException nbe) { - return; // no provider is configured to provide events bus, so just return + if (eventDistributor == null) { + setEventDistributor(ComponentContext.getComponent(EventDistributor.class)); } if (!job.getDispatcher().equalsIgnoreCase("ApiAsyncJobDispatcher")) { @@ -340,7 +339,7 @@ public class ApiServer extends ManagerBase implements HttpRequestHandler, ApiSer // Get the event type from the cmdInfo json string String info = job.getCmdInfo(); String cmdEventType = "unknown"; - Map cmdInfoObj = new HashMap(); + Map cmdInfoObj = new HashMap<>(); if (info != null) { Type type = new TypeToken>(){}.getType(); Map cmdInfo = ApiGsonHelper.getBuilder().create().fromJson(info, type); @@ -368,7 +367,7 @@ public class ApiServer extends ManagerBase implements HttpRequestHandler, ApiSer org.apache.cloudstack.framework.events.Event event = new org.apache.cloudstack.framework.events.Event("management-server", EventCategory.ASYNC_JOB_CHANGE_EVENT.getName(), jobEvent, instanceType, instanceUuid); - Map eventDescription = new HashMap(); + Map eventDescription = new HashMap<>(); eventDescription.put("command", job.getCmd()); eventDescription.put("user", userJobOwner.getUuid()); eventDescription.put("account", jobOwner.getUuid()); @@ -389,13 +388,7 @@ public class ApiServer extends ManagerBase implements HttpRequestHandler, ApiSer eventDescription.put("domainname", domain.getName()); } event.setDescription(eventDescription); - - try { - eventBus.publish(event); - } catch (EventBusException evx) { - String errMsg = "Failed to publish async job event on the event bus."; - logger.warn(errMsg, evx); - } + eventDistributor.publish(event); } @Override diff --git a/server/src/main/java/com/cloud/event/ActionEventUtils.java b/server/src/main/java/com/cloud/event/ActionEventUtils.java index 8ea93684877..d625aaca466 100644 --- a/server/src/main/java/com/cloud/event/ActionEventUtils.java +++ b/server/src/main/java/com/cloud/event/ActionEventUtils.java @@ -32,12 +32,11 @@ import org.apache.cloudstack.api.Identity; import org.apache.cloudstack.api.InternalIdentity; import org.apache.cloudstack.context.CallContext; import org.apache.cloudstack.framework.config.dao.ConfigurationDao; -import org.apache.cloudstack.framework.events.EventBus; -import org.apache.cloudstack.framework.events.EventBusException; +import org.apache.cloudstack.framework.events.EventDistributor; import org.apache.commons.lang3.ObjectUtils; import org.apache.commons.lang3.StringUtils; -import org.apache.logging.log4j.Logger; import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; import org.springframework.beans.factory.NoSuchBeanDefinitionException; import com.cloud.configuration.Config; @@ -63,7 +62,7 @@ public class ActionEventUtils { private static AccountDao s_accountDao; private static ProjectDao s_projectDao; protected static UserDao s_userDao; - protected static EventBus s_eventBus = null; + private static EventDistributor eventDistributor; protected static EntityManager s_entityMgr; protected static ConfigurationDao s_configDao; @@ -101,8 +100,10 @@ public class ActionEventUtils { public static Long onActionEvent(Long userId, Long accountId, Long domainId, String type, String description, Long resourceId, String resourceType) { Ternary resourceDetails = getResourceDetails(resourceId, resourceType, type); - publishOnEventBus(userId, accountId, EventCategory.ACTION_EVENT.getName(), type, com.cloud.event.Event.State.Completed, description, resourceDetails.second(), resourceDetails.third()); - Event event = persistActionEvent(userId, accountId, domainId, null, type, Event.State.Completed, true, description, resourceDetails.first(), resourceDetails.third(), null); + Event event = persistActionEvent(userId, accountId, domainId, null, type, Event.State.Completed, + true, description, resourceDetails.first(), resourceDetails.third(), null); + publishOnEventBus(event, userId, accountId, domainId, EventCategory.ACTION_EVENT.getName(), type, + com.cloud.event.Event.State.Completed, description, resourceDetails.second(), resourceDetails.third()); return event.getId(); } @@ -111,8 +112,10 @@ public class ActionEventUtils { */ public static Long onScheduledActionEvent(Long userId, Long accountId, String type, String description, Long resourceId, String resourceType, boolean eventDisplayEnabled, long startEventId) { Ternary resourceDetails = getResourceDetails(resourceId, resourceType, type); - publishOnEventBus(userId, accountId, EventCategory.ACTION_EVENT.getName(), type, com.cloud.event.Event.State.Scheduled, description, resourceDetails.second(), resourceDetails.third()); - Event event = persistActionEvent(userId, accountId, null, null, type, Event.State.Scheduled, eventDisplayEnabled, description, resourceDetails.first(), resourceDetails.third(), startEventId); + Event event = persistActionEvent(userId, accountId, null, null, type, Event.State.Scheduled, + eventDisplayEnabled, description, resourceDetails.first(), resourceDetails.third(), startEventId); + publishOnEventBus(event, userId, accountId, EventCategory.ACTION_EVENT.getName(), type, + com.cloud.event.Event.State.Scheduled, description, resourceDetails.second(), resourceDetails.third()); return event.getId(); } @@ -136,8 +139,10 @@ public class ActionEventUtils { */ public static Long onStartedActionEvent(Long userId, Long accountId, String type, String description, Long resourceId, String resourceType, boolean eventDisplayEnabled, long startEventId) { Ternary resourceDetails = getResourceDetails(resourceId, resourceType, type); - publishOnEventBus(userId, accountId, EventCategory.ACTION_EVENT.getName(), type, com.cloud.event.Event.State.Started, description, resourceDetails.second(), resourceDetails.third()); - Event event = persistActionEvent(userId, accountId, null, null, type, Event.State.Started, eventDisplayEnabled, description, resourceDetails.first(), resourceDetails.third(), startEventId); + Event event = persistActionEvent(userId, accountId, null, null, type, Event.State.Started, + eventDisplayEnabled, description, resourceDetails.first(), resourceDetails.third(), startEventId); + publishOnEventBus(event, userId, accountId, EventCategory.ACTION_EVENT.getName(), type, + com.cloud.event.Event.State.Started, description, resourceDetails.second(), resourceDetails.third()); return event.getId(); } @@ -148,16 +153,20 @@ public class ActionEventUtils { public static Long onCompletedActionEvent(Long userId, Long accountId, String level, String type, boolean eventDisplayEnabled, String description, Long resourceId, String resourceType, long startEventId) { Ternary resourceDetails = getResourceDetails(resourceId, resourceType, type); - publishOnEventBus(userId, accountId, EventCategory.ACTION_EVENT.getName(), type, com.cloud.event.Event.State.Completed, description, resourceDetails.second(), resourceDetails.third()); - Event event = persistActionEvent(userId, accountId, null, level, type, Event.State.Completed, eventDisplayEnabled, description, resourceDetails.first(), resourceDetails.third(), startEventId); + Event event = persistActionEvent(userId, accountId, null, level, type, Event.State.Completed, + eventDisplayEnabled, description, resourceDetails.first(), resourceDetails.third(), startEventId); + publishOnEventBus(event, userId, accountId, EventCategory.ACTION_EVENT.getName(), type, + com.cloud.event.Event.State.Completed, description, resourceDetails.second(), resourceDetails.third()); return event.getId(); } public static Long onCreatedActionEvent(Long userId, Long accountId, String level, String type, boolean eventDisplayEnabled, String description, Long resourceId, String resourceType) { Ternary resourceDetails = getResourceDetails(resourceId, resourceType, type); - publishOnEventBus(userId, accountId, EventCategory.ACTION_EVENT.getName(), type, com.cloud.event.Event.State.Created, description, resourceDetails.second(), resourceDetails.third()); - Event event = persistActionEvent(userId, accountId, null, level, type, Event.State.Created, eventDisplayEnabled, description, resourceDetails.first(), resourceDetails.third(), null); + Event event = persistActionEvent(userId, accountId, null, level, type, Event.State.Created, + eventDisplayEnabled, description, resourceDetails.first(), resourceDetails.third(), null); + publishOnEventBus(event, userId, accountId, EventCategory.ACTION_EVENT.getName(), type, + com.cloud.event.Event.State.Created, description, resourceDetails.second(), resourceDetails.third()); return event.getId(); } @@ -193,20 +202,25 @@ public class ActionEventUtils { return event; } - private static void publishOnEventBus(long userId, long accountId, String eventCategory, String eventType, Event.State state, String description, String resourceUuid, String resourceType) { + private static void publishOnEventBus(Event eventRecord, long userId, long accountId, Long domainId, + String eventCategory, String eventType, Event.State state, String description, String resourceUuid, + String resourceType) { String configKey = Config.PublishActionEvent.key(); String value = s_configDao.getValue(configKey); boolean configValue = Boolean.parseBoolean(value); if(!configValue) return; + try { - s_eventBus = ComponentContext.getComponent(EventBus.class); + eventDistributor = ComponentContext.getComponent(EventDistributor.class); } catch (NoSuchBeanDefinitionException nbe) { return; // no provider is configured to provide events bus, so just return } org.apache.cloudstack.framework.events.Event event = - new org.apache.cloudstack.framework.events.Event(ManagementService.Name, eventCategory, eventType, resourceType, resourceUuid); + new org.apache.cloudstack.framework.events.Event(ManagementService.Name, eventCategory, eventType, resourceType, resourceUuid); + event.setEventId(eventRecord.getId()); + event.setEventUuid(eventRecord.getUuid()); Map eventDescription = new HashMap(); Project project = s_projectDao.findByProjectAccountId(accountId); @@ -219,6 +233,9 @@ public class ActionEventUtils { return; if (project != null) eventDescription.put("project", project.getUuid()); + event.setResourceAccountId(accountId); + event.setResourceAccountUuid(account.getUuid()); + event.setResourceDomainId(domainId == null ? account.getDomainId() : domainId); eventDescription.put("user", user.getUuid()); eventDescription.put("account", account.getUuid()); eventDescription.put("event", eventType); @@ -234,11 +251,13 @@ public class ActionEventUtils { event.setDescription(eventDescription); - try { - s_eventBus.publish(event); - } catch (EventBusException e) { - LOGGER.warn("Failed to publish action event on the event bus."); - } + eventDistributor.publish(event); + } + + private static void publishOnEventBus(Event event, long userId, long accountId, String eventCategory, + String eventType, Event.State state, String description, String resourceUuid, String resourceType) { + publishOnEventBus(event, userId, accountId, null, eventCategory, eventType, state, description, + resourceUuid, resourceType); } private static Ternary getResourceDetailsUsingEntityClassAndContext(Class entityClass, ApiCommandResourceType resourceType) { diff --git a/server/src/main/java/com/cloud/event/AlertGenerator.java b/server/src/main/java/com/cloud/event/AlertGenerator.java index 27698f27862..f1b23e87308 100644 --- a/server/src/main/java/com/cloud/event/AlertGenerator.java +++ b/server/src/main/java/com/cloud/event/AlertGenerator.java @@ -25,15 +25,13 @@ import java.util.Map; import javax.annotation.PostConstruct; import javax.inject.Inject; -import org.apache.logging.log4j.Logger; +import org.apache.cloudstack.framework.config.dao.ConfigurationDao; +import org.apache.cloudstack.framework.events.EventDistributor; import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; import org.springframework.beans.factory.NoSuchBeanDefinitionException; import org.springframework.stereotype.Component; -import org.apache.cloudstack.framework.config.dao.ConfigurationDao; -import org.apache.cloudstack.framework.events.EventBus; -import org.apache.cloudstack.framework.events.EventBusException; - import com.cloud.configuration.Config; import com.cloud.dc.DataCenterVO; import com.cloud.dc.HostPodVO; @@ -48,8 +46,8 @@ public class AlertGenerator { protected static Logger LOGGER = LogManager.getLogger(AlertGenerator.class); private static DataCenterDao s_dcDao; private static HostPodDao s_podDao; - protected static EventBus s_eventBus = null; protected static ConfigurationDao s_configDao; + protected static EventDistributor eventDistributor; @Inject DataCenterDao dcDao; @@ -76,9 +74,9 @@ public class AlertGenerator { if(!configValue) return; try { - s_eventBus = ComponentContext.getComponent(EventBus.class); + eventDistributor = ComponentContext.getComponent(EventDistributor.class); } catch (NoSuchBeanDefinitionException nbe) { - return; // no provider is configured to provide events bus, so just return + return; // no provider is configured to provide events distributor, so just return } org.apache.cloudstack.framework.events.Event event = @@ -107,10 +105,6 @@ public class AlertGenerator { event.setDescription(eventDescription); - try { - s_eventBus.publish(event); - } catch (EventBusException e) { - LOGGER.warn("Failed to publish alert on the event bus."); - } + eventDistributor.publish(event); } } diff --git a/server/src/main/java/com/cloud/projects/ProjectManagerImpl.java b/server/src/main/java/com/cloud/projects/ProjectManagerImpl.java index cb1623b5858..803e8600c08 100644 --- a/server/src/main/java/com/cloud/projects/ProjectManagerImpl.java +++ b/server/src/main/java/com/cloud/projects/ProjectManagerImpl.java @@ -18,6 +18,7 @@ package com.cloud.projects; import java.io.UnsupportedEncodingException; import java.security.SecureRandom; +import java.util.ArrayList; import java.util.HashSet; import java.util.List; import java.util.Map; @@ -35,6 +36,7 @@ import javax.inject.Inject; import javax.mail.MessagingException; import javax.naming.ConfigurationException; +import org.apache.cloudstack.acl.ControlledEntity; import org.apache.cloudstack.acl.ProjectRole; import org.apache.cloudstack.acl.SecurityChecker.AccessType; import org.apache.cloudstack.acl.dao.ProjectRoleDao; @@ -48,7 +50,9 @@ import org.apache.cloudstack.managed.context.ManagedContextRunnable; import org.apache.cloudstack.utils.mailing.MailAddress; import org.apache.cloudstack.utils.mailing.SMTPMailProperties; import org.apache.cloudstack.utils.mailing.SMTPMailSender; +import org.apache.cloudstack.webhook.WebhookHelper; import org.apache.commons.lang3.BooleanUtils; +import org.springframework.beans.factory.NoSuchBeanDefinitionException; import org.springframework.stereotype.Component; import com.cloud.api.ApiDBUtils; @@ -89,6 +93,7 @@ import com.cloud.user.ResourceLimitService; import com.cloud.user.User; import com.cloud.user.dao.AccountDao; import com.cloud.user.dao.UserDao; +import com.cloud.utils.component.ComponentContext; import com.cloud.utils.component.ManagerBase; import com.cloud.utils.concurrency.NamedThreadFactory; import com.cloud.utils.db.DB; @@ -163,6 +168,17 @@ public class ProjectManagerImpl extends ManagerBase implements ProjectManager, C private String senderAddress; protected SMTPMailSender mailSender; + protected List listWebhooksForProject(Project project) { + List webhooks = new ArrayList<>(); + try { + WebhookHelper webhookService = ComponentContext.getDelegateComponentOfType(WebhookHelper.class); + webhooks = webhookService.listWebhooksByAccount(project.getProjectAccountId()); + } catch (NoSuchBeanDefinitionException ignored) { + logger.debug("No WebhookHelper bean found"); + } + return webhooks; + } + @Override public boolean configure(final String name, final Map params) throws ConfigurationException { @@ -339,8 +355,9 @@ public class ProjectManagerImpl extends ManagerBase implements ProjectManager, C List volumes = _volumeDao.findDetachedByAccount(project.getProjectAccountId()); List networks = _networkDao.listByOwner(project.getProjectAccountId()); List vpcs = _vpcMgr.getVpcsForAccount(project.getProjectAccountId()); + List webhooks = listWebhooksForProject(project); - Optional message = Stream.of(userTemplates, vmSnapshots, vms, volumes, networks, vpcs) + Optional message = Stream.of(userTemplates, vmSnapshots, vms, volumes, networks, vpcs, webhooks) .filter(entity -> !entity.isEmpty()) .map(entity -> entity.size() + " " + entity.get(0).getEntityType().getSimpleName() + " to clean up") .findFirst(); diff --git a/server/src/main/java/com/cloud/storage/listener/SnapshotStateListener.java b/server/src/main/java/com/cloud/storage/listener/SnapshotStateListener.java index 0dd7e77ac33..18ab4168c24 100644 --- a/server/src/main/java/com/cloud/storage/listener/SnapshotStateListener.java +++ b/server/src/main/java/com/cloud/storage/listener/SnapshotStateListener.java @@ -26,11 +26,9 @@ import javax.annotation.PostConstruct; import javax.inject.Inject; import org.apache.cloudstack.framework.config.dao.ConfigurationDao; -import org.apache.cloudstack.framework.events.EventBus; -import org.apache.cloudstack.framework.events.EventBusException; -import org.apache.logging.log4j.Logger; +import org.apache.cloudstack.framework.events.EventDistributor; import org.apache.logging.log4j.LogManager; -import org.springframework.beans.factory.NoSuchBeanDefinitionException; +import org.apache.logging.log4j.Logger; import org.springframework.stereotype.Component; import com.cloud.configuration.Config; @@ -47,12 +45,12 @@ import com.cloud.utils.fsm.StateMachine2; @Component public class SnapshotStateListener implements StateListener { - protected static EventBus s_eventBus = null; protected static ConfigurationDao s_configDao; @Inject private ConfigurationDao configDao; + private EventDistributor eventDistributor = null; protected Logger logger = LogManager.getLogger(getClass()); public SnapshotStateListener() { @@ -64,6 +62,10 @@ public class SnapshotStateListener implements StateListener eventDescription = new HashMap(); + Map eventDescription = new HashMap<>(); eventDescription.put("resource", resourceName); eventDescription.put("id", vo.getUuid()); eventDescription.put("old-state", oldState.name()); @@ -104,11 +104,7 @@ public class SnapshotStateListener implements StateListener { - protected static EventBus s_eventBus = null; protected ConfigurationDao _configDao; protected VMInstanceDao _vmInstanceDao; + private EventDistributor eventDistributor; protected Logger logger = LogManager.getLogger(getClass()); public VolumeStateListener(ConfigurationDao configDao, VMInstanceDao vmInstanceDao) { @@ -58,6 +55,10 @@ public class VolumeStateListener implements StateListener this._vmInstanceDao = vmInstanceDao; } + public void setEventDistributor(EventDistributor eventDistributor) { + this.eventDistributor = eventDistributor; + } + @Override public boolean preStateTransitionEvent(State oldState, Event event, State newState, Volume vo, boolean status, Object opaque) { pubishOnEventBus(event.name(), "preStateTransitionEvent", vo, oldState, newState); @@ -93,23 +94,21 @@ public class VolumeStateListener implements StateListener return true; } - private void pubishOnEventBus(String event, String status, Volume vo, State oldState, State newState) { + private void pubishOnEventBus(String event, String status, Volume vo, State oldState, State newState) { String configKey = Config.PublishResourceStateEvent.key(); String value = _configDao.getValue(configKey); boolean configValue = Boolean.parseBoolean(value); if(!configValue) return; - try { - s_eventBus = ComponentContext.getComponent(EventBus.class); - } catch (NoSuchBeanDefinitionException nbe) { - return; // no provider is configured to provide events bus, so just return + if (eventDistributor == null) { + setEventDistributor(ComponentContext.getComponent(EventDistributor.class)); } String resourceName = getEntityFromClassName(Volume.class.getName()); org.apache.cloudstack.framework.events.Event eventMsg = new org.apache.cloudstack.framework.events.Event(ManagementService.Name, EventCategory.RESOURCE_STATE_CHANGE_EVENT.getName(), event, resourceName, - vo.getUuid()); + vo.getUuid()); Map eventDescription = new HashMap(); eventDescription.put("resource", resourceName); eventDescription.put("id", vo.getUuid()); @@ -120,11 +119,7 @@ public class VolumeStateListener implements StateListener eventDescription.put("eventDateTime", eventDate); eventMsg.setDescription(eventDescription); - try { - s_eventBus.publish(eventMsg); - } catch (EventBusException e) { - logger.warn("Failed to state change event on the event bus."); - } + eventDistributor.publish(eventMsg); } private String getEntityFromClassName(String entityClassName) { diff --git a/server/src/main/java/com/cloud/user/AccountManagerImpl.java b/server/src/main/java/com/cloud/user/AccountManagerImpl.java index 40424196df6..15121aa0a14 100644 --- a/server/src/main/java/com/cloud/user/AccountManagerImpl.java +++ b/server/src/main/java/com/cloud/user/AccountManagerImpl.java @@ -77,11 +77,13 @@ import org.apache.cloudstack.region.gslb.GlobalLoadBalancerRuleDao; import org.apache.cloudstack.resourcedetail.UserDetailVO; import org.apache.cloudstack.resourcedetail.dao.UserDetailsDao; import org.apache.cloudstack.utils.baremetal.BaremetalUtils; +import org.apache.cloudstack.webhook.WebhookHelper; import org.apache.commons.codec.binary.Base64; import org.apache.commons.collections.CollectionUtils; import org.apache.commons.lang3.BooleanUtils; import org.apache.commons.lang3.StringUtils; import org.jetbrains.annotations.NotNull; +import org.springframework.beans.factory.NoSuchBeanDefinitionException; import com.cloud.api.ApiDBUtils; import com.cloud.api.auth.SetupUserTwoFactorAuthenticationCmd; @@ -168,6 +170,7 @@ import com.cloud.utils.ConstantTimeComparator; import com.cloud.utils.NumbersUtil; import com.cloud.utils.Pair; import com.cloud.utils.Ternary; +import com.cloud.utils.component.ComponentContext; import com.cloud.utils.component.Manager; import com.cloud.utils.component.ManagerBase; import com.cloud.utils.component.PluggableService; @@ -426,6 +429,15 @@ public class AccountManagerImpl extends ManagerBase implements AccountManager, M _querySelectors = querySelectors; } + protected void deleteWebhooksForAccount(long accountId) { + try { + WebhookHelper webhookService = ComponentContext.getDelegateComponentOfType(WebhookHelper.class); + webhookService.deleteWebhooksForAccount(accountId); + } catch (NoSuchBeanDefinitionException ignored) { + logger.debug("No WebhookHelper bean found"); + } + } + @Override public List getApiNameList() { return apiNameList; @@ -1105,6 +1117,9 @@ public class AccountManagerImpl extends ManagerBase implements AccountManager, M // Delete registered UserData userDataDao.removeByAccountId(accountId); + // Delete Webhooks + deleteWebhooksForAccount(accountId); + return true; } catch (Exception ex) { logger.warn("Failed to cleanup account " + account + " due to ", ex); diff --git a/server/src/main/java/com/cloud/vm/UserVmStateListener.java b/server/src/main/java/com/cloud/vm/UserVmStateListener.java index 6fc815dc10b..aa1805d3366 100644 --- a/server/src/main/java/com/cloud/vm/UserVmStateListener.java +++ b/server/src/main/java/com/cloud/vm/UserVmStateListener.java @@ -24,15 +24,11 @@ import java.util.Map; import javax.inject.Inject; -import com.cloud.server.ManagementService; -import com.cloud.utils.fsm.StateMachine2; -import com.cloud.vm.dao.UserVmDao; -import org.apache.logging.log4j.Logger; -import org.apache.logging.log4j.LogManager; -import org.springframework.beans.factory.NoSuchBeanDefinitionException; - import org.apache.cloudstack.framework.config.dao.ConfigurationDao; -import org.apache.cloudstack.framework.events.EventBus; +import org.apache.cloudstack.framework.events.EventDistributor; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; +import org.springframework.beans.factory.NoSuchBeanDefinitionException; import com.cloud.configuration.Config; import com.cloud.event.EventCategory; @@ -41,12 +37,15 @@ import com.cloud.event.UsageEventUtils; import com.cloud.event.dao.UsageEventDao; import com.cloud.network.dao.NetworkDao; import com.cloud.network.dao.NetworkVO; +import com.cloud.server.ManagementService; import com.cloud.service.dao.ServiceOfferingDao; import com.cloud.utils.component.ComponentContext; import com.cloud.utils.fsm.StateListener; +import com.cloud.utils.fsm.StateMachine2; import com.cloud.vm.VirtualMachine.Event; import com.cloud.vm.VirtualMachine.State; import com.cloud.vm.dao.NicDao; +import com.cloud.vm.dao.UserVmDao; public class UserVmStateListener implements StateListener { @@ -57,10 +56,9 @@ public class UserVmStateListener implements StateListener eventDescription = new HashMap(); + Map eventDescription = new HashMap<>(); eventDescription.put("resource", resourceName); eventDescription.put("id", vo.getUuid()); eventDescription.put("old-state", oldState.name()); @@ -150,12 +148,7 @@ public class UserVmStateListener implements StateListener listWebhooksByAccount(long accountId); +} diff --git a/server/src/main/resources/META-INF/cloudstack/core/spring-server-core-managers-context.xml b/server/src/main/resources/META-INF/cloudstack/core/spring-server-core-managers-context.xml index b5f72a1763c..30b1a681968 100644 --- a/server/src/main/resources/META-INF/cloudstack/core/spring-server-core-managers-context.xml +++ b/server/src/main/resources/META-INF/cloudstack/core/spring-server-core-managers-context.xml @@ -145,6 +145,10 @@ + + + + diff --git a/server/src/test/java/com/cloud/event/ActionEventUtilsTest.java b/server/src/test/java/com/cloud/event/ActionEventUtilsTest.java index aed28702df5..aba8acf59c2 100644 --- a/server/src/test/java/com/cloud/event/ActionEventUtilsTest.java +++ b/server/src/test/java/com/cloud/event/ActionEventUtilsTest.java @@ -29,7 +29,8 @@ import org.apache.cloudstack.api.ApiCommandResourceType; import org.apache.cloudstack.context.CallContext; import org.apache.cloudstack.framework.config.dao.ConfigurationDao; import org.apache.cloudstack.framework.events.Event; -import org.apache.cloudstack.framework.events.EventBus; +import org.apache.cloudstack.framework.events.EventBusException; +import org.apache.cloudstack.framework.events.EventDistributor; import org.junit.After; import org.junit.Assert; import org.junit.Before; @@ -97,7 +98,7 @@ public class ActionEventUtilsTest { protected ConfigurationDao configDao; @Mock - protected EventBus eventBus; + protected EventDistributor eventDistributor; private AccountVO account; private UserVO user; @@ -149,7 +150,7 @@ public class ActionEventUtilsTest { //Some basic mocks. Mockito.when(configDao.getValue(Config.PublishActionEvent.key())).thenReturn("true"); componentContextMocked = Mockito.mockStatic(ComponentContext.class); - componentContextMocked.when(() -> ComponentContext.getComponent(EventBus.class)).thenReturn(eventBus); + componentContextMocked.when(() -> ComponentContext.getComponent(EventDistributor.class)).thenReturn(eventDistributor); //Needed for persist to actually set an ID that can be returned from the ActionEventUtils //methods. @@ -166,14 +167,11 @@ public class ActionEventUtilsTest { }); //Needed to record events published on the bus. - Mockito.doAnswer(new Answer() { - @Override public Void answer(InvocationOnMock invocation) throws Throwable { - Event event = (Event)invocation.getArguments()[0]; - publishedEvents.add(event); - return null; - } - - }).when(eventBus).publish(Mockito.any(Event.class)); + Mockito.doAnswer((Answer>) invocation -> { + Event event = (Event)invocation.getArguments()[0]; + publishedEvents.add(event); + return new HashMap<>(); + }).when(eventDistributor).publish(Mockito.any(Event.class)); account = new AccountVO("testaccount", 1L, "networkdomain", Account.Type.NORMAL, "uuid"); account.setId(ACCOUNT_ID); diff --git a/server/src/test/java/com/cloud/projects/ProjectManagerImplTest.java b/server/src/test/java/com/cloud/projects/ProjectManagerImplTest.java index 94dffd9fe8e..b9b568facc2 100644 --- a/server/src/test/java/com/cloud/projects/ProjectManagerImplTest.java +++ b/server/src/test/java/com/cloud/projects/ProjectManagerImplTest.java @@ -16,20 +16,27 @@ // under the License. package com.cloud.projects; -import com.cloud.projects.dao.ProjectDao; +import java.util.ArrayList; +import java.util.List; + +import org.apache.cloudstack.acl.ControlledEntity; +import org.apache.cloudstack.webhook.WebhookHelper; +import org.apache.commons.collections.CollectionUtils; import org.junit.Assert; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.InjectMocks; import org.mockito.Mock; +import org.mockito.MockedStatic; import org.mockito.Mockito; import org.mockito.Spy; import org.mockito.junit.MockitoJUnitRunner; import org.mockito.stubbing.Answer; +import org.springframework.beans.factory.NoSuchBeanDefinitionException; -import java.util.ArrayList; -import java.util.List; +import com.cloud.projects.dao.ProjectDao; +import com.cloud.utils.component.ComponentContext; @RunWith(MockitoJUnitRunner.class) @@ -94,4 +101,31 @@ public class ProjectManagerImplTest { public void testUpdateProjectNameAndDisplayTextUpdateNameDisplayText() { runUpdateProjectNameAndDisplayTextTest(true, true); } + + @Test + public void testDeleteWebhooksForAccount() { + try (MockedStatic mockedComponentContext = Mockito.mockStatic(ComponentContext.class)) { + WebhookHelper webhookHelper = Mockito.mock(WebhookHelper.class); + List webhooks = List.of(Mockito.mock(ControlledEntity.class), + Mockito.mock(ControlledEntity.class)); + Mockito.doReturn(webhooks).when(webhookHelper).listWebhooksByAccount(Mockito.anyLong()); + mockedComponentContext.when(() -> ComponentContext.getDelegateComponentOfType(WebhookHelper.class)) + .thenReturn(webhookHelper); + Project project = Mockito.mock(Project.class); + Mockito.when(project.getProjectAccountId()).thenReturn(1L); + List result = projectManager.listWebhooksForProject(project); + Assert.assertEquals(2, result.size()); + } + } + + @Test + public void testDeleteWebhooksForAccountNoBean() { + try (MockedStatic mockedComponentContext = Mockito.mockStatic(ComponentContext.class)) { + mockedComponentContext.when(() -> ComponentContext.getDelegateComponentOfType(WebhookHelper.class)) + .thenThrow(NoSuchBeanDefinitionException.class); + List result = + projectManager.listWebhooksForProject(Mockito.mock(Project.class)); + Assert.assertTrue(CollectionUtils.isEmpty(result)); + } + } } diff --git a/server/src/test/java/com/cloud/template/HypervisorTemplateAdapterTest.java b/server/src/test/java/com/cloud/template/HypervisorTemplateAdapterTest.java index d90ae5a2f03..5307beb4aba 100644 --- a/server/src/test/java/com/cloud/template/HypervisorTemplateAdapterTest.java +++ b/server/src/test/java/com/cloud/template/HypervisorTemplateAdapterTest.java @@ -18,25 +18,26 @@ package com.cloud.template; -import com.cloud.dc.DataCenterVO; -import com.cloud.dc.dao.DataCenterDao; -import com.cloud.event.EventTypes; -import com.cloud.event.UsageEventUtils; -import com.cloud.event.UsageEventVO; -import com.cloud.event.dao.UsageEventDao; -import com.cloud.exception.InvalidParameterValueException; -import com.cloud.org.Grouping; -import com.cloud.server.StatsCollector; -import com.cloud.storage.Storage.ImageFormat; -import com.cloud.storage.TemplateProfile; -import com.cloud.storage.VMTemplateStorageResourceAssoc.Status; -import com.cloud.storage.VMTemplateVO; -import com.cloud.storage.dao.VMTemplateZoneDao; -import com.cloud.user.AccountVO; -import com.cloud.user.ResourceLimitService; -import com.cloud.user.dao.AccountDao; -import com.cloud.utils.component.ComponentContext; -import com.cloud.utils.exception.CloudRuntimeException; +import static org.mockito.Mockito.any; +import static org.mockito.Mockito.anyLong; +import static org.mockito.Mockito.doAnswer; +import static org.mockito.Mockito.eq; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.when; + +import java.lang.reflect.Field; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.concurrent.ExecutionException; + import org.apache.cloudstack.engine.subsystem.api.storage.DataStore; import org.apache.cloudstack.engine.subsystem.api.storage.DataStoreManager; import org.apache.cloudstack.engine.subsystem.api.storage.TemplateDataFactory; @@ -46,8 +47,7 @@ import org.apache.cloudstack.engine.subsystem.api.storage.TemplateService.Templa import org.apache.cloudstack.framework.async.AsyncCallFuture; import org.apache.cloudstack.framework.config.dao.ConfigurationDao; import org.apache.cloudstack.framework.events.Event; -import org.apache.cloudstack.framework.events.EventBus; -import org.apache.cloudstack.framework.events.EventBusException; +import org.apache.cloudstack.framework.events.EventDistributor; import org.apache.cloudstack.framework.messagebus.MessageBus; import org.apache.cloudstack.secstorage.heuristics.HeuristicType; import org.apache.cloudstack.storage.datastore.db.TemplateDataStoreDao; @@ -70,30 +70,30 @@ import org.mockito.invocation.InvocationOnMock; import org.mockito.junit.MockitoJUnitRunner; import org.mockito.stubbing.Answer; -import java.lang.reflect.Field; -import java.lang.reflect.InvocationTargetException; -import java.lang.reflect.Method; -import java.util.ArrayList; -import java.util.Collections; -import java.util.HashMap; -import java.util.HashSet; -import java.util.List; -import java.util.Map; -import java.util.Set; -import java.util.concurrent.ExecutionException; - -import static org.mockito.Mockito.any; -import static org.mockito.Mockito.anyLong; -import static org.mockito.Mockito.doAnswer; -import static org.mockito.Mockito.eq; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.times; -import static org.mockito.Mockito.when; +import com.cloud.dc.DataCenterVO; +import com.cloud.dc.dao.DataCenterDao; +import com.cloud.event.EventTypes; +import com.cloud.event.UsageEventUtils; +import com.cloud.event.UsageEventVO; +import com.cloud.event.dao.UsageEventDao; +import com.cloud.exception.InvalidParameterValueException; +import com.cloud.org.Grouping; +import com.cloud.server.StatsCollector; +import com.cloud.storage.Storage.ImageFormat; +import com.cloud.storage.TemplateProfile; +import com.cloud.storage.VMTemplateStorageResourceAssoc.Status; +import com.cloud.storage.VMTemplateVO; +import com.cloud.storage.dao.VMTemplateZoneDao; +import com.cloud.user.AccountVO; +import com.cloud.user.ResourceLimitService; +import com.cloud.user.dao.AccountDao; +import com.cloud.utils.component.ComponentContext; +import com.cloud.utils.exception.CloudRuntimeException; @RunWith(MockitoJUnitRunner.class) public class HypervisorTemplateAdapterTest { @Mock - EventBus _bus; + EventDistributor eventDistributor; List events = new ArrayList<>(); @Mock @@ -168,7 +168,7 @@ public class HypervisorTemplateAdapterTest { closeable.close(); } - public UsageEventUtils setupUsageUtils() throws EventBusException { + public UsageEventUtils setupUsageUtils() { Mockito.when(_configDao.getValue(eq("publish.usage.events"))).thenReturn("true"); Mockito.when(_usageEventDao.persist(Mockito.any(UsageEventVO.class))).then(new Answer() { @Override public Void answer(InvocationOnMock invocation) throws Throwable { @@ -180,16 +180,14 @@ public class HypervisorTemplateAdapterTest { Mockito.when(_usageEventDao.listAll()).thenReturn(usageEvents); - doAnswer(new Answer() { - @Override public Void answer(InvocationOnMock invocation) throws Throwable { - Event event = (Event)invocation.getArguments()[0]; - events.add(event); - return null; - } - }).when(_bus).publish(any(Event.class)); + doAnswer((Answer) invocation -> { + Event event = (Event)invocation.getArguments()[0]; + events.add(event); + return null; + }).when(eventDistributor).publish(any(Event.class)); componentContextMocked = Mockito.mockStatic(ComponentContext.class); - when(ComponentContext.getComponent(eq(EventBus.class))).thenReturn(_bus); + when(ComponentContext.getComponent(eq(EventDistributor.class))).thenReturn(eventDistributor); UsageEventUtils utils = new UsageEventUtils(); @@ -257,7 +255,7 @@ public class HypervisorTemplateAdapterTest { } //@Test - public void testEmitDeleteEventUuid() throws InterruptedException, ExecutionException, EventBusException { + public void testEmitDeleteEventUuid() throws InterruptedException, ExecutionException { //All the mocks required for this test to work. ImageStoreEntity store = mock(ImageStoreEntity.class); when(store.getId()).thenReturn(1l); diff --git a/server/src/test/java/com/cloud/user/AccountManagerImplTest.java b/server/src/test/java/com/cloud/user/AccountManagerImplTest.java index 092ddbe5a3d..e5c623ca6df 100644 --- a/server/src/test/java/com/cloud/user/AccountManagerImplTest.java +++ b/server/src/test/java/com/cloud/user/AccountManagerImplTest.java @@ -16,6 +16,38 @@ // under the License. package com.cloud.user; +import static org.mockito.ArgumentMatchers.nullable; + +import java.net.InetAddress; +import java.net.UnknownHostException; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import org.apache.cloudstack.acl.SecurityChecker.AccessType; +import org.apache.cloudstack.api.command.admin.user.DeleteUserCmd; +import org.apache.cloudstack.api.command.admin.user.GetUserKeysCmd; +import org.apache.cloudstack.api.command.admin.user.UpdateUserCmd; +import org.apache.cloudstack.api.response.UserTwoFactorAuthenticationSetupResponse; +import org.apache.cloudstack.auth.UserAuthenticator; +import org.apache.cloudstack.auth.UserAuthenticator.ActionOnFailedAuthentication; +import org.apache.cloudstack.auth.UserTwoFactorAuthenticator; +import org.apache.cloudstack.context.CallContext; +import org.apache.cloudstack.framework.config.ConfigKey; +import org.apache.cloudstack.webhook.WebhookHelper; +import org.junit.Assert; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.InOrder; +import org.mockito.Mock; +import org.mockito.MockedStatic; +import org.mockito.Mockito; +import org.mockito.junit.MockitoJUnitRunner; +import org.springframework.beans.factory.NoSuchBeanDefinitionException; + import com.cloud.acl.DomainChecker; import com.cloud.api.auth.SetupUserTwoFactorAuthenticationCmd; import com.cloud.domain.Domain; @@ -28,40 +60,12 @@ import com.cloud.projects.Project; import com.cloud.projects.ProjectAccountVO; import com.cloud.user.Account.State; import com.cloud.utils.Pair; +import com.cloud.utils.component.ComponentContext; import com.cloud.utils.exception.CloudRuntimeException; import com.cloud.vm.UserVmManagerImpl; import com.cloud.vm.UserVmVO; import com.cloud.vm.VMInstanceVO; import com.cloud.vm.snapshot.VMSnapshotVO; -import org.apache.cloudstack.acl.SecurityChecker.AccessType; -import org.apache.cloudstack.api.command.admin.user.DeleteUserCmd; -import org.apache.cloudstack.api.command.admin.user.GetUserKeysCmd; -import org.apache.cloudstack.api.command.admin.user.UpdateUserCmd; -import org.apache.cloudstack.api.response.UserTwoFactorAuthenticationSetupResponse; -import org.apache.cloudstack.auth.UserAuthenticator; -import org.apache.cloudstack.auth.UserAuthenticator.ActionOnFailedAuthentication; -import org.apache.cloudstack.auth.UserTwoFactorAuthenticator; -import org.apache.cloudstack.context.CallContext; -import org.apache.cloudstack.framework.config.ConfigKey; -import org.junit.Assert; -import org.junit.Before; -import org.junit.Test; -import org.junit.runner.RunWith; -import org.mockito.InOrder; -import org.mockito.Mock; -import org.mockito.MockedStatic; -import org.mockito.Mockito; -import org.mockito.junit.MockitoJUnitRunner; - -import java.net.InetAddress; -import java.net.UnknownHostException; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.HashMap; -import java.util.List; -import java.util.Map; - -import static org.mockito.ArgumentMatchers.nullable; @RunWith(MockitoJUnitRunner.class) public class AccountManagerImplTest extends AccountManagetImplTestBase { @@ -173,6 +177,7 @@ public class AccountManagerImplTest extends AccountManagetImplTestBase { Mockito.when(_sshKeyPairDao.listKeyPairs(Mockito.anyLong(), Mockito.anyLong())).thenReturn(sshkeyList); Mockito.when(_sshKeyPairDao.remove(Mockito.anyLong())).thenReturn(true); Mockito.when(userDataDao.removeByAccountId(Mockito.anyLong())).thenReturn(222); + Mockito.doNothing().when(accountManagerImpl).deleteWebhooksForAccount(Mockito.anyLong()); Assert.assertTrue(accountManagerImpl.deleteUserAccount(42l)); // assert that this was a clean delete @@ -192,6 +197,7 @@ public class AccountManagerImplTest extends AccountManagetImplTestBase { Mockito.when(_vmMgr.expunge(Mockito.any(UserVmVO.class))).thenReturn(false); Mockito.lenient().when(_domainMgr.getDomain(Mockito.anyLong())).thenReturn(domain); Mockito.lenient().when(securityChecker.checkAccess(Mockito.any(Account.class), Mockito.any(Domain.class))).thenReturn(true); + Mockito.doNothing().when(accountManagerImpl).deleteWebhooksForAccount(Mockito.anyLong()); Assert.assertTrue(accountManagerImpl.deleteUserAccount(42l)); // assert that this was NOT a clean delete @@ -1032,4 +1038,24 @@ public class AccountManagerImplTest extends AccountManagetImplTestBase { Assert.assertEquals(userAccountVOList.size(), userAccounts.size()); Assert.assertEquals(userAccountVOList.get(0), userAccounts.get(0)); } + + @Test + public void testDeleteWebhooksForAccount() { + try (MockedStatic mockedComponentContext = Mockito.mockStatic(ComponentContext.class)) { + WebhookHelper webhookHelper = Mockito.mock(WebhookHelper.class); + Mockito.doNothing().when(webhookHelper).deleteWebhooksForAccount(Mockito.anyLong()); + mockedComponentContext.when(() -> ComponentContext.getDelegateComponentOfType(WebhookHelper.class)) + .thenReturn(webhookHelper); + accountManagerImpl.deleteWebhooksForAccount(1L); + } + } + + @Test + public void testDeleteWebhooksForAccountNoBean() { + try (MockedStatic mockedComponentContext = Mockito.mockStatic(ComponentContext.class)) { + mockedComponentContext.when(() -> ComponentContext.getDelegateComponentOfType(WebhookHelper.class)) + .thenThrow(NoSuchBeanDefinitionException.class); + accountManagerImpl.deleteWebhooksForAccount(1L); + } + } } diff --git a/test/integration/smoke/test_webhook_delivery.py b/test/integration/smoke/test_webhook_delivery.py new file mode 100644 index 00000000000..00178bd03ff --- /dev/null +++ b/test/integration/smoke/test_webhook_delivery.py @@ -0,0 +1,212 @@ +# 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. +""" BVT tests for webhooks delivery with a basic server +""" +# Import Local Modules +from marvin.cloudstackTestCase import cloudstackTestCase +from marvin.lib.base import (Account, + Domain, + Webhook, + SSHKeyPair) +from marvin.lib.common import (get_domain, + get_zone) +from marvin.lib.utils import (random_gen) +from marvin.cloudstackException import CloudstackAPIException +from nose.plugins.attrib import attr +from http.server import BaseHTTPRequestHandler, HTTPServer +import logging +# Import System modules +import time +import json +import socket +import _thread + + +_multiprocess_shared_ = True +deliveries_received = [] + +class WebhookReceiver(BaseHTTPRequestHandler): + """ + WebhookReceiver class to receive webhook events + """ + def _set_response(self): + self.send_response(200) + self.send_header('Content-type', 'text/html') + self.end_headers() + + def do_POST(self): + content_length = int(self.headers['Content-Length']) + post_data = self.rfile.read(content_length) + post_data = post_data.decode('utf-8') + event_id = self.headers.get('X-CS-Event-ID') + print("POST request,\nPath: %s\nHeaders:\n%s\n\nBody:\n%s\n" % + (str(self.path), str(self.headers), post_data)) + self._set_response() + global deliveries_received + if deliveries_received is None: + deliveries_received = [] + deliveries_received.append({'event': event_id, 'payload': post_data}) + if event_id != None: + self.wfile.write("Event with ID: {} successfully processed!".format(str(event_id)).encode('utf-8')) + else: + self.wfile.write("POST request for {}".format(self.path).encode('utf-8')) + +class TestWebhookDelivery(cloudstackTestCase): + + @classmethod + def setUpClass(cls): + testClient = super(TestWebhookDelivery, cls).getClsTestClient() + cls.apiclient = testClient.getApiClient() + cls.services = testClient.getParsedTestDataConfig() + cls.mgtSvrDetails = cls.config.__dict__["mgtSvr"][0].__dict__ + + # Get Zone, Domain and templates + cls.domain = get_domain(cls.apiclient) + cls.zone = get_zone(cls.apiclient, testClient.getZoneForTests()) + cls.logger = logging.getLogger('TestWebhookDelivery') + cls.logger.setLevel(logging.DEBUG) + + s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) + s.connect((cls.mgtSvrDetails["mgtSvrIp"], cls.mgtSvrDetails["port"])) + cls.server_ip = s.getsockname()[0] + s.close() + if cls.server_ip == "127.0.0.1": + s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) + s.connect(("8.8.8.8", 80)) + cls.server_ip = s.getsockname()[0] + s.close() + # use random port for webhookreceiver server + s = socket.socket() + s.bind(('', 0)) + cls.server_port = s.getsockname()[1] + s.close() + cls.webhook_receiver_url = "http://" + cls.server_ip + ":" + str(cls.server_port) + cls.logger.debug("Running Webhook receiver @ %s" % cls.webhook_receiver_url) + def startMgmtServer(tname, server): + cls.logger.debug("Starting WebhookReceiver") + try: + server.serve_forever() + except Exception: pass + cls.server = HTTPServer(('0.0.0.0', cls.server_port), WebhookReceiver) + _thread.start_new_thread(startMgmtServer, ("webhook-receiver", cls.server,)) + + cls._cleanup = [] + + @classmethod + def tearDownClass(cls): + if cls.server: + cls.server.socket.close() + global deliveries_received + deliveries_received = [] + super(TestWebhookDelivery, cls).tearDownClass() + + def setUp(self): + self.cleanup = [] + self.domain1 = Domain.create( + self.apiclient, + self.services["domain"]) + self.cleanup.append(self.domain1) + + def tearDown(self): + super(TestWebhookDelivery, self).tearDown() + + def popItemFromCleanup(self, item_id): + for idx, x in enumerate(self.cleanup): + if x.id == item_id: + self.cleanup.pop(idx) + break + + def createDomainAccount(self, isDomainAdmin=False): + self.account = Account.create( + self.apiclient, + self.services["account"], + admin=isDomainAdmin, + domainid=self.domain1.id) + self.cleanup.append(self.account) + self.userapiclient = self.testClient.getUserApiClient( + UserName=self.account.name, + DomainName=self.account.domain + ) + + def createWebhook(self, apiclient, scope=None, domainid=None, account=None, payloadurl=None, description=None, sslverification=None, secretkey=None, state=None): + name = "Test-" + random_gen() + if payloadurl is None: + payloadurl = self.webhook_receiver_url + self.webhook = Webhook.create( + apiclient, + name=name, + payloadurl=payloadurl, + description=description, + scope=scope, + sslverification=sslverification, + secretkey=secretkey, + state=state, + domainid=domainid, + account=account + ) + self.cleanup.append(self.webhook) + + @attr(tags=["devcloud", "advanced", "advancedns", "smoke", "basic", "sg"], required_hardware="false") + def test_01_webhook_deliveries(self): + global deliveries_received + self.createDomainAccount() + self.createWebhook(self.userapiclient) + self.keypair = SSHKeyPair.register( + self.userapiclient, + name="Test-" + random_gen(), + publickey="ssh-rsa: e6:9a:1e:b5:98:75:88:5d:56:bc:92:7b:43:48:05:b2" + ) + self.logger.debug("Registered sshkeypair: %s" % str(self.keypair.__dict__)) + time.sleep(2) + list_deliveries = self.webhook.list_deliveries( + self.userapiclient, + page=1, + pagesize=20 + ) + self.assertNotEqual( + list_deliveries, + None, + "Check webhook deliveries list" + ) + self.assertTrue( + len(list_deliveries) > 0, + "Check webhook deliveries list length" + ) + for delivery in list_deliveries: + self.assertEqual( + delivery.success, + True, + "Check webhook delivery success" + ) + self.assertEqual( + delivery.response, + ("Event with ID: %s successfully processed!" % delivery.eventid), + "Check webhook delivery response" + ) + delivery_matched = False + for received in deliveries_received: + if received['event'] == delivery.eventid: + self.assertEqual( + delivery.payload, + received['payload'], + "Check webhook delivery payload" + ) + delivery_matched = True + self.assertTrue( + delivery_matched, + "Delivery for %s did not match with server" % delivery.id + ) diff --git a/test/integration/smoke/test_webhook_lifecycle.py b/test/integration/smoke/test_webhook_lifecycle.py new file mode 100644 index 00000000000..2d1f322be6b --- /dev/null +++ b/test/integration/smoke/test_webhook_lifecycle.py @@ -0,0 +1,392 @@ +# 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. +""" BVT tests for webhooks lifecycle functionalities +""" +# Import Local Modules +from marvin.cloudstackTestCase import cloudstackTestCase +from marvin.cloudstackAPI import (listEvents) +from marvin.lib.base import (Account, + Domain, + Webhook, + SSHKeyPair) +from marvin.lib.common import (get_domain, + get_zone) +from marvin.lib.utils import (random_gen) +from marvin.cloudstackException import CloudstackAPIException +from nose.plugins.attrib import attr +import logging +# Import System modules +import time +from datetime import datetime + + +_multiprocess_shared_ = True +HTTP_PAYLOAD_URL = "http://smee.io/C9LPa7Ei3iB6Qj2" +HTTPS_PAYLOAD_URL = "https://smee.io/C9LPa7Ei3iB6Qj2" + +class TestWebhooks(cloudstackTestCase): + + @classmethod + def setUpClass(cls): + testClient = super(TestWebhooks, cls).getClsTestClient() + cls.apiclient = testClient.getApiClient() + cls.services = testClient.getParsedTestDataConfig() + + # Get Zone, Domain and templates + cls.domain = get_domain(cls.apiclient) + cls.zone = get_zone(cls.apiclient, testClient.getZoneForTests()) + + cls._cleanup = [] + cls.logger = logging.getLogger('TestWebhooks') + cls.logger.setLevel(logging.DEBUG) + + @classmethod + def tearDownClass(cls): + super(TestWebhooks, cls).tearDownClass() + + def setUp(self): + self.cleanup = [] + self.domain1 = Domain.create( + self.apiclient, + self.services["domain"]) + self.cleanup.append(self.domain1) + + def tearDown(self): + super(TestWebhooks, self).tearDown() + + def popItemFromCleanup(self, item_id): + for idx, x in enumerate(self.cleanup): + if x.id == item_id: + self.cleanup.pop(idx) + break + + def createDomainAccount(self, isDomainAdmin=False): + self.account = Account.create( + self.apiclient, + self.services["account"], + admin=isDomainAdmin, + domainid=self.domain1.id) + self.cleanup.append(self.account) + self.userapiclient = self.testClient.getUserApiClient( + UserName=self.account.name, + DomainName=self.account.domain + ) + + def runWebhookLifecycleTest(self, apiclient, scope=None, domainid=None, account=None, normaluser=None, payloadurl=None, description=None, sslverification=None, secretkey=None, state=None, isdelete=True): + name = "Test-" + random_gen() + if payloadurl is None: + payloadurl = HTTP_PAYLOAD_URL + self.webhook = Webhook.create( + apiclient, + name=name, + payloadurl=payloadurl, + description=description, + scope=scope, + sslverification=sslverification, + secretkey=secretkey, + state=state, + domainid=domainid, + account=account + ) + self.cleanup.append(self.webhook) + self.assertNotEqual( + self.webhook, + None, + "Check webhook created" + ) + webhook_id = self.webhook.id + self.logger.debug("Created webhook: %s" % str(self.webhook.__dict__)) + self.assertEqual( + name, + self.webhook.name, + "Check webhook name" + ) + self.assertEqual( + payloadurl, + self.webhook.payloadurl, + "Check webhook payloadurl" + ) + if state is None: + state = 'Enabled' + self.assertEqual( + state, + self.webhook.state, + "Check webhook state" + ) + if scope is None or normaluser is not None: + scope = 'Local' + self.assertEqual( + scope, + self.webhook.scope, + "Check webhook scope" + ) + if sslverification is None: + sslverification = False + self.assertEqual( + sslverification, + self.webhook.sslverification, + "Check webhook sslverification" + ) + if domainid is not None: + if normaluser is not None: + domainid = normaluser.domainid + self.assertEqual( + domainid, + self.webhook.domainid, + "Check webhook domainid" + ) + if account is not None: + self.assertEqual( + account, + self.webhook.account, + "Check webhook account" + ) + if description is not None: + self.assertEqual( + description, + self.webhook.description, + "Check webhook description" + ) + if secretkey is not None: + self.assertEqual( + secretkey, + self.webhook.secretkey, + "Check webhook secretkey" + ) + list_webhook = Webhook.list( + apiclient, + id=webhook_id + ) + self.assertNotEqual( + list_webhook, + None, + "Check webhook list" + ) + self.assertEqual( + len(list_webhook), + 1, + "Check webhook list length" + ) + self.assertEqual( + list_webhook[0].id, + webhook_id, + "Check webhook list item" + ) + if isdelete == False: + return + self.webhook.delete(apiclient) + self.popItemFromCleanup(webhook_id) + list_webhook = Webhook.list( + apiclient, + id=webhook_id + ) + self.assertTrue( + list_webhook is None or len(list_webhook) == 0, + "Check webhook list after delete" + ) + + @attr(tags=["devcloud", "advanced", "advancedns", "smoke", "basic", "sg"], required_hardware="false") + def test_01_create_webhook_admin_local(self): + self.runWebhookLifecycleTest(self.apiclient) + + @attr(tags=["devcloud", "advanced", "advancedns", "smoke", "basic", "sg"], required_hardware="false") + def test_02_create_webhook_admin_domain(self): + self.runWebhookLifecycleTest(self.apiclient, 'Domain', self.domain1.id) + + @attr(tags=["devcloud", "advanced", "advancedns", "smoke", "basic", "sg"], required_hardware="false") + def test_03_create_webhook_admin_global(self): + self.runWebhookLifecycleTest(self.apiclient, 'Global') + + @attr(tags=["devcloud", "advanced", "advancedns", "smoke", "basic", "sg"], required_hardware="false") + def test_04_create_webhook_domainadmin_local(self): + self.createDomainAccount(True) + self.runWebhookLifecycleTest(self.userapiclient) + + @attr(tags=["devcloud", "advanced", "advancedns", "smoke", "basic", "sg"], required_hardware="false") + def test_05_create_webhook_domainadmin_subdomain(self): + self.createDomainAccount(True) + self.domain11 = Domain.create( + self.apiclient, + self.services["domain"], + parentdomainid=self.domain1.id) + self.cleanup.append(self.domain11) + self.runWebhookLifecycleTest(self.userapiclient, 'Domain', self.domain11.id) + + @attr(tags=["devcloud", "advanced", "advancedns", "smoke", "basic", "sg"], required_hardware="false") + def test_06_create_webhook_domainadmin_global_negative(self): + self.createDomainAccount(True) + try: + self.runWebhookLifecycleTest(self.userapiclient, 'Global') + except CloudstackAPIException as e: + self.assertTrue( + "errorText:Scope Global can not be specified for owner" in str(e), + "Check Global scope error check" + ) + + @attr(tags=["devcloud", "advanced", "advancedns", "smoke", "basic", "sg"], required_hardware="false") + def test_07_create_webhook_user_local(self): + self.createDomainAccount() + self.runWebhookLifecycleTest(self.userapiclient) + + @attr(tags=["devcloud", "advanced", "advancedns", "smoke", "basic", "sg"], required_hardware="false") + def test_08_create_webhook_user_domain(self): + """For normal user scope will always be Local irrespective of the passed value + """ + self.createDomainAccount() + self.runWebhookLifecycleTest(self.userapiclient, 'Domain', self.domain1.id, normaluser=self.account) + + @attr(tags=["devcloud", "advanced", "advancedns", "smoke", "basic", "sg"], required_hardware="false") + def test_09_create_webhook_user_gloabl(self): + """For normal user scope will always be Local irrespective of the passed value + """ + self.createDomainAccount() + self.runWebhookLifecycleTest(self.userapiclient, 'Global', normaluser=self.account) + + @attr(tags=["devcloud", "advanced", "advancedns", "smoke", "basic", "sg"], required_hardware="false") + def test_10_create_webhook_admin_advanced(self): + self.createDomainAccount() + self.runWebhookLifecycleTest( + self.apiclient, + payloadurl=HTTPS_PAYLOAD_URL, + scope="Local", + description="Webhook", + sslverification=True, + secretkey="webhook", + state="Disabled", + domainid=self.domain1.id, + account=self.account.name + ) + + @attr(tags=["devcloud", "advanced", "advancedns", "smoke", "basic", "sg"], required_hardware="false") + def test_11_update_webhook(self): + self.createDomainAccount() + self.runWebhookLifecycleTest(self.userapiclient, isdelete=False) + description = "Desc-" + random_gen() + secretkey = random_gen() + state = 'Disabled' + updated_webhook = self.webhook.update( + self.userapiclient, + description=description, + secretkey=secretkey, + state=state + )['webhook'] + self.assertNotEqual( + updated_webhook, + None, + "Check updated webhook" + ) + self.assertEqual( + description, + updated_webhook.description, + "Check webhook description" + ) + self.assertEqual( + secretkey, + updated_webhook.secretkey, + "Check webhook secretkey" + ) + self.assertEqual( + state, + updated_webhook.state, + "Check webhook state" + ) + + @attr(tags=["devcloud", "advanced", "advancedns", "smoke", "basic", "sg"], required_hardware="false") + def test_12_list_user_webhook_deliveries(self): + self.createDomainAccount() + self.runWebhookLifecycleTest(self.userapiclient, isdelete=False) + now = datetime.now() # current date and time + start_time = now.strftime("%Y-%m-%d %H:%M:%S") + self.keypair = SSHKeyPair.register( + self.userapiclient, + name="Test-" + random_gen(), + publickey="ssh-rsa: e6:9a:1e:b5:98:75:88:5d:56:bc:92:7b:43:48:05:b2" + ) + self.logger.debug("Registered sshkeypair: %s" % str(self.keypair.__dict__)) + cmd = listEvents.listEventsCmd() + cmd.startdate = start_time + cmd.listall = True + events = self.apiclient.listEvents(cmd) + register_sshkeypair_event_count = 0 + if events is not None: + for event in events: + if event.type == "REGISTER.SSH.KEYPAIR": + register_sshkeypair_event_count = register_sshkeypair_event_count + 1 + time.sleep(5) + list_deliveries = self.webhook.list_deliveries( + self.userapiclient, + page=1, + pagesize=20 + ) + self.assertNotEqual( + list_deliveries, + None, + "Check webhook deliveries list" + ) + self.assertTrue( + len(list_deliveries) > 0, + "Check webhook deliveries list length" + ) + register_sshkeypair_delivery_count = 0 + for delivery in list_deliveries: + if delivery.eventtype == "REGISTER.SSH.KEYPAIR": + register_sshkeypair_delivery_count = register_sshkeypair_delivery_count + 1 + self.assertEqual( + register_sshkeypair_event_count, + register_sshkeypair_delivery_count, + "Check sshkeypair webhook deliveries count" + ) + self.webhook.delete_deliveries( + self.userapiclient + ) + list_deliveries = self.webhook.list_deliveries( + self.userapiclient + ) + self.assertTrue( + list_deliveries is None or len(list_deliveries) == 0, + "Check webhook deliveries list after delete" + ) + + @attr(tags=["devcloud", "advanced", "advancedns", "smoke", "basic", "sg"], required_hardware="false") + def test_13_webhook_execute_delivery(self): + self.createDomainAccount() + self.runWebhookLifecycleTest(self.userapiclient, isdelete=False) + payload = "{ \"CloudStack\": \"Integration Test\" }" + delivery = self.webhook.execute_delivery( + self.userapiclient, + payload=payload + ) + self.assertNotEqual( + delivery, + None, + "Check test webhook delivery" + ) + self.assertEqual( + self.webhook.id, + delivery.webhookid, + "Check test webhook delivery webhook" + ) + self.assertEqual( + payload, + delivery.payload, + "Check test webhook delivery payload" + ) + self.assertEqual( + self.webhook.id, + delivery.webhookid, + "Check test webhook delivery webhook" + ) diff --git a/tools/apidoc/gen_toc.py b/tools/apidoc/gen_toc.py index 9df6bf9efc5..d980583c8ce 100644 --- a/tools/apidoc/gen_toc.py +++ b/tools/apidoc/gen_toc.py @@ -274,7 +274,9 @@ known_categories = { 'deleteBucket': 'Object Store', 'listBuckets': 'Object Store', 'listVmsForImport': 'Virtual Machine', - 'importVm': 'Virtual Machine' + 'importVm': 'Virtual Machine', + 'Webhook': 'Webhook', + 'Webhooks': 'Webhook' } diff --git a/tools/marvin/marvin/lib/base.py b/tools/marvin/marvin/lib/base.py index a855908eb0d..6acf6a8ad63 100755 --- a/tools/marvin/marvin/lib/base.py +++ b/tools/marvin/marvin/lib/base.py @@ -7227,3 +7227,65 @@ class Bucket: cmd.id = self.id [setattr(cmd, k, v) for k, v in list(kwargs.items())] return apiclient.updateBucket(cmd) + +class Webhook: + """Manage Webhook Life cycle""" + + def __init__(self, items): + self.__dict__.update(items) + + @classmethod + def create(cls, apiclient, name, payloadurl, **kwargs): + """Create Webhook""" + cmd = createWebhook.createWebhookCmd() + cmd.name = name + cmd.payloadurl = payloadurl + [setattr(cmd, k, v) for k, v in list(kwargs.items())] + + return Webhook(apiclient.createWebhook(cmd).__dict__) + + @classmethod + def list(cls, apiclient, **kwargs): + cmd = listWebhooks.listWebhooksCmd() + [setattr(cmd, k, v) for k, v in list(kwargs.items())] + if 'account' in list(kwargs.keys()) and 'domainid' in list(kwargs.keys()): + cmd.listall = True + return apiclient.listWebhooks(cmd) + + def delete(self, apiclient): + """Delete Webhook""" + cmd = deleteWebhook.deleteWebhookCmd() + cmd.id = self.id + apiclient.deleteWebhook(cmd) + + def update(self, apiclient, **kwargs): + """Update Webhook""" + + cmd = updateWebhook.updateWebhookCmd() + cmd.id = self.id + [setattr(cmd, k, v) for k, v in list(kwargs.items())] + return apiclient.updateWebhook(cmd) + + def list_deliveries(self, apiclient, **kwargs): + """List Webhook Deliveries""" + + cmd = listWebhookDeliveries.listWebhookDeliveriesCmd() + cmd.webhookid = self.id + [setattr(cmd, k, v) for k, v in list(kwargs.items())] + return apiclient.listWebhookDeliveries(cmd) + + def execute_delivery(self, apiclient, **kwargs): + """Execute Webhook Delivery""" + + cmd = executeWebhookDelivery.executeWebhookDeliveryCmd() + cmd.webhookid = self.id + [setattr(cmd, k, v) for k, v in list(kwargs.items())] + return apiclient.executeWebhookDelivery(cmd) + + def delete_deliveries(self, apiclient, **kwargs): + """Delete Webhook Deliveries""" + + cmd = deleteWebhookDelivery.deleteWebhookDeliveryCmd() + cmd.webhookid = self.id + [setattr(cmd, k, v) for k, v in list(kwargs.items())] + return apiclient.deleteWebhookDelivery(cmd) diff --git a/ui/public/locales/en.json b/ui/public/locales/en.json index 70674fcbf6f..01e8fdaeace 100644 --- a/ui/public/locales/en.json +++ b/ui/public/locales/en.json @@ -60,6 +60,8 @@ "label.action.bulk.release.public.ip.address": "Bulk release public IP addresses", "label.action.cancel.maintenance.mode": "Cancel maintenance mode", "label.action.change.password": "Change password", +"label.action.clear.webhook.deliveries": "Clear deliveries", +"label.action.delete.webhook.deliveries": "Delete deliveries", "label.action.configure.stickiness": "Stickiness", "label.action.copy.iso": "Copy ISO", "label.action.copy.snapshot": "Copy Snapshot", @@ -583,6 +585,7 @@ "label.create.tungsten.routing.policy": "Create Tungsten-Fabric routing policy", "label.create.user": "Create User", "label.create.vpn.connection": "Create VPN connection", +"label.create.webhook": "Create Webhook", "label.created": "Created", "label.creating": "Creating", "label.creating.iprange": "Creating IP ranges", @@ -603,6 +606,9 @@ "label.data.disk": "Data disk", "label.data.disk.offering": "Data disk offering", "label.date": "Date", +"label.datetime.filter.period": "From {startDate} to {endDate}", +"label.datetime.filter.starting": "Starting {startDate}.", +"label.datetime.filter.up.to": "Up to {endDate}.", "label.day": "Day", "label.day.of.month": "Day of month", "label.day.of.week": "Day of week", @@ -672,6 +678,8 @@ "label.delete.vpn.customer.gateway": "Delete VPN customer gateway", "label.delete.vpn.gateway": "Delete VPN gateway", "label.delete.vpn.user": "Delete VPN User", +"label.delete.webhook": "Delete Webhook", +"label.delete.webhook.delivery": "Delete Webhook Delivery", "label.deleteconfirm": "Please confirm that you would like to delete this", "label.deleting": "Deleting", "label.deleting.failed": "Deleting failed", @@ -730,6 +738,7 @@ "label.disable.storage": "Disable storage pool", "label.disable.vpc.offering": "Disable VPC offering", "label.disable.vpn": "Disable remote access VPN", +"label.disable.webhook": "Disable Webhook", "label.disabled": "Disabled", "label.disconnected": "Last disconnected", "label.disk": "Disk", @@ -837,6 +846,7 @@ "label.enable.storage": "Enable storage pool", "label.enable.vpc.offering": "Enable VPC offering", "label.enable.vpn": "Enable remote access VPN", +"label.enable.webhook": "Enable Webhook", "label.enabled": "Enabled", "label.encrypt": "Encrypt", "label.encryptroot": "Encrypt Root Disk", @@ -872,6 +882,8 @@ "label.esppolicy": "ESP policy", "label.esx.host": "ESX/ESXi host", "label.event": "Event", +"label.eventid": "Event", +"label.eventtype": "Event type", "label.event.archived": "Event(s) archived", "label.event.deleted": "Event(s) deleted", "label.event.timeline": "Event timeline", @@ -987,6 +999,7 @@ "label.hardware": "Hardware", "label.hasrules":"FW rules defined", "label.hastate": "HA state", +"label.headers": "Headers", "label.header.backup.schedule": "You can set up recurring backup schedules by selecting from the available options below and applying your policy preference.", "label.header.volume.snapshot": "You can set up recurring Snapshot schedules by selecting from the available options below and applying your policy preference.", "label.header.volume.take.snapshot": "Please confirm that you want to take a Snapshot of this volume.", @@ -1281,6 +1294,8 @@ "label.managed.volumes": "Managed Volumes", "label.managedstate": "Managed state", "label.management": "Management", +"label.managementserverid": "Management server", +"label.managementservername": "Management server", "label.management.ips": "Management IP addresses", "label.management.server": "Management server", "label.management.servers": "Management servers", @@ -1562,6 +1577,7 @@ "label.patp": "Palo Alto threat profile", "label.pavr": "Virtual router", "label.payload": "Payload", +"label.payloadurl": "Payload URL", "label.pcidevice": "GPU", "label.pending.jobs": "Pending Jobs", "label.per.account": "Per Account", @@ -1709,9 +1725,11 @@ "label.readonly": "Read-Only", "label.reason": "Reason", "label.reboot": "Reboot", +"label.recent.deliveries": "Recent deliveries", "label.receivedbytes": "Bytes received", "label.recover.vm": "Recover Instance", "label.recovering": "Recovering", +"label.redeliver": "Redeliver", "label.redirect": "Redirect to:", "label.redirecturi": "Redirect URI", "label.redundantrouter": "Redundant router", @@ -1790,6 +1808,7 @@ "label.resources": "Resources", "label.resourcestate": "Resource state", "label.resourcetype": "Resource type", +"label.response": "Response", "label.restart.network": "Restart Network", "label.restart.vpc": "Restart VPC", "label.restartrequired": "Restart required", @@ -1998,6 +2017,7 @@ "label.sshkeypair": "New SSH key pair", "label.sshkeypairs": "SSH key pairs", "label.sslcertificates": "SSL certificates", +"label.sslverification": "SSL verification", "label.standard.us.keyboard": "Standard (US) keyboard", "label.start": "Start", "label.start.date.and.time": "Start date and time", @@ -2118,6 +2138,8 @@ "label.templatetype": "Template type", "label.templateversion": "Template version", "label.term.type": "Term type", +"label.test": "Test", +"label.test.webhook.delivery": "Test Webhook Delivery", "label.tftpdir": "TFTP root directory", "label.theme.alert": "The setting is only visible to the current browser. To apply the setting, please download the JSON file and replace its content in the `theme` section of the `config.json` file under the path: `/public/config.json`", "label.theme.color": "Theme color", @@ -2221,6 +2243,7 @@ "label.update.to": "updated to", "label.update.traffic.label": "Update traffic labels", "label.update.vmware.datacenter": "Update VMWare datacenter", +"label.update.webhook": "Update Webhook", "label.updating": "Updating", "label.upgrade.router.newer.template": "Upgrade router to use newer Template", "label.upgrading": "Upgrading", @@ -2399,6 +2422,10 @@ "label.warn": "Warn", "label.warn.upper": "WARN", "label.warning": "Warning", +"label.webhook": "Webhook", +"label.webhooks": "Webhooks", +"label.webhookname": "Webhook", +"label.webhook.deliveries": "Webhook deliveries", "label.wednesday": "Wednesday", "label.weekly": "Weekly", "label.welcome": "Welcome", @@ -2547,6 +2574,7 @@ "message.add.ip.v6.firewall.rule.failed": "Failed to add IPv6 firewall rule", "message.add.ip.v6.firewall.rule.processing": "Adding IPv6 firewall rule...", "message.add.ip.v6.firewall.rule.success": "Added IPv6 firewall rule", +"message.redeliver.webhook.delivery": "Redeliver this Webhook delivery", "message.remove.ip.v6.firewall.rule.failed": "Failed to remove IPv6 firewall rule", "message.remove.ip.v6.firewall.rule.processing": "Removing IPv6 firewall rule...", "message.remove.ip.v6.firewall.rule.success": "Removed IPv6 firewall rule", @@ -2658,12 +2686,14 @@ "message.confirm.disable.provider": "Please confirm that you would like to disable this provider.", "message.confirm.disable.storage": "Please confirm that you want to disable the storage pool.", "message.confirm.disable.vpc.offering": "Are you sure you want to disable this VPC offering?", +"message.confirm.disable.webhook": "Please confirm that you want to disable this webhook.", "message.confirm.enable.autoscale.vmgroup": "Please confirm that you want to enable this autoscale Instance group.", "message.confirm.enable.host": "Please confirm that you want to enable the host.", "message.confirm.enable.network.offering": "Are you sure you want to enable this Network offering?", "message.confirm.enable.provider": "Please confirm that you would like to enable this provider.", "message.confirm.enable.storage": "Please confirm that you want to enable the storage pool.", "message.confirm.enable.vpc.offering": "Are you sure you want to enable this VPC offering?", +"message.confirm.enable.webhook": "Please confirm that you want to enable this webhook.", "message.confirm.remove.firewall.rule": "Please confirm that you want to delete this Firewall Rule?", "message.confirm.remove.ip.range": "Please confirm that you would like to remove this IP range.", "message.confirm.remove.network.offering": "Are you sure you want to remove this Network offering?", @@ -2745,6 +2775,8 @@ "message.delete.vpn.connection": "Please confirm that you want to delete VPN connection.", "message.delete.vpn.customer.gateway": "Please confirm that you want to delete this VPN customer gateway.", "message.delete.vpn.gateway": "Please confirm that you want to delete this VPN Gateway.", +"message.delete.webhook": "Please confirm that you want to delete this Webhook.", +"message.delete.webhook.delivery": "Please confirm that you want to delete this Webhook delivery.", "message.deleting.firewall.policy": "Deleting Firewall Policy", "message.deleting.node": "Deleting Node", "message.deleting.vm": "Deleting Instance", @@ -2782,6 +2814,7 @@ "message.disable.vpn": "Are you sure you want to disable VPN?", "message.disable.vpn.failed": "Failed to disable VPN.", "message.disable.vpn.processing": "Disabling VPN...", +"message.disable.webhook.ssl.verification": "Disabling SSL verification is not recommended", "message.discovering.feature": "Discovering features, please wait...", "message.disk.offering.created": "Disk offering created:", "message.disk.usage.info.data.points": "Each data point represents the difference in read/write data since the last data point.", @@ -2956,6 +2989,9 @@ "message.error.zone.type": "Please select zone type.", "message.error.linstor.resourcegroup": "Please enter the Linstor Resource-Group.", "message.error.fixed.offering.kvm": "It's not possible to scale up Instances that utilize KVM hypervisor with a fixed compute offering.", +"message.error.create.webhook.local.account": "Account must be provided for creating a Webhook with Local scope.", +"message.error.create.webhook.name": "Name must be provided for creating a Webhook.", +"message.error.create.webhook.payloadurl": "Payload URL must be provided for creating a Webhook.", "message.fail.to.delete": "Failed to delete.", "message.failed.to.add": "Failed to add", "message.failed.to.assign.vms": "Failed to assign Instances", @@ -3215,6 +3251,7 @@ "message.success.change.affinity.group": "Successfully changed affinity groups", "message.success.change.offering": "Successfully changed offering", "message.success.change.password": "Successfully changed password for User", +"message.success.clear.webhook.deliveries": "Successfully cleared webhook deliveries", "message.success.config.backup.schedule": "Successfully configured Instance backup schedule", "message.success.config.health.monitor": "Successfully Configure Health Monitor", "message.success.config.sticky.policy": "Successfully configured sticky policy", @@ -3231,6 +3268,7 @@ "message.success.create.template": "Successfully created Template", "message.success.create.user": "Successfully created User", "message.success.create.volume": "Successfully created volume", +"message.success.create.webhook": "Successfully created Webhook", "message.success.delete": "Successfully deleted", "message.success.delete.acl.rule": "Successfully removed ACL rule", "message.success.delete.backup.schedule": "Successfully deleted configure Instance backup schedule", @@ -3316,6 +3354,7 @@ "message.update.autoscale.vm.profile.failed": "Failed to update autoscale Instance profile", "message.update.condition.failed": "Failed to update condition", "message.update.condition.processing": "Updating condition...", +"message.test.webhook.delivery": "Test delivery to the Webhook with an optional payload", "message.two.factor.authorization.failed": "Unable to verify 2FA with provided code, please retry.", "message.two.fa.auth": "Open the two-factor authentication app on your mobile device to view your authentication code.", "message.two.fa.auth.register.account": "Open the two-factor authentication application and scan the QR code add the User Account.", @@ -3404,6 +3443,7 @@ "message.warn.filetype": "jpg, jpeg, png, bmp and svg are the only supported image formats.", "message.warn.importing.instance.without.nic": "WARNING: This Instance is being imported without NICs and many Network resources will not be available. Consider creating a NIC via vCenter before importing or as soon as the Instance is imported.", "message.warn.zone.mtu.update": "Please note that this limit won't affect pre-existing Network’s MTU settings", +"message.webhook.deliveries.time.filter": "Webhook deliveries list can be filtered based on date-time. Select 'Custom' for specifying start and end date range.", "message.zone.creation.complete": "Zone creation complete.", "message.zone.detail.description": "Populate zone details.", "message.zone.detail.hint": "A zone is the largest organizational unit in CloudStack, and it typically corresponds to a single datacenter. Zones provide physical isolation and redundancy. A zone consists of one or more pods (each of which contains hosts and primary storage servers) and a secondary storage server which is shared by all pods in the zone.", diff --git a/ui/src/components/view/stats/FilterStats.vue b/ui/src/components/view/DateTimeFilter.vue similarity index 93% rename from ui/src/components/view/stats/FilterStats.vue rename to ui/src/components/view/DateTimeFilter.vue index 20cd63af873..aca958f58f7 100644 --- a/ui/src/components/view/stats/FilterStats.vue +++ b/ui/src/components/view/DateTimeFilter.vue @@ -22,12 +22,12 @@ :model="form" @finish="handleSubmit" layout="vertical"> -
+
- +
@@ -64,7 +64,7 @@ import { ref, reactive, toRaw } from 'vue' import moment from 'moment' export default { - name: 'FilterStats', + name: 'DateTimeFilter', emits: ['closeAction', 'onSubmit'], props: { startDateProp: { @@ -74,6 +74,14 @@ export default { endDateProp: { type: [Date, String, Number], required: false + }, + showAllDataOption: { + type: Boolean, + default: true + }, + allDataMessage: { + type: String, + value: '' } }, computed: { diff --git a/ui/src/components/view/DetailsTab.vue b/ui/src/components/view/DetailsTab.vue index f731766f878..c9ab6b89ec8 100644 --- a/ui/src/components/view/DetailsTab.vue +++ b/ui/src/components/view/DetailsTab.vue @@ -103,6 +103,12 @@
{{ dataResource.serviceofferingdetails[item] }}
+
+ {{ dataResource[item] }} +
+
+ {{ JSON.stringify(JSON.parse(dataResource[item]), null, 4) || dataResource[item] }} +
{{ dataResource[item] }}
@@ -120,6 +126,13 @@
{{ dataResource[item] }}
+ +
+ {{ $t('label.' + item.replace('date', '.date.and.time'))}} +
+
{{ $toLocaleDate(dataResource[item]) }}
+
+
@@ -174,7 +187,12 @@ export default { }, computed: { customDisplayItems () { - return ['ip6routes', 'privatemtu', 'publicmtu', 'provider'] + var items = ['ip6routes', 'privatemtu', 'publicmtu', 'provider'] + if (this.$route.meta.name === 'webhookdeliveries') { + items.push('startdate') + items.push('enddate') + } + return items }, vnfAccessMethods () { if (this.resource.templatetype === 'VNF' && ['vm', 'vnfapp'].includes(this.$route.meta.name)) { diff --git a/ui/src/components/view/InfoCard.vue b/ui/src/components/view/InfoCard.vue index a4fa1191d13..fb335ace3d2 100644 --- a/ui/src/components/view/InfoCard.vue +++ b/ui/src/components/view/InfoCard.vue @@ -140,6 +140,12 @@
+
+
{{ $t('label.success') }}
+
+ +
+
{{ $t('label.id') }}
@@ -672,6 +678,22 @@ {{ resource.domain || resource.domainid }}
+
+
{{ $t('label.payloadurl') }}
+
+ + {{ resource.payloadurl }} + {{ resource.payloadurl }} +
+
+
+
{{ $t('label.webhook') }}
+
+ + {{ resource.webhookname || resource.webhookid }} + {{ resource.webhookname || resource.webhookid }} +
+
{{ $t('label.management.servers') }}
diff --git a/ui/src/components/view/ListView.vue b/ui/src/components/view/ListView.vue index cf3d9361638..2b1036a293d 100644 --- a/ui/src/components/view/ListView.vue +++ b/ui/src/components/view/ListView.vue @@ -23,7 +23,7 @@ :dataSource="items" :rowKey="(record, idx) => record.id || record.name || record.usageType || idx + '-' + Math.random()" :pagination="false" - :rowSelection=" enableGroupAction() || $route.name === 'event' ? {selectedRowKeys: selectedRowKeys, onChange: onSelectChange, columnWidth: 30} : null" + :rowSelection="explicitlyAllowRowSelection || enableGroupAction() || $route.name === 'event' ? {selectedRowKeys: selectedRowKeys, onChange: onSelectChange, columnWidth: 30} : null" :rowClassName="getRowClassName" style="overflow-y: auto" > @@ -364,12 +364,47 @@ {{ record.enabled ? 'Enabled' : 'Disabled' }} - - + - + - + - + - + - + - + - + - +