/*
* 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.aliyun.odps;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import javax.xml.bind.JAXBException;
import javax.xml.bind.annotation.XmlAccessType;
import javax.xml.bind.annotation.XmlAccessorType;
import javax.xml.bind.annotation.XmlAttribute;
import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlElementWrapper;
import javax.xml.bind.annotation.XmlRootElement;
import javax.xml.bind.annotation.XmlValue;
import javax.xml.bind.annotation.adapters.XmlJavaTypeAdapter;
import org.apache.commons.codec.binary.Base64;
import com.alibaba.fastjson.JSON;
import com.aliyun.odps.Instance.InstanceResultModel.TaskResult;
import com.aliyun.odps.Instance.TaskStatusModel.InstanceTaskModel;
import com.aliyun.odps.Job.JobModel;
import com.aliyun.odps.commons.transport.Headers;
import com.aliyun.odps.commons.transport.Response;
import com.aliyun.odps.commons.util.DateUtils;
import com.aliyun.odps.data.Record;
import com.aliyun.odps.rest.JAXBUtils;
import com.aliyun.odps.rest.ResourceBuilder;
import com.aliyun.odps.rest.RestClient;
/**
* Instance表示ODPS中计算任务的一个运行实例
*/
public class Instance extends com.aliyun.odps.LazyLoad {
/**
* Instance的运行状态。
*
* @author xiaoming.yin
*/
public static enum Status {
/**
* 运行
*/
RUNNING("Running"),
/**
* 挂起
*/
SUSPENDED("Suspended"),
/**
* 终止
*/
TERMINATED("Terminated");
private static final Map<String, Status> strToEnum = new HashMap<String, Status>();
static { // Initialize the map
for (Status t : values()) {
strToEnum.put(t.toString(), t);
}
}
private String strVal;
private Status(String strVal) {
this.strVal = strVal;
}
// package-private only.
static Status fromString(String value) {
if (value == null) {
throw new NullPointerException();
}
return strToEnum.get(value);
}
/**
* RUNNING 被转换成 "Running",SUSPENDED 被转换成 "Suspended",TERMINATED 被转换成
* "Terminated"。
*/
@Override
public String toString() {
return this.strVal;
}
}
private String project;
private Map<String, Result> results;
private boolean isSync = false;
private TaskStatusModel model;
private Status status;
private RestClient client;
private Odps odps;
Instance(String project, TaskStatusModel model, Map<String, Result> results, Odps odps) {
this.project = project;
this.model = model;
this.results = results;
if (model.status != null) {
this.status = Status.valueOf(model.status.toUpperCase());
}
if (results != null && results.size() > 0) {
isSync = true;
status = Status.TERMINATED;
}
this.odps = odps;
this.client = odps.getRestClient();
}
@XmlRootElement(name = "Instance")
private static class InstanceStatusModel {
@XmlElement(name = "Status")
String status;
}
@XmlRootElement
private static class InstanceDebugModel {
@XmlElement(name = "LogId")
String logId;
@XmlElement(name = "DebugId")
String debugId;
}
/**
* 表示Instance运行结果的API响应内容
*
* @author shenggong.wang@alibaba-inc.com
*/
@XmlRootElement(name = "Instance")
static class InstanceResultModel {
static class TaskResult {
@XmlAttribute(name = "Type")
String type;
@XmlElement(name = "Name")
String name;
@XmlElement(name = "Result")
Result result;
@XmlElement(name = "Status")
String status;
}
@XmlElementWrapper(name = "Tasks")
@XmlElement(name = "Task")
List<TaskResult> taskResults = new ArrayList<TaskResult>();
}
@XmlRootElement
public static class Result {
@XmlAttribute(name = "Transform")
String transform;
@XmlAttribute(name = "Format")
String format;
@XmlValue
String text;
/**
* {@link Instance}执行后,无论成功与否,返回给客户端的结果或失败信息。
*/
public String getString() {
if (transform != null && "Base64".equals(transform)) {
try {
String decodedString = new String(Base64.decodeBase64(text), "UTF-8");
return decodedString;
} catch (Exception e) {
// return original text
return text;
}
} else {
return text;
}
}
/**
* 指明返回结果的格式,包括:XML,JSON,CSV
*
* @return 格式信息包括:"XML","JSON","CSV"
*/
public String getFormat() {
return format;
}
}
@Override
public void reload() throws OdpsException {
reload(false);
}
private void reload(boolean isBlock) throws OdpsException {
Map<String, String> params = null;
if (isBlock) {
params = new HashMap<String, String>();
params.put("instancestatus", null);
}
Response resp = client.request(getResource(), "GET", params, null, null);
model.owner = resp.getHeaders().get(Headers.ODPS_OWNER);
String startTimeStr = resp.getHeaders().get("x-odps-start-time");
String endTimeStr = resp.getHeaders().get("x-odps-end-time");
try {
model.startTime = DateUtils.parseRfc822Date(startTimeStr);
} catch (ParseException e) {
throw new OdpsException("Invalid response, x-odps-start-time:" + startTimeStr);
}
try {
model.endTime = DateUtils.parseRfc822Date(endTimeStr);
} catch (ParseException e) {
// nothing
}
try {
InstanceStatusModel sm = JAXBUtils.unmarshal(resp, InstanceStatusModel.class);
status = Status.valueOf(sm.status.toUpperCase());
} catch (JAXBException e) {
throw new OdpsException("Invalid instance status response.", e);
}
// do not set load flat to true
}
/**
* 停止正在执行的Instance
*
* @throws OdpsException
*/
public void stop() throws OdpsException {
InstanceStatusModel sm = new InstanceStatusModel();
sm.status = "Terminated";
try {
String ret = JAXBUtils.marshal(sm, InstanceStatusModel.class);
HashMap<String, String> headers = new HashMap<String, String>();
headers.put(Headers.CONTENT_TYPE, "application/xml");
client.stringRequest(getResource(), "PUT", null, headers, ret);
} catch (Exception e) {
throw new OdpsException(e.getMessage(), e);
}
}
/**
* 获得Instance中{@link Task}的运行结果
*
* @return {@link Task}的运行结果, key为{@link Task}的名称,value为{@link Result}
* @throws OdpsException
*/
public Map<String, Result> getTaskResultsWithFormat() throws OdpsException {
if (isSync) {
return results;
}
results = new HashMap<String, Result>();
Map<String, String> params = new HashMap<String, String>();
params.put("result", null);
InstanceResultModel rm = client
.request(InstanceResultModel.class, getResource(), "GET", params);
for (TaskResult r : rm.taskResults) {
results.put(r.name, r.result);
}
return results;
}
/**
* 获得Instance中Task的运行结果
*
* @return {@link Task}的运行结果, key为{@link Task}的名称,value为{@link Result}
* .getString()的结果。
* @throws OdpsException
*/
public Map<String, String> getTaskResults() throws OdpsException {
Map<String, Result> results = getTaskResultsWithFormat();
Map<String, String> result = new HashMap<String, String>();
for (Entry<String, Result> entry : results.entrySet()) {
result.put(entry.getKey(), entry.getValue().getString());
}
return result;
}
public static class TaskCost {
public Integer getCPUCost() {
return cpuCost;
}
public void setCPUCost(Integer cpuCost) {
this.cpuCost = cpuCost;
}
public Integer getMemoryCost() {
return memoryCost;
}
public void setMemoryCost(Integer memoryCost) {
this.memoryCost = memoryCost;
}
public Integer getInputSize() {
return inputSize;
}
public void setInputSize(Integer inputSize) {
this.inputSize = inputSize;
}
private Integer cpuCost = 0; // 100core*second
private Integer memoryCost = 0; // MB*second
private Integer inputSize = 0; // bytes
}
/**
* TaskSummary包含{@link Task}运行结束后的汇总信息
*
* <p>
* Summary信息以key-value对的形式组织
* </p>
*
* @author shenggong.wang@alibaba-inc.com
*/
@SuppressWarnings({"serial", "rawtypes"})
public static class TaskSummary extends HashMap {
private String text;
private String jsonSummary = "{}"; // An emtpy JSON string by default
/**
* 返回文本化汇总信息,类似于:
*
* <pre>
* Summary:
* inputs:
* my_project.wc_in: 1 (269 bytes)
* outputs:
* my_project.wc_out: 2 (253 bytes)
* M1_Stg1:
* worker count: 1
* input records:
* input: 1 (min: 1, max: 1, avg: 1)
* output records:
* R2_1_Stg1: 2 (min: 2, max: 2, avg: 2)
* R2_1_Stg1:
* worker count: 1
* input records:
* input: 2 (min: 2, max: 2, avg: 2)
* output records:
* R2_1_Stg1FS_6415: 2 (min: 2, max: 2, avg: 2)
* </pre>
*
* @return 汇总信息
*/
public String getSummaryText() {
return text;
}
void setSummaryText(String text) {
this.text = text;
}
public String getJsonSummary() {
return this.jsonSummary;
}
void setJsonSummary(String jsonSummary) {
this.jsonSummary = jsonSummary;
}
}
/**
* 获得 Instance 中 Task 的运行资源消耗信息
* 当且仅当 Task 产生计量信息时,返回资源消耗信息,否则返回 null
*
* @param taskName
* 指定的TaskName
* @return 资源消耗信息 {@link com.aliyun.odps.Instance.TaskCost}
* @throws OdpsException
*/
public TaskCost getTaskCost(String taskName) throws OdpsException {
TaskSummary summary = getTaskSummary(taskName);
if (summary == null) {
return null;
}
try {
if (summary.get("Cost") != null) {
Map<String, Integer> taskCostMap = (Map)summary.get("Cost");
TaskCost cost = new TaskCost();
if (taskCostMap.get("CPU") != null) {
cost.setCPUCost(taskCostMap.get("CPU"));
}
if (taskCostMap.get("Memory") != null) {
cost.setMemoryCost(taskCostMap.get("Memory"));
}
if (taskCostMap.get("Input") != null) {
cost.setInputSize(taskCostMap.get("Input"));
}
return cost;
}
} catch (Exception e) {
// ignore
}
return null;
}
/**
* 获得 Instance 中 Task 运行时的指定相关信息
*
* @param taskName
* 指定的TaskName
* @param infoKey
* 指定相关信息的标志符
* @return 相关信息
* @throws OdpsException
*/
public String getTaskInfo(String taskName, String infoKey) throws OdpsException {
Map<String, String> params = new HashMap<String, String>();
params.put("info", null);
params.put("taskname", taskName);
params.put("key", infoKey);
Response result = client.request(getResource(), "GET", params, null, null);
try {
return new String(result.getBody(), "utf-8");
} catch (UnsupportedEncodingException e) {
throw new OdpsException(e);
}
}
/**
* 获得Instance中Task的运行汇总信息 当 Server 端返回信息,但是信息格式错误时,返回 null
*
* @param taskName
* 指定的TaskName
* @return 汇总信息 {@link TaskSummary}
* @throws OdpsException
*/
@SuppressWarnings({"unchecked", "rawtypes"})
public TaskSummary getTaskSummary(String taskName) throws OdpsException {
Map<String, String> params = new HashMap<String, String>();
params.put("instancesummary", null);
params.put("taskname", taskName);
Response result = client.request(getResource(), "GET", params, null, null);
TaskSummary summary = null;
try {
Map<Object, Object> map = JSON.parseObject(result.getBody(), Map.class);
if (map.get("Instance") != null) {
Map mapReduce = (Map) map.get("Instance");
String jsonSummary = (String) mapReduce.get("JsonSummary");
if (jsonSummary != null) {
summary = (TaskSummary) JSON.parseObject(jsonSummary, TaskSummary.class);
}
if (summary != null) {
summary.setSummaryText((String) mapReduce.get("Summary"));
summary.setJsonSummary(jsonSummary);
}
}
} catch (Exception e) {
return null;
}
return summary;
}
/* Response of get instance task status8 */
@XmlRootElement(name = "Instance")
static class TaskStatusModel {
static class InstanceTaskModel {
@XmlElement(name = "Name")
String name;
@XmlAttribute(name = "Type")
String type;
@XmlElement(name = "StartTime")
@XmlJavaTypeAdapter(JAXBUtils.DateBinding.class)
Date startTime;
@XmlElement(name = "EndTime")
@XmlJavaTypeAdapter(JAXBUtils.DateBinding.class)
Date endTime;
@XmlElement(name = "Status")
String status;
@XmlElementWrapper(name = "Histories")
@XmlElement(name = "History")
List<InstanceTaskModel> histories = new ArrayList<InstanceTaskModel>();
}
@XmlElement(name = "Name")
String name;
@XmlElement(name = "Owner")
String owner;
@XmlElement(name = "StartTime")
@XmlJavaTypeAdapter(JAXBUtils.DateBinding.class)
Date startTime;
@XmlElement(name = "EndTime")
@XmlJavaTypeAdapter(JAXBUtils.DateBinding.class)
Date endTime;
@XmlElement(name = "Status")
String status;
@XmlElementWrapper(name = "Tasks")
@XmlElement(name = "Task")
List<InstanceTaskModel> tasks = new ArrayList<InstanceTaskModel>();
}
/**
* Task执行状态
*/
public static class TaskStatus {
/**
* Instance中Task的状态
*
* @author shenggong.wang@alibaba-inc.com
*/
public static enum Status {
/**
* 等待
*/
WAITING,
/**
* 运行中
*/
RUNNING,
/**
* 执行成功
*/
SUCCESS,
/**
* 执行失败
*/
FAILED,
/**
* 被挂起
*/
SUSPENDED,
/**
* 被取消
*/
CANCELLED
}
private InstanceTaskModel model;
TaskStatus(InstanceTaskModel model) {
this.model = model;
}
/**
* 返回状态名称
*
* @return 状态名称
*/
public String getName() {
return model.name;
}
/**
* 返回{@link Task}类型
*
* @return 类型,包括:SQL,GRAPH,XLIB
*/
public String getType() {
return model.type;
}
/**
* 返回状态对象
*
* @return 状态对象 {@link Status}
*/
public Status getStatus() {
return TaskStatus.Status.valueOf(model.status.toUpperCase());
}
}
/**
* 查询Instance中所有{@link Task}的执行状态
*
* @return Map, key为Task名称, value为表示Task执行状态的{@link TaskStatus}对象
* @throws OdpsException
*/
public Map<String, TaskStatus> getTaskStatus() throws OdpsException {
HashMap<String, TaskStatus> taskStatus = new HashMap<String, Instance.TaskStatus>();
// the model has enough values for task status in sync instance
TaskStatusModel taskStatusModel = model;
if (!isSync || !hasTaskStatus(taskStatusModel)) {
// async instance get task status directly
// task status in sync instance model may be null, get task status to keep compatibility
HashMap<String, String> params = new HashMap<String, String>();
params.put("taskstatus", null);
// cannot assign to model, the resp below do not has such field such as model.name and so on
taskStatusModel = client.request(TaskStatusModel.class, getResource(), "GET", params);
}
for (InstanceTaskModel taskModel : taskStatusModel.tasks) {
TaskStatus status = new TaskStatus(taskModel);
taskStatus.put(status.getName(), status);
}
return taskStatus;
}
private boolean hasTaskStatus(TaskStatusModel model) {
for (InstanceTaskModel taskModel : model.tasks) {
TaskStatus status = new TaskStatus(taskModel);
if (status.model.status == null) {
return false;
}
}
return true;
}
/**
* 获得Instance所包含的所有{@link Task}的名称
*
* @return {@link Task}的名称集合
* @throws OdpsException
*/
public Set<String> getTaskNames() throws OdpsException {
return getTaskStatus().keySet();
}
/**
* 检查{@link Instance}是否执行成功
*
* <p>
* 成功的含义为Instance执行结束,并且所有Task的状态为成功
* </p>
*
* @return true如果成功
* @throws OdpsException
*/
public boolean isSuccessful() throws OdpsException {
boolean r = true;
Map<String, TaskStatus> status = getTaskStatus();
for (TaskStatus s : status.values()) {
if (s.getStatus() != TaskStatus.Status.SUCCESS) {
r = false;
break;
}
}
return r;
}
/**
* 阻塞当前线程, 直到Instance结束
*
* @throws OdpsException
* Instance失败
*/
public void waitForSuccess() throws OdpsException {
waitForSuccess(1000);
}
/**
* 阻塞当前线程, 直到Instance结束
*
* @param interval
* 内部轮询间隔
* @throws OdpsException
* Instance失败
*/
public void waitForSuccess(long interval) throws OdpsException {
while (!isTerminated()) {
try {
Thread.sleep(interval);
} catch (InterruptedException e) {
break;
}
}
if (!isSuccessful()) {
for (Entry<String, TaskStatus> e : getTaskStatus().entrySet()) {
if (e.getValue().getStatus() == TaskStatus.Status.FAILED) {
throw new OdpsException(getTaskResults().get(e.getKey()));
} else if (e.getValue().getStatus() != TaskStatus.Status.SUCCESS) {
throw new OdpsException(e.getKey() + ", Status=" + e.getValue().getStatus());
}
}
}
}
// SysTask
/**
* StageProgress表示{@link Task}执行过程中各阶段的进度统计
*/
@XmlAccessorType(XmlAccessType.FIELD)
public static class StageProgress {
public static enum Status {
READY,
WAITING,
RUNNING,
SUSPENDED,
FAILED,
TERMINATED,
CANCELLED,
CANCELLING,
}
@XmlAttribute(name = "ID")
String name;
@XmlElement(name = "Status")
String status;
@XmlElement(name = "BackupWorkers")
int backupSysInstances;
@XmlElement(name = "TerminatedWorkers")
int terminatedSysInstances;
@XmlElement(name = "RunningWorkers")
int runningSysInstances;
@XmlElement(name = "TotalWorkers")
int totalSysInstances;
@XmlElement(name = "InputRecords")
long inputRecords;
@XmlElement(name = "OutputRecords")
long outputRecords;
@XmlElement(name = "FinishedPercentage")
int finishedPercentage;
public Status getStatus() {
if (status == null) {
return null;
}
return Status.valueOf(status.toUpperCase());
}
/**
* 获得backup的Worker数,对应于original worker,backup会在长尾时被启动,同时运行。
*
* @return Worker数
*/
public int getBackupWorkers() {
return backupSysInstances;
}
/**
* 获得正在运行的Worker数。多个Worker构成一个Stage,Worker可以简单的被看做一个进程。
*
* @return Worker数
*/
public int getRunningWorkers() {
return runningSysInstances;
}
/**
* 获得Stage名称。任意{@link Task}
* 可以由任意多个Stage构成。Stage由任意多个Worker构成。Stage内的Worker有相同的数据处理逻辑。
*
* @return Stage名称
*/
public String getName() {
return name;
}
/**
* 获得结束运行的Worker数
*
* @return 结束运行的Worker数
*/
public int getTerminatedWorkers() {
return terminatedSysInstances;
}
/**
* 获得所有Worker数
*
* @return 所有Worker数
*/
public int getTotalWorkers() {
return totalSysInstances;
}
/**
* 获得输入的{@link Record}数
*
* @return Record数
*/
public long getInputRecords() {
return inputRecords;
}
/**
* 获得输出的{@link Record}数
*
* @return Record数
*/
public long getOutputRecords() {
return outputRecords;
}
/**
* 获得已处理数据量的百分比
*
* @return finishedPercentage
*/
public int getFinishedPercentage() {
return finishedPercentage;
}
}
public static String getStageProgressFormattedString(List<StageProgress> stages) {
StringBuilder result = new StringBuilder();
SimpleDateFormat sim = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
String dateString = sim.format(new Date());
result.append(dateString + ' ');
for (StageProgress stage : stages) {
result.append(String.format("%s:%s/%s/%s%s%s", stage.getName(), stage.getRunningWorkers(),
stage.getTerminatedWorkers(), stage.getTotalWorkers(),
stage.getBackupWorkers() > 0 ? "(+" + stage.getBackupWorkers() + " backups)" : "",
"[" + stage.getFinishedPercentage() + "%]\t"
));
}
return result.toString();
}
@XmlRootElement(name = "Progress")
@XmlAccessorType(XmlAccessType.FIELD)
private static class TaskProgress {
@XmlElement(name = "Stage")
List<StageProgress> stages = new LinkedList<StageProgress>();
/**
* 获得Instance的Stage统计
*
* @return {@link com.aliyun.odps.Instance.StageProgress}列表
*/
List<StageProgress> getStages() {
return stages;
}
}
/**
* 查询指定Task的Stage进度
*
* @param taskName
* Task名称
* @return {@link com.aliyun.odps.Instance.StageProgress}列表
* @throws OdpsException
*/
public List<StageProgress> getTaskProgress(String taskName) throws OdpsException {
HashMap<String, String> params = new HashMap<String, String>();
params.put("instanceprogress", taskName);
params.put("taskname", taskName);
TaskProgress r = client.request(TaskProgress.class, getResource(), "GET", params);
return r.getStages();
}
/**
* 获得Instance的 ODPS 对象
*
* @return Odps 对象
*/
public Odps getOdps() {
return odps;
}
/**
* 获得Instance的ID
*
* @return Instance ID
*/
public String getId() {
return model.name;
}
/**
* 获得Instance状态
*
*
* <p>
* Instance状态如下:<br />
* <ul>
* <li>RUNNING: 正在执行</li>
* <li>SUSPENDED: 被挂起</li>
* <li>TERMINATED: 执行结束, 包括成功、失败、取消等</li>
* </p>
*
* @param isBlock 是否使用 block 模式
* 若启用 block 模式, 请求会被 block 住一定时间 (一般是 5s),随后再返回 instance 的状态。
* 若不启用,请求将会立即返回。与接口 {@link #getStatus()} 的行为一致。
*
* @return 返回{@link Status.TERMINATED}
*/
public Status getStatus(boolean isBlock) {
if (status != Status.TERMINATED) {
try {
reload(isBlock); // always reload status until terminated
} catch (OdpsException e) {
throw new ReloadException(e.getMessage(), e);
}
}
try {
if (hooks != null) {
if (status == Status.TERMINATED) {
if (!hookInvoked) {
hookInvoked = true;
hooks.after(this, odps);
}
}
}
} catch (OdpsException e) {
throw new ReloadException(e.getMessage(), e);
}
return status;
}
/**
* 获得Instance状态
*
* <p>
* Instance状态如下:<br />
* <ul>
* <li>RUNNING: 正在执行</li>
* <li>SUSPENDED: 被挂起</li>
* <li>TERMINATED: 执行结束, 包括成功、失败、取消等</li>
* </p>
*
* @return 返回{@link Status.TERMINATED}
*/
public Status getStatus() {
return getStatus(false);
}
/**
* 检查是否执行完成
*
* @return TERMINATED状态返回true, 否则返回false
*/
public boolean isTerminated() {
return getStatus() == Status.TERMINATED;
}
/**
* 获得Instance所属{@link Project}名称
*
* @return {@link Project}名称
*/
public String getProject() {
return project;
}
/**
* 获得Instance是否为同步执行
*
* @return true如果同步执行
*/
public boolean isSync() {
return isSync;
}
/**
* 获得Instance所属用户
*
* @return 用户名
*/
public String getOwner() {
if (model.owner == null) {
lazyLoad();
}
return model.owner;
}
/**
* 获得Instance开始执行时间
*
* @return 开始执行时间
*/
public Date getStartTime() {
if (model.startTime == null) {
lazyLoad();
}
return model.startTime;
}
/**
* 获得Instance结束执行时间
*
* @return 结束执行时间
*/
public Date getEndTime() {
if (model.endTime == null) {
lazyLoad();
}
return model.endTime;
}
/**
* 返回与作业运行实例相关的作业{@link Task}实例。
*
* @return 与作业运行实例相关的作业{@link Task}实例。
*/
public List<Task> getTasks() throws OdpsException {
String resource = getResource();
Map<String, String> params = new HashMap<String, String>();
params.put("source", null);
JobModel model = client.request(JobModel.class, resource, "GET", params, null, null);
Job job = new Job(model);
return job.getTasks();
}
/**
* 获取 Instance 的优先级
*
* @return
* @throws OdpsException
*/
public int getPriority() throws OdpsException {
String resource = getResource();
Map<String, String> params = new HashMap<String, String>();
params.put("source", null);
JobModel model = client.request(JobModel.class, resource, "GET", params, null, null);
Job job = new Job(model);
return job.getPriority();
}
private String getResource() {
return ResourceBuilder.buildInstanceResource(project, model.name);
}
/**
* 获取Worker运行的LOG内容
*
* @param workerId
* Worker ID,{@link Task} 内Worker的唯一标示。
* @param logType
* {@link LogType} 对象
* @param size
* 展示Log内容的长度
* @return Log内容
* @throws OdpsException
* @throws IOException
*/
public String getLog(String workerId, LogType logType, int size) throws OdpsException {
if (workerId == null) {
throw new OdpsException("Missing argument: workerId");
}
Map<String, String> params = new HashMap<String, String>();
params.put("log", null);
params.put("id", workerId);
if (logType != null) {
params.put("logtype", logType.toString());
}
if (size > 0) {
params.put("size", String.valueOf(size));
}
Response resp = client.request(getResource(), "GET", params, null, null);
return new String(resp.getBody());
}
/* Un-document */
public String getTaskDetailJson(String taskName) throws OdpsException {
Map<String, String> params = new HashMap<String, String>();
params.put("instancedetail", null);
params.put("taskname", taskName);
Response result = client.request(getResource(), "GET", params, null, null);
return new String(result.getBody());
}
public OdpsHooks getOdpsHooks() {
return hooks;
}
public void setOdpsHooks(OdpsHooks hooks) {
this.hooks = hooks;
}
public boolean isHookInvoked() {
return hookInvoked;
}
public void setHookInvoked(boolean hookInvoked) {
this.hookInvoked = hookInvoked;
}
private boolean hookInvoked = false;
private OdpsHooks hooks;
/* Un-document */
public String getTaskDetailJson2(String taskName) throws OdpsException {
Map<String, String> params = new HashMap<String, String>();
params.put("detail", null);
params.put("taskname", taskName);
Response result = client.request(getResource(), "GET", params, null, null);
return new String(result.getBody());
}
/**
* 获取 task 运行 quota group 信息以及 quota group 中作业排队情况
*
* 注意,此接口的调用频率受限,两次调用间隔不能少于 30s, 否则返回字符串为 {}。
*
* @param taskName
* @return task quota 相关信息
*
* 返回的字符串为 json 格式,下面给出一个例子:
* {"quotaInfos": { // 格式为 [ odps task 中 fuxi job 名称, fuxi job quota infos]
* "job0": // fuxi job 名称
* "{
* "AmWaitingResInfo": // 本 fuxi job 中 fuxi task 的资源使用情况 [taskId, info]
* [[0, // fuxi task id
* {
* "RunningInstanceNum": 1, // fuxi task 中正在运行的 instance 数量
* "SlotDesc": {}, // 一个最小资源单元的描述
* "SuId": 0, // fuxi task id
* "TotalInstanceNum": 1, // fuxi task 中运行的 fuxi instance 数量
* "totalAheadResourceDesire": {}, // 等待队列中,排在本 fuxi task 之前的 task 所需资源总和,资源包括 CPU 和 memory
* "totalAheadTaskNum": 0, // 等待队列中,排在本 fuxi task 之前的 task 数量
* "waitingQueueItemInfo": {} // 排在本 fuxi task 之前的等待的 job 队列所需资源信息 [jobname, info(cpu,mem)]
* }]],
* "GroupAlias": "ttt", // odps task 运行所在 quota 组名称
* "GroupId": 1, // odps task 运行所在 quota 组 id
* "GroupMaxQuota": { // quota 组可用资源上限
* "CPU": 67200,
* "Memory": 2437512},
* "GroupRequest": { // 当前 quota 组中 job 申请资源的总和
* "CPU": 100,
* "Memory": 2068},
* "RuntimeQuota": { // quota 组当前可被使用的资源总值
* "CPU": 100,
* "Memory": 2068},
* "UsedQuota": { // quota 组当前已经被使用的资源总值
* "CPU": 100,
* "Memory": 2068}}" // 空闲 quota = RuntimeQuota - UsedQuota
* }
* }
*
* @throws OdpsException
*/
public String getTaskQuotaJson(String taskName) throws OdpsException {
Map<String, String> params = new HashMap<String, String>();
params.put("instancequota", null);
params.put("taskname", taskName);
Response result = client.request(getResource(), "GET", params, null, null);
return new String(result.getBody());
}
/**
* 以 debug 模式,启动 instance
*
* 本接口是提供给内部 debug 工具使用的,不建议用户直接使用
*
* @param workerId
* Worker ID,{@link Task} 内Worker的唯一标示。
* @param debugId
* Debug ID, 标志此次 debug
* @return 启动的结果
* @throws OdpsException
*/
public String startDebug(String workerId, String debugId) throws OdpsException {
if (workerId == null) {
throw new OdpsException("Missing argument: workerId");
}
if (debugId == null) {
throw new OdpsException("Missing argument: debugId");
}
InstanceDebugModel model = new InstanceDebugModel();
model.debugId = debugId;
model.logId = workerId;
try {
String body = JAXBUtils.marshal(model, InstanceDebugModel.class);
HashMap<String, String> headers = new HashMap<String, String>();
headers.put(Headers.CONTENT_TYPE, "application/xml");
Response resp = client.stringRequest(getResource() + "/debug", "POST", null, headers, body);
return new String(resp.getBody());
} catch (Exception e) {
throw new OdpsException(e.getMessage(), e);
}
}
}