package com.bocloud.ims.service.installation;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.bocloud.cmp.entity.enums.BocloudStatus;
import com.bocloud.cmp.entity.enums.ServerAssetCategory;
import com.bocloud.cmp.entity.enums.installation.EndPoints;
import com.bocloud.cmp.entity.enums.installation.InstallImageType;
import com.bocloud.cmp.entity.enums.installation.InstallStatus;
import com.bocloud.cmp.entity.enums.installation.TaskType;
import com.bocloud.cmp.model.CpuInfo;
import com.bocloud.cmp.model.NotifyMessage;
import com.bocloud.cmp.model.ServerCpu;
import com.bocloud.cmp.model.installation.IpmiInfo;
import com.bocloud.cmp.model.installation.NetworkDevice;
import com.bocloud.cmp.model.installation.Options;
import com.bocloud.cmp.model.installation.catalog.*;
import com.bocloud.common.enums.BoCloudMethod;
import com.bocloud.common.model.BocloudResult;
import com.bocloud.common.model.OperateResult;
import com.bocloud.common.model.RequestContext;
import com.bocloud.common.model.Result;
import com.bocloud.ims.common.utils.constant.Constant;
import com.bocloud.ims.entity.installation.InstallResult;
import com.bocloud.ims.entity.installation.InstallTask;
import com.bocloud.ims.entity.resource.Disk;
import com.bocloud.ims.entity.resource.*;
import com.bocloud.ims.entity.resource.installation.ServerPci;
import com.bocloud.ims.entity.resource.installation.ServerRaidCtrl;
import com.bocloud.ims.interfaces.installation.CatalogService;
import com.bocloud.ims.interfaces.installation.HookService;
import com.bocloud.ims.interfaces.installation.RackHdService;
import com.bocloud.ims.repository.installation.InstallResultRepository;
import com.bocloud.ims.repository.installation.InstallTaskRepository;
import com.bocloud.ims.service.internal.worker.WorkerService;
import com.bocloud.ims.service.resource.server.InstallationServerServiceImpl;
import com.bocloud.ims.service.utils.PingUtils;
import com.bocloud.ims.service.utils.ResourceEventPublisher;
import com.bocloud.service.core.Service;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.amqp.core.AmqpTemplate;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;

import java.math.BigDecimal;
import java.math.RoundingMode;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Date;
import java.util.List;
import java.util.stream.Collectors;

/**
 * RackHD hook业务处理
 *
 * @author 007
 */
@org.springframework.stereotype.Service("hookService")
public class HookServiceImpl implements HookService {
	private static Logger logger = LoggerFactory.getLogger(HookServiceImpl.class);

	private static String DISCOVER = "node.discover";
	private static String INSTALL = "node.os.install";
	private static String BMC_TASK = "Set BMC Credentials";
	@Autowired
	private InstallationServerServiceImpl installationServerService;
	@Autowired
	private CatalogService catalogService;
	@Autowired
	private Service service;
	@Autowired
	private RackHdService rackHdService;
	@Autowired
	private InstallResultRepository installResultRepository;
	@Autowired
	private InstallTaskRepository installTaskRepository;
	@Autowired
	private ResourceEventPublisher resourceEventPublisher;
	@Autowired
	private AmqpTemplate amqpTemplate;
	@Autowired
	private WorkerService workerService;

	private void sendMessage(boolean success, String operate, NotifyMessage message, String content,
			RequestContext context) {
		// 站内信和邮件发送
		amqpTemplate.convertAndSend("event.notice.queue", message);
		// 界面消息
		OperateResult operateResult = new OperateResult(success, content, operate, null,
				OperateResult.OperateCategory.ResourceEvent, context);
		resourceEventPublisher.send(operateResult);
	}

	private ServerPci convert(Hardware hardware) {
		ServerPci serverPci = new ServerPci();
		BeanUtils.copyProperties(hardware, serverPci, "children");
		if (hardware.getChildren() != null) {
			serverPci.setChildren(new ArrayList<>());
			for (Hardware child : hardware.getChildren()) {
				serverPci.getChildren().add(convert(child));
			}
		}
		return serverPci;
	}

