From 738a326dfaedff5cfe6b86f74d1c1ded77b0a306 Mon Sep 17 00:00:00 2001 From: Kelven Yang Date: Thu, 28 Jun 2012 16:20:40 -0700 Subject: [PATCH] Inter-VLAN support in VMware --- .../vmware/resource/VmwareResource.java | 295 +++++++++++++++++- 1 file changed, 292 insertions(+), 3 deletions(-) diff --git a/core/src/com/cloud/hypervisor/vmware/resource/VmwareResource.java b/core/src/com/cloud/hypervisor/vmware/resource/VmwareResource.java index bbb47364c97..19e90cb3531 100755 --- a/core/src/com/cloud/hypervisor/vmware/resource/VmwareResource.java +++ b/core/src/com/cloud/hypervisor/vmware/resource/VmwareResource.java @@ -29,6 +29,7 @@ import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Random; +import java.util.Set; import java.util.TimeZone; import java.util.UUID; @@ -87,6 +88,8 @@ import com.cloud.agent.api.NetworkUsageCommand; import com.cloud.agent.api.PingCommand; import com.cloud.agent.api.PingRoutingCommand; import com.cloud.agent.api.PingTestCommand; +import com.cloud.agent.api.PlugNicAnswer; +import com.cloud.agent.api.PlugNicCommand; import com.cloud.agent.api.PoolEjectCommand; import com.cloud.agent.api.PrepareForMigrationAnswer; import com.cloud.agent.api.PrepareForMigrationCommand; @@ -95,8 +98,11 @@ import com.cloud.agent.api.ReadyCommand; import com.cloud.agent.api.RebootAnswer; import com.cloud.agent.api.RebootCommand; import com.cloud.agent.api.RebootRouterCommand; +import com.cloud.agent.api.SetSourceNatAnswer; import com.cloud.agent.api.SetupAnswer; import com.cloud.agent.api.SetupCommand; +import com.cloud.agent.api.SetupGuestNetworkAnswer; +import com.cloud.agent.api.SetupGuestNetworkCommand; import com.cloud.agent.api.StartAnswer; import com.cloud.agent.api.StartCommand; import com.cloud.agent.api.StartupCommand; @@ -105,6 +111,8 @@ import com.cloud.agent.api.StartupStorageCommand; import com.cloud.agent.api.StopAnswer; import com.cloud.agent.api.StopCommand; import com.cloud.agent.api.StoragePoolInfo; +import com.cloud.agent.api.UnPlugNicAnswer; +import com.cloud.agent.api.UnPlugNicCommand; import com.cloud.agent.api.UpgradeSnapshotCommand; import com.cloud.agent.api.ValidateSnapshotAnswer; import com.cloud.agent.api.ValidateSnapshotCommand; @@ -114,14 +122,19 @@ import com.cloud.agent.api.check.CheckSshCommand; import com.cloud.agent.api.routing.DhcpEntryCommand; import com.cloud.agent.api.routing.IpAssocAnswer; import com.cloud.agent.api.routing.IpAssocCommand; +import com.cloud.agent.api.routing.IpAssocVpcCommand; import com.cloud.agent.api.routing.LoadBalancerConfigCommand; import com.cloud.agent.api.routing.NetworkElementCommand; import com.cloud.agent.api.routing.RemoteAccessVpnCfgCommand; import com.cloud.agent.api.routing.SavePasswordCommand; import com.cloud.agent.api.routing.SetFirewallRulesAnswer; import com.cloud.agent.api.routing.SetFirewallRulesCommand; +import com.cloud.agent.api.routing.SetNetworkACLAnswer; +import com.cloud.agent.api.routing.SetNetworkACLCommand; import com.cloud.agent.api.routing.SetPortForwardingRulesAnswer; import com.cloud.agent.api.routing.SetPortForwardingRulesCommand; +import com.cloud.agent.api.routing.SetPortForwardingRulesVpcCommand; +import com.cloud.agent.api.routing.SetSourceNatCommand; import com.cloud.agent.api.routing.SetStaticNatRulesAnswer; import com.cloud.agent.api.routing.SetStaticNatRulesCommand; import com.cloud.agent.api.routing.VmDataCommand; @@ -230,6 +243,9 @@ import com.vmware.vim25.VirtualMachineGuestOsIdentifier; import com.vmware.vim25.VirtualMachinePowerState; import com.vmware.vim25.VirtualMachineRuntimeInfo; import com.vmware.vim25.VirtualSCSISharing; +import com.xensource.xenapi.Connection; +import com.xensource.xenapi.VIF; +import com.xensource.xenapi.VM; public class VmwareResource implements StoragePoolResource, ServerResource, VmwareHostService { private static final Logger s_logger = Logger.getLogger(VmwareResource.class); @@ -409,6 +425,20 @@ public class VmwareResource implements StoragePoolResource, ServerResource, Vmwa answer = execute((GetDomRVersionCmd)cmd); } else if (cmd instanceof CheckNetworkCommand) { answer = execute((CheckNetworkCommand) cmd); + } else if (cmd instanceof SetupGuestNetworkCommand) { + return execute((SetupGuestNetworkCommand) cmd); + } else if (cmd instanceof PlugNicCommand) { + return execute((PlugNicCommand) cmd); + } else if (cmd instanceof UnPlugNicCommand) { + return execute((UnPlugNicCommand) cmd); + } else if (cmd instanceof IpAssocVpcCommand) { + return execute((IpAssocVpcCommand) cmd); + } else if (cmd instanceof SetSourceNatCommand) { + return execute((SetSourceNatCommand) cmd); + } else if (cmd instanceof SetNetworkACLCommand) { + return execute((SetNetworkACLCommand) cmd); + } else if (cmd instanceof SetPortForwardingRulesVpcCommand) { + return execute((SetPortForwardingRulesVpcCommand) cmd); } else { answer = Answer.createUnsupportedCommandAnswer(cmd); } @@ -704,10 +734,269 @@ public class VmwareResource implements StoragePoolResource, ServerResource, Vmwa s_logger.error("Unexpected exception: " + e.toString(), e); return new Answer(cmd, false, "LoadBalancerConfigCommand failed due to " + VmwareHelper.getExceptionMessage(e)); } - } - + } + + private int allocRouterEthDeviceIndex(String domrName, String routerIp, NicTO nic) { + // TODO need to figure out which device number to use for the NIC + return 1; + } + + private int findRouterEthDeviceIndex(String domrName, String routerIp, NicTO nic) { + // TODO + return 1; + } + + private int findRouterEthDeviceIndex(String domrName, String routerIp, IpAddressTO ip) { + + // TODO need to find the dev index inside router based on IP address + return 1; + } + + private SetupGuestNetworkAnswer execute(SetupGuestNetworkCommand cmd) { + VmwareManager mgr = getServiceContext().getStockObject(VmwareManager.CONTEXT_STOCK_NAME); + + NicTO nic = cmd.getNic(); + String routerIp = getRouterSshControlIp(cmd); + String domrGIP = cmd.getAccessDetail(NetworkElementCommand.ROUTER_GUEST_IP); + String domrName = cmd.getAccessDetail(NetworkElementCommand.ROUTER_NAME); + String gw = cmd.getAccessDetail(NetworkElementCommand.GUEST_NETWORK_GATEWAY); + String cidr = Long.toString(NetUtils.getCidrSize(nic.getNetmask()));; + String domainName = cmd.getNetworkDomain(); + String dns = cmd.getDefaultDns1(); + if (dns == null || dns.isEmpty()) { + dns = cmd.getDefaultDns2(); + } else { + String dns2= cmd.getDefaultDns2(); + if ( dns2 != null && !dns2.isEmpty()) { + dns += "," + dns2; + } + } + + try { + int ethDeviceNum = allocRouterEthDeviceIndex(domrName, routerIp, nic); + + String args = "-C "; + String dev = "eth" + ethDeviceNum; + args += " -d " + dev; + args += " -i " + domrGIP; + args += " -g " + gw; + args += " -m " + cidr; + args += " -n " + NetUtils.getSubNet(domrGIP, nic.getNetmask()); + if ( dns != null && !dns.isEmpty() ) { + args += " -s " + dns; + } + if ( domainName != null && !domainName.isEmpty() ) { + args += " -e " + domainName; + } + + Pair result = SshHelper.sshExecute(routerIp, DEFAULT_DOMR_SSHPORT, "root", mgr.getSystemVMKeyFile(), null, + "/opt/cloud/bin/vpc_guestnw.sh " + args); + + if (!result.first()) { + String msg = "SetupGuestNetworkCommand on domain router " + routerIp + " failed. message: " + result.second(); + s_logger.error(msg); + + return new SetupGuestNetworkAnswer(cmd, false, msg); + } + + if (s_logger.isInfoEnabled()) { + s_logger.info("SetupGuestNetworkCommand on domain router " + routerIp + " completed"); + } + + return new SetupGuestNetworkAnswer(cmd, true, "success"); + } catch (Exception e) { + String msg = " UnPlug Nic failed due to " + e.toString(); + s_logger.warn(msg, e); + return new SetupGuestNetworkAnswer(cmd, false, msg); + } + } + + protected IpAssocAnswer execute(IpAssocVpcCommand cmd) { + String[] results = new String[cmd.getIpAddresses().length]; + int i = 0; + String routerName = cmd.getAccessDetail(NetworkElementCommand.ROUTER_NAME); + String routerIp = getRouterSshControlIp(cmd); + + try { + IpAddressTO[] ips = cmd.getIpAddresses(); + for (IpAddressTO ip : ips) { + + assignVPCPublicIpAddress(routerName, routerIp, ip); + results[i++] = ip.getPublicIp() + " - success"; + } + } catch (Exception e) { + s_logger.error("Ip Assoc failure on applying one ip due to exception: ", e); + results[i++] = IpAssocAnswer.errorResult; + } + + return new IpAssocAnswer(cmd, results); + } + + protected SetSourceNatAnswer execute(SetSourceNatCommand cmd) { + VmwareManager mgr = getServiceContext().getStockObject(VmwareManager.CONTEXT_STOCK_NAME); + + String routerName = cmd.getAccessDetail(NetworkElementCommand.ROUTER_NAME); + String routerIp = getRouterSshControlIp(cmd); + IpAddressTO pubIp = cmd.getIpAddress(); + try { + int ethDeviceNum = findRouterEthDeviceIndex(routerName, routerIp, pubIp); + String args = ""; + args += " -A "; + args += " -l "; + args += pubIp.getPublicIp(); + + args += " -c "; + args += "eth" + ethDeviceNum; + + Pair result = SshHelper.sshExecute(routerIp, DEFAULT_DOMR_SSHPORT, "root", mgr.getSystemVMKeyFile(), null, + "/opt/cloud/bin/vpc_snat.sh " + args); + + if (!result.first()) { + String msg = "SetupGuestNetworkCommand on domain router " + routerIp + " failed. message: " + result.second(); + s_logger.error(msg); + + return new SetSourceNatAnswer(cmd, false, msg); + } + + return new SetSourceNatAnswer(cmd, true, "success"); + } catch (Exception e) { + String msg = "Ip SNAT failure due to " + e.toString(); + s_logger.error(msg, e); + return new SetSourceNatAnswer(cmd, false, msg); + } + } + + private SetNetworkACLAnswer execute(SetNetworkACLCommand cmd) { + + VmwareManager mgr = getServiceContext().getStockObject(VmwareManager.CONTEXT_STOCK_NAME); + String routerName = cmd.getAccessDetail(NetworkElementCommand.ROUTER_NAME); + String routerIp = getRouterSshControlIp(cmd); + + String[] results = new String[cmd.getRules().length]; + try { + String [][] rules = cmd.generateFwRules(); + StringBuilder sb = new StringBuilder(); + String[] aclRules = rules[0]; + if (aclRules.length == 0) { + return new SetNetworkACLAnswer(cmd, true, results); + } + + for (int i = 0; i < aclRules.length; i++) { + sb.append(aclRules[i]).append(','); + } + + NicTO nic = cmd.getNic(); + int ethDeviceNum = findRouterEthDeviceIndex(routerName, routerIp, nic); + String args = ""; + args += " -d " + "eth" + ethDeviceNum; + args += " -i " + nic.getIp(); + args += " -m " + Long.toString(NetUtils.getCidrSize(nic.getNetmask())); + args += " -a " + sb.toString(); + + Pair result = SshHelper.sshExecute(routerIp, DEFAULT_DOMR_SSHPORT, "root", mgr.getSystemVMKeyFile(), null, + "/opt/cloud/bin/vpc_acl.sh " + args); + + if (!result.first()) { + String msg = "SetNetworkACLAnswer on domain router " + routerIp + " failed. message: " + result.second(); + s_logger.error(msg); + + return new SetNetworkACLAnswer(cmd, false, results); + } + + return new SetNetworkACLAnswer(cmd, true, results); + } catch (Exception e) { + String msg = "SetNetworkACL failed due to " + e.toString(); + s_logger.error(msg, e); + return new SetNetworkACLAnswer(cmd, false, results); + } + } + + protected SetPortForwardingRulesAnswer execute(SetPortForwardingRulesVpcCommand cmd) { + VmwareManager mgr = getServiceContext().getStockObject(VmwareManager.CONTEXT_STOCK_NAME); + + String routerIp = getRouterSshControlIp(cmd); + + String[] results = new String[cmd.getRules().length]; + int i = 0; + + boolean endResult = true; + for (PortForwardingRuleTO rule : cmd.getRules()) { + String args =""; + args += rule.revoked() ? " -D" : " -A"; + args += " -P " + rule.getProtocol().toLowerCase(); + args += " -l " + rule.getSrcIp(); + args += " -p " + rule.getStringSrcPortRange().replace(":", "-"); + args += " -r " + rule.getDstIp(); + args += " -d " + rule.getStringDstPortRange().replace(":", "-"); + + try { + Pair sshResult = SshHelper.sshExecute(routerIp, DEFAULT_DOMR_SSHPORT, "root", mgr.getSystemVMKeyFile(), null, + "/opt/cloud/bin/vpc_portforwarding " + args); + + if (!sshResult.first()) { + results[i++] = "Failed"; + endResult = false; + } else { + results[i++] = null; + } + } catch(Exception e) { + results[i++] = "Failed"; + endResult = false; + } + } + return new SetPortForwardingRulesAnswer(cmd, results, endResult); + } + + private UnPlugNicAnswer execute(UnPlugNicCommand cmd) { + // TODO + return new UnPlugNicAnswer(cmd, false, "Unsupported yet"); + } + + private PlugNicAnswer execute(PlugNicCommand cmd) { + // TODO + return new PlugNicAnswer(cmd, false, "Unsupported yet"); + } + + protected void assignVPCPublicIpAddress(String domrName, String routerIp, IpAddressTO ip) throws Exception { + VmwareManager mgr = getServiceContext().getStockObject(VmwareManager.CONTEXT_STOCK_NAME); + + int ethDeviceNum = this.findRouterEthDeviceIndex(domrName, routerIp, ip); + if (ethDeviceNum < 0) { + throw new InternalErrorException("Failed to find DomR VIF to associate/disassociate IP with."); + } + + String args = ""; + if (ip.isAdd()) { + args += " -A "; + } else { + args += " -D "; + } + + args += " -l "; + args += ip.getPublicIp(); + + args += " -c "; + args += "eth" + ethDeviceNum; + + args += " -g "; + args += ip.getVlanGateway(); + + args += " -m "; + args += Long.toString(NetUtils.getCidrSize(ip.getVlanNetmask())); + + args += " -n "; + args += NetUtils.getSubNet(ip.getPublicIp(), ip.getVlanNetmask()); + + Pair result = SshHelper.sshExecute(routerIp, DEFAULT_DOMR_SSHPORT, "root", mgr.getSystemVMKeyFile(), null, + "/opt/cloud/bin/vpc_ipassoc.sh " + args); + + if (!result.first()) { + throw new InternalErrorException("Unable to assign public IP address"); + } + } + protected void assignPublicIpAddress(VirtualMachineMO vmMo, final String vmName, final String privateIpAddress, final String publicIpAddress, final boolean add, final boolean firstIP, - final boolean sourceNat, final String vlanId, final String vlanGateway, final String vlanNetmask, final String vifMacAddress, String guestIp) throws Exception { + final boolean sourceNat, final String vlanId, final String vlanGateway, final String vlanNetmask, final String vifMacAddress, String guestIp) throws Exception { String publicNeworkName = HypervisorHostHelper.getPublicNetworkNamePrefix(vlanId); Pair publicNicInfo = vmMo.getNicDeviceIndex(publicNeworkName);