server: Support for persistence mode in L2 networks (#4561)

This PR aims at introducing persistence mode in L2 networks and enhancing the behavior in Isolated networks
Doc PR apache/cloudstack-documentation#183

Co-authored-by: Pearl Dsilva <pearl.dsilva@shapeblue.com>
This commit is contained in:
Pearl Dsilva 2021-04-05 14:37:11 +05:30 committed by GitHub
parent 3783fd5cec
commit 0dbeb262e4
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
41 changed files with 1257 additions and 89 deletions

View File

@ -0,0 +1,27 @@
// 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 CleanupPersistentNetworkResourceAnswer extends Answer {
public CleanupPersistentNetworkResourceAnswer() {
}
public CleanupPersistentNetworkResourceAnswer(CleanupPersistentNetworkResourceCommand cmd, boolean success, String result) {
super(cmd, success, result);
}
}

View File

@ -0,0 +1,43 @@
// 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 com.cloud.agent.api.to.NicTO;
public class CleanupPersistentNetworkResourceCommand extends Command {
NicTO nicTO;
protected CleanupPersistentNetworkResourceCommand() {}
public CleanupPersistentNetworkResourceCommand(NicTO nicTO) {
this.nicTO = nicTO;
}
public NicTO getNicTO() {
return nicTO;
}
public void setNicTO(NicTO nicTO) {
this.nicTO = nicTO;
}
@Override
public boolean executeInSequence() {
return false;
}
}

View File