	private Server convert(Server node, String nodeId, String identifier, String ipmiIp) {
		try {
			// 从rackhd获取节点的raid卡信息
			List<Controller> raid = catalogService.getRaid(nodeId);
			node.setRaids(convertRaid(raid, node.getId()));

			// 从rackhd获取节点的网卡信息
			node.setNetcards(this.catalogService.interfaces(nodeId));

			// 从rackhd获取节点的pci信息
			node.setServerPci(convert(this.catalogService.hardWard(nodeId)));

			// 从rackhd获取节点的dhcpIp信息
			node.setManageIp(this.catalogService.dhcpIp(identifier, false));

			// 从rackhd获取节点的基本信息
			NodeDetail detail = catalogService.getCatalog(nodeId);
			if (detail == null) {
				throw new Exception("获取catalog信息失败");
			}
			// 从rackhd获取节点的cpu信息
			ServerCpu serverCpu = this.catalogService.cpus(identifier);
			if (serverCpu == null) {
				logger.warn("cpu信息不正常，为空");
			}
			// 获取ipmi ip
			List<String> ipmiIps = catalogService.bmc(nodeId);
			if (ipmiIps == null || ipmiIps.isEmpty()) {
				throw new Exception("获取ipmi ip信息失败");
			}

			// 不正常的信息进行warn提示
			if (detail.getIpmiUsers().isEmpty()) {
				logger.warn("ipmi用户信息不正常，为空");
			}
			ServerConfig config = node.getConfig() == null ? new ServerConfig() : node.getConfig();
			ServerAssets assets = node.getAssets() == null ? new ServerAssets() : node.getAssets();

			config.setIpmiAddress(ipmiIp);
			config.setIpmiPort(623);
			config.setIpmiIps(JSON.toJSONString(ipmiIps));
			config.setVendor(detail.getBasic().getManufacturer());
			config.setIpmiUsers(detail.getIpmiUsers() == null ? null : JSON.toJSONString(detail.getIpmiUsers()));

			if (serverCpu != null) {
				// cmp物理机那里只统计第一个cpu的信息，cop这里也这么写
				CpuInfo cpuInfo = serverCpu.getCpus().get(0);
				config.setCpuVendor(cpuInfo == null ? null : cpuInfo.getVendorId());
				config.setCpuAmount(serverCpu.getTotal());
				config.setCpuMode(cpuInfo == null ? null : cpuInfo.getModelName());
				config.setVirtualized(cpuInfo == null ? null : cpuInfo.getVirtualized());
				// CpuHz单位为MHZ
				config.setCpuHz(cpuInfo == null ? null : new BigDecimal(cpuInfo.getMHz()).longValue());
			}

			// cmp物理机那里只统计第一个mem的信息，cop这里也这么写
			config.setMemoryMode(detail.getMemory().getMemories().get(0).getType());
			// memoryCapacity单位为GB
			String memory = detail.getMemory().getBasic().getCapacity().split("MB")[0].trim();
			if (!StringUtils.isEmpty(memory)) {
				double capacity = new BigDecimal(Double.parseDouble(memory))
						.divide(new BigDecimal(1024), 2, RoundingMode.HALF_UP).doubleValue();
				config.setMemoryCapacity(capacity); // 单位为GB
			}

			// diskCapacity单位为G
			Double diskCapacity = 0.0;
			for (ServerRaidCtrl nodeRaid : node.getRaids()) {
				if (nodeRaid.getDiskGroups() != null) {
					for (Mdisk diskGroup : nodeRaid.getDiskGroups()) {
						diskCapacity += diskGroup.getCapacity();
					}
				}
			}
			config.setDiskCapacity(diskCapacity);

			config.setBiosVersion(detail.getBoard().getBiosBasic().getVersion());
			config.setBiosVendor(detail.getBoard().getBiosBasic().getVendor());
			SimpleDateFormat format = new SimpleDateFormat("MM/dd/yyyy");
			config.setBiosDate(format.parse(detail.getBoard().getBiosBasic().getReleaseDate()));
			node.setConfig(config);

			assets.setSn(detail.getBasic().getSerialNumer());
			assets.setMaker(detail.getBasic().getManufacturer());
			node.setAssets(assets);

			node.setPowerState(BocloudStatus.Server.RUNNING.name());
			node.setIdentifier(identifier);
			node.setInstance(service.getInstance());
			node.setRackhdNodeId(nodeId);
			node.setDiscoverTime(null);
			node.setCategory(detail.getBasic().getProductorName());
			node.setAssetCategory(ServerAssetCategory.BARE_METAL.name());
			node.setDiscovered(true);
			node.setCatalog(JSONObject.toJSONString(detail));
			node.setUuid(detail.getBasic().getUuid());
		} catch (Exception e) {
			logger.error("convert catalog from rackhd exception:", e);
		}
		return node;
	}

