CLOUDSTACK-5561 Support of multiple public vlans on VR running in HyperV

This commit is contained in:
Rajesh Battala 2014-01-28 06:40:47 +05:30
parent ce0dc3b306
commit 807dc09138
15 changed files with 697 additions and 75 deletions

View File

@ -0,0 +1,68 @@
// Licensed to the Apache Software Foundation (ASF) under one
// or more contributor license agreements. See the NOTICE file
// distributed with this work for additional information
// regarding copyright ownership. The ASF licenses this file
// to you under the Apache License, Version 2.0 (the
// "License"); you may not use this file except in compliance
// with 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.
package com.cloud.agent.api;
import java.util.List;
public class GetVmConfigAnswer extends Answer {
String vmName;
List<NicDetails> nics;
protected GetVmConfigAnswer() {
}
public GetVmConfigAnswer(String vmName, List<NicDetails> nics) {
this.vmName = vmName;
this.nics = nics;
}
public String getVmName() {
return vmName;
}
public List<NicDetails> getNics() {
return nics;
}
public class NicDetails {
String macAddress;
int vlanid;
public NicDetails() {
}
public NicDetails(String macAddress, int vlanid) {
this.macAddress = macAddress;
this.vlanid = vlanid;
}
public String getMacAddress() {
return macAddress;
}
public int getVlanid() {
return vlanid;
}
}
@Override
public boolean executeInSequence() {
return false;
}
}

View File

@ -0,0 +1,46 @@
// Licensed to the Apache Software Foundation (ASF) under one
// or more contributor license agreements. See the NOTICE file
// distributed with this work for additional information
// regarding copyright ownership. The ASF licenses this file
// to you under the Apache License, Version 2.0 (the
// "License"); you may not use this file except in compliance
// with 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.
package com.cloud.agent.api;
import java.util.List;
import com.cloud.agent.api.to.NicTO;
public class GetVmConfigCommand extends Command {
String vmName;
List<NicTO> nics;
protected GetVmConfigCommand() {
}
public GetVmConfigCommand(String vmName) {
this.vmName = vmName;
}
public String getVmName() {
return vmName;
}
public void setNics(List<NicTO> nics){
this.nics = nics;
}
@Override
public boolean executeInSequence() {
return false;
}
}

View File

@ -0,0 +1,36 @@
// Licensed to the Apache Software Foundation (ASF) under one
// or more contributor license agreements. See the NOTICE file
// distributed with this work for additional information
// regarding copyright ownership. The ASF licenses this file
// to you under the Apache License, Version 2.0 (the
// "License"); you may not use this file except in compliance
// with 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.
package com.cloud.agent.api;
public class ModifyVmNicConfigAnswer extends Answer {
String vmName;
protected ModifyVmNicConfigAnswer() {
}
public ModifyVmNicConfigAnswer(String vmName) {
this.vmName = vmName;
}
public String getVmName() {
return vmName;
}
@Override
public boolean executeInSequence() {
return false;
}
}

View File

@ -0,0 +1,42 @@
// Licensed to the Apache Software Foundation (ASF) under one
// or more contributor license agreements. See the NOTICE file
// distributed with this work for additional information
// regarding copyright ownership. The ASF licenses this file
// to you under the Apache License, Version 2.0 (the
// "License"); you may not use this file except in compliance
// with 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.
package com.cloud.agent.api;
public class ModifyVmNicConfigCommand extends Command {
String vmName;
int vlan;
String macAddress;
protected ModifyVmNicConfigCommand() {
}
public ModifyVmNicConfigCommand(String vmName, int vlan, String macAddress) {
this.vmName = vmName;
this.vlan = vlan;
this.macAddress = macAddress;
}
public String getVmName() {
return vmName;
}
@Override
public boolean executeInSequence() {
return false;
}
}

View File

@ -96,7 +96,7 @@
<value>2048</value> <value>2048</value>
</setting> </setting>
<setting name="private_ip_address" serializeAs="String"> <setting name="private_ip_address" serializeAs="String">
<value>10.102.192.150</value> <value>0.0.0.0</value>
</setting> </setting>
</CloudStack.Plugin.AgentShell.AgentSettings> </CloudStack.Plugin.AgentShell.AgentSettings>
</applicationSettings> </applicationSettings>

View File

@ -690,6 +690,20 @@ namespace HypervResource
public String entityType; public String entityType;
} }
public class NicDetails
{
[JsonProperty("macAddress")]
public string macaddress;
[JsonProperty("vlanid")]
public int vlanid;
public NicDetails() { }
public NicDetails(String macaddress, int vlanid)
{
this.macaddress = macaddress;
this.vlanid = vlanid;
}
}
/// <summary> /// <summary>
/// Fully qualified named for a number of types used in CloudStack. Used to specify the intended type for JSON serialised objects. /// Fully qualified named for a number of types used in CloudStack. Used to specify the intended type for JSON serialised objects.
/// </summary> /// </summary>
@ -738,6 +752,10 @@ namespace HypervResource
public const string GetVmDiskStatsCommand = "com.cloud.agent.api.GetVmDiskStatsCommand"; public const string GetVmDiskStatsCommand = "com.cloud.agent.api.GetVmDiskStatsCommand";
public const string GetVmStatsAnswer = "com.cloud.agent.api.GetVmStatsAnswer"; public const string GetVmStatsAnswer = "com.cloud.agent.api.GetVmStatsAnswer";
public const string GetVmStatsCommand = "com.cloud.agent.api.GetVmStatsCommand"; public const string GetVmStatsCommand = "com.cloud.agent.api.GetVmStatsCommand";
public const string GetVmConfigCommand = "com.cloud.agent.api.GetVmConfigCommand";
public const string GetVmConfigAnswer = "com.cloud.agent.api.GetVmConfigAnswer";
public const string ModifyVmNicConfigCommand = "com.cloud.agent.api.ModifyVmNicConfigCommand";
public const string ModifyVmNicConfigAnswer = "com.cloud.agent.api.ModifyVmNicConfigAnswer";
public const string GetVncPortAnswer = "com.cloud.agent.api.GetVncPortAnswer"; public const string GetVncPortAnswer = "com.cloud.agent.api.GetVncPortAnswer";
public const string GetVncPortCommand = "com.cloud.agent.api.GetVncPortCommand"; public const string GetVncPortCommand = "com.cloud.agent.api.GetVncPortCommand";
public const string HostStatsEntry = "com.cloud.agent.api.HostStatsEntry"; public const string HostStatsEntry = "com.cloud.agent.api.HostStatsEntry";

