REM:
1. 备份代码
This commit is contained in:
HuangXin 2020-08-20 15:25:38 +08:00
parent 98729d6735
commit 9234d1a1c5
18 changed files with 444 additions and 67 deletions

View File

@ -3,8 +3,8 @@ dispose.debug-model=true
dispose.check-protocol-timeout=false dispose.check-protocol-timeout=false
dispose.split_char=, dispose.split_char=,
dispose.request-timeout-second=5 dispose.request-timeout-second=5
dispose.used-privacy-protect=true dispose.used-privacy-protect=true
dispose.call-error-retry-times=3
# 迪普设备配置 # 迪普设备配置
# 发送超时时间(s) # 发送超时时间(s)

View File

@ -0,0 +1,32 @@
package com.dispose.ability;
import com.dispose.common.DisposeCapacityType;
/**
* The interface Device dispose ability.
*
* @param <T> the type parameter
* @author <huangxin@cmhi.chinamoblie.com>
*/
public interface DeviceDisposeAbility <T> {
/**
* Device dispose exec t.
*
* @param callback the callback
* @param capType the cap type
* @param ipAddr the ip addr
* @param args the args
* @return the t
*/
T deviceDisposeExec(DisposeTaskCallback callback, DisposeCapacityType capType, String ipAddr, Object... args);
/**
* Device dispose stop t.
*
* @param callback the callback
* @param capType the cap type
* @param args the args
* @return the t
*/
T deviceDisposeStop(DisposeTaskCallback callback, DisposeCapacityType capType, Object... args);
}

View File

@ -0,0 +1,17 @@
package com.dispose.ability;
import com.dispose.common.ErrorCode;
/**
* The interface Dispose task callback.
*
* @author <huangxin@cmhi.chinamoblie.com>
*/
public interface DisposeTaskCallback {
/**
* Upgrade status callback.
*
* @param err the err
*/
void upgradeStatusCallback(ErrorCode err);
}

View File

@ -1,6 +1,8 @@
package com.dispose.ability.impl; package com.dispose.ability.impl;
import com.dispose.ability.DeviceDisposeAbility;
import com.dispose.ability.DisposeAbility; import com.dispose.ability.DisposeAbility;
import com.dispose.ability.DisposeTaskCallback;
import com.dispose.common.DDoSAttackType; import com.dispose.common.DDoSAttackType;
import com.dispose.common.DisposeCapacityType; import com.dispose.common.DisposeCapacityType;
import com.dispose.common.ErrorCode; import com.dispose.common.ErrorCode;
@ -22,7 +24,7 @@ import javax.annotation.Nullable;
*/ */
@Component @Component
@Slf4j @Slf4j
public class HaoHanAbilityImpl implements DisposeAbility { public class HaoHanAbilityImpl implements DisposeAbility, DeviceDisposeAbility<MulReturnType<ErrorCode, Long>> {
/** /**
* The constant DISPOSE_PLATFORM_NAME. * The constant DISPOSE_PLATFORM_NAME.
@ -206,4 +208,50 @@ public class HaoHanAbilityImpl implements DisposeAbility {
public Long toDeviceAttackType(Long ddosAttackTypeMask) { public Long toDeviceAttackType(Long ddosAttackTypeMask) {
return ddosAttackTypeMask; return ddosAttackTypeMask;
} }
/**
* Device dispose exec mul return type.
*
* @param capType the cap type
* @param ipAddr the ip addr
* @param duration the duration
* @return the mul return type
*/
@Override
public MulReturnType<ErrorCode, Long> deviceDisposeExec(DisposeTaskCallback callback,
DisposeCapacityType capType,
String ipAddr,
Object... duration) {
MulReturnType<ErrorCode, Long> ret = runDispose(ipAddr,
capType,
null,
DDoSAttackType.ALL_ATTACKS,
(long) duration[0]);
callback.upgradeStatusCallback(ret.getFirstParam());
return ret;
}
/**
* Device dispose stop mul return type.
*
* @param capType the cap type
* @param taskId the task id
* @return the mul return type
*/
@Override
public MulReturnType<ErrorCode, Long> deviceDisposeStop(DisposeTaskCallback callback,
DisposeCapacityType capType,
Object... taskId) {
MulReturnType<ErrorCode, Long> ret = stopDispose(null,
null,
null,
DDoSAttackType.ALL_ATTACKS,
(long) taskId[0]);
callback.upgradeStatusCallback(ret.getFirstParam());
return ret;
}
} }