	private List<ServerRaidCtrl> convertRaid(List<Controller> controllers, Long serverId) {
		List<ServerRaidCtrl> raidCtrlList = new ArrayList<>();
		for (Controller controller : controllers) {
			ServerRaidCtrl raidCtrl = new ServerRaidCtrl();
			raidCtrl.setControllerId(controller.getControllerId());
			raidCtrl.setModel(controller.getModel());
			raidCtrl.setPciAddress(controller.getPciAddress());
			raidCtrl.setSasAddress(controller.getSasAddress());
			raidCtrl.setSn(controller.getSn());
			raidCtrl.setServerId(serverId);
			raidCtrl.setPath(controller.getPath());
			raidCtrl.setType(controller.getType());

			raidCtrl.setDisks(convertDisk(controller.getDrives()));

			List<Mdisk> mdisks = new ArrayList<>();
			for (DriveGroup driveGroup : controller.getDriveGroups()) {
				Mdisk mdisk = new Mdisk();
				mdisk.setRaidLevel(driveGroup.getType());
				mdisk.setStatus(driveGroup.getState());
				mdisk.setCapacity(parseSize(driveGroup.getSize()));
				mdisk.setDiskCount(driveGroup.getDrives().size());
				mdisk.setDisks(convertDisk(driveGroup.getDrives()));
				mdisk.setMdiskUuid(driveGroup.getId());
				mdisk.setName(driveGroup.getId());
				mdisks.add(mdisk);
			}
			raidCtrl.setDiskGroups(mdisks);
			raidCtrlList.add(raidCtrl);
		}
		return raidCtrlList;
	}

	private List<Disk> convertDisk(List<Drive> drives) {
		List<Disk> disks = new ArrayList<>();
		for (Drive drive : drives) {
			Disk disk = new Disk();
			disk.setEnclosureId(drive.getEnclosureId() == null ? null : drive.getEnclosureId().toString());
			disk.setBlockSize(drive.getSeSz() == null ? null : Double.parseDouble(drive.getSeSz().split("B")[0]));
			if (drive.getDriveId() == null) {
				disk.setDiskUuid(drive.getEnclosureId() + ":" + drive.getSlot());
			} else {
				disk.setDiskUuid(drive.getEnclosureId() + ":" + drive.getSlot() + ":" + drive.getDriveId());
			}
			disk.setProductId(drive.getMed() == null ? "HDD" : drive.getMed().trim());
			disk.setSize(parseSize(drive.getSize()));
			disk.setName(drive.getModel() == null ? null : drive.getModel().trim());
			disk.setStatus(drive.getState());
			disk.setSlotId(drive.getSlot() == null ? null : drive.getSlot().toString());
			disk.setDriveId(drive.getDriveId() == null ? null : drive.getDriveId().toString());
			disk.setMdiskName(drive.getDirveGroup() == null ? null : drive.getDirveGroup().toString());
			disks.add(disk);
		}
		return disks;
	}

	private Double parseSize(String size) {
		try {
			if (size.endsWith("TB")) {
				return Double.parseDouble(size.split("TB")[0].trim()) * 1024;
			}
			if (size.endsWith("GB")) {
				return Double.parseDouble(size.split("GB")[0].trim());
			}
			if (size.endsWith("G")) {
				return Double.parseDouble(size.split("G")[0].trim());
			}
			return Double.parseDouble(size.trim());
		} catch (NumberFormatException e) {
			return null;
		}
	}

	private BocloudResult discoverFail(Result result, String title, String content, RequestContext context) {
		logger.warn(content + "{}", result.getMessage());
		// 第一个true是发送站内信，第二个true是发送邮件
		NotifyMessage message = new NotifyMessage(context, StringUtils.isNotEmpty(title) ? title : "物理主机自动发现情况通知", content,
				"管理员您好，" + content, true, false);
		sendMessage(false, DISCOVER, message, content, context);
		return new BocloudResult(false, content);
	}

	/**
	 * 使用ping确定管理网段的ipmi的ip
	 * <p>
	 * 保证自动发现的机器的ipmi的ip地址都可达
	 *
	 * @param ipmiIps
	 * @return
	 */
	private String judgeIpmiIp(List<String> ipmiIps) {
		for (String ipmiIp : ipmiIps) {
			if (PingUtils.connect(ipmiIp)) {
				return ipmiIp;
			}
		}
		return null;
	}

