mirror of
https://github.com/apache/cloudstack.git
synced 2025-10-26 08:42:29 +01:00
bug 11585: using pre-allocated NIC approach to support additional public interfaces for VMware
This commit is contained in:
parent
c6c5e8f557
commit
dce4e74b58
@ -47,6 +47,7 @@ public interface VmwareManager {
|
|||||||
Pair<Integer, Integer> getAddiionalVncPortRange();
|
Pair<Integer, Integer> getAddiionalVncPortRange();
|
||||||
|
|
||||||
int getMaxHostsPerCluster();
|
int getMaxHostsPerCluster();
|
||||||
|
int getRouterExtraPublicNics();
|
||||||
|
|
||||||
boolean beginExclusiveOperation(int timeOutSeconds);
|
boolean beginExclusiveOperation(int timeOutSeconds);
|
||||||
void endExclusiveOperation();
|
void endExclusiveOperation();
|
||||||
|
|||||||
@ -213,6 +213,7 @@ import com.vmware.vim25.VirtualDeviceConfigSpec;
|
|||||||
import com.vmware.vim25.VirtualDeviceConfigSpecOperation;
|
import com.vmware.vim25.VirtualDeviceConfigSpecOperation;
|
||||||
import com.vmware.vim25.VirtualDisk;
|
import com.vmware.vim25.VirtualDisk;
|
||||||
import com.vmware.vim25.VirtualEthernetCard;
|
import com.vmware.vim25.VirtualEthernetCard;
|
||||||
|
import com.vmware.vim25.VirtualEthernetCardNetworkBackingInfo;
|
||||||
import com.vmware.vim25.VirtualLsiLogicController;
|
import com.vmware.vim25.VirtualLsiLogicController;
|
||||||
import com.vmware.vim25.VirtualMachineConfigSpec;
|
import com.vmware.vim25.VirtualMachineConfigSpec;
|
||||||
import com.vmware.vim25.VirtualMachineFileInfo;
|
import com.vmware.vim25.VirtualMachineFileInfo;
|
||||||
@ -452,7 +453,6 @@ public class VmwareResource implements StoragePoolResource, ServerResource, Vmwa
|
|||||||
s_logger.info("Executing resource SetPortForwardingRulesCommand: " + _gson.toJson(cmd));
|
s_logger.info("Executing resource SetPortForwardingRulesCommand: " + _gson.toJson(cmd));
|
||||||
}
|
}
|
||||||
|
|
||||||
// String routerIp = cmd.getAccessDetail(NetworkElementCommand.ROUTER_IP);
|
|
||||||
String controlIp = getRouterSshControlIp(cmd);
|
String controlIp = getRouterSshControlIp(cmd);
|
||||||
String args = "";
|
String args = "";
|
||||||
String[] results = new String[cmd.getRules().length];
|
String[] results = new String[cmd.getRules().length];
|
||||||
@ -547,7 +547,6 @@ public class VmwareResource implements StoragePoolResource, ServerResource, Vmwa
|
|||||||
s_logger.info("Executing resource SetFirewallRuleCommand: " + _gson.toJson(cmd));
|
s_logger.info("Executing resource SetFirewallRuleCommand: " + _gson.toJson(cmd));
|
||||||
}
|
}
|
||||||
|
|
||||||
// String routerIp = cmd.getAccessDetail(NetworkElementCommand.ROUTER_IP);
|
|
||||||
String args = null;
|
String args = null;
|
||||||
String[] results = new String[cmd.getRules().length];
|
String[] results = new String[cmd.getRules().length];
|
||||||
int i = 0;
|
int i = 0;
|
||||||
@ -759,7 +758,13 @@ public class VmwareResource implements StoragePoolResource, ServerResource, Vmwa
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (removeVif) {
|
if (removeVif) {
|
||||||
vmMo.tearDownDevice(publicNicInfo.second());
|
|
||||||
|
String nicMasksStr = vmMo.getCustomFieldValue(CustomFieldConstants.CLOUD_NIC_MASK);
|
||||||
|
int nicMasks = Integer.parseInt(nicMasksStr);
|
||||||
|
nicMasks &= ~(1 << publicNicInfo.first().intValue());
|
||||||
|
vmMo.setCustomFieldValue(CustomFieldConstants.CLOUD_NIC_MASK, String.valueOf(nicMasks));
|
||||||
|
|
||||||
|
// vmMo.tearDownDevice(publicNicInfo.second());
|
||||||
|
|
||||||
HostMO hostMo = vmMo.getRunningHost();
|
HostMO hostMo = vmMo.getRunningHost();
|
||||||
List<NetworkDetails> networks = vmMo.getNetworksWithDetails();
|
List<NetworkDetails> networks = vmMo.getNetworksWithDetails();
|
||||||
@ -782,12 +787,64 @@ public class VmwareResource implements StoragePoolResource, ServerResource, Vmwa
|
|||||||
Pair<ManagedObjectReference, String> networkInfo = HypervisorHostHelper.preparePublicNetwork(this._publicNetworkVSwitchName,
|
Pair<ManagedObjectReference, String> networkInfo = HypervisorHostHelper.preparePublicNetwork(this._publicNetworkVSwitchName,
|
||||||
vmMo.getRunningHost(), vlanId, null, null, this._ops_timeout, true);
|
vmMo.getRunningHost(), vlanId, null, null, this._ops_timeout, true);
|
||||||
|
|
||||||
VmwareManager mgr = getServiceContext().getStockObject(VmwareManager.CONTEXT_STOCK_NAME);
|
int nicIndex = allocPublicNicIndex(vmMo);
|
||||||
|
|
||||||
|
try {
|
||||||
|
VirtualDevice[] nicDevices = vmMo.getNicDevices();
|
||||||
|
|
||||||
|
VirtualEthernetCard device = (VirtualEthernetCard)nicDevices[nicIndex];
|
||||||
|
|
||||||
|
VirtualEthernetCardNetworkBackingInfo nicBacking = new VirtualEthernetCardNetworkBackingInfo();
|
||||||
|
nicBacking.setDeviceName(networkInfo.second());
|
||||||
|
nicBacking.setNetwork(networkInfo.first());
|
||||||
|
device.setBacking(nicBacking);
|
||||||
|
|
||||||
|
VirtualMachineConfigSpec vmConfigSpec = new VirtualMachineConfigSpec();
|
||||||
|
VirtualDeviceConfigSpec[] deviceConfigSpecArray = new VirtualDeviceConfigSpec[1];
|
||||||
|
deviceConfigSpecArray[0] = new VirtualDeviceConfigSpec();
|
||||||
|
deviceConfigSpecArray[0].setDevice(device);
|
||||||
|
deviceConfigSpecArray[0].setOperation(VirtualDeviceConfigSpecOperation.edit);
|
||||||
|
|
||||||
|
vmConfigSpec.setDeviceChange(deviceConfigSpecArray);
|
||||||
|
if(!vmMo.configureVm(vmConfigSpec)) {
|
||||||
|
throw new Exception("Failed to configure devices when plugPublicNic");
|
||||||
|
}
|
||||||
|
} catch(Exception e) {
|
||||||
|
|
||||||
|
// restore allocation mask in case of exceptions
|
||||||
|
String nicMasksStr = vmMo.getCustomFieldValue(CustomFieldConstants.CLOUD_NIC_MASK);
|
||||||
|
int nicMasks = Integer.parseInt(nicMasksStr);
|
||||||
|
nicMasks &= ~(1 << nicIndex);
|
||||||
|
vmMo.setCustomFieldValue(CustomFieldConstants.CLOUD_NIC_MASK, String.valueOf(nicMasks));
|
||||||
|
|
||||||
|
throw e;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
// Note: public NIC is plugged inside system VM
|
// Note: public NIC is plugged inside system VM
|
||||||
VirtualDevice nic = VmwareHelper.prepareNicDevice(vmMo, networkInfo.first(), VirtualEthernetCardType.Vmxnet3,
|
VirtualDevice nic = VmwareHelper.prepareNicDevice(vmMo, networkInfo.first(), VirtualEthernetCardType.Vmxnet3,
|
||||||
networkInfo.second(), vifMacAddress, -1, 1, true, true);
|
networkInfo.second(), vifMacAddress, -1, 1, true, true);
|
||||||
vmMo.plugDevice(nic);
|
vmMo.plugDevice(nic);
|
||||||
|
*/
|
||||||
|
}
|
||||||
|
|
||||||
|
private int allocPublicNicIndex(VirtualMachineMO vmMo) throws Exception {
|
||||||
|
String nicMasksStr = vmMo.getCustomFieldValue(CustomFieldConstants.CLOUD_NIC_MASK);
|
||||||
|
if(nicMasksStr == null || nicMasksStr.isEmpty()) {
|
||||||
|
throw new Exception("Could not find NIC allocation info");
|
||||||
|
}
|
||||||
|
|
||||||
|
int nicMasks = Integer.parseInt(nicMasksStr);
|
||||||
|
VirtualDevice[] nicDevices = vmMo.getNicDevices();
|
||||||
|
for(int i = 3; i < nicDevices.length; i++) {
|
||||||
|
if((nicMasks & (1 << i)) == 0) {
|
||||||
|
nicMasks |= (1 << i);
|
||||||
|
vmMo.setCustomFieldValue(CustomFieldConstants.CLOUD_NIC_MASK, String.valueOf(nicMasks));
|
||||||
|
return i;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
throw new Exception("Could not allocate a free public NIC");
|
||||||
}
|
}
|
||||||
|
|
||||||
protected Answer execute(IpAssocCommand cmd) {
|
protected Answer execute(IpAssocCommand cmd) {
|
||||||
@ -804,7 +861,6 @@ public class VmwareResource implements StoragePoolResource, ServerResource, Vmwa
|
|||||||
|
|
||||||
IpAddressTO[] ips = cmd.getIpAddresses();
|
IpAddressTO[] ips = cmd.getIpAddresses();
|
||||||
String routerName = cmd.getAccessDetail(NetworkElementCommand.ROUTER_NAME);
|
String routerName = cmd.getAccessDetail(NetworkElementCommand.ROUTER_NAME);
|
||||||
// String routerIp = cmd.getAccessDetail(NetworkElementCommand.ROUTER_IP);
|
|
||||||
String controlIp = VmwareResource.getRouterSshControlIp(cmd);
|
String controlIp = VmwareResource.getRouterSshControlIp(cmd);
|
||||||
|
|
||||||
VirtualMachineMO vmMo = hyperHost.findVmOnHyperHost(routerName);
|
VirtualMachineMO vmMo = hyperHost.findVmOnHyperHost(routerName);
|
||||||
@ -1352,6 +1408,8 @@ public class VmwareResource implements StoragePoolResource, ServerResource, Vmwa
|
|||||||
}
|
}
|
||||||
|
|
||||||
VirtualDevice nic;
|
VirtualDevice nic;
|
||||||
|
int nicMask = 0;
|
||||||
|
int nicCount = 0;
|
||||||
for (NicTO nicTo : sortNicsByDeviceId(nics)) {
|
for (NicTO nicTo : sortNicsByDeviceId(nics)) {
|
||||||
s_logger.info("Prepare NIC device based on NicTO: " + _gson.toJson(nicTo));
|
s_logger.info("Prepare NIC device based on NicTO: " + _gson.toJson(nicTo));
|
||||||
|
|
||||||
@ -1365,7 +1423,12 @@ public class VmwareResource implements StoragePoolResource, ServerResource, Vmwa
|
|||||||
if(s_logger.isDebugEnabled())
|
if(s_logger.isDebugEnabled())
|
||||||
s_logger.debug("Prepare NIC at new device " + _gson.toJson(deviceConfigSpecArray[i]));
|
s_logger.debug("Prepare NIC at new device " + _gson.toJson(deviceConfigSpecArray[i]));
|
||||||
|
|
||||||
|
// this is really a hacking for DomR, upon DomR startup, we will reset all the NIC allocation after eth3
|
||||||
|
if(nicCount < 3)
|
||||||
|
nicMask |= (1 << nicCount);
|
||||||
|
|
||||||
i++;
|
i++;
|
||||||
|
nicCount++;
|
||||||
}
|
}
|
||||||
|
|
||||||
vmConfigSpec.setDeviceChange(deviceConfigSpecArray);
|
vmConfigSpec.setDeviceChange(deviceConfigSpecArray);
|
||||||
@ -1389,6 +1452,8 @@ public class VmwareResource implements StoragePoolResource, ServerResource, Vmwa
|
|||||||
throw new Exception("Failed to configure VM before start. vmName: " + vmName);
|
throw new Exception("Failed to configure VM before start. vmName: " + vmName);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
vmMo.setCustomFieldValue(CustomFieldConstants.CLOUD_NIC_MASK, String.valueOf(nicMask));
|
||||||
|
|
||||||
if (!vmMo.powerOn()) {
|
if (!vmMo.powerOn()) {
|
||||||
throw new Exception("Failed to start VM. vmName: " + vmName);
|
throw new Exception("Failed to start VM. vmName: " + vmName);
|
||||||
}
|
}
|
||||||
@ -1510,7 +1575,6 @@ public class VmwareResource implements StoragePoolResource, ServerResource, Vmwa
|
|||||||
}
|
}
|
||||||
|
|
||||||
protected synchronized Answer execute(final RemoteAccessVpnCfgCommand cmd) {
|
protected synchronized Answer execute(final RemoteAccessVpnCfgCommand cmd) {
|
||||||
String routerIp = cmd.getAccessDetail(NetworkElementCommand.ROUTER_IP);
|
|
||||||
String controlIp = getRouterSshControlIp(cmd);
|
String controlIp = getRouterSshControlIp(cmd);
|
||||||
StringBuffer argsBuf = new StringBuffer();
|
StringBuffer argsBuf = new StringBuffer();
|
||||||
if (cmd.isCreate()) {
|
if (cmd.isCreate()) {
|
||||||
@ -1557,7 +1621,6 @@ public class VmwareResource implements StoragePoolResource, ServerResource, Vmwa
|
|||||||
protected synchronized Answer execute(final VpnUsersCfgCommand cmd) {
|
protected synchronized Answer execute(final VpnUsersCfgCommand cmd) {
|
||||||
VmwareManager mgr = getServiceContext().getStockObject(VmwareManager.CONTEXT_STOCK_NAME);
|
VmwareManager mgr = getServiceContext().getStockObject(VmwareManager.CONTEXT_STOCK_NAME);
|
||||||
|
|
||||||
String routerIp = cmd.getAccessDetail(NetworkElementCommand.ROUTER_IP);
|
|
||||||
String controlIp = getRouterSshControlIp(cmd);
|
String controlIp = getRouterSshControlIp(cmd);
|
||||||
for (VpnUsersCfgCommand.UsernamePassword userpwd : cmd.getUserpwds()) {
|
for (VpnUsersCfgCommand.UsernamePassword userpwd : cmd.getUserpwds()) {
|
||||||
StringBuffer argsBuf = new StringBuffer();
|
StringBuffer argsBuf = new StringBuffer();
|
||||||
@ -1776,6 +1839,8 @@ public class VmwareResource implements StoragePoolResource, ServerResource, Vmwa
|
|||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
vmMo.setCustomFieldValue(CustomFieldConstants.CLOUD_NIC_MASK, "0");
|
||||||
|
|
||||||
if (getVmState(vmMo) != State.Stopped) {
|
if (getVmState(vmMo) != State.Stopped) {
|
||||||
Long bytesSent = 0L;
|
Long bytesSent = 0L;
|
||||||
Long bytesRcvd = 0L;
|
Long bytesRcvd = 0L;
|
||||||
@ -3781,6 +3846,7 @@ public class VmwareResource implements StoragePoolResource, ServerResource, Vmwa
|
|||||||
cfmMo.ensureCustomFieldDef("Datastore", CustomFieldConstants.CLOUD_UUID);
|
cfmMo.ensureCustomFieldDef("Datastore", CustomFieldConstants.CLOUD_UUID);
|
||||||
cfmMo.ensureCustomFieldDef("Network", CustomFieldConstants.CLOUD_GC);
|
cfmMo.ensureCustomFieldDef("Network", CustomFieldConstants.CLOUD_GC);
|
||||||
cfmMo.ensureCustomFieldDef("VirtualMachine", CustomFieldConstants.CLOUD_UUID);
|
cfmMo.ensureCustomFieldDef("VirtualMachine", CustomFieldConstants.CLOUD_UUID);
|
||||||
|
cfmMo.ensureCustomFieldDef("VirtualMachine", CustomFieldConstants.CLOUD_NIC_MASK);
|
||||||
|
|
||||||
VmwareHypervisorHost hostMo = this.getHyperHost(context);
|
VmwareHypervisorHost hostMo = this.getHyperHost(context);
|
||||||
_hostName = hostMo.getHyperHostName();
|
_hostName = hostMo.getHyperHostName();
|
||||||
|
|||||||
@ -24,13 +24,44 @@ log_it() {
|
|||||||
log_action_begin_msg "$@"
|
log_action_begin_msg "$@"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
init_interfaces_orderby_macs() {
|
||||||
|
macs=( $(echo $1 | sed "s/|/ /g") )
|
||||||
|
total_nics=${#macs[@]}
|
||||||
|
interface_file=${2:-"/etc/network/interfaces"}
|
||||||
|
rule_file=${3:-"/etc/udev/rules.d/70-persistent-net.rules"}
|
||||||
|
|
||||||
|
echo -n "auto lo" > $interface_file
|
||||||
|
for((i=0; i<total_nics; i++))
|
||||||
|
do
|
||||||
|
if [[ $i < 3 ]]
|
||||||
|
then
|
||||||
|
echo -n " eth$i" >> $interface_file
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
cat >> $interface_file << EOF
|
||||||
|
|
||||||
|
iface lo inet loopback
|
||||||
|
|
||||||
|
EOF
|
||||||
|
|
||||||
|
echo "" > $rule_file
|
||||||
|
for((i=0; i < ${#macs[@]}; i++))
|
||||||
|
do
|
||||||
|
echo "SUBSYSTEM==\"net\", ACTION==\"add\", DRIVERS==\"?*\", ATTR{address}==\"${macs[$i]}\", NAME=\"eth$i\"" >> $rule_file
|
||||||
|
done
|
||||||
|
}
|
||||||
|
|
||||||
init_interfaces() {
|
init_interfaces() {
|
||||||
cat > /etc/network/interfaces << EOF
|
if [ "$NIC_MACS" == "" ]
|
||||||
|
then
|
||||||
|
cat > /etc/network/interfaces << EOF
|
||||||
auto lo $1 $2 $3
|
auto lo $1 $2 $3
|
||||||
iface lo inet loopback
|
iface lo inet loopback
|
||||||
|
|
||||||
EOF
|
EOF
|
||||||
|
else
|
||||||
|
init_interfaces_orderby_macs "$NIC_MACS"
|
||||||
|
fi
|
||||||
}
|
}
|
||||||
|
|
||||||
hypervisor() {
|
hypervisor() {
|
||||||
@ -120,6 +151,7 @@ patch() {
|
|||||||
hyperVisor=$(hypervisor)
|
hyperVisor=$(hypervisor)
|
||||||
/opt/cloud/bin/patchsystemvm.sh $PATCH_MOUNT $hyperVisor
|
/opt/cloud/bin/patchsystemvm.sh $PATCH_MOUNT $hyperVisor
|
||||||
umount $PATCH_MOUNT
|
umount $PATCH_MOUNT
|
||||||
|
|
||||||
if [ "$shouldpatch" == "true" ]
|
if [ "$shouldpatch" == "true" ]
|
||||||
then
|
then
|
||||||
log_it "Rebooting system since we patched init scripts"
|
log_it "Rebooting system since we patched init scripts"
|
||||||
@ -139,6 +171,7 @@ setup_interface() {
|
|||||||
local ip=$2
|
local ip=$2
|
||||||
local mask=$3
|
local mask=$3
|
||||||
local gw=$4
|
local gw=$4
|
||||||
|
local force=$5
|
||||||
local intf=eth${intfnum}
|
local intf=eth${intfnum}
|
||||||
local bootproto="static"
|
local bootproto="static"
|
||||||
|
|
||||||
@ -151,7 +184,7 @@ setup_interface() {
|
|||||||
fi
|
fi
|
||||||
fi
|
fi
|
||||||
|
|
||||||
if [ "$ip" != "0.0.0.0" -a "$ip" != "" ]
|
if [ "$ip" != "0.0.0.0" -a "$ip" != "" -o "$force" == "force" ]
|
||||||
then
|
then
|
||||||
echo "iface $intf inet $bootproto" >> /etc/network/interfaces
|
echo "iface $intf inet $bootproto" >> /etc/network/interfaces
|
||||||
if [ "$bootproto" == "static" ]
|
if [ "$bootproto" == "static" ]
|
||||||
@ -161,11 +194,16 @@ setup_interface() {
|
|||||||
fi
|
fi
|
||||||
fi
|
fi
|
||||||
|
|
||||||
ifdown $intf
|
if [ "$force" == "force" ]
|
||||||
ifup $intf
|
|
||||||
if [ "$RROUTER" == "1" -a "$1" == "2" ]
|
|
||||||
then
|
then
|
||||||
ifdown $intf
|
ifdown $intf
|
||||||
|
else
|
||||||
|
ifdown $intf
|
||||||
|
ifup $intf
|
||||||
|
if [ "$RROUTER" == "1" -a "$1" == "2" ]
|
||||||
|
then
|
||||||
|
ifdown $intf
|
||||||
|
fi
|
||||||
fi
|
fi
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -391,17 +429,56 @@ setup_redundant_router() {
|
|||||||
|
|
||||||
setup_router() {
|
setup_router() {
|
||||||
log_it "Setting up virtual router system vm"
|
log_it "Setting up virtual router system vm"
|
||||||
|
|
||||||
|
oldmd5=
|
||||||
|
[ -f "/etc/udev/rules.d/70-persistent-net.rules" ] && oldmd5=$(md5sum "/etc/udev/rules.d/70-persistent-net.rules" | awk '{print $1}')
|
||||||
|
|
||||||
if [ -n "$ETH2_IP" ]
|
if [ -n "$ETH2_IP" ]
|
||||||
then
|
then
|
||||||
setup_common eth0 eth1 eth2
|
setup_common eth0 eth1 eth2
|
||||||
if [ "$RROUTER" == "1" ]
|
|
||||||
|
if [ -n "$EXTRA_PUBNICS" ]
|
||||||
then
|
then
|
||||||
setup_redundant_router
|
for((i = 3; i < 3 + $EXTRA_PUBNICS; i++))
|
||||||
|
do
|
||||||
|
setup_interface "$i" "0.0.0.0" "255.255.255.255" $GW "force"
|
||||||
|
done
|
||||||
fi
|
fi
|
||||||
else
|
else
|
||||||
setup_common eth0 eth1
|
setup_common eth0 eth1
|
||||||
|
if [ -n "$EXTRA_PUBNICS" ]
|
||||||
|
then
|
||||||
|
for((i = 2; i < 2 + $EXTRA_PUBNICS; i++))
|
||||||
|
do
|
||||||
|
setup_interface "$i" "0.0.0.0" "255.255.255.255" $GW "force"
|
||||||
|
done
|
||||||
|
fi
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
if [ -n "$ETH2_IP" -a "$RROUTER" == "1" ]
|
||||||
|
then
|
||||||
|
setup_redundant_router
|
||||||
|
fi
|
||||||
|
|
||||||
|
log_it "Checking udev NIC assignment order changes"
|
||||||
|
if [ "$NIC_MACS" != "" ]
|
||||||
|
then
|
||||||
|
init_interfaces_orderby_macs "$NIC_MACS" "/tmp/interfaces" "/tmp/udev-rules"
|
||||||
|
newmd5=$(md5sum "/tmp/udev-rules" | awk '{print $1}')
|
||||||
|
rm /tmp/interfaces
|
||||||
|
rm /tmp/udev-rules
|
||||||
|
|
||||||
|
if [ "$oldmd5" != "$newmd5" ]
|
||||||
|
then
|
||||||
|
log_it "udev NIC assignment requires reboot to take effect"
|
||||||
|
sync
|
||||||
|
sleep 2
|
||||||
|
reboot
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
setup_dnsmasq
|
setup_dnsmasq
|
||||||
|
|
||||||
NS=$NS1
|
NS=$NS1
|
||||||
@ -706,6 +783,12 @@ for i in $CMDLINE
|
|||||||
router_pr)
|
router_pr)
|
||||||
ROUTER_PR=$VALUE
|
ROUTER_PR=$VALUE
|
||||||
;;
|
;;
|
||||||
|
extra_pubnics)
|
||||||
|
EXTRA_PUBNICS=$VALUE
|
||||||
|
;;
|
||||||
|
nic_macs)
|
||||||
|
NIC_MACS=$VALUE
|
||||||
|
;;
|
||||||
esac
|
esac
|
||||||
done
|
done
|
||||||
}
|
}
|
||||||
|
|||||||
@ -151,6 +151,7 @@ public enum Config {
|
|||||||
RestartRetryInterval("Advanced", HighAvailabilityManager.class, Integer.class, "restart.retry.interval", "600", "Time (in seconds) between retries to restart a vm", null),
|
RestartRetryInterval("Advanced", HighAvailabilityManager.class, Integer.class, "restart.retry.interval", "600", "Time (in seconds) between retries to restart a vm", null),
|
||||||
RouterStatsInterval("Advanced", NetworkManager.class, Integer.class, "router.stats.interval", "300", "Interval (in seconds) to report router statistics.", null),
|
RouterStatsInterval("Advanced", NetworkManager.class, Integer.class, "router.stats.interval", "300", "Interval (in seconds) to report router statistics.", null),
|
||||||
RouterTemplateId("Advanced", NetworkManager.class, Long.class, "router.template.id", "1", "Default ID for template.", null),
|
RouterTemplateId("Advanced", NetworkManager.class, Long.class, "router.template.id", "1", "Default ID for template.", null),
|
||||||
|
RouterExtraPublicNics("Advanced", NetworkManager.class, Integer.class, "router.extra.public.nics", "2", "specify extra public nics used for virtual router(up to 5)", "0-5"),
|
||||||
StartRetry("Advanced", AgentManager.class, Integer.class, "start.retry", "10", "Number of times to retry create and start commands", null),
|
StartRetry("Advanced", AgentManager.class, Integer.class, "start.retry", "10", "Number of times to retry create and start commands", null),
|
||||||
StopRetryInterval("Advanced", HighAvailabilityManager.class, Integer.class, "stop.retry.interval", "600", "Time in seconds between retries to stop or destroy a vm" , null),
|
StopRetryInterval("Advanced", HighAvailabilityManager.class, Integer.class, "stop.retry.interval", "600", "Time in seconds between retries to stop or destroy a vm" , null),
|
||||||
StorageCleanupInterval("Advanced", StorageManager.class, Integer.class, "storage.cleanup.interval", "86400", "The interval (in seconds) to wait before running the storage cleanup thread.", null),
|
StorageCleanupInterval("Advanced", StorageManager.class, Integer.class, "storage.cleanup.interval", "86400", "The interval (in seconds) to wait before running the storage cleanup thread.", null),
|
||||||
|
|||||||
@ -3,7 +3,11 @@
|
|||||||
*/
|
*/
|
||||||
package com.cloud.hypervisor.guru;
|
package com.cloud.hypervisor.guru;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.Comparator;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
import javax.ejb.Local;
|
import javax.ejb.Local;
|
||||||
@ -19,9 +23,11 @@ import com.cloud.agent.api.DeleteSnapshotBackupCommand;
|
|||||||
import com.cloud.agent.api.DeleteSnapshotsDirCommand;
|
import com.cloud.agent.api.DeleteSnapshotsDirCommand;
|
||||||
import com.cloud.agent.api.storage.CopyVolumeCommand;
|
import com.cloud.agent.api.storage.CopyVolumeCommand;
|
||||||
import com.cloud.agent.api.storage.PrimaryStorageDownloadCommand;
|
import com.cloud.agent.api.storage.PrimaryStorageDownloadCommand;
|
||||||
|
import com.cloud.agent.api.to.NicTO;
|
||||||
import com.cloud.agent.api.to.VirtualMachineTO;
|
import com.cloud.agent.api.to.VirtualMachineTO;
|
||||||
import com.cloud.cluster.CheckPointManager;
|
import com.cloud.cluster.CheckPointManager;
|
||||||
import com.cloud.cluster.ClusterManager;
|
import com.cloud.cluster.ClusterManager;
|
||||||
|
import com.cloud.exception.InsufficientAddressCapacityException;
|
||||||
import com.cloud.host.HostVO;
|
import com.cloud.host.HostVO;
|
||||||
import com.cloud.host.dao.HostDetailsDao;
|
import com.cloud.host.dao.HostDetailsDao;
|
||||||
import com.cloud.host.dao.HostDao;
|
import com.cloud.host.dao.HostDao;
|
||||||
@ -31,6 +37,10 @@ import com.cloud.hypervisor.HypervisorGuruBase;
|
|||||||
import com.cloud.hypervisor.vmware.VmwareCleanupMaid;
|
import com.cloud.hypervisor.vmware.VmwareCleanupMaid;
|
||||||
import com.cloud.hypervisor.vmware.manager.VmwareManager;
|
import com.cloud.hypervisor.vmware.manager.VmwareManager;
|
||||||
import com.cloud.hypervisor.vmware.mo.VirtualEthernetCardType;
|
import com.cloud.hypervisor.vmware.mo.VirtualEthernetCardType;
|
||||||
|
import com.cloud.network.NetworkManager;
|
||||||
|
import com.cloud.network.NetworkVO;
|
||||||
|
import com.cloud.network.Networks.TrafficType;
|
||||||
|
import com.cloud.network.dao.NetworkDao;
|
||||||
import com.cloud.secstorage.CommandExecLogDao;
|
import com.cloud.secstorage.CommandExecLogDao;
|
||||||
import com.cloud.secstorage.CommandExecLogVO;
|
import com.cloud.secstorage.CommandExecLogVO;
|
||||||
import com.cloud.storage.GuestOSVO;
|
import com.cloud.storage.GuestOSVO;
|
||||||
@ -40,9 +50,11 @@ import com.cloud.template.VirtualMachineTemplate.BootloaderType;
|
|||||||
import com.cloud.utils.Pair;
|
import com.cloud.utils.Pair;
|
||||||
import com.cloud.utils.component.Inject;
|
import com.cloud.utils.component.Inject;
|
||||||
import com.cloud.utils.db.DB;
|
import com.cloud.utils.db.DB;
|
||||||
|
import com.cloud.utils.exception.CloudRuntimeException;
|
||||||
import com.cloud.utils.net.NetUtils;
|
import com.cloud.utils.net.NetUtils;
|
||||||
import com.cloud.vm.ConsoleProxyVO;
|
import com.cloud.vm.ConsoleProxyVO;
|
||||||
import com.cloud.vm.DomainRouterVO;
|
import com.cloud.vm.DomainRouterVO;
|
||||||
|
import com.cloud.vm.NicProfile;
|
||||||
import com.cloud.vm.SecondaryStorageVmVO;
|
import com.cloud.vm.SecondaryStorageVmVO;
|
||||||
import com.cloud.vm.VirtualMachine;
|
import com.cloud.vm.VirtualMachine;
|
||||||
import com.cloud.vm.VirtualMachineProfile;
|
import com.cloud.vm.VirtualMachineProfile;
|
||||||
@ -52,6 +64,7 @@ import com.cloud.vm.VmDetailConstants;
|
|||||||
public class VMwareGuru extends HypervisorGuruBase implements HypervisorGuru {
|
public class VMwareGuru extends HypervisorGuruBase implements HypervisorGuru {
|
||||||
private static final Logger s_logger = Logger.getLogger(VMwareGuru.class);
|
private static final Logger s_logger = Logger.getLogger(VMwareGuru.class);
|
||||||
|
|
||||||
|
@Inject NetworkDao _networkDao;
|
||||||
@Inject GuestOSDao _guestOsDao;
|
@Inject GuestOSDao _guestOsDao;
|
||||||
@Inject HostDao _hostDao;
|
@Inject HostDao _hostDao;
|
||||||
@Inject HostDetailsDao _hostDetailsDao;
|
@Inject HostDetailsDao _hostDetailsDao;
|
||||||
@ -60,6 +73,7 @@ public class VMwareGuru extends HypervisorGuruBase implements HypervisorGuru {
|
|||||||
@Inject VmwareManager _vmwareMgr;
|
@Inject VmwareManager _vmwareMgr;
|
||||||
@Inject SecondaryStorageVmManager _secStorageMgr;
|
@Inject SecondaryStorageVmManager _secStorageMgr;
|
||||||
@Inject CheckPointManager _checkPointMgr;
|
@Inject CheckPointManager _checkPointMgr;
|
||||||
|
@Inject NetworkManager _networkMgr;
|
||||||
|
|
||||||
protected VMwareGuru() {
|
protected VMwareGuru() {
|
||||||
super();
|
super();
|
||||||
@ -109,12 +123,107 @@ public class VMwareGuru extends HypervisorGuruBase implements HypervisorGuru {
|
|||||||
}
|
}
|
||||||
to.setDetails(details);
|
to.setDetails(details);
|
||||||
|
|
||||||
|
if(vm.getVirtualMachine() instanceof DomainRouterVO) {
|
||||||
|
List<NicProfile> nicProfiles = vm.getNics();
|
||||||
|
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 + _vmwareMgr.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 + _vmwareMgr.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");
|
||||||
|
|
||||||
|
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());
|
||||||
to.setOs(guestOS.getDisplayName());
|
to.setOs(guestOS.getDisplayName());
|
||||||
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 @DB
|
@Override @DB
|
||||||
public long getCommandHostDelegation(long hostId, Command cmd) {
|
public long getCommandHostDelegation(long hostId, Command cmd) {
|
||||||
boolean needDelegation = false;
|
boolean needDelegation = false;
|
||||||
|
|||||||
@ -113,6 +113,7 @@ public class VmwareManagerImpl implements VmwareManager, VmwareStorageMount, Lis
|
|||||||
int _additionalPortRangeStart;
|
int _additionalPortRangeStart;
|
||||||
int _additionalPortRangeSize;
|
int _additionalPortRangeSize;
|
||||||
int _maxHostsPerCluster;
|
int _maxHostsPerCluster;
|
||||||
|
int _routerExtraPublicNics = 2;
|
||||||
|
|
||||||
String _cpuOverprovisioningFactor = "1";
|
String _cpuOverprovisioningFactor = "1";
|
||||||
String _reserveCpu = "false";
|
String _reserveCpu = "false";
|
||||||
@ -213,7 +214,6 @@ public class VmwareManagerImpl implements VmwareManager, VmwareStorageMount, Lis
|
|||||||
_managemetPortGroupName = "Management Network";
|
_managemetPortGroupName = "Management Network";
|
||||||
}
|
}
|
||||||
|
|
||||||
configDao.getValue(Config.VmwareServiceConsole.key());
|
|
||||||
_additionalPortRangeStart = NumbersUtil.parseInt(configDao.getValue(Config.VmwareAdditionalVncPortRangeStart.key()), 59000);
|
_additionalPortRangeStart = NumbersUtil.parseInt(configDao.getValue(Config.VmwareAdditionalVncPortRangeStart.key()), 59000);
|
||||||
if(_additionalPortRangeStart > 65535) {
|
if(_additionalPortRangeStart > 65535) {
|
||||||
s_logger.warn("Invalid port range start port (" + _additionalPortRangeStart + ") for additional VNC port allocation, reset it to default start port 59000");
|
s_logger.warn("Invalid port range start port (" + _additionalPortRangeStart + ") for additional VNC port allocation, reset it to default start port 59000");
|
||||||
@ -226,6 +226,8 @@ public class VmwareManagerImpl implements VmwareManager, VmwareStorageMount, Lis
|
|||||||
_additionalPortRangeSize = Math.min(1000, 65535 - _additionalPortRangeStart);
|
_additionalPortRangeSize = Math.min(1000, 65535 - _additionalPortRangeStart);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
_routerExtraPublicNics = NumbersUtil.parseInt(configDao.getValue(Config.RouterExtraPublicNics.key()), 2);
|
||||||
|
|
||||||
_maxHostsPerCluster = NumbersUtil.parseInt(configDao.getValue(Config.VmwarePerClusterHostMax.key()), VmwareManager.MAX_HOSTS_PER_CLUSTER);
|
_maxHostsPerCluster = NumbersUtil.parseInt(configDao.getValue(Config.VmwarePerClusterHostMax.key()), VmwareManager.MAX_HOSTS_PER_CLUSTER);
|
||||||
_cpuOverprovisioningFactor = configDao.getValue(Config.CPUOverprovisioningFactor.key());
|
_cpuOverprovisioningFactor = configDao.getValue(Config.CPUOverprovisioningFactor.key());
|
||||||
if(_cpuOverprovisioningFactor == null || _cpuOverprovisioningFactor.isEmpty())
|
if(_cpuOverprovisioningFactor == null || _cpuOverprovisioningFactor.isEmpty())
|
||||||
@ -242,6 +244,8 @@ public class VmwareManagerImpl implements VmwareManager, VmwareStorageMount, Lis
|
|||||||
if(_reserveMem == null || _reserveMem.isEmpty())
|
if(_reserveMem == null || _reserveMem.isEmpty())
|
||||||
_reserveMem = "false";
|
_reserveMem = "false";
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
s_logger.info("Additional VNC port allocation range is settled at " + _additionalPortRangeStart + " to " + (_additionalPortRangeStart + _additionalPortRangeSize));
|
s_logger.info("Additional VNC port allocation range is settled at " + _additionalPortRangeStart + " to " + (_additionalPortRangeStart + _additionalPortRangeSize));
|
||||||
|
|
||||||
value = configDao.getValue("vmware.host.scan.interval");
|
value = configDao.getValue("vmware.host.scan.interval");
|
||||||
@ -829,4 +833,9 @@ public class VmwareManagerImpl implements VmwareManager, VmwareStorageMount, Lis
|
|||||||
public int getMaxHostsPerCluster() {
|
public int getMaxHostsPerCluster() {
|
||||||
return this._maxHostsPerCluster;
|
return this._maxHostsPerCluster;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getRouterExtraPublicNics() {
|
||||||
|
return this._routerExtraPublicNics;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -112,6 +112,7 @@ import com.cloud.host.HostVO;
|
|||||||
import com.cloud.host.Status;
|
import com.cloud.host.Status;
|
||||||
import com.cloud.host.dao.HostDao;
|
import com.cloud.host.dao.HostDao;
|
||||||
import com.cloud.hypervisor.Hypervisor.HypervisorType;
|
import com.cloud.hypervisor.Hypervisor.HypervisorType;
|
||||||
|
import com.cloud.hypervisor.vmware.manager.VmwareManager;
|
||||||
import com.cloud.network.IPAddressVO;
|
import com.cloud.network.IPAddressVO;
|
||||||
import com.cloud.network.IpAddress;
|
import com.cloud.network.IpAddress;
|
||||||
import com.cloud.network.LoadBalancerVO;
|
import com.cloud.network.LoadBalancerVO;
|
||||||
@ -329,6 +330,7 @@ public class VirtualNetworkApplianceManagerImpl implements VirtualNetworkApplian
|
|||||||
private String _dnsBasicZoneUpdates = "all";
|
private String _dnsBasicZoneUpdates = "all";
|
||||||
|
|
||||||
private boolean _disable_rp_filter = false;
|
private boolean _disable_rp_filter = false;
|
||||||
|
int _routerExtraPublicNics = 2;
|
||||||
private long mgmtSrvrId = MacAddress.getMacAddress().toLong();
|
private long mgmtSrvrId = MacAddress.getMacAddress().toLong();
|
||||||
|
|
||||||
ScheduledExecutorService _executor;
|
ScheduledExecutorService _executor;
|
||||||
@ -592,6 +594,9 @@ public class VirtualNetworkApplianceManagerImpl implements VirtualNetworkApplian
|
|||||||
_mgmt_host = configs.get("host");
|
_mgmt_host = configs.get("host");
|
||||||
_routerRamSize = NumbersUtil.parseInt(configs.get("router.ram.size"), DEFAULT_ROUTER_VM_RAMSIZE);
|
_routerRamSize = NumbersUtil.parseInt(configs.get("router.ram.size"), DEFAULT_ROUTER_VM_RAMSIZE);
|
||||||
_routerCpuMHz = NumbersUtil.parseInt(configs.get("router.cpu.mhz"), DEFAULT_ROUTER_CPU_MHZ);
|
_routerCpuMHz = NumbersUtil.parseInt(configs.get("router.cpu.mhz"), DEFAULT_ROUTER_CPU_MHZ);
|
||||||
|
|
||||||
|
_routerExtraPublicNics = NumbersUtil.parseInt(_configDao.getValue(Config.RouterExtraPublicNics.key()), 2);
|
||||||
|
|
||||||
String value = configs.get("start.retry");
|
String value = configs.get("start.retry");
|
||||||
_retry = NumbersUtil.parseInt(value, 2);
|
_retry = NumbersUtil.parseInt(value, 2);
|
||||||
|
|
||||||
@ -1354,7 +1359,6 @@ public class VirtualNetworkApplianceManagerImpl implements VirtualNetworkApplian
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
String rpValue = _configDao.getValue(Config.NetworkRouterRpFilter.key());
|
String rpValue = _configDao.getValue(Config.NetworkRouterRpFilter.key());
|
||||||
if (rpValue != null && rpValue.equalsIgnoreCase("true")) {
|
if (rpValue != null && rpValue.equalsIgnoreCase("true")) {
|
||||||
_disable_rp_filter = true;
|
_disable_rp_filter = true;
|
||||||
@ -1484,6 +1488,10 @@ public class VirtualNetworkApplianceManagerImpl implements VirtualNetworkApplian
|
|||||||
buf.append(" useextdns=true");
|
buf.append(" useextdns=true");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if(profile.getHypervisorType() == HypervisorType.VMware) {
|
||||||
|
buf.append(" extra_pubnics=" + _routerExtraPublicNics);
|
||||||
|
}
|
||||||
|
|
||||||
if (s_logger.isDebugEnabled()) {
|
if (s_logger.isDebugEnabled()) {
|
||||||
s_logger.debug("Boot Args for " + profile + ": " + buf.toString());
|
s_logger.debug("Boot Args for " + profile + ": " + buf.toString());
|
||||||
}
|
}
|
||||||
|
|||||||
@ -7,4 +7,5 @@ package com.cloud.hypervisor.vmware.mo;
|
|||||||
public interface CustomFieldConstants {
|
public interface CustomFieldConstants {
|
||||||
public final static String CLOUD_UUID = "cloud.uuid";
|
public final static String CLOUD_UUID = "cloud.uuid";
|
||||||
public final static String CLOUD_GC = "cloud.gc";
|
public final static String CLOUD_GC = "cloud.gc";
|
||||||
|
public final static String CLOUD_NIC_MASK = "cloud.nic.mask";
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user