@ -40,6 +40,7 @@ public class MigrateCommand extends Command {
private boolean executeInSequence = false;
private List<MigrateDiskInfo> migrateDiskInfoList = new ArrayList<>();
private Map<String, DpdkTO> dpdkInterfaceMapping = new HashMap<>();
Map<String, Boolean> vlanToPersistenceMap = new HashMap<>();
public Map<String, DpdkTO> getDpdkInterfaceMapping() {
return dpdkInterfaceMapping;
@ -49,6 +50,14 @@ public class MigrateCommand extends Command {
this.dpdkInterfaceMapping = dpdkInterfaceMapping;
}
public Map<String, Boolean> getVlanToPersistenceMap() {
return vlanToPersistenceMap;
}
public void setVlanToPersistenceMap(Map<String, Boolean> vlanToPersistenceMap) {
this.vlanToPersistenceMap = vlanToPersistenceMap;
}
protected MigrateCommand() {
}

View File

@ -0,0 +1,26 @@
// 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 SetupPersistentNetworkAnswer extends Answer {
public SetupPersistentNetworkAnswer(){}
public SetupPersistentNetworkAnswer(SetupPersistentNetworkCommand cmd, boolean success, String result) {
super(cmd, success, result);
}
}

View File

@ -0,0 +1,41 @@
// 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 com.cloud.agent.api.to.NicTO;
public class SetupPersistentNetworkCommand extends Command {
NicTO nic;
public SetupPersistentNetworkCommand(NicTO nic) {
this.nic = nic;
}
public NicTO getNic() {
return nic;
}
protected SetupPersistentNetworkCommand() {
}
@Override
public boolean executeInSequence() {
return false;
}
}

View File

@ -36,6 +36,7 @@ public class StopCommand extends RebootCommand {
String controlIp = null;
boolean forceStop = false;
private Map<String, DpdkTO> dpdkInterfaceMapping;
Map<String, Boolean> vlanToPersistenceMap;
public Map<String, DpdkTO> getDpdkInterfaceMapping() {
return dpdkInterfaceMapping;
@ -129,4 +130,12 @@ public class StopCommand extends RebootCommand {
public List<Map<String, String>> getVolumesToDisconnect() {
return volumesToDisconnect;
}
public Map<String, Boolean> getVlanToPersistenceMap() {
return vlanToPersistenceMap;
}
public void setVlanToPersistenceMap(Map<String, Boolean> vlanToPersistenceMap) {
this.vlanToPersistenceMap = vlanToPersistenceMap;
}
}

View File

@ -19,11 +19,14 @@
package com.cloud.agent.api;
import java.util.Map;
import com.cloud.agent.api.to.NicTO;
public class UnPlugNicCommand extends Command {
NicTO nic;
String instanceName;
Map<String, Boolean> vlanToPersistenceMap;
public NicTO getNic() {
return nic;
@ -45,4 +48,12 @@ public class UnPlugNicCommand extends Command {
public String getVmName() {
return instanceName;
}
public Map<String, Boolean> getVlanToPersistenceMap() {
return vlanToPersistenceMap;
}
public void setVlanToPersistenceMap(Map<String, Boolean> vlanToPersistenceMap) {
this.vlanToPersistenceMap = vlanToPersistenceMap;
}
}

View File

@ -43,6 +43,11 @@ import java.util.concurrent.TimeUnit;
import javax.inject.Inject;
import javax.naming.ConfigurationException;
import com.cloud.api.query.dao.DomainRouterJoinDao;
import com.cloud.api.query.dao.UserVmJoinDao;
import com.cloud.api.query.vo.DomainRouterJoinVO;
import com.cloud.api.query.vo.UserVmJoinVO;
import com.cloud.deployasis.dao.UserVmDeployAsIsDetailsDao;
import org.apache.cloudstack.affinity.dao.AffinityGroupVMMapDao;
import org.apache.cloudstack.api.ApiConstants;
import org.apache.cloudstack.api.command.admin.vm.MigrateVMCmd;
@ -143,7 +148,6 @@ import com.cloud.deploy.DeploymentPlan;
import com.cloud.deploy.DeploymentPlanner;
import com.cloud.deploy.DeploymentPlanner.ExcludeList;
import com.cloud.deploy.DeploymentPlanningManager;
import com.cloud.deployasis.dao.UserVmDeployAsIsDetailsDao;
import com.cloud.event.EventTypes;
import com.cloud.event.UsageEventUtils;
import com.cloud.event.UsageEventVO;
@ -169,6 +173,7 @@ import com.cloud.hypervisor.HypervisorGuru;
import com.cloud.hypervisor.HypervisorGuruManager;
import com.cloud.network.Network;
import com.cloud.network.NetworkModel;
import com.cloud.network.Networks;
import com.cloud.network.dao.NetworkDao;
import com.cloud.network.dao.NetworkDetailVO;
import com.cloud.network.dao.NetworkDetailsDao;
@ -179,6 +184,8 @@ import com.cloud.offering.DiskOffering;
import com.cloud.offering.DiskOfferingInfo;
import com.cloud.offering.NetworkOffering;
import com.cloud.offering.ServiceOffering;
import com.cloud.offerings.NetworkOfferingVO;
import com.cloud.offerings.dao.NetworkOfferingDao;
import com.cloud.offerings.dao.NetworkOfferingDetailsDao;
import com.cloud.org.Cluster;
import com.cloud.resource.ResourceManager;
@ -350,6 +357,12 @@ public class VirtualMachineManagerImpl extends ManagerBase implements VirtualMac
private SecurityGroupManager _securityGroupManager;
@Inject
private UserVmDeployAsIsDetailsDao userVmDeployAsIsDetailsDao;
@Inject
private UserVmJoinDao userVmJoinDao;
@Inject
private NetworkOfferingDao networkOfferingDao;
@Inject
private DomainRouterJoinDao domainRouterJoinDao;
VmWorkJobHandlerProxy _jobHandlerProxy = new VmWorkJobHandlerProxy(this);
@ -1247,6 +1260,10 @@ public class VirtualMachineManagerImpl extends ManagerBase implements VirtualMac
}
StopCommand stopCmd = new StopCommand(vm, getExecuteInSequence(vm.getHypervisorType()), false);
stopCmd.setControlIp(getControlNicIpForVM(vm));
Map<String, Boolean> vlanToPersistenceMap = getVlanToPersistenceMapForVM(vm.getId());
if (MapUtils.isNotEmpty(vlanToPersistenceMap)) {
stopCmd.setVlanToPersistenceMap(vlanToPersistenceMap);
}
final StopCommand cmd = stopCmd;
final Answer answer = _agentMgr.easySend(destHostId, cmd);
if (answer != null && answer instanceof StopAnswer) {
@ -1646,7 +1663,11 @@ public class VirtualMachineManagerImpl extends ManagerBase implements VirtualMac
protected boolean sendStop(final VirtualMachineGuru guru, final VirtualMachineProfile profile, final boolean force, final boolean checkBeforeCleanup) {
final VirtualMachine vm = profile.getVirtualMachine();
Map<String, Boolean> vlanToPersistenceMap = getVlanToPersistenceMapForVM(vm.getId());
StopCommand stpCmd = new StopCommand(vm, getExecuteInSequence(vm.getHypervisorType()), checkBeforeCleanup);
if (MapUtils.isNotEmpty(vlanToPersistenceMap)) {
stpCmd.setVlanToPersistenceMap(vlanToPersistenceMap);
}
stpCmd.setControlIp(getControlNicIpForVM(vm));
stpCmd.setVolumesToDisconnect(getVolumesToDisconnect(vm));
final StopCommand stop = stpCmd;
@ -1847,6 +1868,64 @@ public class VirtualMachineManagerImpl extends ManagerBase implements VirtualMac
advanceStop(vm, cleanUpEvenIfUnableToStop);
}
private void updatePersistenceMap(Map<String, Boolean> vlanToPersistenceMap, NetworkVO networkVO) {
NetworkOfferingVO offeringVO = networkOfferingDao.findById(networkVO.getNetworkOfferingId());
if (offeringVO != null) {
Pair<String, Boolean> data = getVMNetworkDetails(networkVO, offeringVO.isPersistent());
Boolean shouldDeleteNwResource = (MapUtils.isNotEmpty(vlanToPersistenceMap) && data != null) ? vlanToPersistenceMap.get(data.first()) : null;
if (data != null && (shouldDeleteNwResource == null || shouldDeleteNwResource)) {
vlanToPersistenceMap.put(data.first(), data.second());
}
}
}
private Map<String, Boolean> getVlanToPersistenceMapForVM(long vmId) {
List<UserVmJoinVO> userVmJoinVOs = userVmJoinDao.searchByIds(vmId);
Map<String, Boolean> vlanToPersistenceMap = new HashMap<>();
if (userVmJoinVOs != null && !userVmJoinVOs.isEmpty()) {
for (UserVmJoinVO userVmJoinVO : userVmJoinVOs) {
NetworkVO networkVO = _networkDao.findById(userVmJoinVO.getNetworkId());
updatePersistenceMap(vlanToPersistenceMap, networkVO);
}
} else {
VMInstanceVO vmInstanceVO = _vmDao.findById(vmId);
if (vmInstanceVO != null && vmInstanceVO.getType() == VirtualMachine.Type.DomainRouter) {
DomainRouterJoinVO routerVO = domainRouterJoinDao.findById(vmId);
NetworkVO networkVO = _networkDao.findById(routerVO.getNetworkId());
updatePersistenceMap(vlanToPersistenceMap, networkVO);
}
}
return vlanToPersistenceMap;
}
/**
*
* @param networkVO - the network object used to determine the vlanId from the broadcast URI
* @param isPersistent - indicates if the corresponding network's network offering is Persistent
*
* @return <VlanId, ShouldKVMBridgeBeDeleted> - basically returns the vlan ID which is used to determine the
* bridge name for KVM hypervisor and based on the network and isolation type and persistent setting of the offering
* we decide whether the bridge is to be deleted (KVM) if the last VM in that host is destroyed / migrated
*/
private Pair<String, Boolean> getVMNetworkDetails(NetworkVO networkVO, boolean isPersistent) {
URI broadcastUri = networkVO.getBroadcastUri();
if (broadcastUri != null) {
String scheme = broadcastUri.getScheme();
String vlanId = Networks.BroadcastDomainType.getValue(broadcastUri);
boolean shouldDelete = !((networkVO.getGuestType() == Network.GuestType.L2 || networkVO.getGuestType() == Network.GuestType.Isolated) &&
(scheme != null && scheme.equalsIgnoreCase("vlan"))
&& isPersistent);
if (shouldDelete) {
int persistentNetworksCount = _networkDao.getOtherPersistentNetworksCount(networkVO.getId(), networkVO.getBroadcastUri().toString(), true);
if (persistentNetworksCount > 0) {
shouldDelete = false;
}
}
return new Pair<>(vlanId, shouldDelete);
}
return null;
}
private void advanceStop(final VMInstanceVO vm, final boolean cleanUpEvenIfUnableToStop) throws AgentUnavailableException, OperationTimedoutException,
ConcurrentOperationException {
final State state = vm.getState();
@ -1945,8 +2024,12 @@ public class VirtualMachineManagerImpl extends ManagerBase implements VirtualMac
vmGuru.prepareStop(profile);
Map<String, Boolean> vlanToPersistenceMap = getVlanToPersistenceMapForVM(vm.getId());
final StopCommand stop = new StopCommand(vm, getExecuteInSequence(vm.getHypervisorType()), false, cleanUpEvenIfUnableToStop);
stop.setControlIp(getControlNicIpForVM(vm));
if (MapUtils.isNotEmpty(vlanToPersistenceMap)) {
stop.setVlanToPersistenceMap(vlanToPersistenceMap);
}
boolean stopped = false;
Answer answer = null;
@ -2682,7 +2765,11 @@ public class VirtualMachineManagerImpl extends ManagerBase implements VirtualMac
Map<String, DpdkTO> dpdkInterfaceMapping = null;
try {
final boolean isWindows = _guestOsCategoryDao.findById(_guestOsDao.findById(vm.getGuestOSId()).getCategoryId()).getName().equalsIgnoreCase("Windows");
Map<String, Boolean> vlanToPersistenceMap = getVlanToPersistenceMapForVM(vm.getId());
final MigrateCommand mc = new MigrateCommand(vm.getInstanceName(), dest.getHost().getPrivateIpAddress(), isWindows, to, getExecuteInSequence(vm.getHypervisorType()));
if (MapUtils.isNotEmpty(vlanToPersistenceMap)) {
mc.setVlanToPersistenceMap(vlanToPersistenceMap);
}
boolean kvmAutoConvergence = StorageManager.KvmAutoConvergence.value();
mc.setAutoConvergence(kvmAutoConvergence);
@ -3525,6 +3612,10 @@ public class VirtualMachineManagerImpl extends ManagerBase implements VirtualMac
if (MapUtils.isNotEmpty(dpdkInterfaceMapping)) {
cmd.setDpdkInterfaceMapping(dpdkInterfaceMapping);
}
Map<String, Boolean> vlanToPersistenceMap = getVlanToPersistenceMapForVM(vm.getId());
if (MapUtils.isNotEmpty(vlanToPersistenceMap)) {
cmd.setVlanToPersistenceMap(vlanToPersistenceMap);
}
return cmd;
}
@ -3543,6 +3634,10 @@ public class VirtualMachineManagerImpl extends ManagerBase implements VirtualMac
StopCommand cmd = new StopCommand(vmName, getExecuteInSequence(null), false);
cmd.setControlIp(getControlNicIpForVM(vm));
Map<String, Boolean> vlanToPersistenceMap = getVlanToPersistenceMapForVM(vm.getId());
if (MapUtils.isNotEmpty(vlanToPersistenceMap)) {
cmd.setVlanToPersistenceMap(vlanToPersistenceMap);
}
return cmd;
}
@ -4329,8 +4424,12 @@ public class VirtualMachineManagerImpl extends ManagerBase implements VirtualMac
boolean migrated = false;
try {
Map<String, Boolean> vlanToPersistenceMap = getVlanToPersistenceMapForVM(vm.getId());
final boolean isWindows = _guestOsCategoryDao.findById(_guestOsDao.findById(vm.getGuestOSId()).getCategoryId()).getName().equalsIgnoreCase("Windows");
final MigrateCommand mc = new MigrateCommand(vm.getInstanceName(), dest.getHost().getPrivateIpAddress(), isWindows, to, getExecuteInSequence(vm.getHypervisorType()));
if (MapUtils.isNotEmpty(vlanToPersistenceMap)) {
mc.setVlanToPersistenceMap(vlanToPersistenceMap);
}
boolean kvmAutoConvergence = StorageManager.KvmAutoConvergence.value();
mc.setAutoConvergence(kvmAutoConvergence);
@ -4487,6 +4586,10 @@ public class VirtualMachineManagerImpl extends ManagerBase implements VirtualMac
try {
final Commands cmds = new Commands(Command.OnError.Stop);
final UnPlugNicCommand unplugNicCmd = new UnPlugNicCommand(nic, vm.getName());
Map<String, Boolean> vlanToPersistenceMap = getVlanToPersistenceMapForVM(vm.getId());
if (MapUtils.isNotEmpty(vlanToPersistenceMap)) {
unplugNicCmd.setVlanToPersistenceMap(vlanToPersistenceMap);
}
cmds.addCommand("unplugnic", unplugNicCmd);
_agentMgr.send(dest.getHost().getId(), cmds);

View File

@ -40,6 +40,12 @@ import java.util.stream.Collectors;
import javax.inject.Inject;
import javax.naming.ConfigurationException;
import com.cloud.agent.api.CleanupPersistentNetworkResourceAnswer;
import com.cloud.agent.api.CleanupPersistentNetworkResourceCommand;
import com.cloud.agent.api.SetupPersistentNetworkAnswer;
import com.cloud.agent.api.SetupPersistentNetworkCommand;
import com.cloud.dc.ClusterVO;
import com.cloud.dc.dao.ClusterDao;
import com.cloud.deployasis.dao.TemplateDeployAsIsDetailsDao;
import org.apache.cloudstack.acl.ControlledEntity.ACLType;
import org.apache.cloudstack.api.ApiConstants;
@ -90,16 +96,19 @@ import com.cloud.deploy.DeploymentPlan;
import com.cloud.domain.Domain;
import com.cloud.event.EventTypes;
import com.cloud.event.UsageEventUtils;
import com.cloud.exception.AgentUnavailableException;
import com.cloud.exception.ConcurrentOperationException;
import com.cloud.exception.ConnectionException;
import com.cloud.exception.InsufficientAddressCapacityException;
import com.cloud.exception.InsufficientCapacityException;
import com.cloud.exception.InsufficientVirtualNetworkCapacityException;
import com.cloud.exception.InvalidParameterValueException;
import com.cloud.exception.OperationTimedoutException;
import com.cloud.exception.ResourceAllocationException;
import com.cloud.exception.ResourceUnavailableException;
import com.cloud.exception.UnsupportedServiceException;
import com.cloud.host.Host;
import com.cloud.host.HostVO;
import com.cloud.host.Status;
import com.cloud.host.dao.HostDao;
import com.cloud.hypervisor.Hypervisor.HypervisorType;
@ -182,6 +191,7 @@ import com.cloud.offerings.NetworkOfferingVO;
import com.cloud.offerings.dao.NetworkOfferingDao;
import com.cloud.offerings.dao.NetworkOfferingDetailsDao;
import com.cloud.offerings.dao.NetworkOfferingServiceMapDao;
import com.cloud.resource.ResourceManager;
import com.cloud.user.Account;
import com.cloud.user.ResourceLimitService;
import com.cloud.user.User;
@ -305,6 +315,8 @@ public class NetworkOrchestrator extends ManagerBase implements NetworkOrchestra
UserVmManager _userVmMgr;
@Inject
TemplateDeployAsIsDetailsDao templateDeployAsIsDetailsDao;
@Inject
ResourceManager resourceManager;
List<NetworkGuru> networkGurus;
@ -385,6 +397,8 @@ public class NetworkOrchestrator extends ManagerBase implements NetworkOrchestra
NetworkModel _networkModel;
@Inject
NicSecondaryIpDao _nicSecondaryIpDao;
@Inject
ClusterDao clusterDao;
protected StateMachine2<Network.State, Network.Event, Network> _stateMachine;
ScheduledExecutorService _executor;
@ -1152,11 +1166,12 @@ public class NetworkOrchestrator extends ManagerBase implements NetworkOrchestra
boolean isNetworkImplemented(final NetworkVO network) {
final Network.State state = network.getState();
final NetworkOfferingVO offeringVO = _networkOfferingDao.findById(network.getNetworkOfferingId());
if (state == Network.State.Implemented) {
return true;
} else if (state == Network.State.Setup) {
final DataCenterVO zone = _dcDao.findById(network.getDataCenterId());
if (!isSharedNetworkOfferingWithServices(network.getNetworkOfferingId()) || zone.getNetworkType() == NetworkType.Basic) {
if ((!isSharedNetworkOfferingWithServices(network.getNetworkOfferingId()) && !offeringVO.isPersistent()) || zone.getNetworkType() == NetworkType.Basic) {
return true;
}
}
@ -1181,6 +1196,85 @@ public class NetworkOrchestrator extends ManagerBase implements NetworkOrchestra
return implemented;
}
/**
*
* Creates a dummy NicTO object which is used by the respective hypervisors to setup network elements / resources
* - bridges(KVM), VLANs(Xen) and portgroups(VMWare) for L2 network
*/
private NicTO createNicTOFromNetworkAndOffering(NetworkVO networkVO, NetworkOfferingVO networkOfferingVO, HostVO hostVO) {
NicTO to = new NicTO();
to.setName(_networkModel.getNetworkTag(hostVO.getHypervisorType(), networkVO));
to.setBroadcastType(networkVO.getBroadcastDomainType());
to.setType(networkVO.getTrafficType());
to.setBroadcastUri(networkVO.getBroadcastUri());
to.setIsolationuri(networkVO.getBroadcastUri());
to.setNetworkRateMbps(_configMgr.getNetworkOfferingNetworkRate(networkOfferingVO.getId(), networkVO.getDataCenterId()));
to.setSecurityGroupEnabled(_networkModel.isSecurityGroupSupportedInNetwork(networkVO));
return to;
}
private Pair<Boolean, NicTO> isNtwConfiguredInCluster(HostVO hostVO, Map<Long, List<Long>> clusterToHostsMap, NetworkVO networkVO, NetworkOfferingVO networkOfferingVO) {
Long clusterId = hostVO.getClusterId();
List<Long> hosts = clusterToHostsMap.get(clusterId);
if (hosts == null) {
hosts = new ArrayList<>();
}
if (hostVO.getHypervisorType() == HypervisorType.KVM || hostVO.getHypervisorType() == HypervisorType.XenServer ) {
hosts.add(hostVO.getId());
clusterToHostsMap.put(clusterId, hosts);
return new Pair<>(false, createNicTOFromNetworkAndOffering(networkVO, networkOfferingVO, hostVO));
}
if (hosts != null && !hosts.isEmpty()) {
return new Pair<>(true, createNicTOFromNetworkAndOffering(networkVO, networkOfferingVO, hostVO));
}
hosts.add(hostVO.getId());
clusterToHostsMap.put(clusterId, hosts);
return new Pair<>(false, createNicTOFromNetworkAndOffering(networkVO, networkOfferingVO, hostVO));
}
private void setupPersistentNetwork(NetworkVO network, NetworkOfferingVO offering, Long dcId) throws AgentUnavailableException, OperationTimedoutException {
List<ClusterVO> clusterVOs = clusterDao.listClustersByDcId(dcId);
List<HostVO> hosts = resourceManager.listAllUpAndEnabledHostsInOneZoneByType(Host.Type.Routing, dcId);
Map<Long, List<Long>> clusterToHostsMap = new HashMap<>();
for (HostVO host : hosts) {
try {
Pair<Boolean, NicTO> networkCfgStateAndDetails = isNtwConfiguredInCluster(host, clusterToHostsMap, network, offering);
if (networkCfgStateAndDetails.first()) {
continue;
}
NicTO to = networkCfgStateAndDetails.second();
SetupPersistentNetworkCommand cmd = new SetupPersistentNetworkCommand(to);
final SetupPersistentNetworkAnswer answer = (SetupPersistentNetworkAnswer) _agentMgr.send(host.getId(), cmd);
if (answer == null) {
s_logger.warn("Unable to get an answer to the SetupPersistentNetworkCommand from agent:" + host.getId());
clusterToHostsMap.get(host.getClusterId()).remove(host.getId());
continue;
}
if (!answer.getResult()) {
s_logger.warn("Unable to setup agent " + host.getId() + " due to " + answer.getDetails());
clusterToHostsMap.get(host.getClusterId()).remove(host.getId());
}
} catch (Exception e) {
s_logger.warn("Failed to connect to host: "+ host.getName());
}
}
if (clusterToHostsMap.keySet().size() != clusterVOs.size()) {
s_logger.warn("Hosts on all clusters may not have been configured with network devices.");
}
}
private boolean networkMeetsPersistenceCriteria(NetworkVO network, NetworkOfferingVO offering, boolean cleanup) {
boolean criteriaMet = offering.isPersistent() &&
(network.getBroadcastUri() != null && BroadcastDomainType.getSchemeValue(network.getBroadcastUri()) == BroadcastDomainType.Vlan);
if (!cleanup) {
return criteriaMet && network.getGuestType() == GuestType.L2;
} else {
return criteriaMet && (network.getGuestType() == GuestType.L2 || network.getGuestType() == GuestType.Isolated);
}
}
@Override
@DB
public Pair<NetworkGuru, NetworkVO> implementNetwork(final long networkId, final DeployDestination dest, final ReservationContext context) throws ConcurrentOperationException,
@ -1239,6 +1333,10 @@ public class NetworkOrchestrator extends ManagerBase implements NetworkOrchestra
// implement network elements and re-apply all the network rules
implementNetworkElementsAndResources(dest, context, network, offering);
long dcId = dest.getDataCenter().getId();
if (networkMeetsPersistenceCriteria(network,offering, false)) {
setupPersistentNetwork(network, offering, dcId);
}
if (isSharedNetworkWithServices(network)) {
network.setState(Network.State.Implemented);
} else {
@ -1251,10 +1349,10 @@ public class NetworkOrchestrator extends ManagerBase implements NetworkOrchestra
return implemented;
} catch (final NoTransitionException e) {
s_logger.error(e.getMessage());
return null;
} catch (final CloudRuntimeException e) {
return new Pair<NetworkGuru, NetworkVO>(null, null);
} catch (final CloudRuntimeException | OperationTimedoutException e) {
s_logger.error("Caught exception: " + e.getMessage());
return null;
return new Pair<NetworkGuru, NetworkVO>(null, null);
} finally {
if (implemented.first() == null) {
s_logger.debug("Cleaning up because we're unable to implement the network " + network);
@ -2632,7 +2730,6 @@ public class NetworkOrchestrator extends ManagerBase implements NetworkOrchestra
userNetwork.setPvlanType(isolatedPvlanType);
}
}
final List<? extends Network> networks = setupNetwork(owner, ntwkOff, userNetwork, plan, name, displayText, true, domainId, aclType, subdomainAccessFinal, vpcId,
isDisplayNetworkEnabled);
Network network = null;
@ -2868,6 +2965,34 @@ public class NetworkOrchestrator extends ManagerBase implements NetworkOrchestra
return success;
}
private void cleanupPersistentnNetworkResources(NetworkVO network) {
long networkOfferingId = network.getNetworkOfferingId();
NetworkOfferingVO offering = _networkOfferingDao.findById(networkOfferingId);
if (offering != null) {
if (networkMeetsPersistenceCriteria(network, offering, true) &&
_networksDao.getOtherPersistentNetworksCount(network.getId(), network.getBroadcastUri().toString(), offering.isPersistent()) == 0) {
List<HostVO> hosts = resourceManager.listAllUpAndEnabledHostsInOneZoneByType(Host.Type.Routing, network.getDataCenterId());
for (HostVO host : hosts) {
try {
NicTO to = createNicTOFromNetworkAndOffering(network, offering, host);
CleanupPersistentNetworkResourceCommand cmd = new CleanupPersistentNetworkResourceCommand(to);
CleanupPersistentNetworkResourceAnswer answer = (CleanupPersistentNetworkResourceAnswer) _agentMgr.send(host.getId(), cmd);
if (answer == null) {
s_logger.warn("Unable to get an answer to the CleanupPersistentNetworkResourceCommand from agent:" + host.getId());
continue;
}
if (!answer.getResult()) {
s_logger.warn("Unable to setup agent " + host.getId() + " due to " + answer.getDetails());
}
} catch (Exception e) {
s_logger.warn("Failed to cleanup network resources on host: "+ host.getName());
}
}
}
}
}
@Override
@DB
public boolean destroyNetwork(final long networkId, final ReservationContext context, final boolean forced) {
@ -2907,6 +3032,8 @@ public class NetworkOrchestrator extends ManagerBase implements NetworkOrchestra
}
}
cleanupPersistentnNetworkResources(network);
// Shutdown network first
shutdownNetwork(networkId, context, false);
@ -3069,7 +3196,6 @@ public class NetworkOrchestrator extends ManagerBase implements NetworkOrchestra
s_logger.info("NetworkGarbageCollector uses '" + netGcWait + "' seconds for GC interval.");
for (final Long networkId : networkIds) {
if (!_networkModel.isNetworkReadyForGc(networkId)) {
continue;
}
@ -3941,7 +4067,6 @@ public class NetworkOrchestrator extends ManagerBase implements NetworkOrchestra
public NicProfile createNicForVm(final Network network, final NicProfile requested, final ReservationContext context, final VirtualMachineProfile vmProfile, final boolean prepare)
throws InsufficientVirtualNetworkCapacityException, InsufficientAddressCapacityException, ConcurrentOperationException, InsufficientCapacityException,
ResourceUnavailableException {
final VirtualMachine vm = vmProfile.getVirtualMachine();
final DataCenter dc = _entityMgr.findById(DataCenter.class, network.getDataCenterId());
final Host host = _hostDao.findById(vm.getHostId());

View File

@ -45,6 +45,8 @@ public interface NetworkDao extends GenericDao<NetworkVO, Long>, StateDao<State,
List<NetworkVO> getNetworksForOffering(long offeringId, long dataCenterId, long accountId);
int getOtherPersistentNetworksCount(long id, String broadcastURI, boolean isPersistent);
@Override
@Deprecated
NetworkVO persist(NetworkVO vo);

View File

@ -79,6 +79,7 @@ public class NetworkDaoImpl extends GenericDaoBase<NetworkVO, Long>implements Ne
SearchBuilder<NetworkVO> SourceNATSearch;
GenericSearchBuilder<NetworkVO, Long> VpcNetworksCount;
SearchBuilder<NetworkVO> OfferingAccountNetworkSearch;
SearchBuilder<NetworkVO> PersistentNetworkSearch;
GenericSearchBuilder<NetworkVO, Long> GarbageCollectedSearch;
SearchBuilder<NetworkVO> PrivateNetworkSearch;
@ -181,6 +182,16 @@ public class NetworkDaoImpl extends GenericDaoBase<NetworkVO, Long>implements Ne
CountBy.join("offerings", ntwkOffJoin, CountBy.entity().getNetworkOfferingId(), ntwkOffJoin.entity().getId(), JoinBuilder.JoinType.INNER);
CountBy.done();
PersistentNetworkSearch = createSearchBuilder();
PersistentNetworkSearch.and("id", PersistentNetworkSearch.entity().getId(), Op.NEQ);
PersistentNetworkSearch.and("guestType", PersistentNetworkSearch.entity().getGuestType(), Op.IN);
PersistentNetworkSearch.and("broadcastUri", PersistentNetworkSearch.entity().getBroadcastUri(), Op.EQ);
PersistentNetworkSearch.and("removed", PersistentNetworkSearch.entity().getRemoved(), Op.NULL);
final SearchBuilder<NetworkOfferingVO> persistentNtwkOffJoin = _ntwkOffDao.createSearchBuilder();
persistentNtwkOffJoin.and("persistent", persistentNtwkOffJoin.entity().isPersistent(), Op.EQ);
PersistentNetworkSearch.join("persistent", persistentNtwkOffJoin, PersistentNetworkSearch.entity().getNetworkOfferingId(), persistentNtwkOffJoin.entity().getId(), JoinType.INNER);
PersistentNetworkSearch.done();
PhysicalNetworkSearch = createSearchBuilder();
PhysicalNetworkSearch.and("physicalNetworkId", PhysicalNetworkSearch.entity().getPhysicalNetworkId(), Op.EQ);
PhysicalNetworkSearch.done();
@ -390,6 +401,18 @@ public class NetworkDaoImpl extends GenericDaoBase<NetworkVO, Long>implements Ne
return search(sc, null);
}
@Override
public int getOtherPersistentNetworksCount(long id, String broadcastURI, boolean isPersistent) {
Object[] guestTypes = {"Isolated", "L2"};
final SearchCriteria<NetworkVO> sc = PersistentNetworkSearch.create();
sc.setParameters("id", id);
sc.setParameters("broadcastUri", broadcastURI);
sc.setParameters("guestType", guestTypes);
sc.setJoinParameters("persistent", "persistent", isPersistent);
List<NetworkVO> persistentNetworks = search(sc, null);
return persistentNetworks.size();
}
@Override
public String getNextAvailableMacAddress(final long networkConfigId, Integer zoneMacIdentifier) {
final SequenceFetcher fetch = SequenceFetcher.getInstance();

View File

@ -267,8 +267,8 @@ public class BridgeVifDriver extends VifDriverBase {
}
@Override
public void unplug(LibvirtVMDef.InterfaceDef iface) {
deleteVnetBr(iface.getBrName());
public void unplug(LibvirtVMDef.InterfaceDef iface, boolean deleteBr) {
deleteVnetBr(iface.getBrName(), deleteBr);
}
@Override
@ -327,7 +327,7 @@ public class BridgeVifDriver extends VifDriverBase {
}
}
private void deleteVnetBr(String brName) {
private void deleteVnetBr(String brName, boolean deleteBr) {
synchronized (_vnetBridgeMonitor) {
String cmdout = Script.runSimpleBashScript("ls /sys/class/net/" + brName);
if (cmdout == null)
@ -376,6 +376,7 @@ public class BridgeVifDriver extends VifDriverBase {
command.add("-v", vNetId);
command.add("-p", pName);
command.add("-b", brName);
command.add("-d", String.valueOf(deleteBr));
final String result = command.execute();
if (result != null) {
@ -436,4 +437,22 @@ public class BridgeVifDriver extends VifDriverBase {
return false;
}
}
@Override
public void deleteBr(NicTO nic) {
String vlanId = Networks.BroadcastDomainType.getValue(nic.getBroadcastUri());
String trafficLabel = nic.getName();
String pifName = _pifs.get(trafficLabel);
if (pifName == null) {
// if not found in bridge map, maybe traffic label refers to pif already?
File pif = new File("/sys/class/net/" + trafficLabel);
if (pif.isDirectory()) {
pifName = trafficLabel;
}
}
if (vlanId != null && pifName != null) {
String brName = generateVnetBrName(pifName, vlanId);
deleteVnetBr(brName, true);
}
}
}

View File

@ -62,7 +62,7 @@ public class DirectVifDriver extends VifDriverBase {
}
@Override
public void unplug(LibvirtVMDef.InterfaceDef iface) {
public void unplug(LibvirtVMDef.InterfaceDef iface, boolean deleteBr) {
// not needed, libvirt will cleanup
}
@ -80,4 +80,7 @@ public class DirectVifDriver extends VifDriverBase {
public void createControlNetwork(String privBrName) {
}
@Override
public void deleteBr(NicTO nic) {
}
}

View File

@ -141,7 +141,7 @@ public class IvsVifDriver extends VifDriverBase {
}
@Override
public void unplug(InterfaceDef iface) {
public void unplug(InterfaceDef iface, boolean deleteBr) {
}
@Override
@ -287,6 +287,10 @@ public class IvsVifDriver extends VifDriverBase {
}
}
@Override
public void deleteBr(NicTO nic) {
}
private boolean isBridgeExists(String bridgeName) {
File f = new File("/sys/devices/virtual/net/" + bridgeName);
if (f.exists()) {

View File

@ -1870,7 +1870,7 @@ public class LibvirtComputingResource extends ServerResourceBase implements Serv
// We don't know which "traffic type" is associated with
// each interface at this point, so inform all vif drivers
for (final VifDriver vifDriver : getAllVifDrivers()) {
vifDriver.unplug(pluggedNic);
vifDriver.unplug(pluggedNic, true);
}
}
}
@ -3510,7 +3510,7 @@ public class LibvirtComputingResource extends ServerResourceBase implements Serv
if (nics != null) {
for (final InterfaceDef nic : nics) {
for (final VifDriver vifDriver : getAllVifDrivers()) {
vifDriver.unplug(nic);
vifDriver.unplug(nic, true);
}
}
}
@ -4219,6 +4219,24 @@ public class LibvirtComputingResource extends ServerResourceBase implements Serv
return vmsnapshots;
}
public String getVlanIdFromBridgeName(String brName) {
if (org.apache.commons.lang.StringUtils.isNotBlank(brName)) {
String[] s = brName.split("-");
if (s.length > 1) {
return s[1];
}
return null;
}
return null;
}
public boolean shouldDeleteBridge(Map<String, Boolean> vlanToPersistenceMap, String vlanId) {
if (MapUtils.isNotEmpty(vlanToPersistenceMap) && vlanId != null && vlanToPersistenceMap.containsKey(vlanId)) {
return vlanToPersistenceMap.get(vlanId);
}
return true;
}
private static String getTagValue(String tag, Element eElement) {
NodeList nlList = eElement.getElementsByTagName(tag).item(0).getChildNodes();
Node nValue = nlList.item(0);

View File

@ -194,7 +194,7 @@ public class OvsVifDriver extends VifDriverBase {
}
@Override
public void unplug(InterfaceDef iface) {
public void unplug(InterfaceDef iface, boolean deleteBr) {
// Libvirt apparently takes care of this, see BridgeVifDriver unplug
if (_libvirtComputingResource.dpdkSupport && StringUtils.isNotBlank(iface.getDpdkSourcePort())) {
// If DPDK is enabled, we'll need to cleanup the port as libvirt won't
@ -260,4 +260,8 @@ public class OvsVifDriver extends VifDriverBase {
return false;
}
}
@Override
public void deleteBr(NicTO nic) {
}
}

View File

@ -34,7 +34,7 @@ public interface VifDriver {
public LibvirtVMDef.InterfaceDef plug(NicTO nic, String guestOsType, String nicAdapter, Map<String, String> extraConfig) throws InternalErrorException, LibvirtException;
public void unplug(LibvirtVMDef.InterfaceDef iface);
public void unplug(LibvirtVMDef.InterfaceDef iface, boolean delete);
void attach(LibvirtVMDef.InterfaceDef iface);
@ -44,4 +44,6 @@ public interface VifDriver {
boolean isExistingBridge(String bridgeName);
void deleteBr(NicTO nic);
}

View File

@ -45,7 +45,7 @@ public abstract class VifDriverBase implements VifDriver {
public abstract LibvirtVMDef.InterfaceDef plug(NicTO nic, String guestOsType, String nicAdapter, Map<String, String> extraConfig) throws InternalErrorException, LibvirtException;
@Override
public abstract void unplug(LibvirtVMDef.InterfaceDef iface);
public abstract void unplug(LibvirtVMDef.InterfaceDef iface, boolean deleteBr);
protected LibvirtVMDef.InterfaceDef.NicModel getGuestNicModel(String platformEmulator, String nicAdapter) {
// if nicAdapter is found in ENUM, use it. Otherwise, match guest OS type as before

View File

@ -0,0 +1,44 @@
// 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.hypervisor.kvm.resource.wrapper;
import org.apache.log4j.Logger;
import com.cloud.agent.api.Answer;
import com.cloud.agent.api.CleanupPersistentNetworkResourceAnswer;
import com.cloud.agent.api.CleanupPersistentNetworkResourceCommand;
import com.cloud.agent.api.to.NicTO;
import com.cloud.hypervisor.kvm.resource.BridgeVifDriver;
import com.cloud.hypervisor.kvm.resource.LibvirtComputingResource;
import com.cloud.hypervisor.kvm.resource.VifDriver;
import com.cloud.resource.CommandWrapper;
import com.cloud.resource.ResourceWrapper;
@ResourceWrapper(handles = CleanupPersistentNetworkResourceCommand.class)
public class LibvirtCleanupPersistentNetworkResourceCommandWrapper extends CommandWrapper<CleanupPersistentNetworkResourceCommand, Answer, LibvirtComputingResource> {
private static final Logger s_logger = Logger.getLogger(LibvirtCleanupPersistentNetworkResourceCommandWrapper.class);
@Override
public Answer execute(CleanupPersistentNetworkResourceCommand command, LibvirtComputingResource serverResource) {
NicTO nic = command.getNicTO();
VifDriver driver = serverResource.getVifDriver(nic.getType());
if (driver instanceof BridgeVifDriver) {
driver.deleteBr(nic);
}
return new CleanupPersistentNetworkResourceAnswer(command, true, "Successfully deleted bridge");
}
}

View File

@ -96,6 +96,7 @@ public final class LibvirtMigrateCommandWrapper extends CommandWrapper<MigrateCo
@Override
public Answer execute(final MigrateCommand command, final LibvirtComputingResource libvirtComputingResource) {
final String vmName = command.getVmName();
final Map<String, Boolean> vlanToPersistenceMap = command.getVlanToPersistenceMap();
final String destinationUri = createMigrationURI(command.getDestinationIp(), libvirtComputingResource);
final List<MigrateDiskInfo> migrateDiskInfoList = command.getMigrateDiskInfoList();
@ -284,11 +285,12 @@ public final class LibvirtMigrateCommandWrapper extends CommandWrapper<MigrateCo
} else {
libvirtComputingResource.destroyNetworkRulesForVM(conn, vmName);
for (final InterfaceDef iface : ifaces) {
String vlanId = libvirtComputingResource.getVlanIdFromBridgeName(iface.getBrName());
// We don't know which "traffic type" is associated with
// each interface at this point, so inform all vif drivers
final List<VifDriver> allVifDrivers = libvirtComputingResource.getAllVifDrivers();
for (final VifDriver vifDriver : allVifDrivers) {
vifDriver.unplug(iface);
vifDriver.unplug(iface, libvirtComputingResource.shouldDeleteBridge(vlanToPersistenceMap, vlanId));
}
}
}

View File

@ -90,7 +90,7 @@ public final class LibvirtReplugNicCommandWrapper extends CommandWrapper<ReplugN
// We don't know which "traffic type" is associated with
// each interface at this point, so inform all vif drivers
for (final VifDriver vifDriver : libvirtComputingResource.getAllVifDrivers()) {
vifDriver.unplug(oldPluggedNic);
vifDriver.unplug(oldPluggedNic, true);
}
return new ReplugNicAnswer(command, true, "success");

View File

@ -0,0 +1,50 @@
// 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.hypervisor.kvm.resource.wrapper;
import org.apache.log4j.Logger;
import org.libvirt.LibvirtException;
import com.cloud.agent.api.Answer;
import com.cloud.agent.api.SetupPersistentNetworkAnswer;
import com.cloud.agent.api.SetupPersistentNetworkCommand;
import com.cloud.agent.api.to.NicTO;
import com.cloud.exception.InternalErrorException;
import com.cloud.hypervisor.kvm.resource.LibvirtComputingResource;
import com.cloud.hypervisor.kvm.resource.VifDriver;
import com.cloud.resource.CommandWrapper;
import com.cloud.resource.ResourceWrapper;
@ResourceWrapper(handles = SetupPersistentNetworkCommand.class)
public class LibvirtSetupPersistentNetworkCommandWrapper extends CommandWrapper<SetupPersistentNetworkCommand, Answer, LibvirtComputingResource> {
private static final Logger s_logger = Logger.getLogger(LibvirtSetupPersistentNetworkCommandWrapper.class);
@Override
public Answer execute(SetupPersistentNetworkCommand command, LibvirtComputingResource serverResource) {
NicTO nic = command.getNic();
VifDriver driver = serverResource.getVifDriver(nic.getType());
try {
driver.plug(nic, null, "", null);
} catch (InternalErrorException | LibvirtException e) {
return new SetupPersistentNetworkAnswer(command, false, e.getLocalizedMessage());
}
return new SetupPersistentNetworkAnswer(command, true, "Successfully setup persistent network");
}
}

View File

@ -56,7 +56,7 @@ public final class LibvirtStopCommandWrapper extends CommandWrapper<StopCommand,
@Override
public Answer execute(final StopCommand command, final LibvirtComputingResource libvirtComputingResource) {
final String vmName = command.getVmName();
final Map<String, Boolean> vlanToPersistenceMap = command.getVlanToPersistenceMap();
final LibvirtUtilitiesHelper libvirtUtilitiesHelper = libvirtComputingResource.getLibvirtUtilitiesHelper();
if (command.checkBeforeCleanup()) {
@ -125,10 +125,11 @@ public final class LibvirtStopCommandWrapper extends CommandWrapper<StopCommand,
}
} else {
for (final InterfaceDef iface : ifaces) {
String vlanId = libvirtComputingResource.getVlanIdFromBridgeName(iface.getBrName());
// We don't know which "traffic type" is associated with
// each interface at this point, so inform all vif drivers
for (final VifDriver vifDriver : libvirtComputingResource.getAllVifDrivers()) {
vifDriver.unplug(iface);
vifDriver.unplug(iface, libvirtComputingResource.shouldDeleteBridge(vlanToPersistenceMap, vlanId));
}
}
}
@ -159,5 +160,4 @@ public final class LibvirtStopCommandWrapper extends CommandWrapper<StopCommand,
s_logger.warn("Exception occurred when handling LibVirt VM onStop hook: {}", e);
}
}
}

View File

@ -20,6 +20,7 @@
package com.cloud.hypervisor.kvm.resource.wrapper;
import java.util.List;
import java.util.Map;
import org.apache.log4j.Logger;
import org.libvirt.Connect;
@ -45,6 +46,7 @@ public final class LibvirtUnPlugNicCommandWrapper extends CommandWrapper<UnPlugN
public Answer execute(final UnPlugNicCommand command, final LibvirtComputingResource libvirtComputingResource) {
final NicTO nic = command.getNic();
final String vmName = command.getVmName();
final Map<String, Boolean> vlanToPersistenceMap = command.getVlanToPersistenceMap();
Domain vm = null;
try {
final LibvirtUtilitiesHelper libvirtUtilitiesHelper = libvirtComputingResource.getLibvirtUtilitiesHelper();
@ -59,10 +61,11 @@ public final class LibvirtUnPlugNicCommandWrapper extends CommandWrapper<UnPlugN
libvirtComputingResource.destroyNetworkRulesForNic(conn, vmName, nic);
}
vm.detachDevice(pluggedNic.toString());
String vlanId = libvirtComputingResource.getVlanIdFromBridgeName(pluggedNic.getBrName());
// We don't know which "traffic type" is associated with
// each interface at this point, so inform all vif drivers
for (final VifDriver vifDriver : libvirtComputingResource.getAllVifDrivers()) {
vifDriver.unplug(pluggedNic);
vifDriver.unplug(pluggedNic, libvirtComputingResource.shouldDeleteBridge(vlanToPersistenceMap, vlanId));
}
return new UnPlugNicAnswer(command, true, "success");
}

View File

@ -3364,7 +3364,7 @@ public class LibvirtComputingResourceTest {
when(libvirtComputingResource.getAllVifDrivers()).thenReturn(drivers);
doNothing().when(vifDriver).unplug(intDef);
doNothing().when(vifDriver).unplug(intDef, true);
} catch (final LibvirtException e) {
fail(e.getMessage());

View File

@ -48,6 +48,8 @@ import java.util.stream.Collectors;
import javax.naming.ConfigurationException;
import javax.xml.datatype.XMLGregorianCalendar;
import com.cloud.agent.api.SetupPersistentNetworkAnswer;
import com.cloud.agent.api.SetupPersistentNetworkCommand;
import org.apache.cloudstack.api.ApiConstants;
import org.apache.cloudstack.storage.command.CopyCommand;
import org.apache.cloudstack.storage.command.StorageSubSystemCommand;
@ -578,6 +580,8 @@ public class VmwareResource implements StoragePoolResource, ServerResource, Vmwa
answer = execute((PrepareUnmanageVMInstanceCommand) cmd);
} else if (clz == ValidateVcenterDetailsCommand.class) {
answer = execute((ValidateVcenterDetailsCommand) cmd);
} else if (clz == SetupPersistentNetworkCommand.class) {
answer = execute((SetupPersistentNetworkCommand) cmd);
} else {
answer = Answer.createUnsupportedCommandAnswer(cmd);
}
@ -618,6 +622,21 @@ public class VmwareResource implements StoragePoolResource, ServerResource, Vmwa
return answer;
}
private Answer execute(SetupPersistentNetworkCommand cmd) {
VmwareHypervisorHost host = getHyperHost(getServiceContext());
String hostname = null;
VmwareContext context = getServiceContext();
HostMO hostMO = new HostMO(context, host.getMor());
try {
prepareNetworkFromNicInfo(hostMO, cmd.getNic(), false, null);
hostname = host.getHyperHostName();
} catch (Exception e) {
return new SetupPersistentNetworkAnswer(cmd, false, "failed to setup port-group due to: "+ e.getLocalizedMessage());
}
return new SetupPersistentNetworkAnswer(cmd, true, hostname);
}
/**
* Check if storage NFS version is already set or needs to be reconfigured.<br>
* If _storageNfsVersion is not null -> nothing to do, version already set.<br>

View File

@ -678,7 +678,7 @@ public abstract class CitrixResourceBase implements ServerResource, HypervisorRe
correctVif.destroy(conn);
// Disable the VLAN network if necessary
disableVlanNetwork(conn, network);
disableVlanNetwork(conn, network, true);
}
}
}
@ -1574,7 +1574,7 @@ public abstract class CitrixResourceBase implements ServerResource, HypervisorRe
}
}
public void disableVlanNetwork(final Connection conn, final Network network) {
public void disableVlanNetwork(final Connection conn, final Network network, boolean deleteVlan) {
}
@Override
@ -3612,7 +3612,7 @@ public abstract class CitrixResourceBase implements ServerResource, HypervisorRe
}
for (final Network network : networks) {
if (network.getNameLabel(conn).startsWith("VLAN")) {
disableVlanNetwork(conn, network);
disableVlanNetwork(conn, network, true);
}
}
} catch (final Exception e) {

View File

@ -37,38 +37,40 @@ public class XenServer56Resource extends CitrixResourceBase {
}
@Override
public void disableVlanNetwork(final Connection conn, final Network network) {
public void disableVlanNetwork(final Connection conn, final Network network, boolean deleteVlan) {
try {
final Network.Record networkr = network.getRecord(conn);
if (!networkr.nameLabel.startsWith("VLAN")) {
return;
}
final String bridge = networkr.bridge.trim();
for (final PIF pif : networkr.PIFs) {
final PIF.Record pifr = pif.getRecord(conn);
if (!pifr.host.getUuid(conn).equalsIgnoreCase(_host.getUuid())) {
continue;
}
if (deleteVlan) {
final String bridge = networkr.bridge.trim();
for (final PIF pif : networkr.PIFs) {
final PIF.Record pifr = pif.getRecord(conn);
if (!pifr.host.getUuid(conn).equalsIgnoreCase(_host.getUuid())) {
continue;
}
final VLAN vlan = pifr.VLANMasterOf;
if (vlan != null) {
final String vlannum = pifr.VLAN.toString();
final String device = pifr.device.trim();
if (vlannum.equals("-1")) {
return;
}
try {
vlan.destroy(conn);
final Host host = Host.getByUuid(conn, _host.getUuid());
host.forgetDataSourceArchives(conn, "pif_" + bridge + "_tx");
host.forgetDataSourceArchives(conn, "pif_" + bridge + "_rx");
host.forgetDataSourceArchives(conn, "pif_" + device + "." + vlannum + "_tx");
host.forgetDataSourceArchives(conn, "pif_" + device + "." + vlannum + "_rx");
} catch (final XenAPIException e) {
s_logger.trace("Catch " + e.getClass().getName() + ": failed to destory VLAN " + device + " on host " + _host.getUuid() + " due to " + e.toString());
final VLAN vlan = pifr.VLANMasterOf;
if (vlan != null) {
final String vlannum = pifr.VLAN.toString();
final String device = pifr.device.trim();
if (vlannum.equals("-1")) {
return;
}
try {
vlan.destroy(conn);
final Host host = Host.getByUuid(conn, _host.getUuid());
host.forgetDataSourceArchives(conn, "pif_" + bridge + "_tx");
host.forgetDataSourceArchives(conn, "pif_" + bridge + "_rx");
host.forgetDataSourceArchives(conn, "pif_" + device + "." + vlannum + "_tx");
host.forgetDataSourceArchives(conn, "pif_" + device + "." + vlannum + "_rx");
} catch (final XenAPIException e) {
s_logger.trace("Catch " + e.getClass().getName() + ": failed to destroy VLAN " + device + " on host " + _host.getUuid() + " due to " + e.toString());
}
}
return;
}
return;
}
} catch (final XenAPIException e) {
final String msg = "Unable to disable VLAN network due to " + e.toString();

View File

@ -0,0 +1,55 @@
// 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.hypervisor.xenserver.resource.wrapper.xenbase;
import org.apache.log4j.Logger;
import com.cloud.agent.api.Answer;
import com.cloud.agent.api.CleanupPersistentNetworkResourceAnswer;
import com.cloud.agent.api.CleanupPersistentNetworkResourceCommand;
import com.cloud.agent.api.to.NicTO;
import com.cloud.hypervisor.xenserver.resource.CitrixResourceBase;
import com.cloud.hypervisor.xenserver.resource.XsHost;
import com.cloud.resource.CommandWrapper;
import com.cloud.resource.ResourceWrapper;
import com.xensource.xenapi.Connection;
import com.xensource.xenapi.Network;
@ResourceWrapper(handles = CleanupPersistentNetworkResourceCommand.class)
public class CitrixCleanupPersistentNetworkResourceCommandWrapper extends CommandWrapper<CleanupPersistentNetworkResourceCommand, Answer, CitrixResourceBase> {
private static final Logger s_logger = Logger.getLogger(CitrixCleanupPersistentNetworkResourceCommandWrapper.class);
@Override
public Answer execute(CleanupPersistentNetworkResourceCommand command, CitrixResourceBase citrixResourceBase) {
final Connection conn = citrixResourceBase.getConnection();
final XsHost host = citrixResourceBase.getHost();
NicTO nic = command.getNicTO();
try {
Network network = citrixResourceBase.getNetwork(conn, nic);
if (network == null) {
return new CleanupPersistentNetworkResourceAnswer(command, false, "Failed to find network on host " + host.getIp() + " to cleanup");
}
citrixResourceBase.disableVlanNetwork(conn, network, true);
return new CleanupPersistentNetworkResourceAnswer(command, true, "Successfully deleted network VLAN on host: "+ host.getIp());
} catch (final Exception e) {
final String msg = " Failed to cleanup network VLAN on host: " + host.getIp() + " due to: " + e.toString();
s_logger.error(msg, e);
return new CleanupPersistentNetworkResourceAnswer(command, false, msg);
}
}
}

View File

@ -0,0 +1,54 @@
// 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.hypervisor.xenserver.resource.wrapper.xenbase;
import org.apache.log4j.Logger;
import com.cloud.agent.api.Answer;
import com.cloud.agent.api.SetupPersistentNetworkAnswer;
import com.cloud.agent.api.SetupPersistentNetworkCommand;
import com.cloud.hypervisor.xenserver.resource.CitrixResourceBase;
import com.cloud.hypervisor.xenserver.resource.XsHost;
import com.cloud.resource.CommandWrapper;
import com.cloud.resource.ResourceWrapper;
import com.xensource.xenapi.Connection;
import com.xensource.xenapi.Network;
@ResourceWrapper(handles = SetupPersistentNetworkCommand.class)
public class CitrixSetupPersistentNetworkCommandWrapper extends CommandWrapper<SetupPersistentNetworkCommand, Answer, CitrixResourceBase> {
private static final Logger s_logger = Logger.getLogger(CitrixSetupPersistentNetworkCommandWrapper.class);
@Override
public Answer execute(SetupPersistentNetworkCommand command, CitrixResourceBase citrixResourceBase) {
final Connection conn = citrixResourceBase.getConnection();
final XsHost host = citrixResourceBase.getHost();
try {
Network network = citrixResourceBase.getNetwork(conn, command.getNic());
if (network == null) {
return new SetupPersistentNetworkAnswer(command, false, "Failed to setup network on host: "+ host.getIp());
}
return new SetupPersistentNetworkAnswer(command, true, "Successfully setup network on host: "+ host.getIp());
} catch (final Exception e) {
final String msg = " Failed to setup network on host: " + host.getIp() + " due to: " + e.toString();
s_logger.error(msg, e);
return new SetupPersistentNetworkAnswer(command, false, msg);
}
}
}

View File

@ -23,8 +23,10 @@ import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
import org.apache.commons.collections.MapUtils;
import org.apache.log4j.Logger;
import com.cloud.agent.api.Answer;
@ -53,6 +55,7 @@ public final class CitrixStopCommandWrapper extends CommandWrapper<StopCommand,
@Override
public Answer execute(final StopCommand command, final CitrixResourceBase citrixResourceBase) {
final String vmName = command.getVmName();
final Map<String, Boolean> vlanToPersistenceMap = command.getVlanToPersistenceMap();
String platformstring = null;
try {
final Connection conn = citrixResourceBase.getConnection();
@ -148,7 +151,8 @@ public final class CitrixStopCommandWrapper extends CommandWrapper<StopCommand,
for (final Network network : networks) {
try {
if (network.getNameLabel(conn).startsWith("VLAN")) {
citrixResourceBase.disableVlanNetwork(conn, network);
String networkLabel = network.getNameLabel(conn);
citrixResourceBase.disableVlanNetwork(conn, network, shouldDeleteVlan(networkLabel, vlanToPersistenceMap));
}
} catch (final Exception e) {
// network might be destroyed by other host
@ -172,4 +176,18 @@ public final class CitrixStopCommandWrapper extends CommandWrapper<StopCommand,
}
return new StopAnswer(command, "Stop VM failed", platformstring, false);
}
private boolean shouldDeleteVlan(String networkLabel, Map<String, Boolean> vlanToPersistenceMap) {
String[] networkNameParts = null;
if (networkLabel.contains("-")) {
networkNameParts = networkLabel.split("-");
} else {
networkNameParts = networkLabel.split("VLAN");
}
String networkVlan = networkNameParts.length > 0 ? networkNameParts[networkNameParts.length - 1] : null;
if (networkVlan != null && MapUtils.isNotEmpty(vlanToPersistenceMap) && vlanToPersistenceMap.containsKey(networkVlan)) {
return vlanToPersistenceMap.get(networkVlan);
}
return true;
}
}

View File

@ -19,9 +19,12 @@
package com.cloud.hypervisor.xenserver.resource.wrapper.xenbase;
import java.util.Map;
import java.util.Set;
import org.apache.commons.collections.MapUtils;
import org.apache.log4j.Logger;
import org.apache.xmlrpc.XmlRpcException;
import com.cloud.agent.api.Answer;
import com.cloud.agent.api.UnPlugNicAnswer;
@ -32,6 +35,7 @@ import com.cloud.resource.CommandWrapper;
import com.cloud.resource.ResourceWrapper;
import com.xensource.xenapi.Connection;
import com.xensource.xenapi.Network;
import com.xensource.xenapi.Types;
import com.xensource.xenapi.VIF;
import com.xensource.xenapi.VM;
@ -44,6 +48,7 @@ public final class CitrixUnPlugNicCommandWrapper extends CommandWrapper<UnPlugNi
public Answer execute(final UnPlugNicCommand command, final CitrixResourceBase citrixResourceBase) {
final Connection conn = citrixResourceBase.getConnection();
final String vmName = command.getVmName();
final Map<String, Boolean> vlanToPersistenceMap = command.getVlanToPersistenceMap();
try {
final Set<VM> vms = VM.getByNameLabel(conn, vmName);
if (vms == null || vms.isEmpty()) {
@ -59,7 +64,8 @@ public final class CitrixUnPlugNicCommandWrapper extends CommandWrapper<UnPlugNi
vif.destroy(conn);
try {
if (network.getNameLabel(conn).startsWith("VLAN")) {
citrixResourceBase.disableVlanNetwork(conn, network);
String networkLabel = network.getNameLabel(conn);
citrixResourceBase.disableVlanNetwork(conn, network, shouldDeleteVlan(networkLabel, vlanToPersistenceMap));
}
} catch (final Exception e) {
}
@ -71,4 +77,13 @@ public final class CitrixUnPlugNicCommandWrapper extends CommandWrapper<UnPlugNi
return new UnPlugNicAnswer(command, false, msg);
}
}
private boolean shouldDeleteVlan(String networkLabel, Map<String, Boolean> vlanToPersistenceMap) throws XmlRpcException, Types.XenAPIException {
String[] networkNameParts = networkLabel.split("-");
String networkVlan = networkNameParts[networkNameParts.length -1];
if (MapUtils.isNotEmpty(vlanToPersistenceMap) && networkVlan != null && vlanToPersistenceMap.containsKey(networkVlan)) {
return vlanToPersistenceMap.get(networkVlan);
}
return true;
}
}

View File

@ -22,7 +22,7 @@
# set -x
usage() {
printf "Usage: %s: -o <op>(add | delete) -v <vlan id> -p <pif> -b <bridge name>\n"
printf "Usage: %s: -o <op>(add | delete) -v <vlan id> -p <pif> -b <bridge name> -d <delete bridge>(true | false)\n"
}
addVlan() {
@ -90,40 +90,43 @@ deleteVlan() {
local vlanId=$1
local pif=$2
local vlanDev=$pif.$vlanId
local vlanBr=$3
local vlanBr=$3
local deleteBr=$4
ip link delete $vlanDev type vlan > /dev/null
if [ $deleteBr == "true" ]
then
ip link delete $vlanDev type vlan > /dev/null
if [ $? -gt 0 ]
then
printf "Failed to del vlan: $vlanId"
return 1
fi
if [ $? -gt 0 ]
then
printf "Failed to del vlan: $vlanId"
return 1
fi
ip link set $vlanBr down
ip link set $vlanBr down
if [ $? -gt 0 ]
then
return 1
fi
if [ $? -gt 0 ]
then
return 1
fi
ip link delete $vlanBr type bridge
if [ $? -gt 0 ]
then
printf "Failed to del bridge $vlanBr"
return 1
fi
ip link delete $vlanBr type bridge
if [ $? -gt 0 ]
then
printf "Failed to del bridge $vlanBr"
return 1
fi
fi
return 0
}
op=
vlanId=
deleteBr="true"
option=$@
while getopts 'o:v:p:b:' OPTION
while getopts 'o:v:p:b:d:' OPTION
do
case $OPTION in
o) oflag=1
@ -136,8 +139,11 @@ do
pif="$OPTARG"
;;
b) bflag=1
brName="$OPTARG"
;;
brName="$OPTARG"
;;
d) dflag=1
deleteBr="$OPTARG"
;;
?) usage
exit 2
;;
@ -177,7 +183,7 @@ else
if [ "$op" == "delete" ]
then
# Delete the vlan
deleteVlan $vlanId $pif $brName
deleteVlan $vlanId $pif $brName $deleteBr
# Always exit with success
exit 0

View File

@ -1429,10 +1429,6 @@ public class NetworkServiceImpl extends ManagerBase implements NetworkService, C
// if the network offering has persistent set to true, implement the network
if (ntwkOff.isPersistent()) {
try {
if (network.getState() == Network.State.Setup) {
s_logger.debug("Network id=" + network.getId() + " is already provisioned");
return network;
}
DeployDestination dest = new DeployDestination(zone, null, null, null);
UserVO callerUser = _userDao.findById(CallContext.current().getCallingUserId());
Journal journal = new Journal.LogJournal("Implementing " + network, s_logger);

View File

@ -214,7 +214,9 @@ public abstract class GuestNetworkGuru extends AdapterBase implements NetworkGur
if (offering.isSpecifyVlan()) {
network.setBroadcastUri(userSpecified.getBroadcastUri());
network.setState(State.Setup);
if (!offering.isPersistent()) {
network.setState(State.Setup);
}
if (userSpecified.getPvlanType() != null) {
network.setBroadcastDomainType(BroadcastDomainType.Pvlan);
network.setPvlanType(userSpecified.getPvlanType());

View File

@ -74,6 +74,11 @@ public class MockNetworkDaoImpl extends GenericDaoBase<NetworkVO, Long> implemen
return null;
}
@Override
public int getOtherPersistentNetworksCount(long id, String broadcastURI, boolean isPersistent) {
return 0;
}
@Override
public String getNextAvailableMacAddress(final long networkConfigId, Integer zoneMacIdentifier) {
return null;

View File

@ -0,0 +1,385 @@
# 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.
from marvin.cloudstackTestCase import cloudstackTestCase, unittest
from marvin.lib.utils import (cleanup_resources,
validateList,
get_hypervisor_type, get_process_status)
from marvin.lib.base import (Account,
Cluster,
Configurations,
Host,
VPC,
VirtualMachine,
Network,
Router,
ServiceOffering,
NetworkOffering)
from marvin.lib.common import (get_zone,
get_template,
verifyNetworkState,
wait_for_cleanup, list_routers, list_hosts)
from nose.plugins.attrib import attr
from marvin.sshClient import SshClient
from distutils.util import strtobool
from pyVmomi import vim, vmodl
from marvin.lib.vcenter import Vcenter
import logging
logger = logging.getLogger('TestPesistentNetwork')
stream_handler = logging.StreamHandler()
logger.setLevel(logging.DEBUG)
logger.addHandler(stream_handler)
class TestL2PersistentNetworks(cloudstackTestCase):
@classmethod
def setUpClass(cls):
cls.testClient = super(TestL2PersistentNetworks, cls).getClsTestClient()
cls.api_client = cls.testClient.getApiClient()
cls.hypervisor = cls.testClient.getHypervisorInfo()
# Fill services from the external config file
cls.services = cls.testClient.getParsedTestDataConfig()
cls.hostConfig = cls.config.__dict__["zones"][0].__dict__["pods"][0].__dict__["clusters"][0].__dict__["hosts"][
0].__dict__
# Get Zone and templates
cls.zone = get_zone(cls.api_client, cls.testClient.getZoneForTests())
cls.template = get_template(
cls.api_client,
cls.zone.id,
cls.services["ostype"]
)
cls.services["virtual_machine"]["zoneid"] = cls.zone.id
cls.services["virtual_machine"]["template"] = cls.template.id
cls.service_offering = ServiceOffering.create(
cls.api_client,
cls.services["service_offering"]
)
cls.l2_persistent_network_offering = cls.create_network_offering("nw_off_L2_persistent")
cls.isolated_persistent_network_offering = cls.create_network_offering("nw_off_isolated_persistent")
# network will be deleted as part of account cleanup
cls._cleanup = [
cls.service_offering,
cls.isolated_persistent_network_offering,
cls.l2_persistent_network_offering]
return
@classmethod
def tearDownClass(cls):
try:
# Cleanup resources used
cleanup_resources(cls.api_client, cls._cleanup)
except Exception as e:
raise Exception("Warning: Exception during cleanup : %s" % e)
return
def setUp(self):
self.apiclient = self.testClient.getApiClient()
self.dbclient = self.testClient.getDbConnection()
self.hypervisor = self.testClient.getHypervisorInfo()
self.cleanup = []
return
def tearDown(self):
try:
# Clean up, terminate the resources created
cleanup_resources(self.apiclient, self.cleanup)
self.cleanup[:] = []
except Exception as e:
raise Exception("Warning: Exception during cleanup : %s" % e)
return
@classmethod
def is_ssh_enabled(cls):
conf = Configurations.list(cls.apiclient, name="kvm.ssh.to.agent")
if not conf:
return False
else:
return bool(strtobool(conf[0].value)) if conf[0].value else False
@classmethod
def create_network_offering(cls, network_offering_type):
network_offering = NetworkOffering.create(
cls.api_client,
cls.services[network_offering_type],
conservemode=False
)
# Update network offering state from disabled to enabled.
NetworkOffering.update(
network_offering,
cls.api_client,
id=network_offering.id,
state="enabled")
return network_offering
def get_ssh_client(self, ip, username, password, retries=10):
""" Setup ssh client connection and return connection """
try:
ssh_client = SshClient(ip, 22, username, password, retries)
except Exception as e:
raise unittest.SkipTest("Unable to create ssh connection: " % e)
self.assertIsNotNone(
ssh_client, "Failed to setup ssh connection to ip=%s" % ip)
return ssh_client
def list_all_hosts_in_zone(self, zone_id):
hosts = Host.list(
self.apiclient,
type='Routing',
resourcestate='Enabled',
state='Up',
zoneid=zone_id
)
return hosts
'''
Verifies creation of bridge on KVM host
'''
def verify_bridge_creation(self, host, vlan_id):
username = self.hostConfig["username"]
password = self.hostConfig["password"]
try:
ssh_client = self.get_ssh_client(host.ipaddress, username, password)
res = ssh_client.execute("ip addr | grep breth1-" + str(vlan_id) + " > /dev/null 2>&1; echo $?")
return res[0]
except Exception as e:
self.fail(e)
'''
Gets all port groups on the host
'''
def capture_host_portgroups(self, host):
host_portgroups = []
for portgroup in host.config.network.portgroup:
host_portgroups.append(portgroup.spec.name)
return host_portgroups
'''
Fetches port group names based on VMware switch type - Distributed Virtual Switch(DVS) and
Standard Virtual Switch(SVS)
'''
def get_port_group_name(self, switch_type, vlan_id, network_rate):
if switch_type == 'DVS':
return 'cloud.guest.' + str(vlan_id) + '.' + str(network_rate) + '.1-dvSwitch1'
elif switch_type == 'SVS':
return 'cloud.guest.' + str(vlan_id) + '.' + str(network_rate) + '.1-vSwitch1'
else:
return None
'''
Verifies creation of port group on the Distributed vSwitch or a host in a cluster connected to
a Standard vSwitch
'''
def verify_port_group_creation(self, vlan_id):
config = self.get_vmware_dc_config(self.zone.id)
vc_object = Vcenter(config[0][0], config[0][1], 'P@ssword123')
dvs = vc_object.get_dvswitches()
port_group_present = False
if dvs is not None:
port_group_name = self.get_port_group_name('DVS', vlan_id,
self.isolated_persistent_network_offering.networkrate)
port_group_present = port_group_name in dvs[0]['dvswitch']['portgroupNameList']
else:
port_group_name = self.get_port_group_name('SVS', vlan_id,
self.isolated_persistent_network_offering.networkrate)
hosts = vc_object._get_obj([vim.HostSystem])
host = hosts[0]['host']
host_pg = self.capture_host_portgroups(host)
port_group_present = port_group_name in host_pg
return port_group_present
'''
Fetch vmware datacenter login details
'''
def get_vmware_dc_config(self, zone_id):
zid = self.dbclient.execute("select id from data_center where uuid='%s';" %
zone_id)
vmware_dc_id = self.dbclient.execute(
"select vmware_data_center_id from vmware_data_center_zone_map where zone_id='%s';" %
zid[0])
vmware_dc_config = self.dbclient.execute(
"select vcenter_host, username, password from vmware_data_center where id = '%s';" % vmware_dc_id[0])
return vmware_dc_config
'''
Verify VLAN creation on specific host in a cluster
'''
def verify_vlan_network_creation(self, host, vlan_id):
username = self.hostConfig["username"]
password = self.hostConfig["password"]
try:
ssh_client = self.get_ssh_client(host.ipaddress, username, password)
res = ssh_client.execute(
"xe vlan-list | grep -x \"^\s*tag ( RO): \"" + str(vlan_id) + "> /dev/null 2>&1; echo $?")
return res[0]
except Exception as e:
self.fail(e)
def verify_network_setup_on_host_per_cluster(self, hypervisor, vlan_id):
clusters = Cluster.list(
self.apiclient,
zoneid=self.zone.id,
allocationstate="Enabled",
listall=True
)
for cluster in clusters:
hosts = Host.list(self.apiclient,
clusterid=cluster.id,
type="Routing",
state="Up",
resourcestate="Enabled")
host = hosts[0]
if hypervisor == "xenserver":
result = self.verify_vlan_network_creation(host, vlan_id)
self.assertEqual(
int(result),
0,
"Failed to find vlan on host: " + host.name + " in cluster: " + cluster.name)
if hypervisor == "vmware":
result = self.verify_port_group_creation(vlan_id)
self.assertEqual(
result,
True,
"Failed to find port group on hosts of cluster: " + cluster.name)
'''
This test verifies that on creation of an Isolated network with network offering with isPersistent flag
set to true the corresponding network resources are created without having to deploy a VM - VR created
'''
@attr(tags=["advanced", "isolated", "persistent", "network"], required_hardware="false")
def test_01_isolated_persistent_network(self):
network = Network.create(
self.apiclient,
self.services["isolated_network"],
networkofferingid=self.isolated_persistent_network_offering.id,
zoneid=self.zone.id)
self.cleanup.append(network)
networkVlan = network.vlan
response = verifyNetworkState(
self.apiclient,
network.id,
"implemented")
exceptionOccured = response[0]
isNetworkInDesiredState = response[1]
exceptionMessage = response[2]
if (exceptionOccured or (not isNetworkInDesiredState)):
self.fail(exceptionMessage)
self.assertIsNotNone(
networkVlan,
"vlan must not be null for persistent network")
router = Router.list(self.apiclient, networkid=network.id)[0]
router_host_id = router.hostid
host = Host.list(self.apiclient, id=router_host_id)[0]
if host.hypervisor.lower() in "kvm":
result = self.verify_bridge_creation(host, networkVlan)
self.assertEqual(
int(result),
0,
"Failed to find bridge on the breth1$-{networkVlan}")
elif host.hypervisor.lower() in ["xenserver", "vmware"]:
self.verify_network_setup_on_host_per_cluster(host.hypervisor.lower(), networkVlan)
'''
This test verifies that on creation of an L2 network with network offering with isPersistent flag
set to true the corresponding network resources are created without having to deploy a VM - VR created
'''
@attr(tags=["advanced", "l2", "persistent", "network"], required_hardware="false")
def test_02_L2_persistent_network(self):
network_vlan = 90
network = Network.create(
self.apiclient,
self.services["l2_network"],
networkofferingid=self.l2_persistent_network_offering.id,
zoneid=self.zone.id,
vlan=network_vlan)
self.cleanup.append(network)
response = verifyNetworkState(
self.apiclient,
network.id,
"implemented")
exceptionOccured = response[0]
isNetworkInDesiredState = response[1]
exceptionMessage = response[2]
if (exceptionOccured or (not isNetworkInDesiredState)):
self.fail(exceptionMessage)
self.assertIsNotNone(
network_vlan,
"vlan must not be null for persistent network")
self.validate_persistent_network_resources_created_on_host(network_vlan)
@attr(tags=["advanced", "l2", "persistent", "network"], required_hardware="false")
def test_03_deploy_and_destroy_VM_and_verify_network_resources_persist(self):
network_vlan = 99
network = Network.create(
self.apiclient,
self.services["l2_network"],
networkofferingid=self.l2_persistent_network_offering.id,
zoneid=self.zone.id,
vlan=network_vlan)
self.cleanup.append(network)
response = verifyNetworkState(
self.apiclient,
network.id,
"implemented")
logger.debug(response)
exceptionOccured = response[0]
isNetworkInDesiredState = response[1]
exceptionMessage = response[2]
if (exceptionOccured or (not isNetworkInDesiredState)):
self.fail(exceptionMessage)
self.assertIsNotNone(
network_vlan,
"vlan must not be null for persistent network")
try:
virtual_machine = VirtualMachine.create(
self.apiclient,
self.services["virtual_machine"],
networkids=[
network.id],
serviceofferingid=self.service_offering.id)
VirtualMachine.delete(virtual_machine, self.apiclient, expunge=True)
self.validate_persistent_network_resources_created_on_host(network_vlan)
except Exception as e:
self.fail("Exception occurred: %s" % e)
return
def validate_persistent_network_resources_created_on_host(self, network_vlan):
hosts = self.list_all_hosts_in_zone(self.zone.id)
if self.hypervisor.lower() in "kvm":
for host in hosts:
result = self.verify_bridge_creation(host, network_vlan)
self.assertEqual(
int(result),
0,
"Failed to find bridge on the breth1-" + str(network_vlan))
elif self.hypervisor.lower() in ["xenserver", "vmware"]:
self.verify_network_setup_on_host_per_cluster(self.hypervisor.lower(), network_vlan)

View File

@ -306,13 +306,21 @@ test_data = {
"StaticNat": "VirtualRouter"
}
},
"nw_off_L2_persistent": {
"name": 'Test L2 Network Offering persistent',
"displaytext": 'Test L2 Network Offering persistent',
"guestiptype": 'L2',
"traffictype": 'GUEST',
"ispersistent": 'True',
"specifyVlan": 'True'
},
"network_offering_vlan": {
"name": 'Test Network offering',
"displaytext": 'Test Network offering',
"guestiptype": 'Isolated',
"supportedservices": 'Dhcp,Dns,SourceNat,PortForwarding',
"traffictype": 'GUEST',
"specifyvlan": 'False',
"specifyVlan": 'False',
"availability": 'Optional',
"serviceProviderList": {
"Dhcp": 'VirtualRouter',
@ -338,6 +346,10 @@ test_data = {
"name": "Isolated Network",
"displaytext": "Isolated Network"
},
"l2_network": {
"name": "L2 Network",
"displaytext": "L2 Network"
},
"netscaler_VPX": {
"ipaddress": "10.223.240.174",
"username": "nsroot",

View File

@ -137,6 +137,8 @@ class Vcenter():
parsedObject['dvportgroup'] = Vcenter._parse_dvportgroup(obj)
elif vim.VirtualMachine in vimtype:
parsedObject['vm'] = Vcenter._parse_vm(obj)
elif vim.HostSystem in vimtype:
parsedObject['host'] = obj
else:
parsedObject['name'] = obj.name
return parsedObject

View File

@ -630,6 +630,7 @@
"label.create.ssh.key.pair": "Create a SSH Key Pair",
"label.create.template": "Create template",
"label.create.user": "Create user",
"label.create.vpc.tier": "Create VPC tier",
"label.create.vpn.connection": "Create VPN Connection",
"label.created": "Created",
"label.created.by.system": "Created by system",

View File

@ -169,12 +169,26 @@
</a-form-item>
<a-form-item :label="$t('label.networkofferingid')">
<a-select
v-decorator="['networkOffering',{rules: [{ required: true, message: `${$t('label.required')}` }]}]">
v-decorator="['networkOffering',{rules: [{ required: true, message: `${$t('label.required')}` }]}]"
@change="val => { this.handleNetworkOfferingChange(val) }">
<a-select-option v-for="item in networkOfferings" :key="item.id" :value="item.id">
{{ item.displaytext || item.name || item.description }}
</a-select-option>
</a-select>
</a-form-item>
<a-form-item v-if="!this.isObjectEmpty(this.selectedNetworkOffering) && this.selectedNetworkOffering.specifyvlan">
<span slot="label">
{{ $t('label.vlan') }}
<a-tooltip :title="$t('label.vlan')">
<a-icon type="info-circle" style="color: rgba(0,0,0,.45)" />
</a-tooltip>
</span>
<a-input
v-decorator="['vlan', {
rules: [{ required: true, message: $t('message.please.enter.value') }]
}]"
:placeholder="this.$t('label.vlan')"/>
</a-form-item>
<a-form-item :label="$t('label.gateway')">
<a-input
:placeholder="$t('label.create.network.gateway.description')"
@ -284,6 +298,7 @@ export default {
LBPublicIPs: {},
staticNats: {},
vms: {},
selectedNetworkOffering: {},
algorithms: {
Source: 'source',
'Round-robin': 'roundrobin',
@ -387,6 +402,9 @@ export default {
this.form = this.$form.createForm(this)
},
methods: {
isObjectEmpty (obj) {
return !(obj !== null && obj !== undefined && Object.keys(obj).length > 0 && obj.constructor === Object)
},
showIlb (network) {
return network.service.filter(s => (s.name === 'Lb') && (s.capability.filter(c => c.name === 'LbSchemes' && c.value === 'Internal').length > 0)).length > 0 || false
},
@ -432,6 +450,7 @@ export default {
networkOffering: this.networkOfferings[0].id
})
})
this.selectedNetworkOffering = this.networkOfferings[0]
}).catch(error => {
this.$notifyError(error)
}).finally(() => {
@ -469,6 +488,9 @@ export default {
this.fetchLoading = false
})
},
handleNetworkOfferingChange (networkOfferingId) {
this.selectedNetworkOffering = this.networkOfferings.filter(offering => offering.id === networkOfferingId)[0]
},
closeModal () {
this.$emit('close-action')
},
@ -493,7 +515,7 @@ export default {
}
this.showCreateNetworkModal = false
api('createNetwork', {
var params = {
vpcid: this.resource.id,
domainid: this.resource.domainid,
account: this.resource.account,
@ -505,7 +527,13 @@ export default {
zoneId: this.resource.zoneid,
externalid: values.externalId,
aclid: values.acl
}).then(() => {
}
if (values.vlan) {
params.vlan = values.vlan
}
api('createNetwork', params).then(() => {
this.$notification.success({
message: this.$t('message.success.add.vpc.network')
})