	@Override
    public BocloudResult discover(JSONObject info) {
		logger.info("handler discover, info={}", info.toString());
		try {
			String nodeId = info.getString(Constant.NODE_ID);

			// 获取nodeType
			JSONObject nodeObject = info.getJSONObject(Constant.DATA);
			String nodeType = nodeObject.getString(Constant.NODE_TYPE);
			if (Constant.ENCLOSURE.equals(nodeType)) {
				logger.info("该节点类型为enclosure,不做处理");
				return new BocloudResult(true, "该节点类型为enclosure,不做处理");
			}

            // 检查是否已经存在
            BocloudResult dbResult = installationServerService.get(JSON.toJSONString(Collections.singletonMap("rackhdNodeId", nodeId)));
            if (dbResult.isSuccess()) {
                Server dbNode = (Server) dbResult.getData();
				RequestContext context = new RequestContext();
				context.setTarget(dbNode.getDiscoverUserId());
				context.setCatalog(RequestContext.Catalog.Manager);
                logger.info("该节点已经被发现, 更新节点的catalog信息");
                convert(dbNode, nodeId, dbNode.getIdentifier(), dbNode.getConfig().getIpmiAddress());
                dbNode.setStatus(InstallStatus.DISCOVERY_SUCCEED.getText());
                installationServerService.update(dbNode, context);
                NotifyMessage notifyMessage = new NotifyMessage(context, "物理主机重新发现情况通知",
                        "重新发现" + dbNode.getName() + "成功", "管理员您好，" + "重新发现" + dbNode.getName() + "成功", true, false);
                sendMessage(true, DISCOVER, notifyMessage, "重新发现" + dbNode.getName() + "成功", context);
                return new BocloudResult(true, "重新发现" + dbNode.getName() + "成功");
            }

			// 获取ipmi ip
			List<String> ipmiIps = catalogService.bmc(nodeId);
			if (ipmiIps == null || ipmiIps.isEmpty()) {
				String content = "获取{" + nodeId + "}ipmi信息失败";
				RequestContext context = new RequestContext();
				context.setTarget(1L);
				context.setCatalog(RequestContext.Catalog.Manager);
				return discoverFail(new Result(false, content), null, content, context);
			}

			// 根据ipmiIp确定是自动发现还是手动增加
			boolean autoDiscover = true;

			Server node = null;
			String title = "物理主机自动发现情况通知";
			String ipmiIp = judgeIpmiIp(ipmiIps);
			for (String ip : ipmiIps) {
				node = installationServerService.getServerByIpmiIp(ip);
				if (node != null) {
					autoDiscover = false;
					title = "物理主机新增情况通知";
					ipmiIp = node.getConfig().getIpmiAddress();
					break;
				}
			}
			RequestContext context = new RequestContext();
			context.setTarget(node == null ? 1L : node.getDiscoverUserId());
			context.setCatalog(RequestContext.Catalog.Manager);

			if (!StringUtils.isNotEmpty(ipmiIp)) {
				logger.warn("不能从{}里面获取到一个可达的ipmi ip.", ipmiIps);
				String content = "获取{" + nodeId + "}ipmi信息失败";
				return discoverFail(new Result(false, content), title, content, context);
			}

			// 查询node信息,获取identifier
			String identifier;
			Result result = rackHdService.request(null, rackHdService.buildUrl(EndPoints.NODE_V2, nodeId),
					BoCloudMethod.GET);
			if (result.isFailed()) {
				String content = "获取{" + nodeId + "}信息失败";
				return discoverFail(result, title, content, context);
			}
			JSONObject rackhdNode = JSONObject.parseObject(result.getMessage());
			List<String> identifiers = JSONObject.parseArray(rackhdNode.getString(Constant.IDENTIFIERS), String.class);
			if (identifiers.size() != 1) {
				logger.warn("identifier={}", rackhdNode.getString(Constant.IDENTIFIERS));
				String content = "获取{" + nodeId + "}identifier信息异常";
				return discoverFail(result, title, content, context);
			}
			identifier = identifiers.get(0);

			// 组装机器信息
			node = convert(node == null ? new Server() : node, nodeId, identifier, ipmiIp);
			if (autoDiscover) {
				node.setCreatorId(context.getTarget());
				node.setName("auto_discover_" + ipmiIp.replaceAll("\\.", "_"));
			} else {
				node.setGmtModify(new Date());
			}
			node.setMenderId(context.getTarget());
			node.setDiscoverUserId(context.getTarget());
			node.setStatus(InstallStatus.DISCOVERY_SUCCEED.getText());

			String message;
			if (autoDiscover) {
				// 保存由RackHD自动发现的机器的信息
				BocloudResult saveResult = installationServerService.save(node, context);
				message = "发现物理机" + ipmiIp + ", 请编辑该节点的IPMI信息";
				logger.info("save physical server info {};\nmessage={}", saveResult, message);
				// send node add successfully message to rabbitmq
				// 第一个true是发送站内信，第二个true是发送邮件
				NotifyMessage notifyMessage = new NotifyMessage(context, title, message, "管理员您好，" + message, true,
						false);
				sendMessage(true, DISCOVER, notifyMessage, message, context);
			} else {
				// 这里处理手动增加到平台且允许重启的机器
				BocloudResult updateResult = installationServerService.update(node, context);
				message = "新增物理机[" + node.getName() + "]成功";
				logger.info("update physical server info {};\nmessage={}", updateResult, message);

				// 检查IPMI信息，若有误，则提示用户手动编辑IPMI信息
				IpmiInfo ipmiInfo = new IpmiInfo(
						node.getConfig().getIpmiAddress(),
						node.getConfig().getIpmiAccount(),
						node.getConfig().getIpmiPassword(),
						node.getConfig().getIpmiPort()
				);
				BocloudResult checkResult = workerService.check(ipmiInfo);
				if (checkResult.isFailed()) {
					logger.error("物理机{}的ipmi信息[{}]有误.", node.getName(), JSONObject.toJSONString(ipmiInfo));
					message += ", 但是物理机IPMI信息有误，请手动编辑IPMI信息";
				}

				// send node add successfully message to rabbitmq
				// 第一个true是发送站内信，第二个true是发送邮件
				NotifyMessage notifyMessage = new NotifyMessage(context, title, message, null, true, false);
				sendMessage(true, DISCOVER, notifyMessage, message, context);
			}

			return new BocloudResult(true, message);
		} catch (Exception e) {
			logger.error("处理节点发现失败:", e);
			return new BocloudResult(false, "处理节点发现失败");
		}
	}

