Bug 8208 - bare metal provisioning

Successfully add start entry into LinMin PXE server
This commit is contained in:
Frank 2011-02-25 18:58:07 -08:00
parent 5035778b1a
commit a9728998ff
19 changed files with 969 additions and 97 deletions

View File

@ -0,0 +1,9 @@
package com.cloud.agent.api;
import com.cloud.host.Host;
public class StartupPxeServerCommand extends StartupCommand {
public StartupPxeServerCommand() {
super(Host.Type.PxeServer);
}
}

View File

@ -0,0 +1,14 @@
package com.cloud.agent.api.baremetal;
import com.cloud.agent.api.Answer;
public class PrepareLinMinPxeServerAnswer extends Answer {
public PrepareLinMinPxeServerAnswer(PrepareLinMinPxeServerCommand cmd) {
super(cmd, true, "SUCCESS");
}
public PrepareLinMinPxeServerAnswer(PrepareLinMinPxeServerCommand cmd, String details) {
super(cmd, false, details);
}
}

View File

@ -0,0 +1,63 @@
package com.cloud.agent.api.baremetal;
import com.cloud.agent.api.Command;
public class PrepareLinMinPxeServerCommand extends Command {
String ip;
String mac;
String netMask;
String gateway;
String dns;
String template;
String vmName;
String hostName;
@Override
public boolean executeInSequence() {
return true;
}
public PrepareLinMinPxeServerCommand(String ip, String mac, String netMask, String gateway, String dns, String template, String vmName, String hostName) {
this.ip = ip;
this.mac = mac;
this.netMask = netMask;
this.gateway = gateway;
this.dns = dns;
this.template = template;
this.vmName = vmName;
this.hostName = hostName;
}
public String getIp() {
return ip;
}
public String getMac() {
return mac;
}
public String getNetMask() {
return netMask;
}
public String getGateWay() {
return gateway;
}
public String getDns() {
return dns;
}
public String getTemplate() {
return template;
}
public String getVmName() {
return vmName;
}
public String getHostName() {
return hostName;
}
}

View File

@ -194,5 +194,7 @@ public class ApiConstants {
public static final String HOST_CPU_NUM = "hostcpunum";
public static final String HOST_MEM_CAPACITY = "hostmemcapacity";
public static final String HOST_MAC = "hostmac";
public static final String PXE_SERVER_TYPE = "pxeservertype";
}

View File

