/* * To change this license header, choose License Headers in Project Properties. * To change this template file, choose Tools | Templates * and open the template in the editor. */ package se.kth.karamel.backend.command; import com.google.common.collect.Lists; import com.google.gson.JsonObject; import java.io.IOException; import java.util.ArrayList; import java.util.Collection; import java.util.List; import java.util.Map; import java.util.Set; import java.util.regex.Matcher; import java.util.regex.Pattern; import org.apache.log4j.Logger; import se.kth.karamel.backend.ClusterDefinitionService; import se.kth.karamel.backend.ClusterManager; import se.kth.karamel.backend.ClusterService; import se.kth.karamel.backend.LogService; import se.kth.karamel.backend.converter.ChefJsonGenerator; import se.kth.karamel.backend.converter.UserClusterDataExtractor; import se.kth.karamel.backend.dag.Dag; import se.kth.karamel.backend.kandy.KandyRestClient; import se.kth.karamel.backend.launcher.OsType; import se.kth.karamel.backend.launcher.amazon.Ec2Context; import se.kth.karamel.backend.machines.MachinesMonitor; import se.kth.karamel.backend.machines.SshMachine; import se.kth.karamel.backend.machines.SshShell; import se.kth.karamel.backend.machines.TaskSubmitter; import se.kth.karamel.backend.mocking.MockingUtil; import se.kth.karamel.backend.running.model.ClusterRuntime; import se.kth.karamel.backend.running.model.Failure; import se.kth.karamel.backend.running.model.GroupRuntime; import se.kth.karamel.backend.running.model.MachineRuntime; import se.kth.karamel.backend.running.model.tasks.DagBuilder; import se.kth.karamel.backend.running.model.tasks.Task; import se.kth.karamel.common.TextTable; import se.kth.karamel.common.clusterdef.json.JsonCluster; import se.kth.karamel.common.exception.KaramelException; import se.kth.karamel.common.stats.ClusterStats; import se.kth.karamel.common.stats.PhaseStat; import se.kth.karamel.common.stats.TaskStat; import se.kth.karamel.common.util.IoUtils; import se.kth.karamel.common.util.SshKeyPair; /** * Terminal backend, a replacement for API with more flexibilities. It processes user commands and generates mere * hyper-link aware textual pages. Each hyper-link is another command and results of each command is another page. * * @author kamal */ public class CommandService { private static final Logger logger = Logger.getLogger(CommandService.class); private static String chosenCluster = null; private static String autoselectedCluster = null; private static String chosenMachine = ""; private static final ClusterService clusterService = ClusterService.getInstance(); private static SshShell shell = null; private static String MENU_BAR = ""; private static String HELP_PAGE_TEMPLATE = ""; private static String HOME_PAGE_TEMPLATE = ""; private static String RUNNING_PAGE_TEMPLATE = ""; private static String YAMLS_TABLE_PLH = "%YAMLS_TABLE%"; private static String CLUSTERS_TABLE_PLH = "%CLUSTERS_TABLE%"; private static String HYPERLINKS_PLH = "%HYPERLINKS%"; static { try { HELP_PAGE_TEMPLATE = IoUtils.readContentFromClasspath("se/kth/karamel/backend/command/helppage"); HOME_PAGE_TEMPLATE = IoUtils.readContentFromClasspath("se/kth/karamel/backend/command/homepage"); RUNNING_PAGE_TEMPLATE = IoUtils.readContentFromClasspath("se/kth/karamel/backend/command/running"); } catch (IOException e) { } } public static CommandResponse processCommand(String command, String... args) throws KaramelException { String cmd = command; String nextCmd = null; CommandResponse.Renderer renderer = CommandResponse.Renderer.INFO; CommandResponse response = new CommandResponse(); response.addMenuItem("Home", "home"); response.addMenuItem("New Cluster", "new"); response.addMenuItem("List Clusters", "list"); selectCluster(); String result = null; String successMessage = null; String context = chosenCluster(); if (cmd.equals("help")) { result = HELP_PAGE_TEMPLATE; } else if (cmd.equals("running")) { result = RUNNING_PAGE_TEMPLATE.replace(CLUSTERS_TABLE_PLH, clustersTable()); ClusterManager cluster = cluster(context); String hyperLinks; if (cluster != null) { hyperLinks = UserClusterDataExtractor.clusterLinks(cluster.getDefinition(), cluster.getRuntime()); } else { String yml = ClusterDefinitionService.loadYaml(context); JsonCluster json = ClusterDefinitionService.yamlToJsonObject(yml); hyperLinks = UserClusterDataExtractor.clusterLinks(json, null); } result = result.replace(HYPERLINKS_PLH, hyperLinks); nextCmd = "running"; } else if (cmd.equals("home")) { result = HOME_PAGE_TEMPLATE.replace(YAMLS_TABLE_PLH, yamlsTable()); result = result.replace(CLUSTERS_TABLE_PLH, clustersTable()); nextCmd = "home"; } else if (cmd.equals("clusters")) { result = clustersTable(); nextCmd = "clusters"; } else if (cmd.equals("list")) { StringBuilder builder = new StringBuilder(); builder.append("Yaml Definitions:").append("\n").append("---------------------").append("\n"); builder.append(yamlsTable()); builder.append("\n").append("Running Clusters:").append("\n").append("----------------------").append("\n"); builder.append(clustersTable()); result = builder.toString(); nextCmd = "list"; } else if (cmd.equals("save")) { if (args.length == 0) { throw new KaramelException("Provide the yaml definition"); } ClusterDefinitionService.saveYaml(args[0]); successMessage = "Yaml updated"; } else if (cmd.equals("new")) { result = ""; response.addMenuItem("Save", "save"); renderer = CommandResponse.Renderer.YAML; } else { boolean found = false; Pattern p = Pattern.compile("use\\s+(\\w+)"); Matcher matcher = p.matcher(cmd); if (!found && matcher.matches()) { found = true; String clusterName = matcher.group(1); if (cluster(clusterName) != null) { chosenCluster = clusterName; successMessage = String.format("switched to %s now", clusterName); } else { throw new KaramelException(String.format("cluster %s is not registered yet!!", clusterName)); } } p = Pattern.compile("remove\\s+(\\w+)"); matcher = p.matcher(cmd); if (!found && matcher.matches()) { found = true; String clusterName = matcher.group(1); if (cluster(clusterName) != null) { throw new KaramelException(String.format("%s is running now, terminate it first!!", clusterName)); } else { ClusterDefinitionService.removeDefinition(clusterName); successMessage = String.format("cluster definition %s removed successfully..", clusterName); } } p = Pattern.compile("yaml\\s+(\\w+)"); matcher = p.matcher(cmd); if (!found && matcher.matches()) { found = true; String clusterName = matcher.group(1); if (chosenCluster().toLowerCase().equals(clusterName.toLowerCase())) { addActiveClusterMenus(response); } renderer = CommandResponse.Renderer.YAML; result = ClusterDefinitionService.loadYaml(clusterName); } String clusterNameInUserInput = getClusterNameIfRunningAndMatchesForCommand(cmd, "install"); if (!found && clusterNameInUserInput != null) { found = true; clusterService.submitInstallationDag(clusterNameInUserInput); successMessage = clusterNameInUserInput + " was scheduled for installing, " + "it might take some time please be patient!"; nextCmd = "status " + clusterNameInUserInput; } clusterNameInUserInput = getClusterNameIfRunningAndMatchesForCommand(cmd, "purge"); if (!found && clusterNameInUserInput != null) { found = true; clusterService.submitPurgeDag(clusterNameInUserInput); successMessage = clusterNameInUserInput + " was scheduled for purging, " + "it might take some time please be patient!"; nextCmd = "status " + clusterNameInUserInput; } clusterNameInUserInput = getClusterNameIfRunningAndMatchesForCommand(cmd, "pause"); if (!found && clusterNameInUserInput != null) { found = true; clusterService.pauseDag(clusterNameInUserInput); successMessage = clusterNameInUserInput + " was scheduled for pausing, " + "it might take some time please be patient!"; nextCmd = "status " + clusterNameInUserInput; } clusterNameInUserInput = getClusterNameIfRunningAndMatchesForCommand(cmd, "resume"); if (!found && clusterNameInUserInput != null) { found = true; clusterService.resumeDag(clusterNameInUserInput); successMessage = clusterNameInUserInput + " was scheduled for resuming, " + "it might take some time please be patient!"; nextCmd = "status " + clusterNameInUserInput; } clusterNameInUserInput = getClusterNameIfRunningAndMatchesForCommand(cmd, "terminate"); if (!found && clusterNameInUserInput != null) { found = true; clusterService.terminateCluster(clusterNameInUserInput); successMessage = clusterNameInUserInput + " was scheduled for terminating, " + "it might take some time please be patient!"; nextCmd = "status " + clusterNameInUserInput; } clusterNameInUserInput = getClusterNameIfRunningAndMatchesForCommand(cmd, "status"); if (!found && clusterNameInUserInput != null) { found = true; addActiveClusterMenus(response); StringBuilder builder = new StringBuilder(); ClusterManager cluster = cluster(clusterNameInUserInput); ClusterRuntime clusterEntity = cluster.getRuntime(); builder.append(clusterEntity.getName()).append(" is ").append(clusterEntity.getPhase()); if (clusterEntity.isPaused()) { builder.append(" but it is on pause.").append("\n"); } if (clusterEntity.isFailed() && !clusterEntity.getFailures().isEmpty()) { // builder.append(" List of failures: ").append("\n"); // builder.append(failureTable(clusterEntity.getFailures().values(), true)); } builder.append("\n\n"); builder.append("Passed Phases:"); builder.append("\n"); builder.append(phaseStatsTable(cluster.getStats())); builder.append("\n\n"); builder.append("Tasks' Status:"); builder.append("\n"); builder.append(machinesTasksTable(clusterEntity)); result = builder.toString(); nextCmd = "status " + clusterNameInUserInput; } clusterNameInUserInput = getClusterNameIfRunningAndMatchesForCommand(cmd, "detail"); if (!found && clusterNameInUserInput != null) { found = true; addActiveClusterMenus(response); ClusterManager cluster = cluster(clusterNameInUserInput); JsonCluster json = cluster.getDefinition(); result = ClusterDefinitionService.serializeJson(json); nextCmd = "detail " + clusterNameInUserInput; } clusterNameInUserInput = getClusterNameIfRunningAndMatchesForCommand(cmd, "groups"); if (!found && clusterNameInUserInput != null) { found = true; addActiveClusterMenus(response); ClusterManager cluster = cluster(clusterNameInUserInput); ClusterRuntime clusterEntity = cluster.getRuntime(); String[] columnNames = {"Name", "Phase"}; String[][] data = new String[clusterEntity.getGroups().size()][2]; for (int i = 0; i < clusterEntity.getGroups().size(); i++) { GroupRuntime group = clusterEntity.getGroups().get(i); data[i][0] = group.getName(); data[i][1] = String.valueOf(group.getPhase()); } result = TextTable.makeTable(columnNames, 0, data, true); nextCmd = "groups " + clusterNameInUserInput; } clusterNameInUserInput = getClusterNameIfRunningAndMatchesForCommand(cmd, "machines"); if (!found && clusterNameInUserInput != null) { found = true; addActiveClusterMenus(response); ClusterManager cluster = cluster(clusterNameInUserInput); ClusterRuntime clusterEntity = cluster.getRuntime(); ArrayList<MachineRuntime> machines = new ArrayList<>(); for (GroupRuntime group : clusterEntity.getGroups()) { for (MachineRuntime machine : group.getMachines()) { machines.add(machine); } } result = machinesTable(machines, true); nextCmd = "machines " + clusterNameInUserInput; } clusterNameInUserInput = getClusterNameIfRunningAndMatchesForCommand(cmd, "tasks"); if (!found && clusterNameInUserInput != null) { found = true; addActiveClusterMenus(response); ClusterManager cluster = cluster(clusterNameInUserInput); ClusterRuntime clusterEntity = cluster.getRuntime(); result = machinesTasksTable(clusterEntity); nextCmd = "tasks " + clusterNameInUserInput; } clusterNameInUserInput = getClusterNameIfRunningAndMatchesForCommand(cmd, "stats"); if (!found && clusterNameInUserInput != null) { found = true; addActiveClusterMenus(response); ClusterManager cluster = cluster(clusterNameInUserInput); ClusterStats stats = cluster.getStats(); result = statsTable(cluster.getDefinition().getName(), stats); nextCmd = "stats " + clusterNameInUserInput; } p = Pattern.compile("shellconnect\\s+((\\d|.)+)"); matcher = p.matcher(cmd); if (!found && matcher.matches()) { found = true; String ip = matcher.group(1); if (chosenCluster() != null) { String clusterName = chosenCluster(); ClusterManager clusterMgr = cluster(clusterName); MachinesMonitor mm = clusterMgr.getMachinesMonitor(); SshMachine machine = mm.getMachine(ip); if (machine != null) { shell = machine.getShell(); if (!shell.isConnected()) { shell.connect(); if (!shell.isConnected()) { throw new KaramelException("attempt to connect shell was not successful!!"); } else { try { Thread.sleep(200); } catch (InterruptedException ex) { logger.error("", ex); } } } context = chosenCluster() + "/" + shell.getIpAddress(); result = shell.readStreams(); renderer = CommandResponse.Renderer.SSH; addActiveMachineMenus(response); } else { throw new KaramelException("Opps, machine was not found, make sure cluster is chosen first"); } } else { throw new KaramelException("no cluster has been chosen yet!!"); } } p = Pattern.compile("shelldisconnect"); matcher = p.matcher(cmd); if (!found && matcher.matches()) { found = true; if (chosenCluster() != null) { if (shell != null) { shell.disconnect(); successMessage = "Shell disconnected"; renderer = CommandResponse.Renderer.INFO; } else { throw new KaramelException("Opps, there is not connected shell.."); } } else { throw new KaramelException("no cluster has been chosen yet!!"); } } p = Pattern.compile("shellexec((.|\n|\t|\r|\033|\003|\004)+)"); matcher = p.matcher(cmd); if (!found && matcher.matches()) { found = true; String cmdStr = matcher.group(1); cmdStr = cmdStr.replace("arrup", "\033[A"); cmdStr = cmdStr.replace("arrdown", "\033[B"); if (chosenCluster() != null) { if (shell != null) { if (!shell.isConnected()) { throw new KaramelException("shell is not connected."); } else { shell.exec(cmdStr); try { Thread.sleep(100); } catch (InterruptedException ex) { logger.error("", ex); } context = chosenCluster() + "/" + shell.getIpAddress(); result = shell.readStreams(); renderer = CommandResponse.Renderer.SSH; addActiveMachineMenus(response); } } else { throw new KaramelException("Opps, there is not connected shell.."); } } else { throw new KaramelException("no cluster has been chosen yet!!"); } } p = Pattern.compile("shellread"); matcher = p.matcher(cmd); if (!found && matcher.matches()) { found = true; if (chosenCluster() != null) { if (shell != null) { if (!shell.isConnected()) { throw new KaramelException("shell is not connected."); } else { context = chosenCluster() + "/" + shell.getIpAddress(); result = shell.readStreams(); renderer = CommandResponse.Renderer.SSH; addActiveMachineMenus(response); } } else { throw new KaramelException("Opps, there is not connected shell.."); } } else { throw new KaramelException("no cluster has been chosen yet!!"); } } p = Pattern.compile("tdag\\s+(\\w+)"); matcher = p.matcher(cmd); if (!found && matcher.matches()) { found = true; String clusterName = matcher.group(1); ClusterManager cluster = cluster(clusterName); if (cluster == null || cluster.getCurrentDag() == null) { TaskSubmitter dummyTaskSubmitter = new TaskSubmitter() { @Override public void submitTask(Task task) throws KaramelException { logger.info(" Received request to process a command with info: " + task.uniqueId()); task.succeed(); } @Override public void prepareToStart(Task task) throws KaramelException { } @Override public void killMe(Task task) throws KaramelException { } @Override public void retryMe(Task task) throws KaramelException { } @Override public void skipMe(Task task) throws KaramelException { } @Override public void terminate(Task task) throws KaramelException { } }; String yml = ClusterDefinitionService.loadYaml(clusterName); JsonCluster json = ClusterDefinitionService.yamlToJsonObject(yml); ClusterRuntime dummyRuntime = MockingUtil.dummyRuntime(json); Map<String, JsonObject> chefJsons = ChefJsonGenerator. generateClusterChefJsonsForInstallation(json, dummyRuntime); ClusterStats clusterStats = new ClusterStats(); Dag installationDag = DagBuilder.getInstallationDag(json, dummyRuntime, clusterStats, dummyTaskSubmitter, chefJsons); installationDag.validate(); result = installationDag.print(); } else { result = cluster.getCurrentDag().print(); addActiveClusterMenus(response); nextCmd = cmd; } renderer = CommandResponse.Renderer.INFO; } p = Pattern.compile("vdag\\s+(\\w+)"); matcher = p.matcher(cmd); if (!found && matcher.matches()) { found = true; String clusterName = matcher.group(1); ClusterManager cluster = cluster(clusterName); if (cluster == null || cluster.getCurrentDag() == null) { TaskSubmitter dummyTaskSubmitter = new TaskSubmitter() { @Override public void submitTask(Task task) throws KaramelException { task.succeed(); } @Override public void prepareToStart(Task task) throws KaramelException { } @Override public void killMe(Task task) throws KaramelException { } @Override public void retryMe(Task task) throws KaramelException { } @Override public void skipMe(Task task) throws KaramelException { } @Override public void terminate(Task task) throws KaramelException { } }; String yml = ClusterDefinitionService.loadYaml(clusterName); JsonCluster json = ClusterDefinitionService.yamlToJsonObject(yml); ClusterRuntime dummyRuntime = MockingUtil.dummyRuntime(json); Map<String, JsonObject> chefJsons = ChefJsonGenerator. generateClusterChefJsonsForInstallation(json, dummyRuntime); ClusterStats clusterStats = new ClusterStats(); Dag installationDag = DagBuilder.getInstallationDag(json, dummyRuntime, clusterStats, dummyTaskSubmitter, chefJsons); installationDag.validate(); result = installationDag.asJson(); if (cluster != null) { addActiveClusterMenus(response); } } else { result = cluster.getCurrentDag().asJson(); addActiveClusterMenus(response); nextCmd = cmd; } renderer = CommandResponse.Renderer.DAG; } p = Pattern.compile("links\\s+(\\w+)"); matcher = p.matcher(cmd); if (!found && matcher.matches()) { found = true; String clusterName = matcher.group(1); ClusterManager cluster = cluster(clusterName); if (cluster != null) { result = UserClusterDataExtractor.clusterLinks(cluster.getDefinition(), cluster.getRuntime()); addActiveClusterMenus(response); } else { String yml = ClusterDefinitionService.loadYaml(clusterName); JsonCluster json = ClusterDefinitionService.yamlToJsonObject(yml); result = UserClusterDataExtractor.clusterLinks(json, null); } renderer = CommandResponse.Renderer.INFO; } p = Pattern.compile("launch\\s+(\\w+)"); matcher = p.matcher(cmd); if (!found && matcher.matches()) { found = true; String clusterName = matcher.group(1); if (cluster(clusterName) != null) { throw new KaramelException(String.format("%s is already running, terminate it first!!", clusterName)); } else { String yaml = ClusterDefinitionService.loadYaml(clusterName); String json = ClusterDefinitionService.yamlToJson(yaml); clusterService.startCluster(json); successMessage = String.format("cluster %s launched successfully..", clusterName); nextCmd = "status"; addActiveClusterMenus(response); } } p = Pattern.compile("cost\\s+(\\w+)"); matcher = p.matcher(cmd); if (!found && matcher.matches()) { found = true; String clusterName = matcher.group(1); String yaml = ClusterDefinitionService.loadYaml(clusterName); if (yaml != null) { result = KandyRestClient.estimateCost(yaml); if (result != null) { renderer = CommandResponse.Renderer.INFO; } else { throw new KaramelException("Kandy could not estimate the cost."); } } else { throw new KaramelException("The cluster definition does not exist."); } } p = Pattern.compile("log\\s+(.*)"); matcher = p.matcher(cmd); if (!found && matcher.matches()) { found = true; String taskuuid = matcher.group(1); if (chosenCluster() != null) { boolean taskFound = false; ClusterManager cluster = cluster(chosenCluster()); ClusterRuntime clusterEntity = cluster.getRuntime(); for (GroupRuntime group : clusterEntity.getGroups()) { for (MachineRuntime machine : group.getMachines()) { for (Task task : machine.getTasks()) { if (task.getUuid().equals(taskuuid)) { taskFound = true; result = LogService.loadLog(clusterEntity.getName(), machine.getPublicIp(), task.getName()); } } } } if (!taskFound) { throw new KaramelException("Opps, task was not found, make sure cluster is chosen first"); } addActiveClusterMenus(response); } else { throw new KaramelException("no cluster has been chosen yet!!"); } } p = Pattern.compile("skipfailed\\s+(.*)"); matcher = p.matcher(cmd); if (!found && matcher.matches()) { found = true; String taskuuid = matcher.group(1); if (chosenCluster() != null) { boolean taskFound = false; ClusterManager cluster = cluster(chosenCluster()); ClusterRuntime clusterEntity = cluster.getRuntime(); for (GroupRuntime group : clusterEntity.getGroups()) { for (MachineRuntime machine : group.getMachines()) { for (Task task : machine.getTasks()) { if (task.getUuid().equals(taskuuid)) { taskFound = true; if (task.getStatus() == Task.Status.FAILED) { task.skip(); successMessage = String.format("The failed task '%s' on '%s' has been skipped successfully", task.getName(), task.getMachineId()); } } } } } if (!taskFound) { throw new KaramelException("Opps, task was not found, make sure cluster is chosen first"); } addActiveClusterMenus(response); } else { throw new KaramelException("no cluster has been chosen yet!!"); } nextCmd = "status"; } p = Pattern.compile("retryfailed\\s+(.*)"); matcher = p.matcher(cmd); if (!found && matcher.matches()) { found = true; String taskuuid = matcher.group(1); if (chosenCluster() != null) { boolean taskFound = false; ClusterManager cluster = cluster(chosenCluster()); ClusterRuntime clusterEntity = cluster.getRuntime(); for (GroupRuntime group : clusterEntity.getGroups()) { for (MachineRuntime machine : group.getMachines()) { for (Task task : machine.getTasks()) { if (task.getUuid().equals(taskuuid)) { taskFound = true; if (task.getStatus() == Task.Status.FAILED) { task.retry(); successMessage = String.format("The failed task '%s' on '%s' has been retried successfully", task.getName(), task.getMachineId()); } } } } } if (!taskFound) { throw new KaramelException("Opps, task was not found, make sure cluster is chosen first"); } addActiveClusterMenus(response); } else { throw new KaramelException("no cluster has been chosen yet!!"); } nextCmd = "status"; } p = Pattern.compile("kill\\s+(.*)"); matcher = p.matcher(cmd); if (!found && matcher.matches()) { found = true; String taskuuid = matcher.group(1); if (chosenCluster() != null) { boolean taskFound = false; ClusterManager cluster = cluster(chosenCluster()); ClusterRuntime clusterEntity = cluster.getRuntime(); for (GroupRuntime group : clusterEntity.getGroups()) { for (MachineRuntime machine : group.getMachines()) { for (Task task : machine.getTasks()) { if (task.getUuid().equals(taskuuid)) { taskFound = true; if (task.getStatus() == Task.Status.ONGOING) { task.kill(); successMessage = String.format("The failed task '%s' on '%s' has been killed successfully", task.getName(), task.getMachineId()); } } } } } if (!taskFound) { throw new KaramelException("Opps, task was not found, make sure cluster is chosen first"); } addActiveClusterMenus(response); } else { throw new KaramelException("no cluster has been chosen yet!!"); } nextCmd = "status"; } p = Pattern.compile("which\\s+(cluster|aws|ssh)"); matcher = p.matcher(cmd); if (!found && matcher.matches()) { found = true; String subcmd = matcher.group(1); if (subcmd.equals("cluster")) { if (chosenCluster() != null) { result = String.format("%s has been chosen.", chosenCluster()); } else { throw new KaramelException("no cluster has been chosen yet!!"); } } else if (subcmd.equals("aws")) { Ec2Context ec2Context = clusterService.getCommonContext().getEc2Context(); if (ec2Context != null) { result = String.format("aws account id is %s", ec2Context.getCredentials().getAccessKey()); } else { throw new KaramelException("no aws account has been chosen yet!!"); } } else if (subcmd.equals("ssh")) { SshKeyPair sshKeyPair = clusterService.getCommonContext().getSshKeyPair(); if (sshKeyPair != null) { result = String.format("public key path: %s \nprivate key path: %s", sshKeyPair.getPublicKeyPath(), sshKeyPair.getPrivateKeyPath()); } else { throw new KaramelException("no ssh keys has been chosen yet!!"); } } } } if (result == null && successMessage == null) { throw new KaramelException(String.format("Command '%s' not found", cmd)); } response.setContext(context); response.setSuccessMessage(successMessage); response.addMenuItem("Help", "help"); response.setNextCmd(nextCmd); response.setResult(result); response.setRenderer(renderer); return response; } private static String chosenCluster() { if (chosenCluster != null) { return chosenCluster; } else if (autoselectedCluster != null) { return autoselectedCluster; } else { return null; } } private static void selectCluster() { Map<String, ClusterManager> repository = clusterService.getRepository(); if (autoselectedCluster != null && cluster(autoselectedCluster) == null) { autoselectedCluster = null; } if (chosenCluster != null && cluster(chosenCluster) == null) { chosenCluster = null; } if (chosenCluster == null && repository.size() == 1) { autoselectedCluster = (String) repository.keySet().toArray()[0]; } } private static ClusterManager cluster(String name) { Map<String, ClusterManager> repository = clusterService.getRepository(); Set<Map.Entry<String, ClusterManager>> clusters = repository.entrySet(); for (Map.Entry<String, ClusterManager> cluster : clusters) { if (cluster.getKey().toLowerCase().equals(name.toLowerCase())) { return cluster.getValue(); } } return null; } private static String clustersTable() { Map<String, ClusterManager> repository = clusterService.getRepository(); String[] columnNames = {"Cluster Name", "Phase", "Failed/Paused", "Actions"}; Object[][] data = new Object[repository.size()][columnNames.length]; int i = 0; for (ClusterManager cluster : repository.values()) { String name = cluster.getDefinition().getName(); data[i][0] = name; data[i][1] = cluster.getRuntime().getPhase(); data[i][2] = cluster.getRuntime().isFailed() + "/" + cluster.getRuntime().isPaused(); data[i][3] = "<a kref='status " + name + "'>status</a> <a kref='tdag " + name + "'>tdag</a> <a kref='vdag " + name + "'>vdag</a> <a kref='groups " + name + "'>groups</a> <a kref='machines " + name + "'>machines</a> <a kref='tasks " + name + "'>tasks</a> <a kref='terminate " + name + "'>terminate</a> <a kref='links " + name + "'>services</a> <a kref='yaml " + name + "'>yaml</a> <a kref='cost " + name + "'>cost</a>"; i++; } return TextTable.makeTable(columnNames, 1, data, true); } private static String yamlsTable() throws KaramelException { List<String> defs = ClusterDefinitionService.listClusters(); String[] columnNames = {"Yaml Name", "Actions"}; Object[][] data = new Object[defs.size()][columnNames.length]; int i = 0; for (String yaml : defs) { data[i][0] = yaml; data[i][1] = "<a kref='yaml " + yaml + "'>edit</a> <a kref='tdag " + yaml + "'>tdag</a> <a kref='vdag " + yaml + "'>vdag</a> <a kref='launch " + yaml + "'>launch</a> <a kref='remove " + yaml + "'>remove</a> <a kref='links " + yaml + "'>services</a> <a kref='cost " + yaml + "'>cost</a>"; i++; } return TextTable.makeTable(columnNames, 1, data, true); } private static String failureTable(Collection<Failure> failures, boolean rowNumbering) { String[] columnNames = {"Failure", "Id", "Message"}; Object[][] data = new Object[failures.size()][columnNames.length]; int i = 0; for (Failure failure : failures) { data[i][0] = failure.getType().name(); data[i][1] = (failure.getId() == null) ? "" : failure.getId(); data[i][2] = failure.getMessage(); i++; } return TextTable.makeTable(columnNames, 1, data, rowNumbering); } private static String tasksTable(List<Task> tasks, boolean rowNumbering) { String[] columnNames = {"Task", "Status", "Actions", "Duration(ms)"}; Object[][] data = new Object[tasks.size()][columnNames.length]; for (int i = 0; i < tasks.size(); i++) { Task task = tasks.get(i); data[i][0] = task.getName(); data[i][1] = task.getStatus(); String uuid = task.getUuid(); String actions = ""; if (task.getStatus().ordinal() == Task.Status.ONGOING.ordinal()) { actions += "<a kref='kill " + uuid + "'>kill</a>"; } if (task.getStatus().ordinal() == Task.Status.FAILED.ordinal()) { actions += "<a kref='retryfailed " + uuid + "'>retry</a>" + " <a kref='skipfailed " + uuid + "'>skip</a>"; } if (task.getStatus().ordinal() > Task.Status.ONGOING.ordinal()) { actions += " <a kref='log " + uuid + "'>log</a>"; } data[i][2] = actions; if (task.getDuration() > 0) { data[i][3] = task.getDuration(); } else { data[i][3] = ""; } } return TextTable.makeTable(columnNames, 1, data, rowNumbering); } private static String machinesTable(ArrayList<MachineRuntime> machines, boolean rowNumbering) { String[] columnNames = {"OS Family", "Public IP", "Private IP", "SSH Port", "SSH User", "Life Status", "Task Status"}; Object[][] data = new Object[machines.size()][columnNames.length]; for (int i = 0; i < machines.size(); i++) { MachineRuntime machine = machines.get(i); OsType osType = machine.getOsType(); data[i][0] = (osType == null) ? "?" : osType.family.toString(); data[i][1] = "<a kref='shellconnect " + machine.getPublicIp() + "'>" + machine.getPublicIp() + "</a>"; data[i][2] = machine.getPrivateIp(); data[i][3] = machine.getSshPort(); data[i][4] = machine.getSshUser(); data[i][5] = machine.getLifeStatus(); data[i][6] = machine.getTasksStatus(); } return TextTable.makeTable(columnNames, 6, data, rowNumbering); } private static String statsTable(String clusterName, ClusterStats stats) { StringBuilder builder = new StringBuilder(); builder.append("Total Time: ").append(stats.getEndTime() - stats.getStartTime()); builder.append("\n"); builder.append(phaseStatsTable(stats)); builder.append("\n"); builder.append(taskStatsTable(stats)); builder.append("\n"); return builder.toString(); } private static String phaseStatsTable(ClusterStats stats) { String[] columnNames = {"Phase", "Status", "Duration"}; ArrayList<PhaseStat> phases = Lists.newArrayList(stats.getPhases()); Object[][] data = new Object[phases.size()][columnNames.length]; for (int i = 0; i < phases.size(); i++) { PhaseStat phase = phases.get(i); data[i][0] = phase.getName(); data[i][1] = phase.getStatus(); data[i][2] = phase.getDuration(); } return TextTable.makeTable(columnNames, 0, data, true); } private static String taskStatsTable(ClusterStats stats) { String[] columnNames = {"Task", "Status", "Duration"}; ArrayList<TaskStat> tasks = Lists.newArrayList(stats.getTasks()); Object[][] data = new Object[tasks.size()][columnNames.length]; for (int i = 0; i < tasks.size(); i++) { TaskStat task = tasks.get(i); data[i][0] = task.getTaskId(); data[i][1] = task.getStatus(); data[i][2] = task.getDuration(); } return TextTable.makeTable(columnNames, 0, data, true); } private static String machinesTasksTable(ClusterRuntime clusterEntity) { StringBuilder builder = new StringBuilder(); for (GroupRuntime group : clusterEntity.getGroups()) { for (MachineRuntime machine : group.getMachines()) { ArrayList<MachineRuntime> machines = new ArrayList<>(); machines.add(machine); builder.append(machinesTable(machines, false)); builder.append("\n"); builder.append(tasksTable(machine.getTasks(), true)); builder.append("\n"); } } return builder.toString(); } private static void addActiveMachineMenus(CommandResponse response) { response.addMenuItem("Status", "status"); response.addMenuItem("Machines", "machines"); response.addMenuItem("Close Shell", "shelldisconnect"); } private static void addActiveClusterMenus(CommandResponse response) { ClusterManager cluster = cluster(chosenCluster()); String clusterName = cluster.getDefinition().getName().toLowerCase(); ClusterRuntime clusterEntity = cluster.getRuntime(); response.addMenuItem("View Definition", "yaml"); response.addMenuItem("Status", "status"); response.addMenuItem("Orchestration DAG", "vdag " + clusterName); response.addMenuItem("Quick Links", "links " + clusterName); response.addMenuItem("Statistics", "stats " + clusterName); if (clusterEntity.getPhase().ordinal() >= ClusterRuntime.ClusterPhases.MACHINES_FORKED.ordinal() || clusterEntity.getPhase().ordinal() <= ClusterRuntime.ClusterPhases.DAG_DONE.ordinal()) { response.addMenuItem("Install", "install"); response.addMenuItem("Purge", "purge"); } if (clusterEntity.getPhase() == ClusterRuntime.ClusterPhases.RUNNING_DAG) { if (clusterEntity.isPaused()) { response.addMenuItem("Resume", "resume"); } else { response.addMenuItem("Pause", "pause"); } } response.addMenuItem("Terminate", "terminate"); } private static String getClusterNameIfRunningAndMatchesForCommand(String userinput, String cmd) throws KaramelException { Pattern p = Pattern.compile(cmd + "(\\s+(\\w+))?"); Matcher matcher = p.matcher(userinput); if (matcher.matches()) { String clusterName; if (matcher.group(2) == null) { if (chosenCluster() != null) { ClusterManager cluster = cluster(chosenCluster()); clusterName = cluster.getDefinition().getName(); } else { throw new KaramelException("No cluster has been chosen yet! When you terminate a cluster it is removed " + "from the context."); } } else { clusterName = matcher.group(2); } return clusterName; } else { return null; } } }