View File

@ -1,6 +1,8 @@
package com.dispose.ability.impl; package com.dispose.ability.impl;
import com.dispose.ability.DeviceDisposeAbility;
import com.dispose.ability.DisposeAbility; import com.dispose.ability.DisposeAbility;
import com.dispose.ability.DisposeTaskCallback;
import com.dispose.common.DDoSAttackType; import com.dispose.common.DDoSAttackType;
import com.dispose.common.DisposeCapacityType; import com.dispose.common.DisposeCapacityType;
import com.dispose.common.ErrorCode; import com.dispose.common.ErrorCode;
@ -15,7 +17,7 @@ import javax.annotation.Nullable;
* *
* @author <huangxin@cmhi.chinamoblie.com> * @author <huangxin@cmhi.chinamoblie.com>
*/ */
public class VirtualAbilityImpl implements DisposeAbility { public class VirtualAbilityImpl implements DisposeAbility, DeviceDisposeAbility<ErrorCode> {
/** /**
* Init device env. * Init device env.
* *
@ -140,4 +142,46 @@ public class VirtualAbilityImpl implements DisposeAbility {
public Long toDeviceAttackType(Long ddosAttackTypeMask) { public Long toDeviceAttackType(Long ddosAttackTypeMask) {
return ddosAttackTypeMask; return ddosAttackTypeMask;
} }
/**
* Device dispose exec error code.
*
* @param capType the cap type
* @param ipAddr the ip addr
* @param duration the duration
* @return the error code
*/
@Override
public ErrorCode deviceDisposeExec(DisposeTaskCallback callback, DisposeCapacityType capType, String ipAddr,
Object... duration) {
ErrorCode ret = runDispose(ipAddr,
capType,
null,
DDoSAttackType.ALL_ATTACKS,
null).getFirstParam();
callback.upgradeStatusCallback(ret);
return ret;
}
/**
* Device dispose stop error code.
*
* @param capType the cap type
* @param taskId the task id
* @return the error code
*/
@Override
public ErrorCode deviceDisposeStop(DisposeTaskCallback callback, DisposeCapacityType capType, Object... taskId) {
ErrorCode ret = stopDispose(null,
capType,
null,
DDoSAttackType.ALL_ATTACKS,
null).getFirstParam();
callback.upgradeStatusCallback(ret);
return ret;
}
} }

View File

@ -21,4 +21,9 @@ public class DisposeConfigValue {
* The constant USED_PRIVACY_PROTECT. * The constant USED_PRIVACY_PROTECT.
*/ */
public static volatile boolean USED_PRIVACY_PROTECT = false; public static volatile boolean USED_PRIVACY_PROTECT = false;
/**
* The constant CALL_ERROR_RETRY_TIMES.
*/
public static volatile int CALL_ERROR_RETRY_TIMES=5;
} }

View File

@ -34,4 +34,9 @@ public class DisposeConfigure {
* The Used privacy protect. * The Used privacy protect.
*/ */
private String usedPrivacyProtect; private String usedPrivacyProtect;
/**
* The Call error retry times.
*/
public String callErrorRetryTimes;
} }

View File

@ -1,12 +1,13 @@
package com.dispose.manager; package com.dispose.manager;
import com.dispose.common.DisposeTaskStatus; import com.dispose.common.DisposeTaskStatus;
import com.dispose.common.NetflowDirection;
import com.dispose.pojo.entity.DeviceTask; import com.dispose.pojo.entity.DeviceTask;
import java.util.List; import java.util.List;
/** /**
* The interface Task info manager. * The interface Device task manager.
* *
* @author <huangxin@cmhi.chinamoblie.com> * @author <huangxin@cmhi.chinamoblie.com>
*/ */
@ -36,4 +37,33 @@ public interface DeviceTaskManager {
* @return the boolean * @return the boolean
*/ */
boolean changeDisposeDeviceTaskInfoStatus(Long id, DisposeTaskStatus status); boolean changeDisposeDeviceTaskInfoStatus(Long id, DisposeTaskStatus status);
/**
* Sets exec attack type.
*
* @param id the id
* @param nf the nf
* @param attackTypeMask the attack type mask
* @return the exec attack type
*/
boolean setExecAttackType(Long id, NetflowDirection nf, Long attackTypeMask);
/**
* Sets attack type status.
*
* @param id the id
* @param nf the nf
* @param attackTypeMask the attack type mask
* @return the attack type status
*/
boolean setAttackTypeStatus(Long id, NetflowDirection nf, Long attackTypeMask);
/**
* Sets task err retry times.
*
* @param id the id
* @param rTimes the r times
* @return the task err retry times
*/
boolean setTaskErrRetryTimes(Long id, Integer rTimes);
} }

