From 08b149a6da8cbdb4e6de7208e62c5b43a4998507 Mon Sep 17 00:00:00 2001 From: Kelven Yang Date: Tue, 17 Aug 2010 16:17:43 -0700 Subject: [PATCH 1/9] Let console proxy servlet support session-less authentication throught API key signed request --- .../cloud/servlet/ConsoleProxyServlet.java | 600 +++++++++++------- 1 file changed, 362 insertions(+), 238 deletions(-) diff --git a/server/src/com/cloud/servlet/ConsoleProxyServlet.java b/server/src/com/cloud/servlet/ConsoleProxyServlet.java index 1901f43cad3..0ed772a960e 100644 --- a/server/src/com/cloud/servlet/ConsoleProxyServlet.java +++ b/server/src/com/cloud/servlet/ConsoleProxyServlet.java @@ -16,56 +16,81 @@ * */ -package com.cloud.servlet; - -import java.io.IOException; - -import javax.servlet.http.HttpServlet; -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; +package com.cloud.servlet; + +import java.io.IOException; +import java.net.URLEncoder; +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import javax.crypto.Mac; +import javax.crypto.spec.SecretKeySpec; +import javax.servlet.http.HttpServlet; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; import javax.servlet.http.HttpSession; - -import org.apache.log4j.Logger; - + +import org.apache.log4j.Logger; + +import com.cloud.api.BaseCmd; import com.cloud.host.HostVO; import com.cloud.server.ManagementServer; import com.cloud.user.Account; import com.cloud.user.User; +import com.cloud.utils.Pair; import com.cloud.utils.component.ComponentLocator; +import com.cloud.utils.db.Transaction; +import com.cloud.utils.encoding.Base64; import com.cloud.vm.UserVmVO; import com.cloud.vm.VMInstanceVO; - -/** - * Thumbnail access : /console?cmd=thumbnail&vm=xxx&w=xxx&h=xxx - * Console access : /conosole?cmd=access&vm=xxx - * Authentication : /console?cmd=auth&vm=xxx&sid=xxx - */ -public class ConsoleProxyServlet extends HttpServlet { - private static final long serialVersionUID = -5515382620323808168L; - public static final Logger s_logger = Logger.getLogger(ConsoleProxyServlet.class.getName()); - private static final int DEFAULT_THUMBNAIL_WIDTH = 144; - private static final int DEFAULT_THUMBNAIL_HEIGHT = 110; - - private final ManagementServer _ms = (ManagementServer)ComponentLocator.getComponent(ManagementServer.Name); - - @Override - protected void doPost(HttpServletRequest req, HttpServletResponse resp) { - doGet(req, resp); - } - - @Override - protected void doGet(HttpServletRequest req, HttpServletResponse resp) { + +/** + * Thumbnail access : /console?cmd=thumbnail&vm=xxx&w=xxx&h=xxx + * Console access : /conosole?cmd=access&vm=xxx + * Authentication : /console?cmd=auth&vm=xxx&sid=xxx + */ +public class ConsoleProxyServlet extends HttpServlet { + private static final long serialVersionUID = -5515382620323808168L; + public static final Logger s_logger = Logger.getLogger(ConsoleProxyServlet.class.getName()); + private static final int DEFAULT_THUMBNAIL_WIDTH = 144; + private static final int DEFAULT_THUMBNAIL_HEIGHT = 110; + + private final ManagementServer _ms = (ManagementServer)ComponentLocator.getComponent(ManagementServer.Name); + + @Override + protected void doPost(HttpServletRequest req, HttpServletResponse resp) { + doGet(req, resp); + } + + @Override + protected void doGet(HttpServletRequest req, HttpServletResponse resp) { try { + String userId = null; + String account = null; + Account accountObj = null; + + Map params = new HashMap(); + params.putAll(req.getParameterMap()); + HttpSession session = req.getSession(false); if(session == null) { - s_logger.info("Invalid web session, reject console/thumbnail access"); - sendResponse(resp, "Access denied. You haven't logged in or your web session has timed out"); - return; + if(verifyRequest(params)) { + userId = (String)params.get(BaseCmd.Properties.USER_ID.getName())[0]; + account = (String)params.get(BaseCmd.Properties.ACCOUNT.getName())[0]; + accountObj = (Account)params.get(BaseCmd.Properties.ACCOUNT_OBJ.getName())[0]; + } else { + s_logger.info("Invalid web session or API key in request, reject console/thumbnail access"); + sendResponse(resp, "Access denied. Invalid web session or API key in request"); + return; + } + } else { + userId = (String)session.getAttribute(BaseCmd.Properties.USER_ID.getName()); + account = (String)session.getAttribute(BaseCmd.Properties.ACCOUNT.getName()); + accountObj = (Account)session.getAttribute(BaseCmd.Properties.ACCOUNT_OBJ.getName()); } - - String userId = (String)session.getAttribute("userId"); - String account = (String)session.getAttribute("account"); - Account accountObj = (Account)session.getAttribute("accountobj"); // Do a sanity check here to make sure the user hasn't already been deleted if ((userId == null) || (account == null) || (accountObj == null) || !verifyUser(Long.valueOf(userId))) { @@ -73,202 +98,199 @@ public class ConsoleProxyServlet extends HttpServlet { sendResponse(resp, "Access denied. Invalid or inconsistent account is found"); return; } - - String cmd = req.getParameter("cmd"); - if(cmd == null || !isValidCmd(cmd)) { - s_logger.info("invalid console servlet command: " + cmd); - sendResponse(resp, ""); - return; - } - - String vmIdString = req.getParameter("vm"); - long vmId = 0; - try { - vmId = Long.parseLong(vmIdString); - } catch(NumberFormatException e) { - s_logger.info("invalid console servlet command parameter: " + vmIdString); - sendResponse(resp, ""); - return; - } - - if(!checkSessionPermision(req, vmId)) { - sendResponse(resp, "Permission denied"); - return; - } - - if(cmd.equalsIgnoreCase("thumbnail")) - handleThumbnailRequest(req, resp, vmId); - else if(cmd.equalsIgnoreCase("access")) - handleAccessRequest(req, resp, vmId); - else - handleAuthRequest(req, resp, vmId); - - } catch (Throwable e) { - s_logger.error("Unexepected exception in ConsoleProxyServlet", e); - sendResponse(resp, ""); - } - } - - private void handleThumbnailRequest(HttpServletRequest req, HttpServletResponse resp, long vmId) { - VMInstanceVO vm = _ms.findVMInstanceById(vmId); - if(vm == null) { - s_logger.warn("VM " + vmId + " does not exist, sending blank response for thumbnail request"); - sendResponse(resp, ""); - return; - } - - if(vm.getHostId() == null) { - s_logger.warn("VM " + vmId + " lost host info, sending blank response for thumbnail request"); - sendResponse(resp, ""); - return; - } - - HostVO host = _ms.getHostBy(vm.getHostId()); - if(host == null) { - s_logger.warn("VM " + vmId + "'s host does not exist, sending blank response for thumbnail request"); - sendResponse(resp, ""); - return; - } - - String rootUrl = _ms.getConsoleAccessUrlRoot(vmId); - if(rootUrl == null) { - sendResponse(resp, ""); - return; - } - - int w = DEFAULT_THUMBNAIL_WIDTH; - int h = DEFAULT_THUMBNAIL_HEIGHT; - - String value = req.getParameter("w"); - try { - w = Integer.parseInt(value); - } catch(NumberFormatException e) { - } - - value = req.getParameter("h"); - try { - h = Integer.parseInt(value); - } catch(NumberFormatException e) { - } - - try { - resp.sendRedirect(composeThumbnailUrl(rootUrl, vm, host, w, h)); - } catch (IOException e) { - if(s_logger.isInfoEnabled()) - s_logger.info("Client may already close the connection"); - } - } - - private void handleAccessRequest(HttpServletRequest req, HttpServletResponse resp, long vmId) { - VMInstanceVO vm = _ms.findVMInstanceById(vmId); - if(vm == null) { - s_logger.warn("VM " + vmId + " does not exist, sending blank response for console access request"); - sendResponse(resp, ""); - return; - } - - if(vm.getHostId() == null) { - s_logger.warn("VM " + vmId + " lost host info, sending blank response for console access request"); - sendResponse(resp, ""); - return; - } - - HostVO host = _ms.getHostBy(vm.getHostId()); - if(host == null) { - s_logger.warn("VM " + vmId + "'s host does not exist, sending blank response for console access request"); - sendResponse(resp, ""); - return; - } - - String rootUrl = _ms.getConsoleAccessUrlRoot(vmId); - if(rootUrl == null) { - sendResponse(resp, "

Console access will be ready in a few minutes. Please try it again later.

"); - return; - } + + String cmd = req.getParameter("cmd"); + if(cmd == null || !isValidCmd(cmd)) { + s_logger.info("invalid console servlet command: " + cmd); + sendResponse(resp, ""); + return; + } + + String vmIdString = req.getParameter("vm"); + long vmId = 0; + try { + vmId = Long.parseLong(vmIdString); + } catch(NumberFormatException e) { + s_logger.info("invalid console servlet command parameter: " + vmIdString); + sendResponse(resp, ""); + return; + } + + if(!checkSessionPermision(req, vmId, accountObj)) { + sendResponse(resp, "Permission denied"); + return; + } + + if(cmd.equalsIgnoreCase("thumbnail")) + handleThumbnailRequest(req, resp, vmId); + else if(cmd.equalsIgnoreCase("access")) + handleAccessRequest(req, resp, vmId); + else + handleAuthRequest(req, resp, vmId); + + } catch (Throwable e) { + s_logger.error("Unexepected exception in ConsoleProxyServlet", e); + sendResponse(resp, "Server Internal Error"); + } + } + + private void handleThumbnailRequest(HttpServletRequest req, HttpServletResponse resp, long vmId) { + VMInstanceVO vm = _ms.findVMInstanceById(vmId); + if(vm == null) { + s_logger.warn("VM " + vmId + " does not exist, sending blank response for thumbnail request"); + sendResponse(resp, ""); + return; + } + + if(vm.getHostId() == null) { + s_logger.warn("VM " + vmId + " lost host info, sending blank response for thumbnail request"); + sendResponse(resp, ""); + return; + } + + HostVO host = _ms.getHostBy(vm.getHostId()); + if(host == null) { + s_logger.warn("VM " + vmId + "'s host does not exist, sending blank response for thumbnail request"); + sendResponse(resp, ""); + return; + } + + String rootUrl = _ms.getConsoleAccessUrlRoot(vmId); + if(rootUrl == null) { + sendResponse(resp, ""); + return; + } + + int w = DEFAULT_THUMBNAIL_WIDTH; + int h = DEFAULT_THUMBNAIL_HEIGHT; + + String value = req.getParameter("w"); + try { + w = Integer.parseInt(value); + } catch(NumberFormatException e) { + } + + value = req.getParameter("h"); + try { + h = Integer.parseInt(value); + } catch(NumberFormatException e) { + } + + try { + resp.sendRedirect(composeThumbnailUrl(rootUrl, vm, host, w, h)); + } catch (IOException e) { + if(s_logger.isInfoEnabled()) + s_logger.info("Client may already close the connection"); + } + } + + private void handleAccessRequest(HttpServletRequest req, HttpServletResponse resp, long vmId) { + VMInstanceVO vm = _ms.findVMInstanceById(vmId); + if(vm == null) { + s_logger.warn("VM " + vmId + " does not exist, sending blank response for console access request"); + sendResponse(resp, ""); + return; + } + + if(vm.getHostId() == null) { + s_logger.warn("VM " + vmId + " lost host info, sending blank response for console access request"); + sendResponse(resp, ""); + return; + } + + HostVO host = _ms.getHostBy(vm.getHostId()); + if(host == null) { + s_logger.warn("VM " + vmId + "'s host does not exist, sending blank response for console access request"); + sendResponse(resp, ""); + return; + } + + String rootUrl = _ms.getConsoleAccessUrlRoot(vmId); + if(rootUrl == null) { + sendResponse(resp, "

Console access will be ready in a few minutes. Please try it again later.

"); + return; + } String vmName = vm.getName(); if(vmName == null) vmName = vm.getInstanceName(); - - StringBuffer sb = new StringBuffer(); - sb.append("").append(vmName).append(""); - sendResponse(resp, sb.toString()); - } - - private void handleAuthRequest(HttpServletRequest req, HttpServletResponse resp, long vmId) { - - // TODO authentication channel between console proxy VM and management server needs to be secured, - // the data is now being sent through private network, but this is apparently not enough - VMInstanceVO vm = _ms.findVMInstanceById(vmId); - if(vm == null) { - s_logger.warn("VM " + vmId + " does not exist, sending failed response for authentication request from console proxy"); - sendResponse(resp, "failed"); - return; - } - - if(vm.getHostId() == null) { - s_logger.warn("VM " + vmId + " lost host info, failed response for authentication request from console proxy"); - sendResponse(resp, "failed"); - return; - } - - HostVO host = _ms.getHostBy(vm.getHostId()); - if(host == null) { - s_logger.warn("VM " + vmId + "'s host does not exist, sending failed response for authentication request from console proxy"); - sendResponse(resp, "failed"); - return; - } - - String sid = req.getParameter("sid"); - if(sid == null || !sid.equals(vm.getVncPassword())) { - s_logger.warn("sid " + sid + " in url does not match stored sid " + vm.getVncPassword()); - sendResponse(resp, "failed"); - return; - } - - sendResponse(resp, "success"); - } - - private String composeThumbnailUrl(String rootUrl, VMInstanceVO vm, HostVO host, int w, int h) { - StringBuffer sb = new StringBuffer(rootUrl); - sb.append("/getscreen?host=").append(host.getPrivateIpAddress()); - sb.append("&port=").append(_ms.getVncPort(vm)); - sb.append("&sid=").append(vm.getVncPassword()); - sb.append("&w=").append(w).append("&h=").append(h); - sb.append("&tag=").append(vm.getId()); - - if(s_logger.isInfoEnabled()) - s_logger.info("Compose thumbnail url: " + sb.toString()); - return sb.toString(); - } - - private String composeConsoleAccessUrl(String rootUrl, VMInstanceVO vm, HostVO host) { - StringBuffer sb = new StringBuffer(rootUrl); - sb.append("/ajax?host=").append(host.getPrivateIpAddress()); - sb.append("&port=").append(_ms.getVncPort(vm)); - sb.append("&sid=").append(vm.getVncPassword()); - sb.append("&tag=").append(vm.getId()); - - if(s_logger.isInfoEnabled()) - s_logger.info("Compose console url: " + sb.toString()); - return sb.toString(); - } - - private void sendResponse(HttpServletResponse resp, String content) { - try { - resp.getWriter().print(content); - } catch(IOException e) { - if(s_logger.isInfoEnabled()) - s_logger.info("Client may already close the connection"); - } - } - - private boolean checkSessionPermision(HttpServletRequest req, long vmId) { + + StringBuffer sb = new StringBuffer(); + sb.append("").append(vmName).append(""); + sendResponse(resp, sb.toString()); + } + + private void handleAuthRequest(HttpServletRequest req, HttpServletResponse resp, long vmId) { + + // TODO authentication channel between console proxy VM and management server needs to be secured, + // the data is now being sent through private network, but this is apparently not enough + VMInstanceVO vm = _ms.findVMInstanceById(vmId); + if(vm == null) { + s_logger.warn("VM " + vmId + " does not exist, sending failed response for authentication request from console proxy"); + sendResponse(resp, "failed"); + return; + } + + if(vm.getHostId() == null) { + s_logger.warn("VM " + vmId + " lost host info, failed response for authentication request from console proxy"); + sendResponse(resp, "failed"); + return; + } + + HostVO host = _ms.getHostBy(vm.getHostId()); + if(host == null) { + s_logger.warn("VM " + vmId + "'s host does not exist, sending failed response for authentication request from console proxy"); + sendResponse(resp, "failed"); + return; + } + + String sid = req.getParameter("sid"); + if(sid == null || !sid.equals(vm.getVncPassword())) { + s_logger.warn("sid " + sid + " in url does not match stored sid " + vm.getVncPassword()); + sendResponse(resp, "failed"); + return; + } + + sendResponse(resp, "success"); + } + + private String composeThumbnailUrl(String rootUrl, VMInstanceVO vm, HostVO host, int w, int h) { + StringBuffer sb = new StringBuffer(rootUrl); + sb.append("/getscreen?host=").append(host.getPrivateIpAddress()); + sb.append("&port=").append(_ms.getVncPort(vm)); + sb.append("&sid=").append(vm.getVncPassword()); + sb.append("&w=").append(w).append("&h=").append(h); + sb.append("&tag=").append(vm.getId()); + + if(s_logger.isInfoEnabled()) + s_logger.info("Compose thumbnail url: " + sb.toString()); + return sb.toString(); + } + + private String composeConsoleAccessUrl(String rootUrl, VMInstanceVO vm, HostVO host) { + StringBuffer sb = new StringBuffer(rootUrl); + sb.append("/ajax?host=").append(host.getPrivateIpAddress()); + sb.append("&port=").append(_ms.getVncPort(vm)); + sb.append("&sid=").append(vm.getVncPassword()); + sb.append("&tag=").append(vm.getId()); + + if(s_logger.isInfoEnabled()) + s_logger.info("Compose console url: " + sb.toString()); + return sb.toString(); + } + + private void sendResponse(HttpServletResponse resp, String content) { + try { + resp.getWriter().print(content); + } catch(IOException e) { + if(s_logger.isInfoEnabled()) + s_logger.info("Client may already close the connection"); + } + } + + private boolean checkSessionPermision(HttpServletRequest req, long vmId, Account accountObj) { - HttpSession session = req.getSession(false); - Account accountObj = (Account)session.getAttribute("accountobj"); - VMInstanceVO vm = _ms.findVMInstanceById(vmId); UserVmVO userVm; switch(vm.getType()) @@ -295,14 +317,14 @@ public class ConsoleProxyServlet extends HttpServlet { break; } - return true; - } - - private boolean isValidCmd(String cmd) { - if(cmd.equalsIgnoreCase("thumbnail") || cmd.equalsIgnoreCase("access") || cmd.equalsIgnoreCase("auth")) - return true; - - return false; + return true; + } + + private boolean isValidCmd(String cmd) { + if(cmd.equalsIgnoreCase("thumbnail") || cmd.equalsIgnoreCase("access") || cmd.equalsIgnoreCase("auth")) + return true; + + return false; } public boolean verifyUser(Long userId) { @@ -320,4 +342,106 @@ public class ConsoleProxyServlet extends HttpServlet { } return true; } -} + + // copied and modified from ApiServer.java. + // TODO need to replace the whole servlet with a API command + private boolean verifyRequest(Map requestParameters) { + try { + String apiKey = null; + String secretKey = null; + String signature = null; + String unsignedRequest = null; + + // - build a request string with sorted params, make sure it's all lowercase + // - sign the request, verify the signature is the same + List parameterNames = new ArrayList(); + + for (Object paramNameObj : requestParameters.keySet()) { + parameterNames.add((String)paramNameObj); // put the name in a list that we'll sort later + } + + Collections.sort(parameterNames); + + for (String paramName : parameterNames) { + // parameters come as name/value pairs in the form String/String[] + String paramValue = ((String[])requestParameters.get(paramName))[0]; + + if ("signature".equalsIgnoreCase(paramName)) { + signature = paramValue; + } else { + if ("apikey".equalsIgnoreCase(paramName)) { + apiKey = paramValue; + } + + if (unsignedRequest == null) { + unsignedRequest = paramName + "=" + URLEncoder.encode(paramValue, "UTF-8").replaceAll("\\+", "%20"); + } else { + unsignedRequest = unsignedRequest + "&" + paramName + "=" + URLEncoder.encode(paramValue, "UTF-8").replaceAll("\\+", "%20"); + } + } + } + + + // if api/secret key are passed to the parameters + if ((signature == null) || (apiKey == null)) { + if (s_logger.isDebugEnabled()) { + s_logger.info("expired session, missing signature, or missing apiKey -- ignoring request...sig: " + signature + ", apiKey: " + apiKey); + } + return false; // no signature, bad request + } + + Transaction txn = Transaction.open(Transaction.CLOUD_DB); + txn.close(); + User user = null; + // verify there is a user with this api key + Pair userAcctPair = _ms.findUserByApiKey(apiKey); + if (userAcctPair == null) { + s_logger.info("apiKey does not map to a valid user -- ignoring request, apiKey: " + apiKey); + return false; + } + + user = userAcctPair.first(); + Account account = userAcctPair.second(); + + if (!user.getState().equals(Account.ACCOUNT_STATE_ENABLED) || !account.getState().equals(Account.ACCOUNT_STATE_ENABLED)) { + s_logger.info("disabled or locked user accessing the api, userid = " + user.getId() + "; name = " + user.getUsername() + "; state: " + user.getState() + "; accountState: " + account.getState()); + return false; + } + + if (account.getType() == Account.ACCOUNT_TYPE_NORMAL) { + requestParameters.put(BaseCmd.Properties.USER_ID.getName(), new String[] { user.getId().toString() }); + requestParameters.put(BaseCmd.Properties.ACCOUNT.getName(), new String[] { account.getAccountName() }); + requestParameters.put(BaseCmd.Properties.DOMAIN_ID.getName(), new String[] { account.getDomainId().toString() }); + requestParameters.put(BaseCmd.Properties.ACCOUNT_OBJ.getName(), new Object[] { account }); + } else { + requestParameters.put(BaseCmd.Properties.USER_ID.getName(), new String[] { user.getId().toString() }); + requestParameters.put(BaseCmd.Properties.ACCOUNT.getName(), new String[] { account.getAccountName() }); + requestParameters.put(BaseCmd.Properties.ACCOUNT_OBJ.getName(), new Object[] { account }); + } + + // verify secret key exists + secretKey = user.getSecretKey(); + if (secretKey == null) { + s_logger.info("User does not have a secret key associated with the account -- ignoring request, username: " + user.getUsername()); + return false; + } + + unsignedRequest = unsignedRequest.toLowerCase(); + + Mac mac = Mac.getInstance("HmacSHA1"); + SecretKeySpec keySpec = new SecretKeySpec(secretKey.getBytes(), "HmacSHA1"); + mac.init(keySpec); + mac.update(unsignedRequest.getBytes()); + byte[] encryptedBytes = mac.doFinal(); + String computedSignature = Base64.encodeBytes(encryptedBytes); + boolean equalSig = signature.equals(computedSignature); + if (!equalSig) { + s_logger.info("User signature: " + signature + " is not equaled to computed signature: " + computedSignature); + } + return equalSig; + } catch (Exception ex) { + s_logger.error("unable to verifty request signature", ex); + } + return false; + } +} From b3bf26e0de030ebe0ae9f29813be7567cf58fe3c Mon Sep 17 00:00:00 2001 From: Kevin Kluge Date: Tue, 17 Aug 2010 16:27:45 -0700 Subject: [PATCH 2/9] Resolved Fixed: 5757 --- core/src/com/cloud/host/Status.java | 1 + 1 file changed, 1 insertion(+) diff --git a/core/src/com/cloud/host/Status.java b/core/src/com/cloud/host/Status.java index 30274622d27..5adf487445e 100644 --- a/core/src/com/cloud/host/Status.java +++ b/core/src/com/cloud/host/Status.java @@ -167,6 +167,7 @@ public enum Status { s_fsm.addTransition(Status.Disconnected, Event.Ping, Status.Up); s_fsm.addTransition(Status.Disconnected, Event.ManagementServerDown, Status.Disconnected); s_fsm.addTransition(Status.Disconnected, Event.WaitedTooLong, Status.Alert); + s_fsm.addTransition(Status.Disconnected, Event.Remove, Status.Removed); s_fsm.addTransition(Status.Down, Event.MaintenanceRequested, Status.PrepareForMaintenance); s_fsm.addTransition(Status.Down, Event.AgentConnected, Status.Connecting); s_fsm.addTransition(Status.Down, Event.Remove, Status.Removed); From a4f3dacd263b2dce6a94677661f9ac692e0cd719 Mon Sep 17 00:00:00 2001 From: Kelven Yang Date: Tue, 17 Aug 2010 17:39:05 -0700 Subject: [PATCH 3/9] Use a dedicated account object for locking test, so that when we run into SQL exception, we won't accidently release a lock that we haven't actually acquired. This will cause NPE exception in Merovingian.java --- server/src/com/cloud/network/NetworkManagerImpl.java | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/server/src/com/cloud/network/NetworkManagerImpl.java b/server/src/com/cloud/network/NetworkManagerImpl.java index ab0a44160d7..31236697bfc 100755 --- a/server/src/com/cloud/network/NetworkManagerImpl.java +++ b/server/src/com/cloud/network/NetworkManagerImpl.java @@ -221,6 +221,9 @@ public class NetworkManagerImpl implements NetworkManager, VirtualMachineManager final long accountId = account.getId(); + // use a dedicated account object for locking test, so that when we get SQL exception + // we won't be misled to release an actually un-owned lock + AccountVO lockedAccount = null; Transaction txn = Transaction.currentTxn(); try { final EventVO event = new EventVO(); @@ -230,8 +233,8 @@ public class NetworkManagerImpl implements NetworkManager, VirtualMachineManager txn.start(); - account = _accountDao.acquire(accountId); - if (account == null) { + lockedAccount = _accountDao.acquire(accountId); + if (lockedAccount == null) { s_logger.warn("Unable to lock account " + accountId); return null; } @@ -339,7 +342,7 @@ public class NetworkManagerImpl implements NetworkManager, VirtualMachineManager txn.commit(); return null; } finally { - if (account != null) { + if (lockedAccount != null) { if(s_logger.isDebugEnabled()) s_logger.debug("Releasing lock account " + accountId); From cc59b5b7de8a52edf3e601c29e4d1c14e28b664f Mon Sep 17 00:00:00 2001 From: Anthony Xu Date: Wed, 18 Aug 2010 17:12:20 -0700 Subject: [PATCH 4/9] bug 5934: if it is standalone, give cluser name as Standalone-ip/name status 5934: resolved fixed --- .../com/cloud/api/commands/AddHostCmd.java | 7 +++++++ .../xen/discoverer/XcpServerDiscoverer.java | 19 +------------------ 2 files changed, 8 insertions(+), 18 deletions(-) diff --git a/server/src/com/cloud/api/commands/AddHostCmd.java b/server/src/com/cloud/api/commands/AddHostCmd.java index 637e30d4609..7c154c9279d 100644 --- a/server/src/com/cloud/api/commands/AddHostCmd.java +++ b/server/src/com/cloud/api/commands/AddHostCmd.java @@ -75,6 +75,13 @@ public class AddHostCmd extends BaseCmd { throw new ServerApiException(BaseCmd.PARAM_ERROR, "Can't specify cluster by both id and name"); } + if (clusterName == null && clusterId == null) { + // Stand alone, assign a name to it + String[] stringarray = url.split("//"); + String address = stringarray[stringarray.length - 1]; + clusterName = "Standalone-" + address; + } + if ((clusterName != null || clusterId != null) && podId == null) { throw new ServerApiException(BaseCmd.PARAM_ERROR, "Can't specify cluster without specifying the pod"); } diff --git a/server/src/com/cloud/hypervisor/xen/discoverer/XcpServerDiscoverer.java b/server/src/com/cloud/hypervisor/xen/discoverer/XcpServerDiscoverer.java index db50afd2693..471123ed9bb 100644 --- a/server/src/com/cloud/hypervisor/xen/discoverer/XcpServerDiscoverer.java +++ b/server/src/com/cloud/hypervisor/xen/discoverer/XcpServerDiscoverer.java @@ -81,7 +81,6 @@ public class XcpServerDiscoverer extends DiscovererBase implements Discoverer, L protected String _privateNic; protected String _storageNic1; protected String _storageNic2; - protected boolean _formPoolsInPod; protected int _wait; protected XenServerConnectionPool _connPool; protected String _increase; @@ -132,19 +131,6 @@ public class XcpServerDiscoverer extends DiscovererBase implements Discoverer, L String cluster = null; if (clusterId != null) { cluster = Long.toString(clusterId); - } else if (_formPoolsInPod) { - if (podId != null) { - List clusters = _clusterDao.listByPodId(podId); - if (clusters.size() > 1) { - throw new DiscoveryException("There are more than one cluster in the pod and we don't know which to add to."); - } else if (clusters.size() == 1) { - clusterId = clusters.get(0).getId(); - cluster = Long.toString(clusterId); - } else { - Map pools = Pool.getAllRecords(conn); - cluster = pools.values().iterator().next().uuid; - } - } } Map hosts = Host.getAllRecords(conn); @@ -300,7 +286,7 @@ public class XcpServerDiscoverer extends DiscovererBase implements Discoverer, L if ( resources.size() == 0 ) { return false; } - if (clusterId == null && !_formPoolsInPod) { + if (clusterId == null ) { if (resources.size() > 1) { s_logger.warn("There's no cluster specified but we found a pool of xenservers " + resources.size()); throw new DiscoveryException("There's no cluster specified but we found a pool of xenservers " + resources.size()); @@ -437,9 +423,6 @@ public class XcpServerDiscoverer extends DiscovererBase implements Discoverer, L value = _params.get("xen.check.hvm"); _checkHvm = value == null ? true : Boolean.parseBoolean(value); - - value = _params.get(Config.CreatePoolsInPod.key()); - _formPoolsInPod = Boolean.parseBoolean(value); _connPool = XenServerConnectionPool.getInstance(); From 9b690f685a342da97d0975508a2a1b2af64aa688 Mon Sep 17 00:00:00 2001 From: edison Date: Wed, 18 Aug 2010 17:39:22 -0700 Subject: [PATCH 5/9] Issue #: 5954 Status 5954: resolved fixed exclude link local gateway --- .../dc/dao/DataCenterLinkLocalIpAddressDaoImpl.java | 2 ++ utils/src/com/cloud/utils/net/NetUtils.java | 10 +++++++++- 2 files changed, 11 insertions(+), 1 deletion(-) diff --git a/core/src/com/cloud/dc/dao/DataCenterLinkLocalIpAddressDaoImpl.java b/core/src/com/cloud/dc/dao/DataCenterLinkLocalIpAddressDaoImpl.java index 17414e28d58..484d84005b0 100644 --- a/core/src/com/cloud/dc/dao/DataCenterLinkLocalIpAddressDaoImpl.java +++ b/core/src/com/cloud/dc/dao/DataCenterLinkLocalIpAddressDaoImpl.java @@ -52,6 +52,7 @@ public class DataCenterLinkLocalIpAddressDaoImpl extends GenericDaoBase Date: Wed, 18 Aug 2010 13:54:53 +0530 Subject: [PATCH 6/9] Check for null vnet when allocation fails --- server/src/com/cloud/network/NetworkManagerImpl.java | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) mode change 100755 => 100644 server/src/com/cloud/network/NetworkManagerImpl.java diff --git a/server/src/com/cloud/network/NetworkManagerImpl.java b/server/src/com/cloud/network/NetworkManagerImpl.java old mode 100755 new mode 100644 index 31236697bfc..33aaa146fa1 --- a/server/src/com/cloud/network/NetworkManagerImpl.java +++ b/server/src/com/cloud/network/NetworkManagerImpl.java @@ -844,7 +844,9 @@ public class NetworkManagerImpl implements NetworkManager, VirtualMachineManager vnet = _dcDao.allocateVnet(router.getDataCenterId(), router.getAccountId()); } vnetAllocated = true; - routerMacAddress = getRouterMacForVnet(dc, vnet); + if(vnet != null){ + routerMacAddress = getRouterMacForVnet(dc, vnet); + } } else if (router.getRole() == Role.DHCP_USERDATA) { if (!Vlan.UNTAGGED.equals(router.getVlanId())) { vnet = router.getVlanId().trim(); From d4682d4c90231bca7e88071ae586f5be5044a243 Mon Sep 17 00:00:00 2001 From: kishan Date: Wed, 18 Aug 2010 19:43:16 +0530 Subject: [PATCH 7/9] Issue #: 5775 Release vnet and private IP allocation when domR fails to start --- .../com/cloud/network/NetworkManagerImpl.java | 53 ++++++++++--------- 1 file changed, 27 insertions(+), 26 deletions(-) diff --git a/server/src/com/cloud/network/NetworkManagerImpl.java b/server/src/com/cloud/network/NetworkManagerImpl.java index 33aaa146fa1..2e7e287befe 100644 --- a/server/src/com/cloud/network/NetworkManagerImpl.java +++ b/server/src/com/cloud/network/NetworkManagerImpl.java @@ -770,6 +770,9 @@ public class NetworkManagerImpl implements NetworkManager, VirtualMachineManager if(s_logger.isDebugEnabled()) s_logger.debug("Lock on router " + routerId + " is acquired"); + boolean started = false; + String vnet = null; + boolean vnetAllocated = false; try { final State state = router.getState(); if (state == State.Running) { @@ -816,10 +819,7 @@ public class NetworkManagerImpl implements NetworkManager, VirtualMachineManager throw new ConcurrentOperationException("Someone else is starting the router: " + router.toString()); } - String vnet = null; - boolean vnetAllocated = false; final boolean mirroredVols = router.isMirroredVols(); - boolean started = false; try { event = new EventVO(); event.setUserId(1L); @@ -832,6 +832,7 @@ public class NetworkManagerImpl implements NetworkManager, VirtualMachineManager for (final UserVmVO vm : vms) { if (vm.getVnet() != null) { vnet = vm.getVnet(); + break; } } } @@ -969,28 +970,6 @@ public class NetworkManagerImpl implements NetworkManager, VirtualMachineManager return _routerDao.findById(routerId); } catch (final Throwable th) { - Transaction txn = Transaction.currentTxn(); - if (!started) { - txn.start(); - if (vnetAllocated == true && vnet != null) { - _dcDao.releaseVnet(vnet, router.getDataCenterId(), router.getAccountId()); - } - - router.setVnet(null); - String privateIpAddress = router.getPrivateIpAddress(); - - router.setPrivateIpAddress(null); - - if (privateIpAddress != null) { - _dcDao.releasePrivateIpAddress(privateIpAddress, router.getDataCenterId(), router.getId()); - } - - - if (_routerDao.updateIf(router, Event.OperationFailed, null)) { - txn.commit(); - } - } - if (th instanceof ExecutionException) { s_logger.error("Error while starting router due to " + th.getMessage()); } else if (th instanceof ConcurrentOperationException) { @@ -1003,8 +982,30 @@ public class NetworkManagerImpl implements NetworkManager, VirtualMachineManager return null; } } finally { + + if (!started){ + Transaction txn = Transaction.currentTxn(); + txn.start(); + if (vnetAllocated == true && vnet != null) { + _dcDao.releaseVnet(vnet, router.getDataCenterId(), router.getAccountId()); + } + + router.setVnet(null); + String privateIpAddress = router.getPrivateIpAddress(); + + router.setPrivateIpAddress(null); + + if (privateIpAddress != null) { + _dcDao.releasePrivateIpAddress(privateIpAddress, router.getDataCenterId(), router.getId()); + } + + + if (_routerDao.updateIf(router, Event.OperationFailed, null)) { + txn.commit(); + } + } + if (router != null) { - if(s_logger.isDebugEnabled()) s_logger.debug("Releasing lock on router " + routerId); _routerDao.release(routerId); From 42a1492610569f2a50f2347e1a1c1ce4e3d3c6e8 Mon Sep 17 00:00:00 2001 From: "Manuel Amador (Rudd-O)" Date: Thu, 19 Aug 2010 10:35:17 -0700 Subject: [PATCH 8/9] read_properties imported from cloud_utils --- client/bindir/cloud-update-xenserver-licenses.in | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/client/bindir/cloud-update-xenserver-licenses.in b/client/bindir/cloud-update-xenserver-licenses.in index 6249f7805fe..94b8e3e8ceb 100755 --- a/client/bindir/cloud-update-xenserver-licenses.in +++ b/client/bindir/cloud-update-xenserver-licenses.in @@ -125,7 +125,7 @@ except IndexError: e("The first argument must be the license file to use") if options.all: if len(args) != 0: e("IP addresses cannot be specified if -a is specified") - config = cloud_utils.read_properties(cfg) + config = read_properties(cfg) creds = getknownhosts(config["db.cloud.host"],config["db.cloud.username"],config["db.cloud.password"]) hosts = creds.keys() else: From d238026769138f8117c6ff7474dc10baae7bfc20 Mon Sep 17 00:00:00 2001 From: "Manuel Amador (Rudd-O)" Date: Thu, 19 Aug 2010 10:55:43 -0700 Subject: [PATCH 9/9] Gitignore ignores pyc ocmpiled files and build.number --- .gitignore | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.gitignore b/.gitignore index f3bba532e27..f0aee896b52 100644 --- a/.gitignore +++ b/.gitignore @@ -12,4 +12,5 @@ dist *~ *.bak cloud-*.tar.bz2 - +*.pyc +build.number