// 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. // // Automatically generated by addcopyright.py at 01/29/2013 // Apache License, Version 2.0 (the "License"); you may not use this // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // // Automatically generated by addcopyright.py at 04/03/2012 package com.cloud.baremetal.networkservice; import com.cloud.agent.IAgentControl; import com.cloud.agent.api.Answer; import com.cloud.agent.api.CheckNetworkAnswer; import com.cloud.agent.api.CheckNetworkCommand; import com.cloud.agent.api.CheckVirtualMachineAnswer; import com.cloud.agent.api.CheckVirtualMachineCommand; import com.cloud.agent.api.Command; import com.cloud.agent.api.HostVmStateReportEntry; import com.cloud.agent.api.MaintainAnswer; import com.cloud.agent.api.MaintainCommand; import com.cloud.agent.api.MigrateAnswer; import com.cloud.agent.api.MigrateCommand; import com.cloud.agent.api.PingCommand; import com.cloud.agent.api.PingRoutingCommand; import com.cloud.agent.api.PrepareForMigrationAnswer; import com.cloud.agent.api.PrepareForMigrationCommand; import com.cloud.agent.api.ReadyAnswer; import com.cloud.agent.api.ReadyCommand; import com.cloud.agent.api.RebootAnswer; import com.cloud.agent.api.RebootCommand; import com.cloud.agent.api.SecurityGroupRulesCmd; import com.cloud.agent.api.StartAnswer; import com.cloud.agent.api.StartCommand; import com.cloud.agent.api.StartupCommand; import com.cloud.agent.api.StartupRoutingCommand; import com.cloud.agent.api.StopAnswer; import com.cloud.agent.api.StopCommand; import com.cloud.agent.api.baremetal.IpmISetBootDevCommand; import com.cloud.agent.api.baremetal.IpmISetBootDevCommand.BootDev; import com.cloud.agent.api.baremetal.IpmiBootorResetCommand; import com.cloud.agent.api.to.VirtualMachineTO; import com.cloud.baremetal.manager.BaremetalManager; import com.cloud.configuration.Config; import com.cloud.host.Host.Type; import com.cloud.hypervisor.Hypervisor; import com.cloud.resource.ServerResource; import com.cloud.utils.component.ManagerBase; import com.cloud.utils.db.QueryBuilder; import com.cloud.utils.db.SearchCriteria; import com.cloud.utils.exception.CloudRuntimeException; import com.cloud.utils.script.OutputInterpreter; import com.cloud.utils.script.Script; import com.cloud.utils.script.Script2; import com.cloud.utils.script.Script2.ParamType; import com.cloud.vm.VMInstanceVO; import com.cloud.vm.VirtualMachine; import com.cloud.vm.VirtualMachine.PowerState; import com.cloud.vm.dao.VMInstanceDao; import org.apache.cloudstack.api.ApiConstants; import org.apache.cloudstack.framework.config.dao.ConfigurationDao; import org.apache.log4j.Logger; import javax.naming.ConfigurationException; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.concurrent.TimeUnit; public class BareMetalResourceBase extends ManagerBase implements ServerResource { private static final Logger s_logger = Logger.getLogger(BareMetalResourceBase.class); protected String _uuid; protected String _zone; protected String _pod; protected Long hostId; protected String _cluster; protected long _memCapacity; protected long _cpuCapacity; protected long _cpuNum; protected String _mac; protected String _username; protected String _password; protected String _ip; protected boolean _isEchoScAgent; protected IAgentControl _agentControl; protected Script2 _pingCommand; protected Script2 _setPxeBootCommand; protected Script2 _setDiskBootCommand; protected Script2 _rebootCommand; protected Script2 _getStatusCommand; protected Script2 _powerOnCommand; protected Script2 _powerOffCommand; protected Script2 _forcePowerOffCommand; protected Script2 _bootOrRebootCommand; protected String _vmName; protected int ipmiRetryTimes = 5; protected boolean provisionDoneNotificationOn = false; protected int isProvisionDoneNotificationTimeout = 1800; protected ConfigurationDao configDao; protected VMInstanceDao vmDao; @Override public boolean configure(String name, Map<String, Object> params) throws ConfigurationException { setName(name); _uuid = (String) params.get("guid"); try { _memCapacity = Long.parseLong((String) params.get(ApiConstants.MEMORY)) * 1024L * 1024L; _cpuCapacity = Long.parseLong((String) params.get(ApiConstants.CPU_SPEED)); _cpuNum = Long.parseLong((String) params.get(ApiConstants.CPU_NUMBER)); } catch (NumberFormatException e) { throw new ConfigurationException(String.format("Unable to parse number of CPU or memory capacity " + "or cpu capacity(cpu number = %1$s memCapacity=%2$s, cpuCapacity=%3$s", params.get(ApiConstants.CPU_NUMBER), params.get(ApiConstants.MEMORY), params.get(ApiConstants.CPU_SPEED))); } _zone = (String) params.get("zone"); _pod = (String) params.get("pod"); _cluster = (String) params.get("cluster"); hostId = (Long) params.get("hostId"); _ip = (String) params.get(ApiConstants.PRIVATE_IP); _mac = (String) params.get(ApiConstants.HOST_MAC); _username = (String) params.get(ApiConstants.USERNAME); _password = (String) params.get(ApiConstants.PASSWORD); _vmName = (String) params.get("vmName"); String echoScAgent = (String) params.get(BaremetalManager.EchoSecurityGroupAgent); vmDao = (VMInstanceDao) params.get("vmDao"); configDao = (ConfigurationDao) params.get("configDao"); if (_pod == null) { throw new ConfigurationException("Unable to get the pod"); } if (_cluster == null) { throw new ConfigurationException("Unable to get the pod"); } if (_ip == null) { throw new ConfigurationException("Unable to get the host address"); } if (_mac.equalsIgnoreCase("unknown")) { throw new ConfigurationException("Unable to get the host mac address"); } if (_mac.split(":").length != 6) { throw new ConfigurationException("Wrong MAC format(" + _mac + "). It must be in format of for example 00:11:ba:33:aa:dd which is not case sensitive"); } if (_uuid == null) { throw new ConfigurationException("Unable to get the uuid"); } if (echoScAgent != null) { _isEchoScAgent = Boolean.valueOf(echoScAgent); } String ipmiIface = "default"; try { ipmiIface = configDao.getValue(Config.BaremetalIpmiLanInterface.key()); } catch (Exception e) { s_logger.debug(e.getMessage(), e); } try { ipmiRetryTimes = Integer.parseInt(configDao.getValue(Config.BaremetalIpmiRetryTimes.key())); } catch (Exception e) { s_logger.debug(e.getMessage(), e); } try { provisionDoneNotificationOn = Boolean.valueOf(configDao.getValue(Config.BaremetalProvisionDoneNotificationEnabled.key())); isProvisionDoneNotificationTimeout = Integer.parseInt(configDao.getValue(Config.BaremetalProvisionDoneNotificationTimeout.key())); } catch (Exception e) { s_logger.debug(e.getMessage(), e); } String injectScript = "scripts/util/ipmi.py"; String scriptPath = Script.findScript("", injectScript); if (scriptPath == null) { throw new ConfigurationException("Cannot find ping script " + scriptPath); } String pythonPath = "/usr/bin/python"; _pingCommand = new Script2(pythonPath, s_logger); _pingCommand.add(scriptPath); _pingCommand.add("ping"); _pingCommand.add("interface=" + ipmiIface); _pingCommand.add("hostname=" + _ip); _pingCommand.add("usrname=" + _username); _pingCommand.add("password=" + _password, ParamType.PASSWORD); _setPxeBootCommand = new Script2(pythonPath, s_logger); _setPxeBootCommand.add(scriptPath); _setPxeBootCommand.add("boot_dev"); _setPxeBootCommand.add("interface=" + ipmiIface); _setPxeBootCommand.add("hostname=" + _ip); _setPxeBootCommand.add("usrname=" + _username); _setPxeBootCommand.add("password=" + _password, ParamType.PASSWORD); _setPxeBootCommand.add("dev=pxe"); _setDiskBootCommand = new Script2(pythonPath, s_logger); _setDiskBootCommand.add(scriptPath); _setDiskBootCommand.add("boot_dev"); _setDiskBootCommand.add("interface=" + ipmiIface); _setDiskBootCommand.add("hostname=" + _ip); _setDiskBootCommand.add("usrname=" + _username); _setDiskBootCommand.add("password=" + _password, ParamType.PASSWORD); _setDiskBootCommand.add("dev=disk"); _rebootCommand = new Script2(pythonPath, s_logger); _rebootCommand.add(scriptPath); _rebootCommand.add("reboot"); _rebootCommand.add("interface=" + ipmiIface); _rebootCommand.add("hostname=" + _ip); _rebootCommand.add("usrname=" + _username); _rebootCommand.add("password=" + _password, ParamType.PASSWORD); _getStatusCommand = new Script2(pythonPath, s_logger); _getStatusCommand.add(scriptPath); _getStatusCommand.add("ping"); _getStatusCommand.add("interface=" + ipmiIface); _getStatusCommand.add("hostname=" + _ip); _getStatusCommand.add("usrname=" + _username); _getStatusCommand.add("password=" + _password, ParamType.PASSWORD); _powerOnCommand = new Script2(pythonPath, s_logger); _powerOnCommand.add(scriptPath); _powerOnCommand.add("power"); _powerOnCommand.add("interface=" + ipmiIface); _powerOnCommand.add("hostname=" + _ip); _powerOnCommand.add("usrname=" + _username); _powerOnCommand.add("password=" + _password, ParamType.PASSWORD); _powerOnCommand.add("action=on"); _powerOffCommand = new Script2(pythonPath, s_logger); _powerOffCommand.add(scriptPath); _powerOffCommand.add("power"); _powerOffCommand.add("interface=" + ipmiIface); _powerOffCommand.add("hostname=" + _ip); _powerOffCommand.add("usrname=" + _username); _powerOffCommand.add("password=" + _password, ParamType.PASSWORD); _powerOffCommand.add("action=soft"); _forcePowerOffCommand = new Script2(pythonPath, s_logger); _forcePowerOffCommand.add(scriptPath); _forcePowerOffCommand.add("power"); _forcePowerOffCommand.add("interface=" + ipmiIface); _forcePowerOffCommand.add("hostname=" + _ip); _forcePowerOffCommand.add("usrname=" + _username); _forcePowerOffCommand.add("password=" + _password, ParamType.PASSWORD); _forcePowerOffCommand.add("action=off"); _bootOrRebootCommand = new Script2(pythonPath, s_logger); _bootOrRebootCommand.add(scriptPath); _bootOrRebootCommand.add("boot_or_reboot"); _bootOrRebootCommand.add("interface=" + ipmiIface); _bootOrRebootCommand.add("hostname=" + _ip); _bootOrRebootCommand.add("usrname=" + _username); _bootOrRebootCommand.add("password=" + _password, ParamType.PASSWORD); return true; } protected boolean doScript(Script cmd) { return doScript(cmd, null); } protected boolean doScript(Script cmd, int retry) { return doScript(cmd, null, retry); } protected boolean doScript(Script cmd, OutputInterpreter interpreter) { return doScript(cmd, interpreter, ipmiRetryTimes); } protected boolean doScript(Script cmd, OutputInterpreter interpreter, int retry) { String res = null; while (retry-- > 0) { if (interpreter == null) { res = cmd.execute(); } else { res = cmd.execute(interpreter); } if (res != null && res.startsWith("Error: Unable to establish LAN")) { s_logger.warn("IPMI script timeout(" + cmd.toString() + "), will retry " + retry + " times"); try { TimeUnit.SECONDS.sleep(1); } catch (InterruptedException e) { s_logger.debug("[ignored] interupted while waiting to retry running script."); } continue; } else if (res == null) { return true; } else { break; } } s_logger.warn("IPMI Scirpt failed due to " + res + "(" + cmd.toString() + ")"); return false; } @Override public boolean start() { return true; } @Override public boolean stop() { return true; } @Override public Type getType() { return com.cloud.host.Host.Type.Routing; } protected Map<String, HostVmStateReportEntry> getHostVmStateReport() { Map<String, HostVmStateReportEntry> states = new HashMap<String, HostVmStateReportEntry>(); if (hostId != null) { final List<? extends VMInstanceVO> vms = vmDao.listByHostId(hostId); for (VMInstanceVO vm : vms) { states.put( vm.getInstanceName(), new HostVmStateReportEntry( vm.getPowerState(), "host-" + hostId ) ); } } return states; } @Override public StartupCommand[] initialize() { StartupRoutingCommand cmd = new StartupRoutingCommand(0, 0, 0, 0, null, Hypervisor.HypervisorType.BareMetal, new HashMap<String, String>()); cmd.setDataCenter(_zone); cmd.setPod(_pod); cmd.setCluster(_cluster); cmd.setGuid(_uuid); cmd.setName(_ip); cmd.setPrivateIpAddress(_ip); cmd.setStorageIpAddress(_ip); cmd.setVersion(BareMetalResourceBase.class.getPackage().getImplementationVersion()); cmd.setCpus((int) _cpuNum); cmd.setSpeed(_cpuCapacity); cmd.setMemory(_memCapacity); cmd.setPrivateMacAddress(_mac); cmd.setPublicMacAddress(_mac); return new StartupCommand[] { cmd }; } private boolean ipmiPing() { return doScript(_pingCommand); } @Override public PingCommand getCurrentStatus(long id) { try { if (!ipmiPing()) { Thread.sleep(1000); if (!ipmiPing()) { s_logger.warn("Cannot ping ipmi nic " + _ip); return null; } } } catch (Exception e) { s_logger.debug("Cannot ping ipmi nic " + _ip, e); return null; } return new PingRoutingCommand(getType(), id, null); /* if (hostId != null) { final List<? extends VMInstanceVO> vms = vmDao.listByHostId(hostId); if (vms.isEmpty()) { return new PingRoutingCommand(getType(), id, null); } else { VMInstanceVO vm = vms.get(0); SecurityGroupHttpClient client = new SecurityGroupHttpClient(); HashMap<String, Pair<Long, Long>> nwGrpStates = client.sync(vm.getInstanceName(), vm.getId(), vm.getPrivateIpAddress()); return new PingRoutingWithNwGroupsCommand(getType(), id, null, nwGrpStates); } } else { return new PingRoutingCommand(getType(), id, null); } */ } protected Answer execute(IpmISetBootDevCommand cmd) { Script bootCmd = null; if (cmd.getBootDev() == BootDev.disk) { bootCmd = _setDiskBootCommand; } else if (cmd.getBootDev() == BootDev.pxe) { bootCmd = _setPxeBootCommand; } else { throw new CloudRuntimeException("Unkonwn boot dev " + cmd.getBootDev()); } String bootDev = cmd.getBootDev().name(); if (!doScript(bootCmd)) { s_logger.warn("Set " + _ip + " boot dev to " + bootDev + "failed"); return new Answer(cmd, false, "Set " + _ip + " boot dev to " + bootDev + "failed"); } s_logger.warn("Set " + _ip + " boot dev to " + bootDev + "Success"); return new Answer(cmd, true, "Set " + _ip + " boot dev to " + bootDev + "Success"); } protected MaintainAnswer execute(MaintainCommand cmd) { return new MaintainAnswer(cmd, false); } protected PrepareForMigrationAnswer execute(PrepareForMigrationCommand cmd) { return new PrepareForMigrationAnswer(cmd); } protected MigrateAnswer execute(MigrateCommand cmd) { if (!doScript(_powerOffCommand)) { return new MigrateAnswer(cmd, false, "IPMI power off failed", null); } return new MigrateAnswer(cmd, true, "success", null); } protected CheckVirtualMachineAnswer execute(final CheckVirtualMachineCommand cmd) { return new CheckVirtualMachineAnswer(cmd, PowerState.PowerOff, null); } protected Answer execute(IpmiBootorResetCommand cmd) { if (!doScript(_bootOrRebootCommand)) { return new Answer(cmd, false, "IPMI boot or reboot failed"); } return new Answer(cmd, true, "Success"); } protected CheckNetworkAnswer execute(CheckNetworkCommand cmd) { return new CheckNetworkAnswer(cmd, true, "Success"); } protected Answer execute(SecurityGroupRulesCmd cmd) { SecurityGroupHttpClient hc = new SecurityGroupHttpClient(); return hc.call(cmd.getGuestIp(), cmd); } @Override public Answer executeRequest(Command cmd) { try { if (cmd instanceof ReadyCommand) { return execute((ReadyCommand) cmd); } else if (cmd instanceof StartCommand) { return execute((StartCommand) cmd); } else if (cmd instanceof StopCommand) { return execute((StopCommand) cmd); } else if (cmd instanceof RebootCommand) { return execute((RebootCommand) cmd); } else if (cmd instanceof IpmISetBootDevCommand) { return execute((IpmISetBootDevCommand) cmd); } else if (cmd instanceof MaintainCommand) { return execute((MaintainCommand) cmd); } else if (cmd instanceof PrepareForMigrationCommand) { return execute((PrepareForMigrationCommand) cmd); } else if (cmd instanceof MigrateCommand) { return execute((MigrateCommand) cmd); } else if (cmd instanceof CheckVirtualMachineCommand) { return execute((CheckVirtualMachineCommand) cmd); } else if (cmd instanceof IpmiBootorResetCommand) { return execute((IpmiBootorResetCommand) cmd); } else if (cmd instanceof SecurityGroupRulesCmd) { return execute((SecurityGroupRulesCmd) cmd); } else if (cmd instanceof CheckNetworkCommand) { return execute((CheckNetworkCommand) cmd); } else { return Answer.createUnsupportedCommandAnswer(cmd); } } catch (Throwable t) { s_logger.debug(t.getMessage(), t); return new Answer(cmd, false, t.getMessage()); } } protected boolean isPowerOn(String str) { if (str.startsWith("Chassis Power is on")) { return true; } else if (str.startsWith("Chassis Power is off")) { return false; } else { throw new CloudRuntimeException("Cannot parse IPMI power status " + str); } } protected RebootAnswer execute(final RebootCommand cmd) { String infoStr = "Command not supported in present state"; OutputInterpreter.AllLinesParser interpreter = new OutputInterpreter.AllLinesParser(); if (!doScript(_rebootCommand, interpreter, 10)) { if (interpreter.getLines().contains(infoStr)) { // try again, this error should be temporary if (!doScript(_rebootCommand, interpreter, 10)) { return new RebootAnswer(cmd, "IPMI reboot failed", false); } } else { return new RebootAnswer(cmd, "IPMI reboot failed", false); } } return new RebootAnswer(cmd, "reboot succeeded", true); } protected StopAnswer execute(final StopCommand cmd) { boolean success = false; int count = 0; Script powerOff = _powerOffCommand; while (count < 10) { if (!doScript(powerOff)) { break; } try { Thread.sleep(1000); } catch (InterruptedException e) { break; } OutputInterpreter.AllLinesParser interpreter = new OutputInterpreter.AllLinesParser(); if (!doScript(_getStatusCommand, interpreter)) { success = true; s_logger.warn("Cannot get power status of " + getName() + ", assume VM state changed successfully"); break; } if (!isPowerOn(interpreter.getLines())) { success = true; break; } else { powerOff = _forcePowerOffCommand; } count++; } return success ? new StopAnswer(cmd, "Success", true) : new StopAnswer(cmd, "IPMI power off failed", false); } protected StartAnswer execute(StartCommand cmd) { VirtualMachineTO vm = cmd.getVirtualMachine(); OutputInterpreter.AllLinesParser interpreter = new OutputInterpreter.AllLinesParser(); if (!doScript(_getStatusCommand, interpreter)) { return new StartAnswer(cmd, "Cannot get current power status of " + getName()); } if (isPowerOn(interpreter.getLines())) { if (!doScript(_rebootCommand)) { return new StartAnswer(cmd, "IPMI reboot failed"); } } else { if (!doScript(_powerOnCommand)) { return new StartAnswer(cmd, "IPMI power on failed"); } } if (_isEchoScAgent) { SecurityGroupHttpClient hc = new SecurityGroupHttpClient(); boolean echoRet = hc.echo(vm.getNics()[0].getIp(), TimeUnit.MINUTES.toMillis(30), TimeUnit.MINUTES.toMillis(1)); if (!echoRet) { return new StartAnswer(cmd, String.format("Call security group agent on vm[%s] timeout", vm.getNics()[0].getIp())); } } if (provisionDoneNotificationOn) { QueryBuilder<VMInstanceVO> q = QueryBuilder.create(VMInstanceVO.class); q.and(q.entity().getInstanceName(), SearchCriteria.Op.EQ, vm.getName()); VMInstanceVO vmvo = q.find(); if (vmvo.getLastHostId() == null) { // this is new created vm long timeout = System.currentTimeMillis() + TimeUnit.SECONDS.toMillis(isProvisionDoneNotificationTimeout); while (timeout > System.currentTimeMillis()) { try { TimeUnit.SECONDS.sleep(5); } catch (InterruptedException e) { s_logger.warn(e.getMessage(), e); } q = QueryBuilder.create(VMInstanceVO.class); q.and(q.entity().getInstanceName(), SearchCriteria.Op.EQ, vm.getName()); vmvo = q.find(); if (vmvo == null) { return new StartAnswer(cmd, String.format("cannot find vm[name:%s] while waiting for baremtal provision done notification", vm.getName())); } if (VirtualMachine.State.Running == vmvo.getState()) { return new StartAnswer(cmd); } s_logger.debug(String.format("still wait for baremetal provision done notification for vm[name:%s], current vm state is %s", vmvo.getInstanceName(), vmvo.getState())); } return new StartAnswer(cmd, String.format("timeout after %s seconds, no baremetal provision done notification received. vm[name:%s] failed to start", isProvisionDoneNotificationTimeout, vm.getName())); } } s_logger.debug("Start bare metal vm " + vm.getName() + "successfully"); _vmName = vm.getName(); return new StartAnswer(cmd); } protected ReadyAnswer execute(ReadyCommand cmd) { // derived resource should check if the PXE server is ready s_logger.debug("Bare metal resource " + getName() + " is ready"); return new ReadyAnswer(cmd); } @Override public void disconnected() { } @Override public IAgentControl getAgentControl() { return _agentControl; } @Override public void setAgentControl(IAgentControl agentControl) { _agentControl = agentControl; } }