// 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); } }