	private BocloudResult raid(String rackhdTaskId, InstallResult installResult, JSONObject info,
			RequestContext context) {
		try {
			logger.info("handler raid, rackhdTaskId={},info={}", rackhdTaskId, info.toString());
			JSONObject data = info.getJSONObject(Constant.DATA);
			String status = data.getString(Constant.STATUS);

			Server server = installationServerService.get(installResult.getNodeId());
			if (server == null) {
				logger.error("请求cmp获取物理机信息失败");
				return new BocloudResult(false, "请求cmp获取物理机信息失败");
			}

			if (Constant.SUCCEEDED.equalsIgnoreCase(status)) {
				// raid配置成功
				installResult.setStatus(InstallStatus.RAID_SUCCEDD.getText());
				installResult.setGmtFinish(new Date());
				installResult.setResult(InstallStatus.RAID_SUCCEDD.getText());
				installResultRepository.update(installResult);

				// raid配置成功，下发装机任务
				InstallTask task = installTaskRepository.query(installResult.getTaskId());
				Options options = JSONObject.parseObject(task.getOptions(), Options.class);
				options.setRaidOptions(null);
				options.setNodes(Collections.singletonList(installResult.getNodeId()));
				BocloudResult result = installationServerService.install(options, task.getId(), context);

				if (result.isSuccess()) {
					// 主要这里需要更新机器的raid信息
					convert(server, server.getRackhdNodeId(), server.getIdentifier(),
							server.getConfig().getIpmiAddress());
					installationServerService.update(server, context);
					return new BocloudResult(true, "处理raid任务完成");
				} else {
					List<InstallResult> installResults = installResultRepository
							.queryByTaskId(installResult.getTaskId());
					List<InstallResult> osInstallResults = installResults.stream()
							.filter(o -> TaskType.OSINSTALL.name().equals(o.getTaskType()))
							.collect(Collectors.toList());
					// 检查install_result里面是否有OSINSTALL类型的失败任务，如果没有，保存进去
					if (osInstallResults.isEmpty()) {
						// 保存OS_INSTASLL类型的install_result
						InstallResult osResult = new InstallResult(server.getId(), installResult.getTaskId(),
								TaskType.OSINSTALL.name(), null,
								"物理机[" + server.getName() + "]安装[" + task.getOsType() + "]", InstallStatus.INSTALL_FAILED.getText(),
								result.getMessage(), installResult.getGmtStart(), new Date());
						installResult = installResultRepository.save(osResult);
					}

					// install任务执行失败
					server.setStatus(InstallStatus.INSTALL_FAILED.getText());
					server.setGmtModify(new Date());
					installationServerService.update(server, context);

					// 执行失败, 发送界面消息
					NotifyMessage notifyMessage = new NotifyMessage(context, "物理安装操作系统情况通知",
							"RAID配置成功后，下发装机任务失败：" + result.getMessage(), null, true, false);
					sendMessage(false, INSTALL, notifyMessage, "RAID配置成功后，下发装机任务失败：" + result.getMessage(), context);
					checkFinished(installResult);
					return new BocloudResult(false, "装机任务下发失败：" + result.getMessage());
				}
			}

			// 收集配置RAID失败的原因
			InstallStatus installStatus;
			InstallStatus taskStatus;
			String nodeResult;
			if (Constant.FAILED.equalsIgnoreCase(status)) {
				installStatus = InstallStatus.INSTALL_FAILED;
				taskStatus = InstallStatus.RAID_FAILED;
				nodeResult = getTaskError(rackhdTaskId);
			} else if (Constant.CANCELLED.equalsIgnoreCase(status)) {
				installStatus = InstallStatus.INSTALL_CANCELED;
				taskStatus = InstallStatus.RAID_CANCEL;
				nodeResult = InstallStatus.RAID_CANCEL.getText();
			} else if (Constant.TIMEOUT.equalsIgnoreCase(status)) {
				installStatus = InstallStatus.INSTALL_FAILED;
				taskStatus = InstallStatus.RAID_TIMEOUT;
				nodeResult = InstallStatus.RAID_TIMEOUT.getText();
			} else {
				installStatus = InstallStatus.INSTALL_FAILED;
				taskStatus = InstallStatus.RAID_FAILED;
				nodeResult = InstallStatus.RAID_FAILED.getText();
			}
			// RAID执行不成功，更新物理机状态为失败
			server.setStatus(installStatus.getText());
			server.setGmtModify(new Date());
			installationServerService.update(server, context);

			// 更新install_result状态
			installResult.setStatus(taskStatus.getText());
			installResult.setGmtFinish(new Date());
			installResult.setResult(nodeResult);
			installResultRepository.update(installResult);

			// 执行失败, 发送界面消息
			NotifyMessage notifyMessage = new NotifyMessage(context, "物理安装操作系统情况通知", nodeResult, null, true, false);
			sendMessage(false, INSTALL, notifyMessage, nodeResult, context);
			checkFinished(installResult);
			return new BocloudResult(true, "处理raid任务完成");
		} catch (Exception e) {
			logger.error("处理raid任务异常:", e);
			return new BocloudResult(false, "处理raid任务失败");
		}
	}