View File

@ -982,6 +982,24 @@ namespace HypervResource
return true; return true;
} }
// POST api/HypervResource/PlugNicCommand
[HttpPost]
[ActionName(CloudStackTypes.PlugNicCommand)]
public JContainer PlugNicCommand([FromBody]dynamic cmd)
{
using (log4net.NDC.Push(Guid.NewGuid().ToString()))
{
logger.Info(CloudStackTypes.PlugNicCommand + cmd.ToString());
object ansContent = new
{
result = true,
details = "instead of plug, change he network settings",
contextMap = contextMap
};
return ReturnCloudStackTypedJArray(ansContent, CloudStackTypes.PlugNicAnswer);
}
}
// POST api/HypervResource/CleanupNetworkRulesCmd // POST api/HypervResource/CleanupNetworkRulesCmd
[HttpPost] [HttpPost]
@ -1264,6 +1282,88 @@ namespace HypervResource
} }
} }
// POST api/HypervResource/ModifyVmVnicVlanCommand
[HttpPost]
[ActionName(CloudStackTypes.ModifyVmNicConfigCommand)]
public JContainer ModifyVmNicConfigCommand([FromBody]dynamic cmd)
{
using (log4net.NDC.Push(Guid.NewGuid().ToString()))
{
logger.Info(CloudStackTypes.ModifyVmNicConfigCommand + cmd.ToString());
bool result = false;
String vmName = cmd.vmName;
uint vlan = (uint)cmd.vlan;
string macAddress = cmd.macAddress;
wmiCallsV2.ModifyVmVLan(vmName, vlan, macAddress);
result = true;
object ansContent = new
{
vmName = vmName,
result = result,
contextMap = contextMap
};
return ReturnCloudStackTypedJArray(ansContent, CloudStackTypes.ModifyVmNicConfigAnswer);
}
}
// POST api/HypervResource/GetVmConfigCommand
[HttpPost]
[ActionName(CloudStackTypes.GetVmConfigCommand)]
public JContainer GetVmConfigCommand([FromBody]dynamic cmd)
{
using (log4net.NDC.Push(Guid.NewGuid().ToString()))
{
logger.Info(CloudStackTypes.GetVmConfigCommand + cmd.ToString());
bool result = false;
String vmName = cmd.vmName;
ComputerSystem vm = wmiCallsV2.GetComputerSystem(vmName);
List<NicDetails> nicDetails = new List<NicDetails>();
var nicSettingsViaVm = wmiCallsV2.GetEthernetPortSettings(vm);
NicDetails nic = null;
String[] macAddress = new String[nicSettingsViaVm.Length];
int index = 0;
foreach (SyntheticEthernetPortSettingData item in nicSettingsViaVm)
{
macAddress[index++] = item.Address;
}
index = 0;
var ethernetConnections = wmiCallsV2.GetEthernetConnections(vm);
int vlanid = 1;
foreach (EthernetPortAllocationSettingData item in ethernetConnections)
{
EthernetSwitchPortVlanSettingData vlanSettings = wmiCallsV2.GetVlanSettings(item);
if (vlanSettings == null)
{
vlanid = -1;
}
else
{
vlanid = vlanSettings.AccessVlanId;
}
nic = new NicDetails(macAddress[index++], vlanid);
nicDetails.Add(nic);
}
result = true;
object ansContent = new
{
vmName = vmName,
nics = nicDetails,
result = result,
contextMap = contextMap
};
return ReturnCloudStackTypedJArray(ansContent, CloudStackTypes.GetVmConfigAnswer);
}
}
// POST api/HypervResource/GetVmStatsCommand // POST api/HypervResource/GetVmStatsCommand
[HttpPost] [HttpPost]
[ActionName(CloudStackTypes.GetVmStatsCommand)] [ActionName(CloudStackTypes.GetVmStatsCommand)]

View File

@ -66,5 +66,6 @@ namespace HypervResource
void patchSystemVmIso(string vmName, string systemVmIso); void patchSystemVmIso(string vmName, string systemVmIso);
void SetState(ComputerSystem vm, ushort requiredState); void SetState(ComputerSystem vm, ushort requiredState);
Dictionary<String, VmState> GetVmSync(String privateIpAddress); Dictionary<String, VmState> GetVmSync(String privateIpAddress);
void ModifyVmVLan(string vmName, uint vlanid, string mac);
} }
} }

View File