@ -32,7 +32,8 @@ public interface Host {
SecondaryStorage(false),
ConsoleProxy(true),
ExternalFirewall(false),
ExternalLoadBalancer(false);
ExternalLoadBalancer(false),
PxeServer(false);
boolean _virtual;
private Type(boolean virtual) {

View File

@ -47,7 +47,8 @@ public interface VirtualMachineProfile<T extends VirtualMachine> {
public static final Param VmPassword = new Param("VmPassword");
public static final Param ControlNic = new Param("ControlNic");
public static final Param RestartNetwork = new Param("RestartNetwork");
public static final Param PxeSeverType = new Param("PxeSeverType");
private String name;
public Param(String name) {

View File

@ -63,6 +63,7 @@ import com.cloud.agent.api.StartupCommand;
import com.cloud.agent.api.StartupExternalFirewallCommand;
import com.cloud.agent.api.StartupExternalLoadBalancerCommand;
import com.cloud.agent.api.StartupProxyCommand;
import com.cloud.agent.api.StartupPxeServerCommand;
import com.cloud.agent.api.StartupRoutingCommand;
import com.cloud.agent.api.StartupStorageCommand;
import com.cloud.agent.api.UnsupportedAnswer;
@ -2363,6 +2364,8 @@ public class AgentManagerImpl implements AgentManager, HandlerFactory,
type = Host.Type.ExternalFirewall;
} else if (startup instanceof StartupExternalLoadBalancerCommand) {
type = Host.Type.ExternalLoadBalancer;
} else if (startup instanceof StartupPxeServerCommand) {
type = Host.Type.PxeServer;
} else {
assert false : "Did someone add a new Startup command?";
}
@ -2617,7 +2620,8 @@ public class AgentManagerImpl implements AgentManager, HandlerFactory,
if (p == null) {
if (type != Host.Type.SecondaryStorage
&& type != Host.Type.ExternalFirewall
&& type != Host.Type.ExternalLoadBalancer) {
&& type != Host.Type.ExternalLoadBalancer
&& type != Host.Type.PxeServer) {
/*
* s_logger.info("Unable to find the pod so we are creating one."

View File

@ -0,0 +1,103 @@
package com.cloud.api;
import org.apache.log4j.Logger;
import com.cloud.api.ApiConstants;
import com.cloud.api.BaseCmd;
import com.cloud.api.Implementation;
import com.cloud.api.Parameter;
import com.cloud.api.ServerApiException;
import com.cloud.api.response.PxeServerResponse;
import com.cloud.baremetal.LinMinPxeServerManager;
import com.cloud.baremetal.PxeServerManager;
import com.cloud.baremetal.PxeServerManager.PxeServerType;
import com.cloud.exception.ConcurrentOperationException;
import com.cloud.exception.InsufficientCapacityException;
import com.cloud.exception.InvalidParameterValueException;
import com.cloud.exception.ResourceAllocationException;
import com.cloud.exception.ResourceUnavailableException;
import com.cloud.host.Host;
import com.cloud.server.ManagementService;
import com.cloud.utils.component.ComponentLocator;
import com.cloud.utils.exception.CloudRuntimeException;
@Implementation(description="Adds a PXE server appliance", responseObject = PxeServerResponse.class)
public class AddPxeServerCmd extends BaseCmd {
public static final Logger s_logger = Logger.getLogger(AddPxeServerCmd.class.getName());
private static final String s_name = "addpxeserverresponse";
/////////////////////////////////////////////////////
//////////////// API parameters /////////////////////
/////////////////////////////////////////////////////
@Parameter(name=ApiConstants.ZONE_ID, type=CommandType.LONG, required = true, description="Zone in which to add the external firewall appliance.")
private Long zoneId;
@Parameter(name=ApiConstants.URL, type=CommandType.STRING, required = true, description="URL of the PXE server appliance.")
private String url;
@Parameter(name=ApiConstants.USERNAME, type=CommandType.STRING, required = true, description="Username of PXE server appliance.")
private String username;
@Parameter(name=ApiConstants.PASSWORD, type=CommandType.STRING, required = true, description="Password of the PXE server appliance.")
private String password;
@Parameter(name=ApiConstants.PXE_SERVER_TYPE, type=CommandType.STRING, required = true, description="Type of PXE server. Current values are LinMin, DMCD")
private String type;
///////////////////////////////////////////////////
/////////////////// Accessors ///////////////////////
/////////////////////////////////////////////////////
public Long getZoneId() {
return zoneId;
}
public String getUrl() {
return url;
}
public String getUsername() {
return username;
}
public String getPassword() {
return password;
}
public String getType() {
return type;
}
/////////////////////////////////////////////////////
/////////////// API Implementation///////////////////
/////////////////////////////////////////////////////
@Override
public String getCommandName() {
return s_name;
}
@Override
public void execute() throws ResourceUnavailableException, InsufficientCapacityException, ServerApiException, ConcurrentOperationException,
ResourceAllocationException {
try {
PxeServerManager pxeServerMgr;
ComponentLocator locator = ComponentLocator.getLocator(ManagementService.Name);
if (getType().equalsIgnoreCase(PxeServerType.LinMin.getName())) {
pxeServerMgr = locator.getManager(LinMinPxeServerManager.class);
} else {
throw new ServerApiException(BaseCmd.PARAM_ERROR, "Unsupport PXE server type " + getType());
}
Host pxeServer = pxeServerMgr.addPxeServer(this);
PxeServerResponse response = pxeServerMgr.getApiResponse(pxeServer);
response.setObjectName("pxeserver");
response.setResponseName(getCommandName());
this.setResponseObject(response);
} catch (InvalidParameterValueException ipve) {
throw new ServerApiException(BaseCmd.PARAM_ERROR, ipve.getMessage());
} catch (CloudRuntimeException cre) {
throw new ServerApiException(BaseCmd.INTERNAL_ERROR, cre.getMessage());
}
}
}

View File

@ -0,0 +1,18 @@
package com.cloud.api.response;
import com.cloud.api.ApiConstants;
import com.cloud.serializer.Param;
import com.google.gson.annotations.SerializedName;
public class PxeServerResponse extends BaseResponse {
@SerializedName(ApiConstants.ID) @Param(description="the ID of the PXE server")
private Long id;
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
}

View File

@ -0,0 +1,5 @@
package com.cloud.baremetal;
public interface LinMinPxeServerManager extends PxeServerManager {
}

View File

@ -0,0 +1,121 @@
package com.cloud.baremetal;
import java.net.URI;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import javax.ejb.Local;
import org.apache.log4j.Logger;
import com.cloud.agent.AgentManager;
import com.cloud.agent.api.baremetal.PrepareLinMinPxeServerAnswer;
import com.cloud.agent.api.baremetal.PrepareLinMinPxeServerCommand;
import com.cloud.api.AddPxeServerCmd;
import com.cloud.dc.DataCenterVO;
import com.cloud.dc.dao.DataCenterDao;
import com.cloud.deploy.DeployDestination;
import com.cloud.exception.InvalidParameterValueException;
import com.cloud.host.Host;
import com.cloud.host.HostVO;
import com.cloud.host.dao.HostDao;
import com.cloud.resource.ServerResource;
import com.cloud.utils.component.Inject;
import com.cloud.utils.exception.CloudRuntimeException;
import com.cloud.vm.NicProfile;
import com.cloud.vm.ReservationContext;
import com.cloud.vm.UserVmVO;
import com.cloud.vm.VirtualMachineProfile;
@Local(value = {LinMinPxeServerManager.class})
public class LinMinPxeServerManagerImpl extends PxeServerManagerImpl implements LinMinPxeServerManager {
private static final org.apache.log4j.Logger s_logger = Logger.getLogger(LinMinPxeServerManagerImpl.class);
@Inject DataCenterDao _dcDao;
@Inject HostDao _hostDao;
@Inject AgentManager _agentMgr;
@Override
public Host addPxeServer(AddPxeServerCmd cmd) throws InvalidParameterValueException, CloudRuntimeException {
long zoneId = cmd.getZoneId();
DataCenterVO zone = _dcDao.findById(zoneId);
String zoneName;
if (zone == null) {
throw new InvalidParameterValueException("Could not find zone with ID: " + zoneId);
} else {
zoneName = zone.getName();
}
List<HostVO> pxeServers = _hostDao.listByTypeDataCenter(Host.Type.PxeServer, zoneId);
if (pxeServers.size() != 0) {
throw new InvalidParameterValueException("Already had a PXE server in zone: " + zoneName);
}
URI uri;
try {
uri = new URI(cmd.getUrl());
} catch (Exception e) {
s_logger.debug(e);
throw new InvalidParameterValueException(e.getMessage());
}
String ipAddress = uri.getHost();
String username = cmd.getUsername();
String password = cmd.getPassword();
String guid = getPxeServerGuid(Long.toString(zoneId), PxeServerType.LinMin.getName(), ipAddress);
Map params = new HashMap<String, String>();
params.put("zone", Long.toString(zoneId));
params.put("ip", ipAddress);
params.put("username", username);
params.put("password", password);
params.put("guid", guid);
ServerResource resource = null;
try {
if (cmd.getType().equalsIgnoreCase(PxeServerType.LinMin.getName())) {
resource = new LinMinPxeServerResource();
resource.configure("LinMin PXE resource", params);
}
} catch (Exception e) {
s_logger.debug(e);
throw new CloudRuntimeException(e.getMessage());
}
Host pxeServer = _agentMgr.addHost(zoneId, resource, Host.Type.PxeServer, params);
if (pxeServer == null) {
throw new CloudRuntimeException("Cannot add PXE server as a host");
}
return pxeServer;
}
@Override
public boolean prepare(VirtualMachineProfile<UserVmVO> profile, DeployDestination dest, ReservationContext context, Long pxeServerId) {
List<NicProfile> nics = profile.getNics();
if (nics.size() == 0) {
throw new CloudRuntimeException("Cannot do PXE start without nic");
}
NicProfile pxeNic = nics.get(0);
String mac = pxeNic.getMacAddress();
String ip = pxeNic.getIp4Address();
String gateway = pxeNic.getGateway();
String mask = pxeNic.getNetmask();
String dns = pxeNic.getDns1();
if (dns == null) {
dns = pxeNic.getDns2();
}
try {
String linMinTpl = profile.getTemplate().getUrl();
assert linMinTpl != null : "How can a null template get here!!!";
PrepareLinMinPxeServerCommand cmd = new PrepareLinMinPxeServerCommand(ip, mac, mask, gateway, dns, linMinTpl,
profile.getVirtualMachine().getName(), dest.getHost().getName());
PrepareLinMinPxeServerAnswer ans = (PrepareLinMinPxeServerAnswer) _agentMgr.send(pxeServerId, cmd);
return ans.getResult();
} catch (Exception e) {
s_logger.warn("Cannot prepare PXE server", e);
return false;
}
}
}

View File

@ -0,0 +1,321 @@
package com.cloud.baremetal;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.StringReader;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLConnection;
import java.util.Map;
import javax.naming.ConfigurationException;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import org.apache.log4j.Logger;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.xml.sax.InputSource;
import com.cloud.agent.IAgentControl;
import com.cloud.agent.api.Answer;
import com.cloud.agent.api.Command;
import com.cloud.agent.api.PingCommand;
import com.cloud.agent.api.ReadyAnswer;
import com.cloud.agent.api.ReadyCommand;
import com.cloud.agent.api.StartupCommand;
import com.cloud.agent.api.StartupPxeServerCommand;
import com.cloud.agent.api.baremetal.PrepareLinMinPxeServerAnswer;
import com.cloud.agent.api.baremetal.PrepareLinMinPxeServerCommand;
import com.cloud.exception.InvalidParameterValueException;
import com.cloud.host.Host.Type;
import com.cloud.resource.ServerResource;
import com.cloud.utils.exception.CloudRuntimeException;
import com.cloud.utils.ssh.SSHCmdHelper;
public class LinMinPxeServerResource implements ServerResource {
private static final Logger s_logger = Logger.getLogger(LinMinPxeServerResource.class);
String _name;
String _guid;
String _username;
String _password;
String _ip;
String _zoneId;
class XmlReturn {
NodeList nList;
public XmlReturn(InputSource s, String tagName) {
try {
DocumentBuilderFactory dbFactory = DocumentBuilderFactory.newInstance();
DocumentBuilder dBuilder = dbFactory.newDocumentBuilder();
Document doc = dBuilder.parse(s);
doc.getDocumentElement().normalize();
nList = doc.getElementsByTagName(tagName);
} catch (Exception e) {
s_logger.debug("The XML file:");
s_logger.debug(s.toString());
s_logger.debug("Cannot parse XMl file", e);
nList = null;
}
}
public String getValue(String tag) {
if (nList == null || nList.getLength() == 0) {
throw new InvalidParameterValueException("invalid XML file");
}
Element e = (Element)nList.item(0);
NodeList nlList= e.getElementsByTagName(tag).item(0).getChildNodes();
Node nValue = (Node)nlList.item(0);
return nValue.getNodeValue();
}
}
@Override
public boolean configure(String name, Map<String, Object> params) throws ConfigurationException {
_name = name;
_guid = (String)params.get("guid");
_ip = (String)params.get("ip");
_username = (String)params.get("username");
_password = (String)params.get("password");
_zoneId = (String)params.get("zone");
if (_guid == null) {
throw new ConfigurationException("No Guid specified");
}
if (_zoneId == null) {
throw new ConfigurationException("No Zone specified");
}
if (_ip == null) {
throw new ConfigurationException("No IP specified");
}
if (_username == null) {
throw new ConfigurationException("No username specified");
}
if (_password == null) {
throw new ConfigurationException("No password specified");
}
com.trilead.ssh2.Connection sshConnection = new com.trilead.ssh2.Connection(_ip, 22);
s_logger.debug(String.format("Trying to connect to LinMin PXE server(IP=%1$s, username=%2$s, password=%3$s", _ip, _username, _password));
try {
sshConnection.connect(null, 60000, 60000);
if (!sshConnection.authenticateWithPassword(_username, _password)) {
s_logger.debug("SSH Failed to authenticate");
throw new ConfigurationException(String.format("Cannot connect to LinMin PXE server(IP=%1$s, username=%2$s, password=%3$s", _ip, _username,
_password));
}
if (!SSHCmdHelper.sshExecuteCmd(sshConnection, "[ -d '/home/tftpboot' ] && [ -d '/usr/local/linmin' ]")) {
throw new ConfigurationException("Cannot find LinMin directory /home/tftpboot, /usr/local/linmin on PXE server");
}
return true;
} catch (Exception e) {
throw new ConfigurationException(e.getMessage());
} finally {
if (sshConnection != null) {
sshConnection.close();
}
}
}
@Override
public boolean start() {
return true;
}
@Override
public boolean stop() {
return true;
}
@Override
public String getName() {
return _name;
}
@Override
public Type getType() {
return Type.PxeServer;
}
@Override
public StartupCommand[] initialize() {
StartupPxeServerCommand cmd = new StartupPxeServerCommand();
cmd.setName(_name);
cmd.setDataCenter(_zoneId);
cmd.setPod("");
cmd.setPrivateIpAddress(_ip);
cmd.setStorageIpAddress("");
cmd.setVersion("");
cmd.setGuid(_guid);
return new StartupCommand[]{cmd};
}
@Override
public PingCommand getCurrentStatus(long id) {
// TODO Auto-generated method stub
return null;
}
protected ReadyAnswer execute(ReadyCommand cmd) {
s_logger.debug("LinMin resource " + _name + " is ready");
return new ReadyAnswer(cmd);
}
private InputSource httpCall(String urlStr) {
try {
s_logger.debug("Execute http call " + urlStr);
URL url = new URL(urlStr);
URLConnection conn = url.openConnection();
conn.setReadTimeout(30000);
BufferedReader in = new BufferedReader(new InputStreamReader(conn.getInputStream()));
StringBuffer xmlStuff = new StringBuffer();
String line;
while ((line = in.readLine()) != null) {
xmlStuff.append(line);
}
StringReader statsReader = new StringReader(xmlStuff.toString());
InputSource statsSource = new InputSource(statsReader);
s_logger.debug("Http call retrun:");
s_logger.debug(xmlStuff.toString());
return statsSource;
} catch (MalformedURLException e) {
throw new CloudRuntimeException("URL is malformed " + urlStr, e);
} catch (IOException e) {
s_logger.warn("can not do http call", e);
return null;
}catch (Exception e) {
s_logger.warn("Cannot do http call " + urlStr, e);
throw new CloudRuntimeException(e.getStackTrace().toString());
}
}
protected PrepareLinMinPxeServerAnswer execute(PrepareLinMinPxeServerCommand cmd) {
String apiUserName = "root";
String apiPassword = "password";
String apid = "2ad644fb479871a0f5543dd6d29fe9ed";
StringBuffer askApid = new StringBuffer();
askApid.append("http://");
askApid.append(_ip);
askApid.append("/tftpboot/www/lbmp-API.php?actiontype=provision&apid=");
askApid.append(apid);
askApid.append("&auth_user=");
askApid.append(apiUserName);
askApid.append("&auth_user_pw=");
askApid.append(apiPassword);
askApid.append("&rtn_format=XML&action=authorize");
InputSource s = httpCall(askApid.toString());
if (s == null) {
return new PrepareLinMinPxeServerAnswer(cmd, "Http call failed");
}
try {
XmlReturn r = new XmlReturn(s, "LinMinBareMetalAPI");
String res = r.getValue("actionResultsMsg");
s_logger.debug(s.toString());
if (!res.startsWith("Successful")) {
return new PrepareLinMinPxeServerAnswer(cmd, "Acquire APID failed");
}
String apid5 = r.getValue("apid");
if (apid5 == null) {
return new PrepareLinMinPxeServerAnswer(cmd, "Cannot get 5 minutes APID " + apid5);
}
StringBuffer addRole = new StringBuffer();
addRole.append("http://");
addRole.append(_ip);
addRole.append("/tftpboot/www/lbmp-API.php?actiontype=provision&user_supplied_id=");
addRole.append(cmd.getVmName());
addRole.append("&mac_address=");
addRole.append(cmd.getMac().replaceAll(":", "%3A"));
addRole.append("&apid=");
addRole.append(apid5);
addRole.append("&control_file_template=");
addRole.append(cmd.getTemplate().replace(' ', '+'));
addRole.append("&node_name=");
addRole.append(cmd.getHostName());
addRole.append("&node_domain=");
addRole.append(cmd.getHostName());
addRole.append("&node_password=password");
addRole.append("&node_time_zone=Etc%2FGMT-8");
if (cmd.getIp() != null) {
addRole.append("&node_ip_address=");
addRole.append(cmd.getIp());
}
if (cmd.getNetMask() != null) {
addRole.append("&node_subnet_mask=");
addRole.append(cmd.getNetMask());
}
if (cmd.getDns() != null) {
addRole.append("&node_nameserver=");
addRole.append(cmd.getDns());
}
if (cmd.getGateWay() != null) {
addRole.append("&node_default_gateway=");
addRole.append(cmd.getGateWay());
}
addRole.append("&enable_provisioning_flag=nextbootonly&rtn_format=XML&action=add");
s = httpCall(addRole.toString());
if (s == null) {
return new PrepareLinMinPxeServerAnswer(cmd, "Http call failed");
}
r = new XmlReturn(s, "LinMinBareMetalAPI");
res = r.getValue("actionResultsMsg");
s_logger.debug(s.toString());
if (!res.startsWith("Successful")) {
return new PrepareLinMinPxeServerAnswer(cmd, "Add LinMin role failed");
}
} catch (Exception e) {
s_logger.warn("Cannot parse result from Lin Min server", e);
return new PrepareLinMinPxeServerAnswer(cmd, e.getMessage());
}
s_logger.debug("Prepare LinMin PXE server successfully");
return new PrepareLinMinPxeServerAnswer(cmd);
}
@Override
public Answer executeRequest(Command cmd) {
if (cmd instanceof ReadyCommand) {
return execute((ReadyCommand) cmd);
} else if (cmd instanceof PrepareLinMinPxeServerCommand) {
return execute((PrepareLinMinPxeServerCommand)cmd);
} else {
return Answer.createUnsupportedCommandAnswer(cmd);
}
}
@Override
public void disconnected() {
// TODO Auto-generated method stub
}
@Override
public IAgentControl getAgentControl() {
// TODO Auto-generated method stub
return null;
}
@Override
public void setAgentControl(IAgentControl agentControl) {
// TODO Auto-generated method stub
}
}

View File

@ -0,0 +1,37 @@
package com.cloud.baremetal;
import com.cloud.agent.api.baremetal.PrepareLinMinPxeServerCommand;
import com.cloud.api.AddPxeServerCmd;
import com.cloud.api.response.PxeServerResponse;
import com.cloud.deploy.DeployDestination;
import com.cloud.exception.InvalidParameterValueException;
import com.cloud.host.Host;
import com.cloud.utils.component.Manager;
import com.cloud.utils.exception.CloudRuntimeException;
import com.cloud.vm.ReservationContext;
import com.cloud.vm.UserVmVO;
import com.cloud.vm.VirtualMachineProfile;
public interface PxeServerManager extends Manager {
public static class PxeServerType {
private String _name;
public static final PxeServerType LinMin = new PxeServerType("LinMin");
public static final PxeServerType DMCD = new PxeServerType("DMCD");
public PxeServerType(String name) {
_name = name;
}
public String getName() {
return _name;
}
}
public Host addPxeServer(AddPxeServerCmd cmd) throws InvalidParameterValueException, CloudRuntimeException;
public PxeServerResponse getApiResponse(Host pxeServer);
public boolean prepare(VirtualMachineProfile<UserVmVO> profile, DeployDestination dest, ReservationContext context, Long pxeServerId);
}

View File

@ -0,0 +1,70 @@
package com.cloud.baremetal;
import java.util.Map;
import javax.ejb.Local;
import javax.naming.ConfigurationException;
import org.apache.log4j.Logger;
import com.cloud.agent.api.baremetal.PrepareLinMinPxeServerCommand;
import com.cloud.api.AddPxeServerCmd;
import com.cloud.api.response.PxeServerResponse;
import com.cloud.deploy.DeployDestination;
import com.cloud.exception.InvalidParameterValueException;
import com.cloud.host.Host;
import com.cloud.utils.exception.CloudRuntimeException;
import com.cloud.vm.ReservationContext;
import com.cloud.vm.UserVmVO;
import com.cloud.vm.VirtualMachineProfile;
@Local(value = {PxeServerManager.class})
public class PxeServerManagerImpl implements PxeServerManager {
private static final org.apache.log4j.Logger s_logger = Logger.getLogger(PxeServerManagerImpl.class);
protected String _name;
@Override
public boolean configure(String name, Map<String, Object> params) throws ConfigurationException {
_name = name;
return true;
}
@Override
public boolean start() {
return true;
}
@Override
public boolean stop() {
return true;
}
@Override
public String getName() {
return _name;
}
protected String getPxeServerGuid(String zoneId, String name, String ip) {
return zoneId + "-" + name + "-" + ip;
}
@Override
public Host addPxeServer(AddPxeServerCmd cmd) throws InvalidParameterValueException, CloudRuntimeException {
// TODO Auto-generated method stub
return null;
}
@Override
public PxeServerResponse getApiResponse(Host pxeServer) {
PxeServerResponse response = new PxeServerResponse();
response.setId(pxeServer.getId());
return response;
}
@Override
public boolean prepare(VirtualMachineProfile<UserVmVO> profile, DeployDestination dest, ReservationContext context, Long pxeServerId) {
return true;
}
}

View File

@ -1,7 +1,5 @@
package com.cloud.hypervisor.kvm.discoverer;
import java.io.IOException;
import java.io.InputStream;
import java.net.InetAddress;
import java.net.URI;
import java.util.HashMap;
@ -38,6 +36,7 @@ import com.cloud.resource.ServerResource;
import com.cloud.utils.component.ComponentLocator;
import com.cloud.utils.component.Inject;
import com.cloud.utils.script.Script;
import com.cloud.utils.ssh.SSHCmdHelper;
import com.trilead.ssh2.ChannelCondition;
import com.trilead.ssh2.SCPClient;
import com.trilead.ssh2.Session;
@ -101,85 +100,6 @@ public class KvmServerDiscoverer extends DiscovererBase implements Discoverer,
// TODO Auto-generated method stub
return false;
}
private static boolean sshExecuteCmd(com.trilead.ssh2.Connection sshConnection, String cmd) {
s_logger.debug("Executing cmd: " + cmd);
Session sshSession = null;
try {
sshSession = sshConnection.openSession();
// There is a bug in Trilead library, wait a second before
// starting a shell and executing commands, from http://spci.st.ewi.tudelft.nl/chiron/xref/nl/tudelft/swerl/util/SSHConnection.html
Thread.sleep(1000);
if (sshSession == null) {
return false;
}
sshSession.execCommand(cmd);
InputStream stdout = sshSession.getStdout();
InputStream stderr = sshSession.getStderr();
byte[] buffer = new byte[8192];
while (true) {
if (stdout == null || stderr == null) {
return false;
}
if ((stdout.available() == 0) && (stderr.available() == 0)) {
int conditions = sshSession.waitForCondition(
ChannelCondition.STDOUT_DATA
| ChannelCondition.STDERR_DATA
| ChannelCondition.EOF, 120000);
if ((conditions & ChannelCondition.TIMEOUT) != 0) {
s_logger.info("Timeout while waiting for data from peer.");
break;
}
if ((conditions & ChannelCondition.EOF) != 0) {
if ((conditions & (ChannelCondition.STDOUT_DATA | ChannelCondition.STDERR_DATA)) == 0) {
break;
}
}
}
while (stdout.available() > 0) {
stdout.read(buffer);
}
while (stderr.available() > 0) {
stderr.read(buffer);
}
}
Thread.sleep(1000);
if (sshSession.getExitStatus() != 0) {
return false;
}
return true;
} catch (IOException e) {
s_logger.debug("Executing cmd: " + cmd + " failed, due to: " + e.toString());
return false;
} catch (InterruptedException e) {
return false;
} catch (Exception e) {
return false;
} finally {
if (sshSession != null)
sshSession.close();
}
}
private static boolean sshExecuteCmd(com.trilead.ssh2.Connection sshConnection, String cmd, int nTimes) {
for (int i = 0; i < nTimes; i ++) {
if (sshExecuteCmd(sshConnection, cmd))
return true;
}
return false;
}
@Override
public Map<? extends ServerResource, Map<String, String>> find(long dcId,
@ -222,7 +142,7 @@ public class KvmServerDiscoverer extends DiscovererBase implements Discoverer,
return null;
}
if (!sshExecuteCmd(sshConnection, "lsmod|grep kvm >& /dev/null", 3)) {
if (!SSHCmdHelper.sshExecuteCmd(sshConnection, "lsmod|grep kvm >& /dev/null", 3)) {
s_logger.debug("It's not a KVM enabled machine");
return null;
}
@ -241,7 +161,7 @@ public class KvmServerDiscoverer extends DiscovererBase implements Discoverer,
parameters += " -N " + _kvmPrivateNic;
}
sshExecuteCmd(sshConnection, "/usr/bin/setup_agent.sh " + parameters + " 1>&2", 3);
SSHCmdHelper.sshExecuteCmd(sshConnection, "/usr/bin/setup_agent.sh " + parameters + " 1>&2", 3);
KvmDummyResourceBase kvmResource = new KvmDummyResourceBase();
Map<String, Object> params = new HashMap<String, Object>();

View File

@ -380,13 +380,18 @@ public class TemplateManagerImpl implements TemplateManager, Manager, TemplateSe
if (imgfmt == null) {
throw new IllegalArgumentException("Image format is incorrect " + format + ". Supported formats are " + EnumUtils.listValues(ImageFormat.values()));
}
URI uri = new URI(url);
if ((uri.getScheme() == null) || (!uri.getScheme().equalsIgnoreCase("http") && !uri.getScheme().equalsIgnoreCase("https") && !uri.getScheme().equalsIgnoreCase("file") && !uri.getScheme().equalsIgnoreCase("baremetal"))) {
throw new IllegalArgumentException("Unsupported scheme for url: " + url);
}
if (!uri.getScheme().equalsIgnoreCase("baremetal")) {
String uriStr;
if (url.startsWith("baremetal://")) {
uriStr = url.substring("baremetal://".length());
} else {
URI uri = new URI(url);
if ((uri.getScheme() == null)
|| (!uri.getScheme().equalsIgnoreCase("http") && !uri.getScheme().equalsIgnoreCase("https") && !uri.getScheme()
.equalsIgnoreCase("file"))) {
throw new IllegalArgumentException("Unsupported scheme for url: " + url);
}
int port = uri.getPort();
if (!(port == 80 || port == 443 || port == -1)) {
throw new IllegalArgumentException("Only ports 80 and 443 are allowed");
@ -403,6 +408,7 @@ public class TemplateManagerImpl implements TemplateManager, Manager, TemplateSe
} catch (UnknownHostException uhe) {
throw new IllegalArgumentException("Unable to resolve " + host);
}
uriStr = uri.toString();
}
// Check that the resource limit for templates/ISOs won't be exceeded
@ -431,13 +437,13 @@ public class TemplateManagerImpl implements TemplateManager, Manager, TemplateSe
}
}
return create(userId, accountId, zoneId, name, displayText, isPublic, featured, isExtractable, imgfmt, diskType, uri, chksum, requiresHvm, bits, enablePassword, guestOSId, bootable, hypervisorType);
return create(userId, accountId, zoneId, name, displayText, isPublic, featured, isExtractable, imgfmt, diskType, uriStr, chksum, requiresHvm, bits, enablePassword, guestOSId, bootable, hypervisorType);
} catch (URISyntaxException e) {
throw new IllegalArgumentException("Invalid URL " + url);
}
}
private VMTemplateVO create(long userId, long accountId, Long zoneId, String name, String displayText, boolean isPublic, boolean featured, boolean isExtractable, ImageFormat format, TemplateType type, URI url, String chksum, boolean requiresHvm, int bits, boolean enablePassword, long guestOSId, boolean bootable, HypervisorType hyperType) {
private VMTemplateVO create(long userId, long accountId, Long zoneId, String name, String displayText, boolean isPublic, boolean featured, boolean isExtractable, ImageFormat format, TemplateType type, String url, String chksum, boolean requiresHvm, int bits, boolean enablePassword, long guestOSId, boolean bootable, HypervisorType hyperType) {
Long id = _tmpltDao.getNextInSequence(Long.class, "id");
AccountVO account = _accountDao.findById(accountId);
@ -445,7 +451,7 @@ public class TemplateManagerImpl implements TemplateManager, Manager, TemplateSe
throw new IllegalArgumentException("Only admins can create templates in all zones");
}
VMTemplateVO template = new VMTemplateVO(id, name, format, isPublic, featured, isExtractable, type, url.toString(), requiresHvm, bits, accountId, chksum, displayText, enablePassword, guestOSId, bootable, hyperType);
VMTemplateVO template = new VMTemplateVO(id, name, format, isPublic, featured, isExtractable, type, url, requiresHvm, bits, accountId, chksum, displayText, enablePassword, guestOSId, bootable, hyperType);
if (zoneId == null) {
List<DataCenterVO> dcs = _dcDao.listAllIncludingRemoved();

View File

@ -1,6 +1,12 @@
package com.cloud.vm;
import java.io.IOException;
import java.io.InputStream;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLConnection;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.Executors;
@ -10,12 +16,16 @@ import javax.naming.ConfigurationException;
import org.apache.log4j.Logger;
import com.cloud.agent.api.baremetal.PrepareLinMinPxeServerCommand;
import com.cloud.agent.manager.Commands;
import com.cloud.api.commands.AttachVolumeCmd;
import com.cloud.api.commands.CreateTemplateCmd;
import com.cloud.api.commands.DeployVMCmd;
import com.cloud.api.commands.DetachVolumeCmd;
import com.cloud.api.commands.UpgradeVMCmd;
import com.cloud.baremetal.LinMinPxeServerManager;
import com.cloud.baremetal.PxeServerManager;
import com.cloud.baremetal.PxeServerManager.PxeServerType;
import com.cloud.configuration.ResourceCount.ResourceType;
import com.cloud.configuration.dao.ConfigurationDao;
import com.cloud.dc.DataCenterVO;
@ -32,28 +42,36 @@ import com.cloud.exception.PermissionDeniedException;
import com.cloud.exception.ResourceAllocationException;
import com.cloud.exception.ResourceUnavailableException;
import com.cloud.exception.StorageUnavailableException;
import com.cloud.host.Host;
import com.cloud.host.HostVO;
import com.cloud.hypervisor.Hypervisor.HypervisorType;
import com.cloud.network.Network;
import com.cloud.network.NetworkVO;
import com.cloud.network.IpAddrAllocator.IpAddr;
import com.cloud.network.Networks.TrafficType;
import com.cloud.server.ManagementService;
import com.cloud.service.ServiceOfferingVO;
import com.cloud.storage.Storage;
import com.cloud.storage.VMTemplateVO;
import com.cloud.storage.Volume;
import com.cloud.storage.Storage.TemplateType;
import com.cloud.user.Account;
import com.cloud.user.AccountVO;
import com.cloud.user.SSHKeyPair;
import com.cloud.user.UserContext;
import com.cloud.user.UserVO;
import com.cloud.uservm.UserVm;
import com.cloud.utils.NumbersUtil;
import com.cloud.utils.Pair;
import com.cloud.utils.component.ComponentLocator;
import com.cloud.utils.component.Manager;
import com.cloud.utils.concurrency.NamedThreadFactory;
import com.cloud.utils.crypt.RSAHelper;
import com.cloud.utils.db.DB;
import com.cloud.utils.exception.CloudRuntimeException;
import com.cloud.utils.net.NetUtils;
import com.cloud.vm.VirtualMachine.Type;
import com.cloud.vm.VirtualMachineProfile.Param;
@Local(value={BareMetalVmManager.class, BareMetalVmService.class})
public class BareMetalVmManagerImpl extends UserVmManagerImpl implements BareMetalVmManager, BareMetalVmService, Manager {
@ -286,7 +304,30 @@ public class BareMetalVmManagerImpl extends UserVmManagerImpl implements BareMet
}
public UserVm startVirtualMachine(DeployVMCmd cmd) throws ResourceUnavailableException, InsufficientCapacityException, ConcurrentOperationException {
return super.startVirtualMachine(cmd);
long vmId = cmd.getEntityId();
UserVmVO vm = _vmDao.findById(vmId);
_vmDao.loadDetails(vm);
List<HostVO> servers = _hostDao.listBy(Host.Type.PxeServer, vm.getDataCenterId());
if (servers.size() == 0) {
throw new CloudRuntimeException("Cannot find PXE server, please make sure there is one PXE server per zone");
}
HostVO pxeServer = servers.get(0);
VMTemplateVO template = _templateDao.findById(vm.getTemplateId());
if (template == null || template.getFormat() != Storage.ImageFormat.BAREMETAL) {
throw new InvalidParameterValueException("Invalid template with id = " + vm.getTemplateId());
}
Map<VirtualMachineProfile.Param, Object> params = new HashMap<VirtualMachineProfile.Param, Object>();
//TODO: have to ugly harding code here
if (pxeServer.getResource().equalsIgnoreCase("com.cloud.baremetal.LinMinPxeServerResource")) {
params.put(Param.PxeSeverType, PxeServerType.LinMin);
} else {
throw new CloudRuntimeException("Unkown PXE server resource " + pxeServer.getResource());
}
return startVirtualMachine(cmd, params);
}
@Override
@ -333,6 +374,39 @@ public class BareMetalVmManagerImpl extends UserVmManagerImpl implements BareMet
throw new PermissionDeniedException("The owner of " + vm + " either does not exist or is disabled: " + vm.getAccountId());
}
if (profile.getTemplate() == null) {
s_logger.debug("This is a normal IPMI start, skip prepartion of PXE server");
return true;
}
s_logger.debug("This is a PXE start, prepare PXE server first");
PxeServerType pxeType = (PxeServerType) profile.getParameter(Param.PxeSeverType);
if (pxeType == null) {
throw new CloudRuntimeException("No PXE type specified");
}
PxeServerManager pxeMgr = null;
ComponentLocator locator = ComponentLocator.getLocator(ManagementService.Name);
if (pxeType == PxeServerType.LinMin) {
pxeMgr = locator.getManager(LinMinPxeServerManager.class);
} else {
throw new CloudRuntimeException("Unsupport PXE type " + pxeType.toString());
}
if (pxeMgr == null) {
throw new CloudRuntimeException("No PXE manager find for type " + pxeType.toString());
}
List<HostVO> servers = _hostDao.listBy(Host.Type.PxeServer, vm.getDataCenterId());
if (servers.size() == 0) {
throw new CloudRuntimeException("Cannot find PXE server, please make sure there is one PXE server per zone");
}
HostVO pxeServer = servers.get(0);
if (!pxeMgr.prepare(profile, dest, context, pxeServer.getId())) {
throw new CloudRuntimeException("Pepare PXE server failed");
}
return true;
}

View File

@ -2106,6 +2106,10 @@ public class UserVmManagerImpl implements UserVmManager, UserVmService, Manager
@Override @ActionEvent (eventType=EventTypes.EVENT_VM_CREATE, eventDescription="starting Vm", async=true)
public UserVm startVirtualMachine(DeployVMCmd cmd) throws ResourceUnavailableException, InsufficientCapacityException, ConcurrentOperationException {
return startVirtualMachine(cmd, null);
}
protected UserVm startVirtualMachine(DeployVMCmd cmd, Map<VirtualMachineProfile.Param, Object> additonalParams) throws ResourceUnavailableException, InsufficientCapacityException, ConcurrentOperationException {
long vmId = cmd.getEntityId();
UserVmVO vm = _vmDao.findById(vmId);
_vmDao.loadDetails(vm);
@ -2142,6 +2146,9 @@ public class UserVmManagerImpl implements UserVmManager, UserVmService, Manager
try {
Map<VirtualMachineProfile.Param, Object> params = new HashMap<VirtualMachineProfile.Param, Object>();
if (additonalParams != null) {
params.putAll(additonalParams);
}
params.put(VirtualMachineProfile.Param.VmPassword, password);
vm = _itMgr.start(vm, params, caller, owner);
} finally {

View File

@ -0,0 +1,96 @@
package com.cloud.utils.ssh;
import java.io.IOException;
import java.io.InputStream;
import org.apache.log4j.Logger;
import com.trilead.ssh2.ChannelCondition;
import com.trilead.ssh2.Session;
public class SSHCmdHelper {
private static final Logger s_logger = Logger.getLogger(SSHCmdHelper.class);
public static boolean sshExecuteCmd(com.trilead.ssh2.Connection sshConnection, String cmd, int nTimes) {
for (int i = 0; i < nTimes; i ++) {
if (sshExecuteCmdOneShot(sshConnection, cmd))
return true;
}
return false;
}
public static boolean sshExecuteCmd(com.trilead.ssh2.Connection sshConnection, String cmd) {
return sshExecuteCmd(sshConnection, cmd, 3);
}
public static boolean sshExecuteCmdOneShot(com.trilead.ssh2.Connection sshConnection, String cmd) {
s_logger.debug("Executing cmd: " + cmd);
Session sshSession = null;
try {
sshSession = sshConnection.openSession();
// There is a bug in Trilead library, wait a second before
// starting a shell and executing commands, from http://spci.st.ewi.tudelft.nl/chiron/xref/nl/tudelft/swerl/util/SSHConnection.html
Thread.sleep(1000);
if (sshSession == null) {
return false;
}
sshSession.execCommand(cmd);
InputStream stdout = sshSession.getStdout();
InputStream stderr = sshSession.getStderr();
byte[] buffer = new byte[8192];
while (true) {
if (stdout == null || stderr == null) {
return false;
}
if ((stdout.available() == 0) && (stderr.available() == 0)) {
int conditions = sshSession.waitForCondition(
ChannelCondition.STDOUT_DATA
| ChannelCondition.STDERR_DATA
| ChannelCondition.EOF, 120000);
if ((conditions & ChannelCondition.TIMEOUT) != 0) {
s_logger.info("Timeout while waiting for data from peer.");
break;
}
if ((conditions & ChannelCondition.EOF) != 0) {
if ((conditions & (ChannelCondition.STDOUT_DATA | ChannelCondition.STDERR_DATA)) == 0) {
break;
}
}
}
while (stdout.available() > 0) {
stdout.read(buffer);
}
while (stderr.available() > 0) {
stderr.read(buffer);
}
}
Thread.sleep(1000);
if (sshSession.getExitStatus() != 0) {
return false;
}
return true;
} catch (IOException e) {
s_logger.debug("Executing cmd: " + cmd + " failed, due to: " + e.toString());
return false;
} catch (InterruptedException e) {
return false;
} catch (Exception e) {
return false;
} finally {
if (sshSession != null)
sshSession.close();
}
}
}