View File

@ -1,6 +1,7 @@
package com.dispose.manager.impl; package com.dispose.manager.impl;
import com.dispose.common.DisposeTaskStatus; import com.dispose.common.DisposeTaskStatus;
import com.dispose.common.NetflowDirection;
import com.dispose.manager.DeviceTaskManager; import com.dispose.manager.DeviceTaskManager;
import com.dispose.mapper.DeviceTaskMapper; import com.dispose.mapper.DeviceTaskMapper;
import com.dispose.pojo.entity.DeviceTask; import com.dispose.pojo.entity.DeviceTask;
@ -11,7 +12,7 @@ import javax.annotation.Resource;
import java.util.List; import java.util.List;
/** /**
* The type Task info manager. * The type Device task manager.
* *
* @author <huangxin@cmhi.chinamoblie.com> * @author <huangxin@cmhi.chinamoblie.com>
*/ */
@ -19,7 +20,7 @@ import java.util.List;
@Slf4j @Slf4j
public class DeviceTaskManagerImpl implements DeviceTaskManager { public class DeviceTaskManagerImpl implements DeviceTaskManager {
/** /**
* The Task info mapper. * The Device task mapper.
*/ */
@Resource @Resource
private DeviceTaskMapper deviceTaskMapper; private DeviceTaskMapper deviceTaskMapper;
@ -67,4 +68,55 @@ public class DeviceTaskManagerImpl implements DeviceTaskManager {
public boolean changeDisposeDeviceTaskInfoStatus(Long id, DisposeTaskStatus status) { public boolean changeDisposeDeviceTaskInfoStatus(Long id, DisposeTaskStatus status) {
return deviceTaskMapper.changeTaskStatus(id, status) == 1; return deviceTaskMapper.changeTaskStatus(id, status) == 1;
} }
/**
* Sets exec attack type.
*
* @param id the id
* @param nf the nf
* @param attackTypeMask the attack type mask
* @return the exec attack type
*/
@Override
public boolean setExecAttackType(Long id, NetflowDirection nf, Long attackTypeMask) {
int ret = 0;
if(nf != NetflowDirection.DIRECTION_IN) {
ret = deviceTaskMapper.changeExecAttackTypeOutValue(id, attackTypeMask);
}
if(nf != NetflowDirection.DIRECTION_OUT) {
ret = deviceTaskMapper.changeAttackTypeStatusInValue(id, attackTypeMask);
}
return ret == 1;
}
/**
* Sets attack type status.
*
* @param id the id
* @param nf the nf
* @param attackTypeMask the attack type mask
* @return the attack type status
*/
@Override
public boolean setAttackTypeStatus(Long id, NetflowDirection nf, Long attackTypeMask) {
int ret = 0;
if(nf != NetflowDirection.DIRECTION_IN) {
ret = deviceTaskMapper.changeAttackTypeStatusOutValue(id, attackTypeMask);
}
if(nf != NetflowDirection.DIRECTION_OUT) {
ret = deviceTaskMapper.changeAttackTypeStatusInValue(id, attackTypeMask);
}
return ret == 1;
}
@Override
public boolean setTaskErrRetryTimes(Long id, Integer rTimes) {
return deviceTaskMapper.changeErrRetry(id, rTimes) == 1;
}
} }

View File

@ -71,4 +71,54 @@ public interface DeviceTaskMapper {
*/ */
int changeTaskStatus(@Param("id") Long id, int changeTaskStatus(@Param("id") Long id,
@Param("status") DisposeTaskStatus status); @Param("status") DisposeTaskStatus status);
/**
* Change exec attack type in value int.
*
* @param id the id
* @param attackTypeValue the attack type value
* @return the int
*/
int changeExecAttackTypeInValue(@Param("id") Long id,
@Param("attackTypeValue") Long attackTypeValue);
/**
* Change exec attack type out value int.
*
* @param id the id
* @param attackTypeValue the attack type value
* @return the int
*/
int changeExecAttackTypeOutValue(@Param("id") Long id,
@Param("attackTypeValue") Long attackTypeValue);
/**
* Change attack type status in value int.
*
* @param id the id
* @param attackStatsValue the attack stats value
* @return the int
*/
int changeAttackTypeStatusInValue(@Param("id") Long id,
@Param("attackStatsValue") Long attackStatsValue);
/**
* Change attack type status out value int.
*
* @param id the id
* @param attackStatsValue the attack stats value
* @return the int
*/
int changeAttackTypeStatusOutValue(@Param("id") Long id,
@Param("attackTypeValue") Long attackStatsValue);
/**
* Change err retry int.
*
* @param id the id
* @param errRetry the err retry
* @return the int
*/
int changeErrRetry(@Param("id") Long id,
@Param("errRetry") Integer errRetry);
} }