	private String getTaskError(String rackhdTaskId) {
		// 任务失败，查询具体task获取详细失败原因
		String nodeResult = null;
		String url = rackHdService.buildUrl(EndPoints.WORKFLOWS_V2, rackhdTaskId);
		Result result = rackHdService.request("", url, BoCloudMethod.GET);
		if (!result.isSuccess()) {
			logger.warn("获取task{}信息失败", rackhdTaskId);
			nodeResult = "获取task{" + rackhdTaskId + "}信息失败";
		} else {
			// 遍历rackhd执行的小任务，获取其中失败的信息
			JSONObject taskMap = JSON.parseObject(result.getMessage());
			List<JSONObject> tasks = JSON.parseArray(taskMap.getString(Constant.TASKS), JSONObject.class);
			for (JSONObject task : tasks) {
				String state = task.getString(Constant.STATE);
				// 寻找哪个小任务失败了，获取原因
				if (!Constant.SUCCEEDED.equalsIgnoreCase(state)) {
					nodeResult = state;
					if (task.containsKey(Constant.ERROR)) {
						nodeResult = task.getString(Constant.ERROR);
					}
					break;
				}
			}
		}
		return nodeResult;
	}

	/**
	 * @since 2018-05-30 modify by sxp 装机成功之后增加调用更新主机状态的接口
	 */
	private BocloudResult install(String rackhdTaskId, InstallResult installResult, JSONObject info,
			RequestContext context) {
		logger.info("handler os install, rackhdTaskId={},info={}", rackhdTaskId, info.toString());
		try {
			JSONObject data = info.getJSONObject(Constant.DATA);
			String status = data.getString(Constant.STATUS);
			String nodeResult;
			InstallStatus installStatus;
			boolean success;
			if (Constant.SUCCEEDED.equalsIgnoreCase(status)) {
				installStatus = InstallStatus.INSTALL_SUCCEED;
				nodeResult = InstallStatus.INSTALL_SUCCEED.getText();
                success = true;
			} else if (Constant.FAILED.equalsIgnoreCase(status)) {
				installStatus = InstallStatus.INSTALL_FAILED;
				nodeResult = getTaskError(rackhdTaskId);
                success = false;
			} else if (Constant.CANCELLED.equalsIgnoreCase(status)) {
				installStatus = InstallStatus.INSTALL_CANCELED;
				nodeResult = InstallStatus.INSTALL_CANCELED.getText();
				success = true;
			} else if (Constant.TIMEOUT.equalsIgnoreCase(status)) {
				installStatus = InstallStatus.INSTALL_FAILED;
				nodeResult = "装机任务执行超时";
                success = false;
			} else {
				installStatus = InstallStatus.INSTALL_FAILED;
				nodeResult = "装机任务执行失败";
                success = false;
			}
			installResult.setStatus(installStatus.getText());
			installResult.setGmtFinish(new Date());
			installResult.setResult(nodeResult);
			installResultRepository.update(installResult);

			// 更新节点装机状态
			Server node = installationServerService.get(installResult.getNodeId());
			if (node == null) {
				logger.error("请求cmp获取物理机信息失败");
				return new BocloudResult(false, "请求cmp获取物理机信息失败");
			}
			node.setStatus(installStatus.getText());
			node.setGmtModify(new Date());
			if (!InstallStatus.INSTALL_SUCCEED.equals(installStatus)) {
				// 装机失败
				logger.error("物理机[{},id={}]安装{}失败{}", node.getName(), node.getId(), node.getOs(), nodeResult);
			} else {
				// 更新dhcpIp
				String dhcpIp = catalogService.dhcpIp(node.getIdentifier(), false);
				if (dhcpIp != null && !dhcpIp.equals(node.getManageIp())) {
					node.setManageIp(dhcpIp);
					if (node.getNetcards() != null) {
						for (NetworkCard netCard : node.getNetcards()) {
							if (node.getIdentifier().equalsIgnoreCase(netCard.getMac())) {
								netCard.setIpAddress(dhcpIp);
								break;
							}
						}
					}
				}
				// 装机成功后，更新osName跟osVersion
				InstallTask installTask = installTaskRepository.query(installResult.getTaskId());
				node.getConfig().setOsName(installTask.getOsType());
				node.getConfig().setOsVersion(installTask.getOsVersion());
				Options options = JSONObject.parseObject(installTask.getOptions(), Options.class);
				node.setHostname(options.getInstallOptions().getHostname());
				node.setOs(installTask.getOsType());
				if (options.getInstallOptions().getRootPassword() != null) {
					node.getConfig().setUsername("root");
					node.getConfig().setPassword(options.getInstallOptions().getRootPassword());
				}
				if (options.getInstallOptions().getPassword() != null) {
					// windows
					node.getConfig().setUsername(options.getInstallOptions().getUsername());
					node.getConfig().setPassword(options.getInstallOptions().getPassword());
				}
				if (InstallImageType.ESXI.name().equalsIgnoreCase(installTask.getOsType())) {
					node.setAssetCategory(ServerAssetCategory.HYPERVISOR.name());
				} else {
					node.setAssetCategory(ServerAssetCategory.X86_SERVER.name());
				}

				// 更新网卡ip地址
				if (options.getInstallOptions() != null && options.getInstallOptions().getNetworkDevices() != null) {
					for (NetworkDevice networkDevice : options.getInstallOptions().getNetworkDevices()) {
						if (node.getNetcards() != null) {
							for (NetworkCard netCard : node.getNetcards()) {
								if (netCard.getNumber().equals(networkDevice.getNumber())) {
									if (networkDevice.getIpv4() == null && networkDevice.getIpv6() == null) {
										continue;
									}
									String ip = networkDevice.getIpv4() != null ? networkDevice.getIpv4().getIpAddr()
											: networkDevice.getIpv6().getIpAddr();
									logger.info("装机成功, 更新物理机{}网卡{}的ip为{}.", node.getName(), netCard.getNumber(), ip);
									netCard.setIpAddress(ip);
									break;
								}
							}
						}
					}
				}
			}
			installationServerService.update(node, context);

			// send node install os successfully message to rabbitmq
			String message = "物理机[" + node.getName() + "]安装" + node.getOs() + installStatus.getText() + ", 结果:"
					+ nodeResult;
			NotifyMessage notifyMessage = new NotifyMessage(context, "物理安装操作系统情况通知", message, null, true, false);
			sendMessage(success, INSTALL, notifyMessage, message, context);
			checkFinished(installResult);
			return new BocloudResult(true, "处理任务成功");
		} catch (Exception e) {
			logger.error("处理任务失败:", e);
			return new BocloudResult(false, "处理任务失败");
		}
	}

