// Copyright 2012 Citrix Systems, Inc. Licensed under the
// Apache License, Version 2.0 (the "License"); you may not use this
// file except in compliance with the License. Citrix Systems, Inc.
// reserves all rights not expressly granted by 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 04/03/2012
package com.cloud.agent.vmdata;
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import java.io.File;
import java.net.InetAddress;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import javax.ejb.Local;
import javax.naming.ConfigurationException;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.apache.commons.codec.binary.Base64;
import org.apache.log4j.Logger;
import org.mortbay.jetty.Connector;
import org.mortbay.jetty.Handler;
import org.mortbay.jetty.Server;
import org.mortbay.jetty.handler.DefaultHandler;
import org.mortbay.jetty.handler.HandlerList;
import org.mortbay.jetty.handler.ResourceHandler;
import org.mortbay.jetty.nio.SelectChannelConnector;
import org.mortbay.jetty.servlet.Context;
import org.mortbay.jetty.servlet.ServletHolder;
import org.mortbay.thread.QueuedThreadPool;
import com.cloud.agent.api.Answer;
import com.cloud.agent.api.routing.VmDataCommand;
import com.cloud.agent.api.to.NicTO;
import com.cloud.agent.api.to.VirtualMachineTO;
import com.cloud.network.Networks.TrafficType;
import com.cloud.storage.JavaStorageLayer;
import com.cloud.storage.StorageLayer;
import com.cloud.utils.net.NetUtils;
import com.cloud.utils.script.Script;
/**
* Serves vm data using embedded Jetty server
*
*/
@Local(value = { VmDataServer.class })
public class JettyVmDataServer implements VmDataServer {
private static final Logger s_logger = Logger
.getLogger(JettyVmDataServer.class);
public static final String USER_DATA = "user-data";
public static final String META_DATA = "meta-data";
protected String _vmDataDir;
protected Server _jetty;
protected String _hostIp;
protected Map<String, String> _ipVmMap = new HashMap<String, String>();
protected StorageLayer _fs = new JavaStorageLayer();
public class VmDataServlet extends HttpServlet {
private static final long serialVersionUID = -1640031398971742349L;
JettyVmDataServer _vmDataServer;
String _dataType; // userdata or meta-data
public VmDataServlet(JettyVmDataServer dataServer, String dataType) {
this._vmDataServer = dataServer;
this._dataType = dataType;
}
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException {
int port = req.getServerPort();
if (port != 80 && port != 8000) {
resp.sendError(HttpServletResponse.SC_NOT_FOUND,
"Request not understood");
return;
}
if (_dataType.equalsIgnoreCase(USER_DATA)) {
handleUserData(req, resp);
} else if (_dataType.equalsIgnoreCase(META_DATA)) {
handleMetaData(req, resp);
}
}
protected void handleUserData(HttpServletRequest req,
HttpServletResponse resp) throws ServletException, IOException {
String metadataItem = req.getPathInfo();
String requester = req.getRemoteAddr();
resp.setContentType("text/html");
resp.setStatus(HttpServletResponse.SC_OK);
String data = null;
if (metadataItem != null) {
String[] path = metadataItem.split("/");
if (path.length > 1) {
metadataItem = path[1];
}
}
if (metadataItem != null)
data = _vmDataServer.getVmDataItem(requester, metadataItem);
if (data != null) {
resp.getWriter().print(data);
} else {
resp.setStatus(HttpServletResponse.SC_NOT_FOUND);
resp.sendError(HttpServletResponse.SC_NOT_FOUND,
"Request not found");
}
}
protected void handleMetaData(HttpServletRequest req,
HttpServletResponse resp) throws ServletException, IOException {
String metadataItem = req.getPathInfo();
String requester = req.getRemoteAddr();
resp.setContentType("text/html");
resp.setStatus(HttpServletResponse.SC_OK);
String metaData = _vmDataServer.getVmDataItem(requester,
metadataItem);
if (metaData != null) {
resp.getWriter().print(
_vmDataServer.getVmDataItem(requester, metadataItem));
} else {
resp.sendError(HttpServletResponse.SC_NOT_FOUND,
"Request not found");
}
}
}
@Override
public boolean configure(String name, Map<String, Object> params)
throws ConfigurationException {
boolean success = true;
try {
int vmDataPort = 80;
int fileservingPort = 8000;
_vmDataDir = (String) params.get("vm.data.dir");
String port = (String) params.get("vm.data.port");
if (port != null) {
vmDataPort = Integer.parseInt(port);
}
port = (String) params.get("file.server.port");
if (port != null) {
fileservingPort = Integer.parseInt(port);
}
_hostIp = (String) params.get("host.ip");
if (_vmDataDir == null) {
_vmDataDir = "/var/www/html";
}
success = _fs.mkdirs(_vmDataDir);
success = success && buildIpVmMap();
if (success) {
setupJetty(vmDataPort, fileservingPort);
}
} catch (Exception e) {
s_logger.warn("Failed to configure jetty", e);
throw new ConfigurationException("Failed to configure jetty!!");
}
return success;
}
protected boolean buildIpVmMap() {
String[] dirs = _fs.listFiles(_vmDataDir);
for (String dir : dirs) {
String[] path = dir.split("/");
String vm = path[path.length - 1];
if (vm.startsWith("i-")) {
String[] dataFiles = _fs.listFiles(dir);
for (String dfile : dataFiles) {
String path2[] = dfile.split("/");
String ipv4file = path2[path2.length - 1];
if (ipv4file.equalsIgnoreCase("local-ipv4")) {
try {
BufferedReader input = new BufferedReader(
new FileReader(dfile));
String line = null;
while ((line = input.readLine()) != null) {
if (NetUtils.isValidIp(line)) {
_ipVmMap.put(line, vm);
s_logger.info("Found ip " + line
+ " for vm " + vm);
} else {
s_logger.info("Invalid ip " + line
+ " for vm " + vm);
}
}
} catch (FileNotFoundException e) {
s_logger.warn("Failed to find file " + dfile);
} catch (IOException e) {
s_logger.warn("Failed to get ip address of " + vm);
}
}
}
}
}
return true;
}
public String getVmDataItem(String requester, String dataItem) {
String vmName = _ipVmMap.get(requester);
if (vmName == null) {
return null;
}
String vmDataFile = _vmDataDir + File.separator + vmName
+ File.separator + dataItem;
try {
BufferedReader input = new BufferedReader(
new FileReader(vmDataFile));
StringBuilder result = new StringBuilder();
String line = null;
while ((line = input.readLine()) != null) {
result.append(line);
}
input.close();
return result.toString();
} catch (FileNotFoundException e) {
s_logger.warn("Failed to find requested file " + vmDataFile);
return null;
} catch (IOException e) {
s_logger.warn("Failed to read requested file " + vmDataFile);
return null;
}
}
private void setupJetty(int vmDataPort, int fileservingPort)
throws Exception {
_jetty = new Server();
SelectChannelConnector connector0 = new SelectChannelConnector();
connector0.setHost(_hostIp);
connector0.setPort(fileservingPort);
connector0.setMaxIdleTime(30000);
connector0.setRequestBufferSize(8192);
SelectChannelConnector connector1 = new SelectChannelConnector();
connector1.setHost(_hostIp);
connector1.setPort(vmDataPort);
connector1.setThreadPool(new QueuedThreadPool(5));
connector1.setMaxIdleTime(30000);
connector1.setRequestBufferSize(8192);
_jetty.setConnectors(new Connector[] { connector0, connector1 });
Context root = new Context(_jetty, "/latest", Context.SESSIONS);
root.setResourceBase(_vmDataDir);
root.addServlet(new ServletHolder(new VmDataServlet(this, USER_DATA)),
"/*");
ResourceHandler resource_handler = new ResourceHandler();
resource_handler.setResourceBase("/var/lib/images/");
HandlerList handlers = new HandlerList();
handlers.setHandlers(new Handler[] { root, resource_handler,
new DefaultHandler() });
_jetty.setHandler(handlers);
_jetty.start();
// _jetty.join();
}
@Override
public boolean start() {
// TODO Auto-generated method stub
return false;
}
@Override
public boolean stop() {
return true;
}
@Override
public String getName() {
return "JettyVmDataServer";
}
@Override
public Answer handleVmDataCommand(VmDataCommand cmd) {
String vmDataDir = _vmDataDir + File.separator + cmd.getVmName();
Script.runSimpleBashScript("rm -rf " + vmDataDir);
_fs.mkdirs(vmDataDir);
for (String[] item : cmd.getVmData()) {
try {
_fs.create(vmDataDir, item[1]);
String vmDataFile = vmDataDir + File.separator + item[1];
byte[] data;
if (item[2] != null) {
if (item[1].equals("user-data")) {
data = Base64.decodeBase64(item[2]);
} else {
data = item[2].getBytes();
}
if (data != null && data.length > 0) {
FileOutputStream writer = new FileOutputStream(
vmDataFile);
writer.write(data);
writer.close();
}
}
} catch (IOException e) {
s_logger.warn("Failed to write vm data item " + item[1], e);
return new Answer(cmd, false, "Failed to write vm data item "
+ item[1]);
}
}
return new Answer(cmd);
}
@Override
public void handleVmStarted(VirtualMachineTO vm) {
for (NicTO nic : vm.getNics()) {
if (nic.getType() == TrafficType.Guest) {
if (nic.getIp() != null) {
String ipv4File = _vmDataDir + File.separator
+ vm.getName() + File.separator + "local-ipv4";
try {
_fs.create(_vmDataDir + File.separator + vm.getName(),
"local-ipv4");
BufferedWriter writer = new BufferedWriter(
new FileWriter(ipv4File));
writer.write(nic.getIp());
_ipVmMap.put(nic.getIp(), vm.getName());
writer.close();
} catch (IOException e) {
s_logger.warn(
"Failed to create or write to local-ipv4 file "
+ ipv4File, e);
}
}
}
}
}
@Override
public void handleVmStopped(String vmName) {
String vmDataDir = _vmDataDir + File.separator + vmName;
Script.runSimpleBashScript("rm -rf " + vmDataDir);
}
}