View File

@ -15,7 +15,9 @@ import javax.persistence.Table;
import java.io.Serializable; import java.io.Serializable;
/** /**
* The type Task info. * The type Device task.
*
* @author <huangxin@cmhi.chinamoblie.com>
*/ */
@Data @Data
@NoArgsConstructor @NoArgsConstructor
@ -77,6 +79,10 @@ public class DeviceTask implements Serializable {
* The Extern id. * The Extern id.
*/ */
private Long externId; private Long externId;
/**
* The Err retry.
*/
private Integer errRetry;
/** /**
* The Status. * The Status.
*/ */

View File

@ -88,7 +88,6 @@ public class DisposeTask implements Serializable {
* The Current status. * The Current status.
*/ */
private DisposeTaskStatus currentStatus; private DisposeTaskStatus currentStatus;
/** /**
* The Task info. * The Task info.
*/ */

View File

@ -1,6 +1,8 @@
package com.dispose.service.impl; package com.dispose.service.impl;
import com.dispose.common.DDoSAttackType; import com.dispose.ability.impl.HaoHanAbilityImpl;
import com.dispose.ability.impl.VirtualAbilityImpl;
import com.dispose.common.DisposeConfigValue;
import com.dispose.common.DisposeTaskStatus; import com.dispose.common.DisposeTaskStatus;
import com.dispose.common.ErrorCode; import com.dispose.common.ErrorCode;
import com.dispose.common.Helper; import com.dispose.common.Helper;
@ -9,7 +11,6 @@ import com.dispose.manager.DeviceTaskManager;
import com.dispose.manager.DisposeTaskManager; import com.dispose.manager.DisposeTaskManager;
import com.dispose.pojo.entity.DisposeTask; import com.dispose.pojo.entity.DisposeTask;
import com.dispose.pojo.po.AbilityInfo; import com.dispose.pojo.po.AbilityInfo;
import com.dispose.pojo.po.MulReturnType;
import com.dispose.service.DeviceTaskManagerService; import com.dispose.service.DeviceTaskManagerService;
import com.dispose.service.DisposeAbilityRouterService; import com.dispose.service.DisposeAbilityRouterService;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
@ -34,7 +35,7 @@ public class DeviceTaskManagerServiceImpl implements DeviceTaskManagerService {
private DisposeTaskManager disposeTaskManager; private DisposeTaskManager disposeTaskManager;
/** /**
* The Task info manager. * The Device task manager.
*/ */
@Resource @Resource
private DeviceTaskManager deviceTaskManager; private DeviceTaskManager deviceTaskManager;
@ -103,25 +104,74 @@ public class DeviceTaskManagerServiceImpl implements DeviceTaskManagerService {
break; break;
case HAOHAN_PLATFORM: case HAOHAN_PLATFORM:
// 设置任务状态为启动中
deviceTaskManager.changeDisposeDeviceTaskInfoStatus(v.getId(), DisposeTaskStatus.TASK_STARTING); deviceTaskManager.changeDisposeDeviceTaskInfoStatus(v.getId(), DisposeTaskStatus.TASK_STARTING);
// 设置启动任务攻击类型状态
MulReturnType<ErrorCode, Long> ret = ai.getDb().runDispose(task.getDisposeIp(), deviceTaskManager.setExecAttackType(v.getId(), NetflowDirection.DIRECTION_BI,
task.getDisposeCapacity(), v.getTaskAttackType());
NetflowDirection.DIRECTION_BI, // 调用设备处置能力
DDoSAttackType.ALL_ATTACKS, ((HaoHanAbilityImpl) ai.getDb()).deviceDisposeExec(
(long)Helper.getTimestampDiffNow(task.getPlanEndTime())); errorCode -> {
// 启动处置任务成功
deviceTaskManager.changeDisposeDeviceTaskInfoStatus(v.getId(), DisposeTaskStatus.TASK_STARTED); if (errorCode == ErrorCode.ERR_OK) {
// 设置攻击类型任务启动结果
if(ret.getFirstParam() != ErrorCode.ERR_OK) { deviceTaskManager.setAttackTypeStatus(v.getId(),
log.error("HAOHAN_PLATFORM setup task error {}: {}", ret.getFirstParam(), v); task.getFlowDirection(), v.getTaskAttackType());
// 更改处置任务状态为处置中
deviceTaskManager.changeDisposeDeviceTaskInfoStatus(v.getId(),
DisposeTaskStatus.TASK_STARTED);
log.info("HAOHAN_PLATFORM setup task succeed: {}", v);
} else if (v.getErrRetry() < DisposeConfigValue.CALL_ERROR_RETRY_TIMES) {
// 设置该任务为新任务待下次重试启动
deviceTaskManager.changeDisposeDeviceTaskInfoStatus(v.getId(),
DisposeTaskStatus.TASK_NEW);
// 记录任务出错重试次数
deviceTaskManager.setTaskErrRetryTimes(v.getId(), v.getErrRetry() + 1);
log.error("HAOHAN_PLATFORM setup task times {} error {}: {}",
v.getErrRetry(), errorCode, v);
} else { } else {
// 任务出错不在重试当做失败任务处理
deviceTaskManager.setAttackTypeStatus(v.getId(),
task.getFlowDirection(), ~v.getTaskAttackType());
log.error("HAOHAN_PLATFORM setup task error {}: {}", errorCode, v);
} }
},
task.getDisposeCapacity(),
task.getDisposeIp(),
(long) Helper.getTimestampDiffNow(task.getPlanEndTime()));
break;
case VIRTUAL_DISPOSE:
// 设置任务状态为启动中
deviceTaskManager.changeDisposeDeviceTaskInfoStatus(v.getId(), DisposeTaskStatus.TASK_STARTING);
// 设置启动任务攻击类型状态
deviceTaskManager.setExecAttackType(v.getId(), NetflowDirection.DIRECTION_BI,
v.getTaskAttackType());
((VirtualAbilityImpl) ai.getDb()).deviceDisposeExec(
errorCode -> {
// 启动处置任务成功
if (errorCode == ErrorCode.ERR_OK) {
// 设置攻击类型任务启动结果
deviceTaskManager.setAttackTypeStatus(v.getId(),
task.getFlowDirection(), v.getTaskAttackType());
// 更改处置任务状态为处置中
deviceTaskManager.changeDisposeDeviceTaskInfoStatus(v.getId(),
DisposeTaskStatus.TASK_STARTED);
log.info("VIRTUAL_DISPOSE setup task succeed: {}", v);
} else {
// 任务出错不在重试当做失败任务处理
deviceTaskManager.setAttackTypeStatus(v.getId(),
task.getFlowDirection(), ~v.getTaskAttackType());
log.error("VIRTUAL_DISPOSE setup task error {}: {}", errorCode, v);
}
},
task.getDisposeCapacity(),
task.getDisposeIp());
break; break;
default: default:
log.error("Unknown dispose device type: {}", ai.getDev());
break; break;
} }

View File

@ -72,7 +72,7 @@ public class DisposeAbilityRouterServiceImpl implements DisposeAbilityRouterServ
.findAny() .findAny()
.orElse(null); .orElse(null);
if(dev != null) { if (dev != null) {
return disposeAbilityMap.get(getAbilityDeviceHashKey(dev.getIpAddr(), dev.getIpPort())); return disposeAbilityMap.get(getAbilityDeviceHashKey(dev.getIpAddr(), dev.getIpPort()));
} }

View File

@ -26,7 +26,6 @@ public class DisposeTaskServiceImpl implements DisposeTaskService {
@Override @Override
public MulReturnType<ErrorCode, Long> createTask(DisposeTask task) { public MulReturnType<ErrorCode, Long> createTask(DisposeTask task) {
if (disposeTaskManager.disposeIpRunning(task.getDeviceId(), if (disposeTaskManager.disposeIpRunning(task.getDeviceId(),
task.getDisposeIp(), task.getDisposeCapacity())) { task.getDisposeIp(), task.getDisposeCapacity())) {

View File

@ -98,6 +98,12 @@ public class SystemInitial implements CommandLineRunner {
} catch (Exception ex) { } catch (Exception ex) {
log.error("load USED_PRIVACY_PROTECT configure error: {}", ex.getMessage()); log.error("load USED_PRIVACY_PROTECT configure error: {}", ex.getMessage());
} }
try {
DisposeConfigValue.CALL_ERROR_RETRY_TIMES = Integer.parseInt(disposeConfigure.getCallErrorRetryTimes());
} catch (Exception ex) {
log.error("load CALL_ERROR_RETRY_TIMES configure error: {}", ex.getMessage());
}
} }
@Async("bizExecutor") @Async("bizExecutor")

View File

@ -13,6 +13,7 @@
<result column="execAttackTypeOut" property="execAttackTypeOut"/> <result column="execAttackTypeOut" property="execAttackTypeOut"/>
<result column="attackTypeStatusOut" property="attackTypeStatusOut"/> <result column="attackTypeStatusOut" property="attackTypeStatusOut"/>
<result column="externId" property="externId"/> <result column="externId" property="externId"/>
<result column="errRetry" property="errRetry"/>
<result column="status" property="status"/> <result column="status" property="status"/>
</resultMap> </resultMap>
@ -75,4 +76,34 @@
WHERE id = #{id} WHERE id = #{id}
</update> </update>
<update id="changeExecAttackTypeInValue">
UPDATE device_task
SET execAttackTypeIn = #{attackTypeValue}
WHERE id = #{id}
</update>
<update id="changeExecAttackTypeOutValue">
UPDATE device_task
SET execAttackTypeOut = #{attackTypeValue}
WHERE id = #{id}
</update>
<update id="changeAttackTypeStatusInValue">
UPDATE device_task
SET attackTypeStatusIn = #{attackStatsValue}
WHERE id = #{id}
</update>
<update id="changeAttackTypeStatusOutValue">
UPDATE device_task
SET execAttackTypeOut = #{attackStatsValue}
WHERE id = #{id}
</update>
<update id="changeErrRetry">
UPDATE device_task
SET errRetry = #{errRetry}
WHERE id = #{id}
</update>
</mapper> </mapper>

View File

@ -26,6 +26,7 @@
<result column="execAttackTypeOut" property="execAttackTypeOut"/> <result column="execAttackTypeOut" property="execAttackTypeOut"/>
<result column="attackTypeStatusOut" property="attackTypeStatusOut"/> <result column="attackTypeStatusOut" property="attackTypeStatusOut"/>
<result column="externId" property="externId"/> <result column="externId" property="externId"/>
<result column="errRetry" property="errRetry"/>
<result column="status" property="status"/> <result column="status" property="status"/>
</collection> </collection>
</resultMap> </resultMap>
@ -43,6 +44,7 @@
ti.execAttackTypeOut execAttackTypeOut, ti.execAttackTypeOut execAttackTypeOut,
ti.attackTypeStatusOut attackTypeStatusOut, ti.attackTypeStatusOut attackTypeStatusOut,
ti.externId externId, ti.externId externId,
ti.errRetry errRetry,
ti.status status ti.status status
FROM dispose_task dt FROM dispose_task dt
LEFT JOIN device_task ti on dt.id = ti.taskId LEFT JOIN device_task ti on dt.id = ti.taskId
@ -61,6 +63,7 @@
ti.execAttackTypeOut execAttackTypeOut, ti.execAttackTypeOut execAttackTypeOut,
ti.attackTypeStatusOut attackTypeStatusOut, ti.attackTypeStatusOut attackTypeStatusOut,
ti.externId externId, ti.externId externId,
ti.errRetry errRetry,
ti.status status ti.status status
FROM dispose_task dt FROM dispose_task dt
LEFT JOIN device_task ti on dt.id = ti.taskId LEFT JOIN device_task ti on dt.id = ti.taskId