/* * Licensed 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.ngrinder.perftest.controller; import com.google.gson.Gson; import com.google.gson.GsonBuilder; import net.grinder.util.LogCompressUtils; import net.grinder.util.Pair; import org.apache.commons.io.FilenameUtils; import org.apache.commons.io.IOUtils; import org.apache.commons.lang.StringUtils; import org.apache.commons.lang.math.NumberUtils; import org.apache.commons.lang.mutable.MutableInt; import org.ngrinder.agent.service.AgentManagerService; import org.ngrinder.common.constant.ControllerConstants; import org.ngrinder.common.controller.BaseController; import org.ngrinder.common.controller.RestAPI; import org.ngrinder.common.util.DateUtils; import org.ngrinder.common.util.FileDownloadUtils; import org.ngrinder.infra.config.Config; import org.ngrinder.infra.logger.CoreLogger; import org.ngrinder.infra.spring.RemainedPath; import org.ngrinder.model.*; import org.ngrinder.perftest.service.AgentManager; import org.ngrinder.perftest.service.PerfTestService; import org.ngrinder.perftest.service.TagService; import org.ngrinder.region.service.RegionService; import org.ngrinder.script.handler.ScriptHandlerFactory; import org.ngrinder.script.model.FileCategory; import org.ngrinder.script.model.FileEntry; import org.ngrinder.script.service.FileEntryService; import org.ngrinder.user.service.UserService; import org.python.google.common.collect.Maps; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.data.domain.Page; import org.springframework.data.domain.PageRequest; import org.springframework.data.domain.Pageable; import org.springframework.data.domain.Sort; import org.springframework.data.domain.Sort.Direction; import org.springframework.data.web.PageableDefault; import org.springframework.http.HttpEntity; import org.springframework.stereotype.Controller; import org.springframework.ui.ModelMap; import org.springframework.web.bind.annotation.*; import javax.annotation.Nullable; import javax.annotation.PostConstruct; import javax.servlet.ServletOutputStream; import javax.servlet.http.HttpServletResponse; import java.io.File; import java.io.FileInputStream; import java.net.URL; import java.util.*; import static com.google.common.collect.Iterables.filter; import static com.google.common.collect.Lists.newArrayList; import static org.apache.commons.lang.StringUtils.trimToEmpty; import static org.ngrinder.common.util.CollectionUtils.buildMap; import static org.ngrinder.common.util.CollectionUtils.newHashMap; import static org.ngrinder.common.util.ExceptionUtils.processException; import static org.ngrinder.common.util.ObjectUtils.defaultIfNull; import static org.ngrinder.common.util.Preconditions.*; /** * Performance Test Controller. * * @author Mavlarn * @author JunHo Yoon */ @SuppressWarnings("SpringJavaAutowiringInspection") @Controller @RequestMapping("/perftest") public class PerfTestController extends BaseController { @Autowired private PerfTestService perfTestService; @Autowired private FileEntryService fileEntryService; @Autowired private AgentManager agentManager; @Autowired private AgentManagerService agentManagerService; @Autowired private TagService tagService; @Autowired private ScriptHandlerFactory scriptHandlerFactory; @Autowired private UserService userService; @Autowired private RegionService regionService; private Gson fileEntryGson; /** * Initialize. */ @PostConstruct public void init() { GsonBuilder gsonBuilder = new GsonBuilder(); gsonBuilder.registerTypeAdapter(FileEntry.class, new FileEntry.FileEntrySerializer()); fileEntryGson = gsonBuilder.create(); } /** * Get the perf test lists. * * @param user user * @param query query string to search the perf test * @param tag tag * @param queryFilter "F" means get only finished, "S" means get only scheduled tests. * @param pageable page * @param model modelMap * @return perftest/list */ @RequestMapping({"/list", "/", ""}) public String getAll(User user, @RequestParam(required = false) String query, @RequestParam(required = false) String tag, @RequestParam(required = false) String queryFilter, @PageableDefault(page = 0, size = 10) Pageable pageable, ModelMap model) { pageable = new PageRequest(pageable.getPageNumber(), pageable.getPageSize(), defaultIfNull(pageable.getSort(), new Sort(Direction.DESC, "lastModifiedDate"))); Page<PerfTest> tests = perfTestService.getPagedAll(user, query, tag, queryFilter, pageable); if (tests.getNumberOfElements() == 0) { pageable = new PageRequest(0, pageable.getPageSize(), defaultIfNull(pageable.getSort(), new Sort(Direction.DESC, "lastModifiedDate"))); tests = perfTestService.getPagedAll(user, query, tag, queryFilter, pageable); } annotateDateMarker(tests); model.addAttribute("tag", tag); model.addAttribute("availTags", tagService.getAllTagStrings(user, StringUtils.EMPTY)); model.addAttribute("testListPage", tests); model.addAttribute("queryFilter", queryFilter); model.addAttribute("query", query); putPageIntoModelMap(model, pageable); return "perftest/list"; } private void annotateDateMarker(Page<PerfTest> tests) { TimeZone userTZ = TimeZone.getTimeZone(getCurrentUser().getTimeZone()); Calendar userToday = Calendar.getInstance(userTZ); Calendar userYesterday = Calendar.getInstance(userTZ); userYesterday.add(Calendar.DATE, -1); for (PerfTest test : tests) { Calendar localedModified = Calendar.getInstance(userTZ); localedModified.setTime(DateUtils.convertToUserDate(getCurrentUser().getTimeZone(), test.getLastModifiedDate())); if (org.apache.commons.lang.time.DateUtils.isSameDay(userToday, localedModified)) { test.setDateString("today"); } else if (org.apache.commons.lang.time.DateUtils.isSameDay(userYesterday, localedModified)) { test.setDateString("yesterday"); } else { test.setDateString("earlier"); } } } /** * Open the new perf test creation form. * * @param user user * @param model model * @return "perftest/detail" */ @RequestMapping("/new") public String openForm(User user, ModelMap model) { return getOne(user, null, model); } /** * Get the perf test detail on the given perf test id. * * @param user user * @param id perf test id * @param model model * @return perftest/detail */ @RequestMapping("/{id}") public String getOne(User user, @PathVariable("id") Long id, ModelMap model) { PerfTest test = null; if (id != null) { test = getOneWithPermissionCheck(user, id, true); } if (test == null) { test = new PerfTest(user); test.init(); } model.addAttribute(PARAM_TEST, test); // Retrieve the agent count map based on create user, if the test is // created by the others. user = test.getCreatedUser() != null ? test.getCreatedUser() : user; Map<String, MutableInt> agentCountMap = agentManagerService.getAvailableAgentCountMap(user); model.addAttribute(PARAM_REGION_AGENT_COUNT_MAP, agentCountMap); model.addAttribute(PARAM_REGION_LIST, regionService.getAllVisibleRegionNames()); model.addAttribute(PARAM_PROCESS_THREAD_POLICY_SCRIPT, perfTestService.getProcessAndThreadPolicyScript()); addDefaultAttributeOnModel(model); return "perftest/detail"; } private ArrayList<String> getRegions(Map<String, MutableInt> agentCountMap) { ArrayList<String> regions = new ArrayList<String>(agentCountMap.keySet()); Collections.sort(regions); return regions; } /** * Search tags based on the given query. * * @param user user to search * @param query query string * @return found tag list in json */ @RequestMapping("/search_tag") public HttpEntity<String> searchTag(User user, @RequestParam(required = false) String query) { List<String> allStrings = tagService.getAllTagStrings(user, query); if (StringUtils.isNotBlank(query)) { allStrings.add(query); } return toJsonHttpEntity(allStrings); } /** * Add the various default configuration values on the model. * * @param model model to which put the default values */ public void addDefaultAttributeOnModel(ModelMap model) { model.addAttribute(PARAM_AVAILABLE_RAMP_UP_TYPE, RampUp.values()); model.addAttribute(PARAM_MAX_VUSER_PER_AGENT, agentManager.getMaxVuserPerAgent()); model.addAttribute(PARAM_MAX_RUN_COUNT, agentManager.getMaxRunCount()); model.addAttribute(PARAM_SECURITY_MODE, getConfig().isSecurityEnabled()); model.addAttribute(PARAM_MAX_RUN_HOUR, agentManager.getMaxRunHour()); model.addAttribute(PARAM_SAFE_FILE_DISTRIBUTION, getConfig().getControllerProperties().getPropertyBoolean(ControllerConstants.PROP_CONTROLLER_SAFE_DIST)); String timeZone = getCurrentUser().getTimeZone(); int offset; if (StringUtils.isNotBlank(timeZone)) { offset = TimeZone.getTimeZone(timeZone).getOffset(System.currentTimeMillis()); } else { offset = TimeZone.getDefault().getOffset(System.currentTimeMillis()); } model.addAttribute(PARAM_TIMEZONE_OFFSET, offset); } /** * Get the perf test creation form for quickStart. * * @param user user * @param urlString URL string to be tested. * @param scriptType scriptType * @param model model * @return perftest/detail */ @RequestMapping("/quickstart") public String getQuickStart(User user, @RequestParam(value = "url", required = true) String urlString, @RequestParam(value = "scriptType", required = true) String scriptType, ModelMap model) { URL url = checkValidURL(urlString); FileEntry newEntry = fileEntryService.prepareNewEntryForQuickTest(user, urlString, scriptHandlerFactory.getHandler(scriptType)); model.addAttribute(PARAM_QUICK_SCRIPT, newEntry.getPath()); model.addAttribute(PARAM_QUICK_SCRIPT_REVISION, newEntry.getRevision()); model.addAttribute(PARAM_TEST, createPerfTestFromQuickStart(user, "Test for " + url.getHost(), url.getHost())); Map<String, MutableInt> agentCountMap = agentManagerService.getAvailableAgentCountMap(user); model.addAttribute(PARAM_REGION_AGENT_COUNT_MAP, agentCountMap); model.addAttribute(PARAM_REGION_LIST, getRegions(agentCountMap)); addDefaultAttributeOnModel(model); model.addAttribute(PARAM_PROCESS_THREAD_POLICY_SCRIPT, perfTestService.getProcessAndThreadPolicyScript()); return "perftest/detail"; } /** * Create a new test from quick start mode. * * @param user user * @param testName test name * @param targetHost target host * @return test {@link PerfTest} */ private PerfTest createPerfTestFromQuickStart(User user, String testName, String targetHost) { PerfTest test = new PerfTest(user); test.init(); test.setTestName(testName); test.setTargetHosts(targetHost); return test; } /** * Create a new test or cloneTo a current test. * * @param user user * @param perfTest {@link PerfTest} * @param isClone true if cloneTo * @param model model * @return redirect:/perftest/list */ @RequestMapping(value = "/new", method = RequestMethod.POST) public String saveOne(User user, PerfTest perfTest, @RequestParam(value = "isClone", required = false, defaultValue = "false") boolean isClone, ModelMap model) { validate(user, null, perfTest); // Point to the head revision perfTest.setTestName(StringUtils.trimToEmpty(perfTest.getTestName())); perfTest.setScriptRevision(-1L); perfTest.prepare(isClone); perfTest = perfTestService.save(user, perfTest); model.clear(); if (perfTest.getStatus() == Status.SAVED || perfTest.getScheduledTime() != null) { return "redirect:/perftest/list"; } else { return "redirect:/perftest/" + perfTest.getId(); } } @SuppressWarnings("ConstantConditions") private void validate(User user, PerfTest oldOne, PerfTest newOne) { if (oldOne == null) { oldOne = new PerfTest(); oldOne.init(); } newOne = oldOne.merge(newOne); checkNotEmpty(newOne.getTestName(), "testName should be provided"); checkArgument(newOne.getStatus().equals(Status.READY) || newOne.getStatus().equals(Status.SAVED), "status only allows SAVE or READY"); if (newOne.isThresholdRunCount()) { final Integer runCount = newOne.getRunCount(); checkArgument(runCount > 0 && runCount <= agentManager .getMaxRunCount(), "runCount should be equal to or less than %s", agentManager.getMaxRunCount()); } else { final Long duration = newOne.getDuration(); checkArgument(duration > 0 && duration <= (((long) agentManager.getMaxRunHour()) * 3600000L), "duration should be equal to or less than %s", agentManager.getMaxRunHour()); } Map<String, MutableInt> agentCountMap = agentManagerService.getAvailableAgentCountMap(user); MutableInt agentCountObj = agentCountMap.get(isClustered() ? newOne.getRegion() : Config.NONE_REGION); checkNotNull(agentCountObj, "region should be within current region list"); int agentMaxCount = agentCountObj.intValue(); checkArgument(newOne.getAgentCount() <= agentMaxCount, "test agent should be equal to or less than %s", agentMaxCount); if (newOne.getStatus().equals(Status.READY)) { checkArgument(newOne.getAgentCount() >= 1, "agentCount should be more than 1 when it's READY status."); } checkArgument(newOne.getVuserPerAgent() <= agentManager.getMaxVuserPerAgent(), "vuserPerAgent should be equal to or less than %s", agentManager.getMaxVuserPerAgent()); if (getConfig().isSecurityEnabled()) { checkArgument(StringUtils.isNotEmpty(newOne.getTargetHosts()), "targetHosts should be provided when security mode is enabled"); } if (newOne.getStatus() != Status.SAVED) { checkArgument(StringUtils.isNotBlank(newOne.getScriptName()), "scriptName should be provided."); } checkArgument(newOne.getVuserPerAgent() == newOne.getProcesses() * newOne.getThreads(), "vuserPerAgent should be equal to (processes * threads)"); } /** * Leave the comment on the perf test. * * @param id testId * @param user user * @param testComment test comment * @param tagString tagString * @return JSON */ @RequestMapping(value = "/{id}/leave_comment", method = RequestMethod.POST) @ResponseBody public String leaveComment(User user, @PathVariable("id") Long id, @RequestParam("testComment") String testComment, @RequestParam(value = "tagString", required = false) String tagString) { perfTestService.addCommentOn(user, id, testComment, tagString); return returnSuccess(); } private Long[] convertString2Long(String ids) { String[] numbers = StringUtils.split(ids, ","); Long[] id = new Long[numbers.length]; int i = 0; for (String each : numbers) { id[i++] = NumberUtils.toLong(each, 0); } return id; } private List<Map<String, Object>> getStatus(List<PerfTest> perfTests) { List<Map<String, Object>> statuses = newArrayList(); for (PerfTest each : perfTests) { Map<String, Object> result = newHashMap(); result.put("id", each.getId()); result.put("status_id", each.getStatus()); result.put("status_type", each.getStatus()); result.put("name", getMessages(each.getStatus().getSpringMessageKey())); result.put("icon", each.getStatus().getIconName()); result.put("message", StringUtils.replace(each.getProgressMessage() + "\n<b>" + each.getLastProgressMessage() + "</b>\n" + each.getLastModifiedDateToStr(), "\n", "<br/>")); result.put("deletable", each.getStatus().isDeletable()); result.put("stoppable", each.getStatus().isStoppable()); result.put("reportable", each.getStatus().isReportable()); statuses.add(result); } return statuses; } /** * Delete the perf tests having given IDs. * * @param user user * @param ids comma operated IDs * @return success json messages if succeeded. */ @RestAPI @RequestMapping(value = "/api", method = RequestMethod.DELETE) public HttpEntity<String> delete(User user, @RequestParam(value = "ids", defaultValue = "") String ids) { for (String idStr : StringUtils.split(ids, ",")) { perfTestService.delete(user, NumberUtils.toLong(idStr, 0)); } return successJsonHttpEntity(); } /** * Stop the perf tests having given IDs. * * @param user user * @param ids comma separated perf test IDs * @return success json if succeeded. */ @RestAPI @RequestMapping(value = "/api", params = "action=stop", method = RequestMethod.PUT) public HttpEntity<String> stop(User user, @RequestParam(value = "ids", defaultValue = "") String ids) { for (String idStr : StringUtils.split(ids, ",")) { perfTestService.stop(user, NumberUtils.toLong(idStr, 0)); } return successJsonHttpEntity(); } /** * Filter out please_modify_this.com from hosts string. * * @param originalString original string * @return filtered string */ private String filterHostString(String originalString) { List<String> hosts = newArrayList(); for (String each : StringUtils.split(StringUtils.trimToEmpty(originalString), ",")) { if (!each.contains("please_modify_this.com")) { hosts.add(each); } } return StringUtils.join(hosts, ","); } private Map<String, Object> getPerfGraphData(Long id, String[] dataTypes, boolean onlyTotal, int imgWidth) { final PerfTest test = perfTestService.getOne(id); int interval = perfTestService.getReportDataInterval(id, dataTypes[0], imgWidth); Map<String, Object> resultMap = Maps.newHashMap(); for (String each : dataTypes) { Pair<ArrayList<String>, ArrayList<String>> tpsResult = perfTestService.getReportData(id, each, onlyTotal, interval); Map<String, Object> dataMap = Maps.newHashMap(); dataMap.put("labels", tpsResult.getFirst()); dataMap.put("data", tpsResult.getSecond()); resultMap.put(StringUtils.replaceChars(each, "()", ""), dataMap); } resultMap.put(PARAM_TEST_CHART_INTERVAL, interval * test.getSamplingInterval()); return resultMap; } /** * Get the running division in perftest configuration page. * * @param user user * @param model model * @param id test id * @return perftest/running */ @RequestMapping(value = "{id}/running_div") public String getReportSection(User user, ModelMap model, @PathVariable long id) { PerfTest test = getOneWithPermissionCheck(user, id, false); model.addAttribute(PARAM_TEST, test); return "perftest/running"; } /** * Get the basic report content in perftest configuration page. * <p/> * This method returns the appropriate points based on the given imgWidth. * * @param user user * @param model model * @param id test id * @param imgWidth image width * @return perftest/basic_report */ @RequestMapping(value = "{id}/basic_report") public String getReportSection(User user, ModelMap model, @PathVariable long id, @RequestParam int imgWidth) { PerfTest test = getOneWithPermissionCheck(user, id, false); int interval = perfTestService.getReportDataInterval(id, "TPS", imgWidth); model.addAttribute(PARAM_LOG_LIST, perfTestService.getLogFiles(id)); model.addAttribute(PARAM_TEST_CHART_INTERVAL, interval * test.getSamplingInterval()); model.addAttribute(PARAM_TEST, test); model.addAttribute(PARAM_TPS, perfTestService.getSingleReportDataAsJson(id, "TPS", interval)); return "perftest/basic_report"; } /** * Download the CSV report for the given perf test id. * * @param user user * @param id test id * @param response response */ @RequestMapping(value = "/{id}/download_csv") public void downloadCSV(User user, @PathVariable("id") long id, HttpServletResponse response) { PerfTest test = getOneWithPermissionCheck(user, id, false); File targetFile = perfTestService.getCsvReportFile(test); checkState(targetFile.exists(), "File %s doesn't exist!", targetFile.getName()); FileDownloadUtils.downloadFile(response, targetFile); } /** * Download logs for the perf test having the given id. * * @param user user * @param id test id * @param path path in the log folder * @param response response */ @RequestMapping(value = "/{id}/download_log/**") public void downloadLog(User user, @PathVariable("id") long id, @RemainedPath String path, HttpServletResponse response) { getOneWithPermissionCheck(user, id, false); File targetFile = perfTestService.getLogFile(id, path); FileDownloadUtils.downloadFile(response, targetFile); } /** * Show the given log for the perf test having the given id. * * @param user user * @param id test id * @param path path in the log folder * @param response response */ @RequestMapping(value = "/{id}/show_log/**") public void showLog(User user, @PathVariable("id") long id, @RemainedPath String path, HttpServletResponse response) { getOneWithPermissionCheck(user, id, false); File targetFile = perfTestService.getLogFile(id, path); response.reset(); response.setContentType("text/plain"); response.setCharacterEncoding("UTF-8"); FileInputStream fileInputStream = null; try { fileInputStream = new FileInputStream(targetFile); ServletOutputStream outputStream = response.getOutputStream(); if (FilenameUtils.isExtension(targetFile.getName(), "zip")) { // Limit log view to 1MB outputStream.println(" Only the last 1MB of a log shows.\n"); outputStream.println("==========================================================================\n\n"); LogCompressUtils.decompress(fileInputStream, outputStream, 1 * 1024 * 1204); } else { IOUtils.copy(fileInputStream, outputStream); } } catch (Exception e) { CoreLogger.LOGGER.error("Error while processing log. {}", targetFile, e); } finally { IOUtils.closeQuietly(fileInputStream); } } /** * Get the running perf test info having the given id. * * @param user user * @param id test id * @return JSON message containing test,agent and monitor status. */ @RequestMapping(value = "/{id}/api/sample") @RestAPI public HttpEntity<String> refreshTestRunning(User user, @PathVariable("id") long id) { PerfTest test = checkNotNull(getOneWithPermissionCheck(user, id, false), "given test should be exist : " + id); Map<String, Object> map = newHashMap(); map.put("status", test.getStatus()); map.put("perf", perfTestService.getStatistics(test)); map.put("agent", perfTestService.getAgentStat(test)); map.put("monitor", perfTestService.getMonitorStat(test)); return toJsonHttpEntity(map); } /** * Get the detailed perf test report. * * @param model model * @param id test id * @return perftest/detail_report */ @SuppressWarnings("MVCPathVariableInspection") @RequestMapping(value = {"/{id}/detail_report", /** for backward compatibility */"/{id}/report"}) public String getReport(ModelMap model, @PathVariable("id") long id) { model.addAttribute("test", perfTestService.getOne(id)); model.addAttribute("plugins", perfTestService.getAvailableReportPlugins(id)); return "perftest/detail_report"; } /** * Get the detailed perf test report. * * @param id test id * @return perftest/detail_report/perf */ @SuppressWarnings({"MVCPathVariableInspection", "UnusedParameters"}) @RequestMapping("/{id}/detail_report/perf") public String getDetailPerfReport(@PathVariable("id") long id) { return "perftest/detail_report/perf"; } /** * Get the detailed perf test monitor report. * * @param id test id * @param targetIP target ip * @param modelMap model map * @return perftest/detail_report/monitor */ @SuppressWarnings("UnusedParameters") @RequestMapping("/{id}/detail_report/monitor") public String getDetailMonitorReport(@PathVariable("id") long id, @RequestParam("targetIP") String targetIP, ModelMap modelMap) { modelMap.addAttribute("targetIP", targetIP); return "perftest/detail_report/monitor"; } /** * Get the detailed perf test report. * * @param id test id * @param plugin test report plugin category * @param modelMap model map * @return perftest/detail_report/plugin */ @SuppressWarnings("UnusedParameters") @RequestMapping("/{id}/detail_report/plugin/{plugin}") public String getDetailPluginReport(@PathVariable("id") long id, @PathVariable("plugin") String plugin, @RequestParam("kind") String kind, ModelMap modelMap) { modelMap.addAttribute("plugin", plugin); modelMap.addAttribute("kind", kind); return "perftest/detail_report/plugin"; } private PerfTest getOneWithPermissionCheck(User user, Long id, boolean withTag) { PerfTest perfTest = withTag ? perfTestService.getOneWithTag(id) : perfTestService.getOne(id); if (user.getRole().equals(Role.ADMIN) || user.getRole().equals(Role.SUPER_USER)) { return perfTest; } if (perfTest != null && !user.equals(perfTest.getCreatedUser())) { throw processException("User " + user.getUserId() + " has no right on PerfTest " + id); } return perfTest; } private Map<String, String> getMonitorGraphData(long id, String targetIP, int imgWidth) { int interval = perfTestService.getMonitorGraphInterval(id, targetIP, imgWidth); Map<String, String> sysMonitorMap = perfTestService.getMonitorGraph(id, targetIP, interval); PerfTest perfTest = perfTestService.getOne(id); sysMonitorMap.put("interval", String.valueOf(interval * (perfTest != null ? perfTest.getSamplingInterval() : 1))); return sysMonitorMap; } /** * Get the count of currently running perf test and the detailed progress info for the given perf test IDs. * * @param user user * @param ids comma separated perf test list * @return JSON message containing perf test status */ @RestAPI @RequestMapping("/api/status") public HttpEntity<String> getStatuses(User user, @RequestParam(value = "ids", defaultValue = "") String ids) { List<PerfTest> perfTests = perfTestService.getAll(user, convertString2Long(ids)); return toJsonHttpEntity(buildMap("perfTestInfo", perfTestService.getCurrentPerfTestStatistics(), "status", getStatus(perfTests))); } /** * Get all available scripts in JSON format for the current factual user. * * @param user user * @param ownerId owner id * @return JSON containing script's list. */ @RestAPI @RequestMapping("/api/script") public HttpEntity<String> getScripts(User user, @RequestParam(value = "ownerId", required = false) String ownerId) { if (StringUtils.isNotEmpty(ownerId)) { user = userService.getOne(ownerId); } List<FileEntry> scripts = newArrayList(filter(fileEntryService.getAll(user), new com.google.common.base.Predicate<FileEntry>() { @Override public boolean apply(@Nullable FileEntry input) { return input != null && input.getFileType().getFileCategory() == FileCategory.SCRIPT; } })); return toJsonHttpEntity(scripts, fileEntryGson); } /** * Get resources and lib file list from the same folder with the given script path. * * @param user user * @param scriptPath script path * @param ownerId ownerId * @return json string representing resources and libs. */ @RequestMapping("/api/resource") public HttpEntity<String> getResources(User user, @RequestParam String scriptPath, @RequestParam(required = false) String ownerId) { if (user.getRole() == Role.ADMIN && StringUtils.isNotBlank(ownerId)) { user = userService.getOne(ownerId); } FileEntry fileEntry = fileEntryService.getOne(user, scriptPath); String targetHosts = ""; List<String> fileStringList = newArrayList(); if (fileEntry != null) { List<FileEntry> fileList = fileEntryService.getScriptHandler(fileEntry).getLibAndResourceEntries(user, fileEntry, -1L); for (FileEntry each : fileList) { fileStringList.add(each.getPath()); } targetHosts = filterHostString(fileEntry.getProperties().get("targetHosts")); } return toJsonHttpEntity(buildMap("targetHosts", trimToEmpty(targetHosts), "resources", fileStringList)); } /** * Get the status of the given perf test. * * @param user user * @param id perftest id * @return JSON message containing perf test status */ @RestAPI @RequestMapping("/api/{id}/status") public HttpEntity<String> getStatus(User user, @PathVariable("id") Long id) { List<PerfTest> perfTests = perfTestService.getAll(user, new Long[]{id}); return toJsonHttpEntity(buildMap("status", getStatus(perfTests))); } /** * Get the logs of the given perf test. * * @param user user * @param id perftest id * @return JSON message containing log file names */ @RestAPI @RequestMapping("/api/{id}/logs") public HttpEntity<String> getLogs(User user, @PathVariable("id") Long id) { // Check permission getOneWithPermissionCheck(user, id, false); return toJsonHttpEntity(perfTestService.getLogFiles(id)); } /** * Get the detailed report graph data for the given perf test id. * This method returns the appropriate points based on the given imgWidth. * * @param id test id * @param dataType which data * @param imgWidth imageWidth * @return json string. */ @SuppressWarnings("MVCPathVariableInspection") @RestAPI @RequestMapping({"/api/{id}/perf", "/api/{id}/graph"}) public HttpEntity<String> getPerfGraph(@PathVariable("id") long id, @RequestParam(required = true, defaultValue = "") String dataType, @RequestParam(defaultValue = "false") boolean onlyTotal, @RequestParam int imgWidth) { String[] dataTypes = checkNotEmpty(StringUtils.split(dataType, ","), "dataType argument should be provided"); return toJsonHttpEntity(getPerfGraphData(id, dataTypes, onlyTotal, imgWidth)); } /** * Get the monitor data of the target having the given IP. * * @param id test Id * @param targetIP targetIP * @param imgWidth image width * @return json message */ @RestAPI @RequestMapping("/api/{id}/monitor") public HttpEntity<String> getMonitorGraph(@PathVariable("id") long id, @RequestParam("targetIP") String targetIP, @RequestParam int imgWidth) { return toJsonHttpEntity(getMonitorGraphData(id, targetIP, imgWidth)); } /** * Get the plugin monitor data of the target. * * @param id test Id * @param plugin monitor plugin category * @param kind kind * @param imgWidth image width * @return json message */ @RestAPI @RequestMapping("/api/{id}/plugin/{plugin}") public HttpEntity<String> getPluginGraph(@PathVariable("id") long id, @PathVariable("plugin") String plugin, @RequestParam("kind") String kind, @RequestParam int imgWidth) { return toJsonHttpEntity(getReportPluginGraphData(id, plugin, kind, imgWidth)); } private Map<String, Object> getReportPluginGraphData(long id, String plugin, String kind, int imgWidth) { int interval = perfTestService.getReportPluginGraphInterval(id, plugin, kind, imgWidth); Map<String, Object> pluginMonitorData = perfTestService.getReportPluginGraph(id, plugin, kind, interval); final PerfTest perfTest = perfTestService.getOne(id); int samplingInterval = 3; if (perfTest != null) { samplingInterval = perfTest.getSamplingInterval(); } pluginMonitorData.put("interval", interval * samplingInterval); return pluginMonitorData; } /** * Get the last perf test details in the form of json. * * @param user user * @param page page * @param size size of retrieved perf test * @return json string */ @RestAPI @RequestMapping(value = {"/api/last", "/api", "/api/"}, method = RequestMethod.GET) public HttpEntity<String> getAll(User user, @RequestParam(value = "page", defaultValue = "0") int page, @RequestParam(value = "size", defaultValue = "1") int size) { PageRequest pageRequest = new PageRequest(page, size, new Sort(Direction.DESC, "id")); Page<PerfTest> testList = perfTestService.getPagedAll(user, null, null, null, pageRequest); return toJsonHttpEntity(testList.getContent()); } /** * Get the perf test detail in the form of json. * * @param user user * @param id perftest id * @return json message containing test info. */ @RestAPI @RequestMapping(value = "/api/{id}", method = RequestMethod.GET) public HttpEntity<String> getOne(User user, @PathVariable("id") Long id) { PerfTest test = checkNotNull(getOneWithPermissionCheck(user, id, false), "PerfTest %s does not exists", id); return toJsonHttpEntity(test); } /** * Create the given perf test. * * @param user user * @param perfTest perf test * @return json message containing test info. */ @RestAPI @RequestMapping(value = {"/api/", "/api"}, method = RequestMethod.POST) public HttpEntity<String> create(User user, PerfTest perfTest) { checkNull(perfTest.getId(), "id should be null"); // Make the vuser count optional. if (perfTest.getVuserPerAgent() == null && perfTest.getThreads() != null && perfTest.getProcesses() != null) { perfTest.setVuserPerAgent(perfTest.getThreads() * perfTest.getProcesses()); } validate(user, null, perfTest); PerfTest savePerfTest = perfTestService.save(user, perfTest); return toJsonHttpEntity(savePerfTest); } /** * Delete the given perf test. * * @param user user * @param id perf test id * @return json success message if succeeded */ @RestAPI @RequestMapping(value = "/api/{id}", method = RequestMethod.DELETE) public HttpEntity<String> delete(User user, @PathVariable("id") Long id) { PerfTest perfTest = getOneWithPermissionCheck(user, id, false); checkNotNull(perfTest, "no perftest for %s exits", id); perfTestService.delete(user, id); return successJsonHttpEntity(); } /** * Update the given perf test. * * @param user user * @param id perf test id * @param perfTest perf test configuration changes * @return json message */ @RestAPI @RequestMapping(value = "/api/{id}", method = RequestMethod.PUT) public HttpEntity<String> update(User user, @PathVariable("id") Long id, PerfTest perfTest) { PerfTest existingPerfTest = getOneWithPermissionCheck(user, id, false); perfTest.setId(id); validate(user, existingPerfTest, perfTest); return toJsonHttpEntity(perfTestService.save(user, perfTest)); } /** * Stop the given perf test. * * @param user user * @param id perf test id * @return json success message if succeeded */ @RestAPI @RequestMapping(value = "/api/{id}", params = "action=stop", method = RequestMethod.PUT) public HttpEntity<String> stop(User user, @PathVariable("id") Long id) { perfTestService.stop(user, id); return successJsonHttpEntity(); } /** * Update the given perf test's status. * * @param user user * @param id perf test id * @param status Status to be moved to * @return json message */ @RestAPI @RequestMapping(value = "/api/{id}", params = "action=status", method = RequestMethod.PUT) public HttpEntity<String> updateStatus(User user, @PathVariable("id") Long id, Status status) { PerfTest perfTest = getOneWithPermissionCheck(user, id, false); checkNotNull(perfTest, "no perftest for %s exits", id).setStatus(status); validate(user, null, perfTest); return toJsonHttpEntity(perfTestService.save(user, perfTest)); } /** * Clone and start the given perf test. * * @param user user * @param id perf test id to be cloned * @param perftest option to override while cloning. * @return json string */ @SuppressWarnings("MVCPathVariableInspection") @RestAPI @RequestMapping(value = {"/api/{id}/clone_and_start", /* for backward compatibility */ "/api/{id}/cloneAndStart"}) public HttpEntity<String> cloneAndStart(User user, @PathVariable("id") Long id, PerfTest perftest) { PerfTest test = getOneWithPermissionCheck(user, id, false); checkNotNull(test, "no perftest for %s exits", id); PerfTest newOne = test.cloneTo(new PerfTest()); newOne.setStatus(Status.READY); if (perftest != null) { if (perftest.getScheduledTime() != null) { newOne.setScheduledTime(perftest.getScheduledTime()); } if (perftest.getScriptRevision() != null) { newOne.setScriptRevision(perftest.getScriptRevision()); } if (perftest.getAgentCount() != null) { newOne.setAgentCount(perftest.getAgentCount()); } } if (newOne.getAgentCount() == null) { newOne.setAgentCount(0); } Map<String, MutableInt> agentCountMap = agentManagerService.getAvailableAgentCountMap(user); MutableInt agentCountObj = agentCountMap.get(isClustered() ? test.getRegion() : Config.NONE_REGION); checkNotNull(agentCountObj, "test region should be within current region list"); int agentMaxCount = agentCountObj.intValue(); checkArgument(newOne.getAgentCount() != 0, "test agent should not be %s", agentMaxCount); checkArgument(newOne.getAgentCount() <= agentMaxCount, "test agent should be equal to or less than %s", agentMaxCount); PerfTest savePerfTest = perfTestService.save(user, newOne); CoreLogger.LOGGER.info("test {} is created through web api by {}", savePerfTest.getId(), user.getUserId()); return toJsonHttpEntity(savePerfTest); } }