@ -443,6 +443,7 @@ namespace HypervResource
nicCount++; nicCount++;
} }
// pass the boot args for the VM using KVP component. // pass the boot args for the VM using KVP component.
// We need to pass the boot args to system vm's to get them configured with cloudstack configuration. // We need to pass the boot args to system vm's to get them configured with cloudstack configuration.
// Add new user data // Add new user data
@ -909,6 +910,37 @@ namespace HypervResource
return new ResourceAllocationSettingData((ManagementBaseObject)defaultDiskDriveSettings.LateBoundObject.Clone()); return new ResourceAllocationSettingData((ManagementBaseObject)defaultDiskDriveSettings.LateBoundObject.Clone());
} }
// Modify the systemvm nic's VLAN id
public void ModifyVmVLan(string vmName, uint vlanid, String mac)
{
ComputerSystem vm = GetComputerSystem(vmName);
SyntheticEthernetPortSettingData[] nicSettingsViaVm = GetEthernetPortSettings(vm);
// Obtain controller for Hyper-V virtualisation subsystem
VirtualSystemManagementService vmMgmtSvc = GetVirtualisationSystemManagementService();
string normalisedMAC = string.Join("", (mac.Split(new char[] { ':' })));
int index = 0;
foreach (SyntheticEthernetPortSettingData item in nicSettingsViaVm)
{
if (normalisedMAC.ToLower().Equals(item.Address.ToLower()))
{
break;
}
index++;
}
//TODO: make sure the index wont be out of range.
EthernetPortAllocationSettingData[] ethernetConnections = GetEthernetConnections(vm);
EthernetSwitchPortVlanSettingData vlanSettings = GetVlanSettings(ethernetConnections[index]);
//Assign configuration to new NIC
vlanSettings.LateBoundObject["AccessVlanId"] = vlanid;
vlanSettings.LateBoundObject["OperationMode"] = 1;
ModifyFeatureVmResources(vmMgmtSvc, vm, new String[] {
vlanSettings.LateBoundObject.GetText(TextFormat.CimDtd20)});
}
public void AttachIso(string displayName, string iso) public void AttachIso(string displayName, string iso)
{ {
logger.DebugFormat("Got request to attach iso {0} to vm {1}", iso, displayName); logger.DebugFormat("Got request to attach iso {0} to vm {1}", iso, displayName);
@ -1420,6 +1452,36 @@ namespace HypervResource
return vSwitch; return vSwitch;
} }
private static void ModifyFeatureVmResources(VirtualSystemManagementService vmMgmtSvc, ComputerSystem vm, string[] resourceSettings)
{
// Resource settings are changed through the management service
System.Management.ManagementPath jobPath;
System.Management.ManagementPath[] results;
var ret_val = vmMgmtSvc.ModifyFeatureSettings(
resourceSettings,
out jobPath,
out results);
// If the Job is done asynchronously
if (ret_val == ReturnCode.Started)
{
JobCompleted(jobPath);
}
else if (ret_val != ReturnCode.Completed)
{
var errMsg = string.Format(
"Failed to update VM {0} (GUID {1}) due to {2} (ModifyVirtualSystem call), existing VM not deleted",
vm.ElementName,
vm.Name,
ReturnCode.ToString(ret_val));
var ex = new WmiException(errMsg);
logger.Error(errMsg, ex);
throw ex;
}
}
private static void ModifyVmResources(VirtualSystemManagementService vmMgmtSvc, ComputerSystem vm, string[] resourceSettings) private static void ModifyVmResources(VirtualSystemManagementService vmMgmtSvc, ComputerSystem vm, string[] resourceSettings)
{ {
// Resource settings are changed through the management service // Resource settings are changed through the management service

View File

@ -16,15 +16,30 @@
// under the License. // under the License.
package com.cloud.hypervisor.hyperv.guru; package com.cloud.hypervisor.hyperv.guru;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
import javax.ejb.Local; import javax.ejb.Local;
import javax.inject.Inject; import javax.inject.Inject;
import com.cloud.agent.api.to.NicTO;
import com.cloud.agent.api.to.VirtualMachineTO; import com.cloud.agent.api.to.VirtualMachineTO;
import com.cloud.hypervisor.Hypervisor.HypervisorType; import com.cloud.hypervisor.Hypervisor.HypervisorType;
import com.cloud.hypervisor.HypervisorGuru; import com.cloud.hypervisor.HypervisorGuru;
import com.cloud.hypervisor.HypervisorGuruBase; import com.cloud.hypervisor.HypervisorGuruBase;
import com.cloud.storage.GuestOSVO; import com.cloud.storage.GuestOSVO;
import com.cloud.storage.dao.GuestOSDao; import com.cloud.storage.dao.GuestOSDao;
import com.cloud.exception.InsufficientAddressCapacityException;
import com.cloud.hypervisor.hyperv.manager.HypervManager;
import com.cloud.network.NetworkModel;
import com.cloud.network.Networks.TrafficType;
import com.cloud.network.dao.NetworkDao;
import com.cloud.network.dao.NetworkVO;
import com.cloud.utils.exception.CloudRuntimeException;
import com.cloud.vm.NicProfile;
import com.cloud.vm.VirtualMachine;
import com.cloud.vm.VirtualMachineProfile; import com.cloud.vm.VirtualMachineProfile;
/** /**
@ -35,6 +50,9 @@ public class HypervGuru extends HypervisorGuruBase implements HypervisorGuru {
@Inject @Inject
private GuestOSDao _guestOsDao; private GuestOSDao _guestOsDao;
@Inject HypervManager _hypervMgr;
@Inject NetworkDao _networkDao;
@Inject NetworkModel _networkMgr;
@Override @Override
public final HypervisorType getHypervisorType() { public final HypervisorType getHypervisorType() {
@ -51,6 +69,78 @@ public class HypervGuru extends HypervisorGuruBase implements HypervisorGuru {
@Override @Override
public final VirtualMachineTO implement(VirtualMachineProfile vm) { public final VirtualMachineTO implement(VirtualMachineProfile vm) {
VirtualMachineTO to = toVirtualMachineTO(vm); VirtualMachineTO to = toVirtualMachineTO(vm);
List<NicProfile> nicProfiles = vm.getNics();
if(vm.getVirtualMachine().getType() == VirtualMachine.Type.DomainRouter) {
NicProfile publicNicProfile = null;
for(NicProfile nicProfile : nicProfiles) {
if(nicProfile.getTrafficType() == TrafficType.Public) {
publicNicProfile = nicProfile;
break;
}
}
if(publicNicProfile != null) {
NicTO[] nics = to.getNics();
// reserve extra NICs
NicTO[] expandedNics = new NicTO[nics.length + _hypervMgr.getRouterExtraPublicNics()];
int i = 0;
int deviceId = -1;
for(i = 0; i < nics.length; i++) {
expandedNics[i] = nics[i];
if(nics[i].getDeviceId() > deviceId)
deviceId = nics[i].getDeviceId();
}
deviceId++;
long networkId = publicNicProfile.getNetworkId();
NetworkVO network = _networkDao.findById(networkId);
for(; i < nics.length + _hypervMgr.getRouterExtraPublicNics(); i++) {
NicTO nicTo = new NicTO();
nicTo.setDeviceId(deviceId++);
nicTo.setBroadcastType(publicNicProfile.getBroadcastType());
nicTo.setType(publicNicProfile.getTrafficType());
nicTo.setIp("0.0.0.0");
nicTo.setNetmask("255.255.255.255");
nicTo.setName(publicNicProfile.getName());
try {
String mac = _networkMgr.getNextAvailableMacAddressInNetwork(networkId);
nicTo.setMac(mac);
} catch (InsufficientAddressCapacityException e) {
throw new CloudRuntimeException("unable to allocate mac address on network: " + networkId);
}
nicTo.setDns1(publicNicProfile.getDns1());
nicTo.setDns2(publicNicProfile.getDns2());
if (publicNicProfile.getGateway() != null) {
nicTo.setGateway(publicNicProfile.getGateway());
} else {
nicTo.setGateway(network.getGateway());
}
nicTo.setDefaultNic(false);
nicTo.setBroadcastUri(publicNicProfile.getBroadCastUri());
nicTo.setIsolationuri(publicNicProfile.getIsolationUri());
Integer networkRate = _networkMgr.getNetworkRate(network.getId(), null);
nicTo.setNetworkRateMbps(networkRate);
expandedNics[i] = nicTo;
}
to.setNics(expandedNics);
}
StringBuffer sbMacSequence = new StringBuffer();
for(NicTO nicTo : sortNicsByDeviceId(to.getNics())) {
sbMacSequence.append(nicTo.getMac()).append("|");
}
sbMacSequence.deleteCharAt(sbMacSequence.length() - 1);
String bootArgs = to.getBootArgs();
to.setBootArgs(bootArgs + " nic_macs=" + sbMacSequence.toString());
}
// Determine the VM's OS description // Determine the VM's OS description
GuestOSVO guestOS = _guestOsDao.findById(vm.getVirtualMachine().getGuestOSId()); GuestOSVO guestOS = _guestOsDao.findById(vm.getVirtualMachine().getGuestOSId());
@ -59,6 +149,29 @@ public class HypervGuru extends HypervisorGuruBase implements HypervisorGuru {
return to; return to;
} }
private NicTO[] sortNicsByDeviceId(NicTO[] nics) {
List<NicTO> listForSort = new ArrayList<NicTO>();
for (NicTO nic : nics) {
listForSort.add(nic);
}
Collections.sort(listForSort, new Comparator<NicTO>() {
@Override
public int compare(NicTO arg0, NicTO arg1) {
if (arg0.getDeviceId() < arg1.getDeviceId()) {
return -1;
} else if (arg0.getDeviceId() == arg1.getDeviceId()) {
return 0;
}
return 1;
}
});
return listForSort.toArray(new NicTO[0]);
}
@Override @Override
public final boolean trackVmHostChange() { public final boolean trackVmHostChange() {
return false; return false;

View File

@ -21,4 +21,5 @@ import com.cloud.utils.component.Manager;
public interface HypervManager extends Manager { public interface HypervManager extends Manager {
public String prepareSecondaryStorageStore(long zoneId); public String prepareSecondaryStorageStore(long zoneId);
int getRouterExtraPublicNics();
} }

View File

@ -45,6 +45,8 @@ import com.cloud.utils.NumbersUtil;
import com.cloud.utils.db.GlobalLock; import com.cloud.utils.db.GlobalLock;
import com.cloud.utils.exception.CloudRuntimeException; import com.cloud.utils.exception.CloudRuntimeException;
import com.cloud.utils.script.Script; import com.cloud.utils.script.Script;
import com.cloud.vm.dao.NicDao;
import com.cloud.vm.dao.VMInstanceDao;
@Local(value = {HypervManager.class}) @Local(value = {HypervManager.class})
public class HypervManagerImpl implements HypervManager { public class HypervManagerImpl implements HypervManager {
@ -60,10 +62,11 @@ public class HypervManagerImpl implements HypervManager {
Map<String, String> _storageMounts = new HashMap<String, String>(); Map<String, String> _storageMounts = new HashMap<String, String>();
StorageLayer _storage; StorageLayer _storage;
@Inject @Inject ConfigurationDao _configDao;
ConfigurationDao _configDao; @Inject DataStoreManager _dataStoreMgr;
@Inject @Inject VMInstanceDao _vminstanceDao;
DataStoreManager _dataStoreMgr; @Inject NicDao _nicDao;
int _routerExtraPublicNics = 2;
@Override @Override
public boolean configure(String name, Map<String, Object> params) throws ConfigurationException { public boolean configure(String name, Map<String, Object> params) throws ConfigurationException {
@ -77,7 +80,7 @@ public class HypervManagerImpl implements HypervManager {
_storage = new JavaStorageLayer(); _storage = new JavaStorageLayer();
_storage.configure("StorageLayer", params); _storage.configure("StorageLayer", params);
} }
_routerExtraPublicNics = NumbersUtil.parseInt(_configDao.getValue(Config.RouterExtraPublicNics.key()), 2);
return true; return true;
} }
@ -373,4 +376,9 @@ public class HypervManagerImpl implements HypervManager {
} }
} }
} }
}
@Override
public int getRouterExtraPublicNics() {
return _routerExtraPublicNics;
}
}

View File

@ -71,7 +71,12 @@ import com.cloud.agent.api.CheckS2SVpnConnectionsCommand;
import com.cloud.agent.api.Command; import com.cloud.agent.api.Command;
import com.cloud.agent.api.GetDomRVersionAnswer; import com.cloud.agent.api.GetDomRVersionAnswer;
import com.cloud.agent.api.GetDomRVersionCmd; import com.cloud.agent.api.GetDomRVersionCmd;
import com.cloud.agent.api.GetVmConfigAnswer;
import com.cloud.agent.api.GetVmConfigAnswer.NicDetails;
import com.cloud.agent.api.GetVmConfigCommand;
import com.cloud.agent.api.HostVmStateReportEntry; import com.cloud.agent.api.HostVmStateReportEntry;
import com.cloud.agent.api.ModifyVmNicConfigAnswer;
import com.cloud.agent.api.ModifyVmNicConfigCommand;
import com.cloud.agent.api.NetworkUsageAnswer; import com.cloud.agent.api.NetworkUsageAnswer;
import com.cloud.agent.api.NetworkUsageCommand; import com.cloud.agent.api.NetworkUsageCommand;
import com.cloud.agent.api.PingCommand; import com.cloud.agent.api.PingCommand;
@ -117,11 +122,13 @@ import com.cloud.agent.api.to.PortForwardingRuleTO;
import com.cloud.agent.api.to.StaticNatRuleTO; import com.cloud.agent.api.to.StaticNatRuleTO;
import com.cloud.agent.api.to.VirtualMachineTO; import com.cloud.agent.api.to.VirtualMachineTO;
import com.cloud.dc.DataCenter.NetworkType; import com.cloud.dc.DataCenter.NetworkType;
import com.cloud.exception.InternalErrorException;
import com.cloud.host.Host.Type; import com.cloud.host.Host.Type;
import com.cloud.hypervisor.Hypervisor; import com.cloud.hypervisor.Hypervisor;
import com.cloud.hypervisor.hyperv.manager.HypervManager; import com.cloud.hypervisor.hyperv.manager.HypervManager;
import com.cloud.network.HAProxyConfigurator; import com.cloud.network.HAProxyConfigurator;
import com.cloud.network.LoadBalancerConfigurator; import com.cloud.network.LoadBalancerConfigurator;
import com.cloud.network.Networks.BroadcastDomainType;
import com.cloud.network.Networks.RouterPrivateIpStrategy; import com.cloud.network.Networks.RouterPrivateIpStrategy;
import com.cloud.network.rules.FirewallRule; import com.cloud.network.rules.FirewallRule;
import com.cloud.resource.ServerResource; import com.cloud.resource.ServerResource;
@ -133,6 +140,8 @@ import com.cloud.utils.net.NetUtils;
import com.cloud.utils.ssh.SshHelper; import com.cloud.utils.ssh.SshHelper;
import com.cloud.vm.VirtualMachine; import com.cloud.vm.VirtualMachine;
import com.cloud.vm.VirtualMachineName; import com.cloud.vm.VirtualMachineName;
/** /**
* Implementation of dummy resource to be returned from discoverer. * Implementation of dummy resource to be returned from discoverer.
**/ **/
@ -706,65 +715,6 @@ public class HypervDirectConnectResource extends ServerResourceBase implements S
} }
} }
//
// find mac address of a specified ethx device
// ip address show ethx | grep link/ether | sed -e 's/^[ \t]*//' | cut -d' ' -f2
// returns
// eth0:xx.xx.xx.xx
//
// list IP with eth devices
// ifconfig ethx |grep -B1 "inet addr" | awk '{ if ( $1 == "inet" ) { print $2 } else if ( $2 == "Link" ) { printf "%s:" ,$1 } }'
// | awk -F: '{ print $1 ": " $3 }'
//
// returns
// eth0:xx.xx.xx.xx
//
//
private int findRouterEthDeviceIndex(String domrName, String routerIp, String mac) throws Exception {
s_logger.info("findRouterEthDeviceIndex. mac: " + mac);
// TODO : this is a temporary very inefficient solution, will refactor it later
Pair<Boolean, String> result = SshHelper.sshExecute(routerIp, DEFAULT_DOMR_SSHPORT, "root", getSystemVMKeyFile(), null, "ls /proc/sys/net/ipv4/conf");
// when we dynamically plug in a new NIC into virtual router, it may take time to show up in guest OS
// we use a waiting loop here as a workaround to synchronize activities in systems
long startTick = System.currentTimeMillis();
while (System.currentTimeMillis() - startTick < 15000) {
if (result.first()) {
String[] tokens = result.second().split("\\s+");
for (String token : tokens) {
if (!("all".equalsIgnoreCase(token) || "default".equalsIgnoreCase(token) || "lo".equalsIgnoreCase(token))) {
String cmd = String.format("ip address show %s | grep link/ether | sed -e 's/^[ \t]*//' | cut -d' ' -f2", token);
if (s_logger.isDebugEnabled())
s_logger.debug("Run domr script " + cmd);
Pair<Boolean, String> result2 = SshHelper.sshExecute(routerIp, DEFAULT_DOMR_SSHPORT, "root", getSystemVMKeyFile(), null,
// TODO need to find the dev index inside router based on IP address
cmd);
if (s_logger.isDebugEnabled())
s_logger.debug("result: " + result2.first() + ", output: " + result2.second());
if (result2.first() && result2.second().trim().equalsIgnoreCase(mac.trim()))
return Integer.parseInt(token.substring(3));
}
}
}
s_logger.warn("can not find intereface associated with mac: " + mac + ", guest OS may still at loading state, retry...");
try {
Thread.currentThread();
Thread.sleep(1000);
} catch (InterruptedException e) {
}
}
return -1;
}
protected Answer execute(SetPortForwardingRulesCommand cmd) { protected Answer execute(SetPortForwardingRulesCommand cmd) {
if (s_logger.isInfoEnabled()) { if (s_logger.isInfoEnabled()) {
s_logger.info("Executing resource SetPortForwardingRulesCommand: " + s_gson.toJson(cmd)); s_logger.info("Executing resource SetPortForwardingRulesCommand: " + s_gson.toJson(cmd));
@ -1170,7 +1120,6 @@ public class HypervDirectConnectResource extends ServerResourceBase implements S
if (s_logger.isInfoEnabled()) { if (s_logger.isInfoEnabled()) {
s_logger.info("Executing resource VmDataCommand: " + s_gson.toJson(cmd)); s_logger.info("Executing resource VmDataCommand: " + s_gson.toJson(cmd));
} }
String routerPrivateIpAddress = cmd.getAccessDetail(NetworkElementCommand.ROUTER_IP); String routerPrivateIpAddress = cmd.getAccessDetail(NetworkElementCommand.ROUTER_IP);
String controlIp = getRouterSshControlIp(cmd); String controlIp = getRouterSshControlIp(cmd);
@ -1386,6 +1335,102 @@ public class HypervDirectConnectResource extends ServerResourceBase implements S
return new Answer(cmd); return new Answer(cmd);
} }
//
// find mac address of a specified ethx device
// ip address show ethx | grep link/ether | sed -e 's/^[ \t]*//' | cut -d' ' -f2
// returns
// eth0:xx.xx.xx.xx
//
// list IP with eth devices
// ifconfig ethx |grep -B1 "inet addr" | awk '{ if ( $1 == "inet" ) { print $2 } else if ( $2 == "Link" ) { printf "%s:" ,$1 } }'
// | awk -F: '{ print $1 ": " $3 }'
//
// returns
// eth0:xx.xx.xx.xx
//
//
private int findRouterEthDeviceIndex(String domrName, String routerIp, String mac) throws Exception {
s_logger.info("findRouterEthDeviceIndex. mac: " + mac);
// TODO : this is a temporary very inefficient solution, will refactor it later
Pair<Boolean, String> result = SshHelper.sshExecute(routerIp, DEFAULT_DOMR_SSHPORT, "root", getSystemVMKeyFile(), null,
"ls /proc/sys/net/ipv4/conf");
// when we dynamically plug in a new NIC into virtual router, it may take time to show up in guest OS
// we use a waiting loop here as a workaround to synchronize activities in systems
long startTick = System.currentTimeMillis();
while (System.currentTimeMillis() - startTick < 15000) {
if (result.first()) {
String[] tokens = result.second().split("\\s+");
for (String token : tokens) {
if (!("all".equalsIgnoreCase(token) || "default".equalsIgnoreCase(token) || "lo".equalsIgnoreCase(token))) {
String cmd = String.format("ip address show %s | grep link/ether | sed -e 's/^[ \t]*//' | cut -d' ' -f2", token);
if (s_logger.isDebugEnabled())
s_logger.debug("Run domr script " + cmd);
Pair<Boolean, String> result2 = SshHelper.sshExecute(routerIp, DEFAULT_DOMR_SSHPORT, "root", getSystemVMKeyFile(), null,
// TODO need to find the dev index inside router based on IP address
cmd);
if (s_logger.isDebugEnabled())
s_logger.debug("result: " + result2.first() + ", output: " + result2.second());
if (result2.first() && result2.second().trim().equalsIgnoreCase(mac.trim()))
return Integer.parseInt(token.substring(3));
}
}
}
s_logger.warn("can not find intereface associated with mac: " + mac + ", guest OS may still at loading state, retry...");
}
return -1;
}
private Pair<Integer, String> findRouterFreeEthDeviceIndex(String routerIp) throws Exception {
s_logger.info("findRouterFreeEthDeviceIndex. mac: ");
// TODO : this is a temporary very inefficient solution, will refactor it later
Pair<Boolean, String> result = SshHelper.sshExecute(routerIp, DEFAULT_DOMR_SSHPORT, "root", getSystemVMKeyFile(), null,
"ip address | grep DOWN| cut -f2 -d :");
// when we dynamically plug in a new NIC into virtual router, it may take time to show up in guest OS
// we use a waiting loop here as a workaround to synchronize activities in systems
long startTick = System.currentTimeMillis();
while (System.currentTimeMillis() - startTick < 15000) {
if (result.first() && !result.second().isEmpty()) {
String[] tokens = result.second().split("\\n");
for (String token : tokens) {
if (!("all".equalsIgnoreCase(token) || "default".equalsIgnoreCase(token) || "lo".equalsIgnoreCase(token))) {
//String cmd = String.format("ip address show %s | grep link/ether | sed -e 's/^[ \t]*//' | cut -d' ' -f2", token);
//TODO: don't check for eth0,1,2, as they will be empty by default.
//String cmd = String.format("ip address show %s ", token);
String cmd = String.format("ip address show %s | grep link/ether | sed -e 's/^[ \t]*//' | cut -d' ' -f2", token);
if (s_logger.isDebugEnabled())
s_logger.debug("Run domr script " + cmd);
Pair<Boolean, String> result2 = SshHelper.sshExecute(routerIp, DEFAULT_DOMR_SSHPORT, "root", getSystemVMKeyFile(), null,
// TODO need to find the dev index inside router based on IP address
cmd);
if (s_logger.isDebugEnabled())
s_logger.debug("result: " + result2.first() + ", output: " + result2.second());
if (result2.first() && result2.second().trim().length() > 0)
return new Pair<Integer, String>(Integer.parseInt(token.trim().substring(3)), result2.second().trim()) ;
}
}
}
//s_logger.warn("can not find intereface associated with mac: , guest OS may still at loading state, retry...");
}
return new Pair<Integer, String>(-1, "");
}
protected Answer execute(IpAssocCommand cmd) { protected Answer execute(IpAssocCommand cmd) {
if (s_logger.isInfoEnabled()) { if (s_logger.isInfoEnabled()) {
s_logger.info("Executing resource IPAssocCommand: " + s_gson.toJson(cmd)); s_logger.info("Executing resource IPAssocCommand: " + s_gson.toJson(cmd));
@ -1401,7 +1446,7 @@ public class HypervDirectConnectResource extends ServerResourceBase implements S
String controlIp = getRouterSshControlIp(cmd); String controlIp = getRouterSshControlIp(cmd);
for (IpAddressTO ip : ips) { for (IpAddressTO ip : ips) {
assignPublicIpAddress(routerName, controlIp, ip.getPublicIp(), ip.isAdd(), ip.isFirstIP(), ip.isSourceNat(), ip.getBroadcastUri(), ip.getVlanGateway(), assignPublicIpAddress(routerName, controlIp, ip.getPublicIp(), ip.isAdd(), ip.isFirstIP(), ip.isSourceNat(), ip.getBroadcastUri(), ip.getVlanGateway(),
ip.getVlanNetmask(), ip.getVifMacAddress()); ip.getVlanNetmask(), ip.getVifMacAddress());
results[i++] = ip.getPublicIp() + " - success"; results[i++] = ip.getPublicIp() + " - success";
} }
@ -1419,16 +1464,97 @@ public class HypervDirectConnectResource extends ServerResourceBase implements S
return new IpAssocAnswer(cmd, results); return new IpAssocAnswer(cmd, results);
} }
protected int getVmNics(String vmName, int vlanid) {
GetVmConfigCommand vmConfig = new GetVmConfigCommand(vmName);
URI agentUri = null;
int nicposition = -1;
try {
String cmdName = GetVmConfigCommand.class.getName();
agentUri =
new URI("https", null, _agentIp, _port,
"/api/HypervResource/" + cmdName, null, null);
} catch (URISyntaxException e) {
String errMsg = "Could not generate URI for Hyper-V agent";
s_logger.error(errMsg, e);
}
String ansStr = postHttpRequest(s_gson.toJson(vmConfig), agentUri);
Answer[] result = s_gson.fromJson(ansStr, Answer[].class);
s_logger.debug("executeRequest received response "
+ s_gson.toJson(result));
if (result.length > 0) {
GetVmConfigAnswer ans = ((GetVmConfigAnswer)result[0]);
List<NicDetails> nics = ans.getNics();
for (NicDetails nic : nics) {
if (nic.getVlanid() == vlanid) {
nicposition = nics.indexOf(nic);
break;
}
}
}
return nicposition;
}
protected void modifyNicVlan(String vmName, int vlanId, String macAddress) {
ModifyVmNicConfigCommand modifynic = new ModifyVmNicConfigCommand(vmName, vlanId, macAddress);
URI agentUri = null;
try {
String cmdName = ModifyVmNicConfigCommand.class.getName();
agentUri =
new URI("https", null, _agentIp, _port,
"/api/HypervResource/" + cmdName, null, null);
} catch (URISyntaxException e) {
String errMsg = "Could not generate URI for Hyper-V agent";
s_logger.error(errMsg, e);
}
String ansStr = postHttpRequest(s_gson.toJson(modifynic), agentUri);
Answer[] result = s_gson.fromJson(ansStr, Answer[].class);
s_logger.debug("executeRequest received response "
+ s_gson.toJson(result));
if (result.length > 0) {
ModifyVmNicConfigAnswer ans = ((ModifyVmNicConfigAnswer)result[0]);
}
}
protected void assignPublicIpAddress(final String vmName, final String privateIpAddress, final String publicIpAddress, final boolean add, final boolean firstIP, protected void assignPublicIpAddress(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) throws Exception { final boolean sourceNat, final String broadcastId, final String vlanGateway, final String vlanNetmask, final String vifMacAddress) throws Exception {
URI broadcastUri = BroadcastDomainType.fromString(broadcastId);
if (BroadcastDomainType.getSchemeValue(broadcastUri) != BroadcastDomainType.Vlan) {
throw new InternalErrorException("Unable to assign a public IP to a VIF on network " + broadcastId);
}
int vlanId = Integer.parseInt(BroadcastDomainType.getValue(broadcastUri));
int publicNicInfo = -1;
publicNicInfo = getVmNics(vmName, vlanId);
boolean addVif = false; boolean addVif = false;
if (add) { if (add && publicNicInfo == -1) {
if (s_logger.isDebugEnabled()) { if (s_logger.isDebugEnabled()) {
s_logger.debug("Plug new NIC to associate" + privateIpAddress + " to " + publicIpAddress); s_logger.debug("Plug new NIC to associate" + privateIpAddress + " to " + publicIpAddress);
} }
addVif = true; addVif = true;
} else if (!add && firstIP) { } else if (!add && firstIP) {
if (s_logger.isDebugEnabled()) {
s_logger.debug("Unplug NIC " + publicNicInfo);
}
}
if (addVif) {
Pair<Integer, String> nicdevice = findRouterFreeEthDeviceIndex(privateIpAddress);
publicNicInfo = nicdevice.first();
if (publicNicInfo > 0) {
modifyNicVlan(vmName, vlanId, nicdevice.second());
// After modifying the vnic on VR, check the VR VNics config in the host and get the device position
publicNicInfo = getVmNics(vmName, vlanId);
// As a new nic got activated in the VR. add the entry in the NIC's table.
networkUsage(privateIpAddress, "addVif", "eth" + publicNicInfo);
}
else {
// we didn't find any eth device available in VR to configure the ip range with new VLAN
String msg = "No Nic is available on DomR VIF to associate/disassociate IP with.";
s_logger.error(msg);
throw new InternalErrorException(msg);
}
} }
String args = null; String args = null;
@ -1450,8 +1576,8 @@ public class HypervDirectConnectResource extends ServerResourceBase implements S
args += publicIpAddress + "/" + cidrSize; args += publicIpAddress + "/" + cidrSize;
args += " -c "; args += " -c ";
args += "eth" + "2"; // currently hardcoding to eth 2 (which is default public ipd)//publicNicInfo.first();
args += "eth" + publicNicInfo;
args += " -g "; args += " -g ";
args += vlanGateway; args += vlanGateway;
@ -1464,7 +1590,7 @@ public class HypervDirectConnectResource extends ServerResourceBase implements S
} }
Pair<Boolean, String> result = Pair<Boolean, String> result =
SshHelper.sshExecute(privateIpAddress, DEFAULT_DOMR_SSHPORT, "root", getSystemVMKeyFile(), null, "/opt/cloud/bin/ipassoc.sh " + args); SshHelper.sshExecute(privateIpAddress, DEFAULT_DOMR_SSHPORT, "root", getSystemVMKeyFile(), null, "/opt/cloud/bin/ipassoc.sh " + args);
if (!result.first()) { if (!result.first()) {
s_logger.error("ipassoc command on domain router " + privateIpAddress + " failed. message: " + result.second()); s_logger.error("ipassoc command on domain router " + privateIpAddress + " failed. message: " + result.second());
@ -1840,7 +1966,7 @@ public class HypervDirectConnectResource extends ServerResourceBase implements S
sch.connect(addr); sch.connect(addr);
return null; return null;
} catch (IOException e) { } catch (IOException e) {
s_logger.info("Could not connect to " + ipAddress + " due to " + e.toString()); s_logger.info("Could] not connect to " + ipAddress + " due to " + e.toString());
if (e instanceof ConnectException) { if (e instanceof ConnectException) {
// if connection is refused because of VM is being started, // if connection is refused because of VM is being started,
// we give it more sleep time // we give it more sleep time

View File

@ -2125,7 +2125,7 @@ public class VirtualNetworkApplianceManagerImpl extends ManagerBase implements V
buf.append(" dnssearchorder=").append(domain_suffix); buf.append(" dnssearchorder=").append(domain_suffix);
} }
if (profile.getHypervisorType() == HypervisorType.VMware) { if (profile.getHypervisorType() == HypervisorType.VMware || profile.getHypervisorType() == HypervisorType.Hyperv) {
buf.append(" extra_pubnics=" + _routerExtraPublicNics); buf.append(" extra_pubnics=" + _routerExtraPublicNics);
} }

View File

@ -254,10 +254,11 @@ remove_first_ip() {
if [ $? -gt 0 -a $? -ne 2 ] if [ $? -gt 0 -a $? -ne 2 ]
then then
remove_routing $1 remove_routing $1
sudo ip link set $ethDev down
return 1 return 1
fi fi
remove_routing $1 remove_routing $1
sudo ip link set $ethDev down
return $? return $?
} }