	private void checkFinished(InstallResult installResult) {
		try {
			// 检查install_task
			List<InstallResult> results = installResultRepository.queryByTaskId(installResult.getTaskId());
			String error = null;
			boolean success = true;
			for (InstallResult result : results) {
				if (InstallStatus.INSTALL_FAILED.getText().equals(result.getStatus())
						|| InstallStatus.INSTALL_CANCELED.getText().equals(result.getStatus())) {
					success = false;
					error = result.getResult();
					break;
				}
			}
			// 检查任务是否结束
			InstallTask task = installTaskRepository.query(installResult.getTaskId());
			Options options = JSONObject.parseObject(task.getOptions(), Options.class);
			if (this.installResultRepository.taskFinished(installResult.getTaskId(), options.getNodes())) {
				// 更新install_task表
				task.setGmtFinish(new Date());
				task.setStatus(success ? InstallStatus.INSTALL_SUCCEED.getText() : InstallStatus.INSTALL_FAILED.getText());
				task.setResult(error);
				installTaskRepository.update(task);
			}
		} catch (Exception e) {
			logger.error("检查任务执行结束异常:", e);
		}
	}

	@Override
    public BocloudResult task(JSONObject info) {
		try {
			logger.info("handler task info:{}", info.toString());
			String rackhdTaskId = info.getString(Constant.TYPE_ID);
			String rackhdNodeId = info.getString(Constant.NODE_ID);
			String dataString = info.getString(Constant.DATA);
			JSONObject data = JSONObject.parseObject(dataString, JSONObject.class);
			if (BMC_TASK.equals(data.getString(Constant.GRAPH_NAME))) {
			    BocloudResult getResult = installationServerService.get(JSON.toJSONString(Collections.singletonMap("rackhdNodeId", rackhdNodeId)));
				if (getResult.isFailed()) {
					logger.error("请求cmp获取物理机信息失败");
					return new BocloudResult(false, "请求cmp获取物理机信息失败");
				}
				InstallResult installResult = installResultRepository.query(rackhdTaskId);
				InstallTask installTask = installResult == null ? null : installTaskRepository.query(installResult.getTaskId());
				RequestContext context = new RequestContext();
				context.setTarget(installTask == null ? 1L : installTask.getUserId());
				context.setCatalog(RequestContext.Catalog.Manager);

				Server node = (Server) getResult.getData();
				if (!StringUtils.isNotEmpty(node.getConfig().getIpmiAddress())
						|| !StringUtils.isNotEmpty(node.getConfig().getIpmiAccount())
						|| !StringUtils.isNotEmpty(node.getConfig().getIpmiPassword())) {
					logger.info("自动设置物理机[{}]的IPMI密码失败", node.getName());
					discoverFail(new BocloudResult(false, "物理机[" + node.getName() + "]ipmi信息异常，请编辑ipmi信息"),
							"物理主机新增情况通知", "物理机[" + node.getName() + "]ipmi信息异常，请编辑ipmi信息", context);
					return new BocloudResult(false, "物理机[" + node.getName() + "]ipmi信息异常，请编辑ipmi信息");
				}
				// 自动设置ipmi的密码
				IpmiInfo ipmiInfo = new IpmiInfo(node.getId(),
						node.getConfig().getIpmiAddress(),
						node.getConfig().getIpmiAccount(),
						node.getConfig().getIpmiPassword(),
						node.getConfig().getIpmiPort()
				);
				BocloudResult result = installationServerService.setIpmiInfo(ipmiInfo, context);
				if (result.isSuccess()) {
					logger.info("自动设置物理机[{}]的IPMI密码成功", node.getName());
					return new BocloudResult(true, "自动设置物理机[" + node.getName() + "]的IPMI密码成功");
				} else {
					String content = "自动设置[" + node.getName() + "]的IPMI密码失败,请手动编辑该节点的IPMI信息";
					discoverFail(result, "物理主机新增情况通知", content, context);
					return new BocloudResult(false, content);
				}
			}
			// 过滤非bsm的任务
			InstallResult installResult = installResultRepository.query(rackhdTaskId);
			if (installResult == null) {
				logger.warn("不是cmp的任务结果，跳过");
				return new BocloudResult(true, "非cmp任务，忽略");
			}
			InstallTask installTask = installTaskRepository.query(installResult.getTaskId());
			if (installTask == null) {
				logger.error("未找到id为{}的InstallTask", installResult.getTaskId());
				return new BocloudResult(false, "未找到InstallTask");
			}
			RequestContext context = new RequestContext();
			context.setTarget(installTask.getUserId());
			context.setCatalog(RequestContext.Catalog.Manager);
			if (TaskType.OSINSTALL.name().equals(installResult.getTaskType())) {
				return install(rackhdTaskId, installResult, info, context);
			} else if (TaskType.RAID.name().equals(installResult.getTaskType())) {
				return raid(rackhdTaskId, installResult, info, context);
			} else {
				logger.warn("不支持的任务类型" + installResult.getTaskType());
				return new BocloudResult(false, "不支持的任务类型" + installResult.getTaskType());
			}
		} catch (Exception e) {
			logger.error("处理任务失败:", e);
			return new BocloudResult(false, "处理任务失败");
		}
	}
}
