diff --git a/api/src/com/cloud/event/EventTypes.java b/api/src/com/cloud/event/EventTypes.java index d5d11e877024..ce410a6795d3 100644 --- a/api/src/com/cloud/event/EventTypes.java +++ b/api/src/com/cloud/event/EventTypes.java @@ -130,6 +130,7 @@ public class EventTypes { public static final String EVENT_NETWORK_CREATE = "NETWORK.CREATE"; public static final String EVENT_NETWORK_DELETE = "NETWORK.DELETE"; public static final String EVENT_NETWORK_UPDATE = "NETWORK.UPDATE"; + public static final String EVENT_NETWORK_MIGRATE = "NETWORK.MIGRATE"; public static final String EVENT_FIREWALL_OPEN = "FIREWALL.OPEN"; public static final String EVENT_FIREWALL_CLOSE = "FIREWALL.CLOSE"; public static final String EVENT_FIREWALL_UPDATE = "FIREWALL.UPDATE"; diff --git a/api/src/com/cloud/network/NetworkService.java b/api/src/com/cloud/network/NetworkService.java index 0ad42b5acdd8..2559cfa97fbf 100644 --- a/api/src/com/cloud/network/NetworkService.java +++ b/api/src/com/cloud/network/NetworkService.java @@ -36,6 +36,7 @@ import com.cloud.exception.ResourceUnavailableException; import com.cloud.network.Network.Service; import com.cloud.network.Networks.TrafficType; +import com.cloud.network.vpc.Vpc; import com.cloud.offering.NetworkOffering; import com.cloud.user.Account; import com.cloud.user.User; @@ -82,6 +83,24 @@ IpAddress allocatePortableIP(Account ipOwner, int regionId, Long zoneId, Long ne Network updateGuestNetwork(long networkId, String name, String displayText, Account callerAccount, User callerUser, String domainSuffix, Long networkOfferingId, Boolean changeCidr, String guestVmCidr, Boolean displayNetwork, String newUUID, boolean updateInSequence, boolean forced); + /** + * Migrate a network from one physical network to another physical network + * @param networkId of the network that needs to be migrated + * @param networkOfferingId new network offering id for the network + * @param resume if previous migration failed try to resume of just fail directly because anomaly is detected + * @return the migrated network + */ + Network migrateGuestNetwork(long networkId, long networkOfferingId, Account callerAccount, User callerUser, boolean resume); + + /** + * Migrate a vpc from on physical network to another physical network + * @param vpcId the id of the vpc that needs to be migrated + * @param vpcNetworkofferingId the new vpc offering id + * @param resume if previous migration failed try to resume of just fail directly because anomaly is detected + * @return the migrated vpc + */ + Vpc migrateVpcNetwork(long vpcId, long vpcNetworkofferingId, Map networkToOffering, Account account, User callerUser, boolean resume); + PhysicalNetwork createPhysicalNetwork(Long zoneId, String vnetRange, String networkSpeed, List isolationMethods, String broadcastDomainRange, Long domainId, List tags, String name); diff --git a/api/src/com/cloud/network/Networks.java b/api/src/com/cloud/network/Networks.java index 37746f0d0f7e..06f4236eb7ae 100644 --- a/api/src/com/cloud/network/Networks.java +++ b/api/src/com/cloud/network/Networks.java @@ -246,6 +246,7 @@ public static String getValue(String uriString) throws URISyntaxException { * encode a string into a BroadcastUri * @param candidate the input string * @return an URI containing an appropriate (possibly given) scheme and the value + * */ public static URI fromString(String candidate) { try { diff --git a/api/src/com/cloud/offering/NetworkOffering.java b/api/src/com/cloud/offering/NetworkOffering.java index 3532010f1fa4..0c8378908e62 100644 --- a/api/src/com/cloud/offering/NetworkOffering.java +++ b/api/src/com/cloud/offering/NetworkOffering.java @@ -38,7 +38,7 @@ public enum State { } public enum Detail { - InternalLbProvider, PublicLbProvider, servicepackageuuid, servicepackagedescription, PromiscuousMode, MacAddressChanges, ForgedTransmits + InternalLbProvider, PublicLbProvider, servicepackageuuid, servicepackagedescription, PromiscuousMode, MacAddressChanges, ForgedTransmits, RelatedNetworkOffering } public final static String SystemPublicNetwork = "System-Public-Network"; diff --git a/api/src/com/cloud/server/ResourceTag.java b/api/src/com/cloud/server/ResourceTag.java index 3bed77d6d024..067cb973e2f1 100644 --- a/api/src/com/cloud/server/ResourceTag.java +++ b/api/src/com/cloud/server/ResourceTag.java @@ -58,7 +58,9 @@ public enum ResourceObjectType { LBStickinessPolicy(false, true), LBHealthCheckPolicy(false, true), SnapshotPolicy(false, true), - GuestOs(false, true); + GuestOs(false, true), + NetworkOffering(false, true), + VpcOffering(true, false); ResourceObjectType(boolean resourceTagsSupport, boolean resourceMetadataSupport) { diff --git a/api/src/com/cloud/vm/NicSecondaryIp.java b/api/src/com/cloud/vm/NicSecondaryIp.java index b7d3668c3e20..2856e0aea756 100644 --- a/api/src/com/cloud/vm/NicSecondaryIp.java +++ b/api/src/com/cloud/vm/NicSecondaryIp.java @@ -32,6 +32,8 @@ public interface NicSecondaryIp extends ControlledEntity, Identity, InternalIden long getNicId(); + void setNicId(long nicId); + String getIp4Address(); String getIp6Address(); diff --git a/api/src/org/apache/cloudstack/api/ApiConstants.java b/api/src/org/apache/cloudstack/api/ApiConstants.java index a5bd95f83c5e..64cdb23674e8 100644 --- a/api/src/org/apache/cloudstack/api/ApiConstants.java +++ b/api/src/org/apache/cloudstack/api/ApiConstants.java @@ -332,6 +332,7 @@ public class ApiConstants { public static final String COUNT = "count"; public static final String TRAFFIC_TYPE = "traffictype"; public static final String NETWORK_OFFERING_ID = "networkofferingid"; + public static final String TIER_NETWORK_OFFERINGS = "tiernetworkofferings"; public static final String NETWORK_IDS = "networkids"; public static final String NETWORK_ID = "networkid"; public static final String NIC_ID = "nicid"; @@ -375,6 +376,7 @@ public class ApiConstants { public static final String ZONE_TOKEN = "zonetoken"; public static final String DHCP_PROVIDER = "dhcpprovider"; public static final String RESULT = "success"; + public static final String RESUME = "resume"; public static final String LUN_ID = "lunId"; public static final String IQN = "iqn"; public static final String AGGREGATE_NAME = "aggregatename"; diff --git a/api/src/org/apache/cloudstack/api/command/admin/network/MigrateNetworkCmd.java b/api/src/org/apache/cloudstack/api/command/admin/network/MigrateNetworkCmd.java new file mode 100644 index 000000000000..651fce87fd3f --- /dev/null +++ b/api/src/org/apache/cloudstack/api/command/admin/network/MigrateNetworkCmd.java @@ -0,0 +1,155 @@ +// 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 org.apache.cloudstack.api.command.admin.network; + +import org.apache.log4j.Logger; + +import org.apache.cloudstack.acl.SecurityChecker.AccessType; +import org.apache.cloudstack.api.ACL; +import org.apache.cloudstack.api.APICommand; +import org.apache.cloudstack.api.ApiConstants; +import org.apache.cloudstack.api.ApiErrorCode; +import org.apache.cloudstack.api.BaseAsyncCmd; +import org.apache.cloudstack.api.Parameter; +import org.apache.cloudstack.api.ResponseObject.ResponseView; +import org.apache.cloudstack.api.ServerApiException; +import org.apache.cloudstack.api.response.NetworkOfferingResponse; +import org.apache.cloudstack.api.response.NetworkResponse; +import org.apache.cloudstack.context.CallContext; + +import com.cloud.event.EventTypes; +import com.cloud.exception.InvalidParameterValueException; +import com.cloud.network.Network; +import com.cloud.offering.NetworkOffering; +import com.cloud.user.Account; +import com.cloud.user.User; + +@APICommand(name = "migrateNetwork", description = "moves a network to another physical network", responseObject = NetworkResponse.class, responseView = ResponseView.Restricted, entityType = {Network.class}, + requestHasSensitiveInfo = false, responseHasSensitiveInfo = false) +public class MigrateNetworkCmd extends BaseAsyncCmd { + public static final Logger s_logger = Logger.getLogger(MigrateNetworkCmd.class.getName()); + + private static final String s_name = "migratenetworkresponse"; + + ///////////////////////////////////////////////////// + //////////////// API parameters ///////////////////// + ///////////////////////////////////////////////////// + @ACL(accessType = AccessType.OperateEntry) + @Parameter(name=ApiConstants.NETWORK_ID, type=CommandType.UUID, entityType = NetworkResponse.class, + required=true, description="the ID of the network") + protected Long id; + + @Parameter(name = ApiConstants.NETWORK_OFFERING_ID, type = CommandType.UUID, entityType = NetworkOfferingResponse.class, description = "network offering ID") + private Long networkOfferingId; + + @Parameter(name = ApiConstants.RESUME, type = CommandType.BOOLEAN, description = "true if previous network migration cmd failed") + private Boolean resume; + + ///////////////////////////////////////////////////// + /////////////////// Accessors /////////////////////// + ///////////////////////////////////////////////////// + + public Long getId() { + return id; + } + + public Long getNetworkOfferingId() { + return networkOfferingId; + } + + public Boolean getResume() { + return resume != null ? resume : false; + } + + ///////////////////////////////////////////////////// + /////////////// API Implementation/////////////////// + ///////////////////////////////////////////////////// + + @Override + public String getCommandName() { + return s_name; + } + + @Override + public long getEntityOwnerId() { + Network network = _networkService.getNetwork(id); + if (network == null) { + throw new InvalidParameterValueException("Networkd id=" + id + " doesn't exist"); + } else { + return _networkService.getNetwork(id).getAccountId(); + } + } + + @Override + public void execute() { + User callerUser = _accountService.getActiveUser(CallContext.current().getCallingUserId()); + Account callerAccount = _accountService.getActiveAccountById(callerUser.getAccountId()); + Network network = _networkService.getNetwork(id); + if (network == null) { + throw new InvalidParameterValueException("Couldn't find network by id"); + } + + Network result = + _networkService.migrateGuestNetwork(getId(), getNetworkOfferingId(), callerAccount, callerUser, getResume()); + + if (result != null) { + NetworkResponse response = _responseGenerator.createNetworkResponse(ResponseView.Restricted, result); + response.setResponseName(getCommandName()); + setResponseObject(response); + } else { + throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, "Failed to update network"); + } + } + + @Override + public String getEventDescription() { + StringBuilder eventMsg = new StringBuilder("Migrating network: " + getId()); + if (getNetworkOfferingId() != null) { + Network network = _networkService.getNetwork(getId()); + if (network == null) { + throw new InvalidParameterValueException("Network id=" + id + " doesn't exist"); + } + if (network.getNetworkOfferingId() != getNetworkOfferingId()) { + NetworkOffering oldOff = _entityMgr.findById(NetworkOffering.class, network.getNetworkOfferingId()); + NetworkOffering newOff = _entityMgr.findById(NetworkOffering.class, getNetworkOfferingId()); + if (newOff == null) { + throw new InvalidParameterValueException("Network offering id supplied is invalid"); + } + + eventMsg.append(". Original network offering id: " + oldOff.getUuid() + ", new network offering id: " + newOff.getUuid()); + } + } + + return eventMsg.toString(); + } + + @Override + public String getEventType() { + return EventTypes.EVENT_NETWORK_MIGRATE; + } + + @Override + public String getSyncObjType() { + return BaseAsyncCmd.networkSyncObject; + } + + @Override + public Long getSyncObjId() { + return id; + } + +} diff --git a/api/src/org/apache/cloudstack/api/command/admin/network/MigrateVPCCmd.java b/api/src/org/apache/cloudstack/api/command/admin/network/MigrateVPCCmd.java new file mode 100644 index 000000000000..6cf115742a8d --- /dev/null +++ b/api/src/org/apache/cloudstack/api/command/admin/network/MigrateVPCCmd.java @@ -0,0 +1,144 @@ +// 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 org.apache.cloudstack.api.command.admin.network; + +import org.apache.cloudstack.acl.SecurityChecker; +import org.apache.cloudstack.api.ACL; +import org.apache.cloudstack.api.APICommand; +import org.apache.cloudstack.api.ApiConstants; +import org.apache.cloudstack.api.ApiErrorCode; +import org.apache.cloudstack.api.BaseAsyncCmd; +import org.apache.cloudstack.api.Parameter; +import org.apache.cloudstack.api.ResponseObject; +import org.apache.cloudstack.api.ServerApiException; +import org.apache.cloudstack.api.response.VpcOfferingResponse; +import org.apache.cloudstack.api.response.VpcResponse; +import org.apache.cloudstack.context.CallContext; +import org.apache.log4j.Logger; + +import java.util.HashMap; +import java.util.Map; + +import com.cloud.event.EventTypes; +import com.cloud.network.vpc.Vpc; +import com.cloud.user.Account; +import com.cloud.user.User; + +@APICommand(name = "migrateVPC", description = "moves a vpc to another physical network", responseObject = VpcResponse.class, responseView = ResponseObject.ResponseView.Restricted, entityType = {Vpc.class}, + requestHasSensitiveInfo = false, responseHasSensitiveInfo = false) +public class MigrateVPCCmd extends BaseAsyncCmd { + public static final Logger s_logger = Logger.getLogger(MigrateVPCCmd.class.getName()); + + private static final String s_name = "migratevpcresponse"; + + ///////////////////////////////////////////////////// + //////////////// API parameters ///////////////////// + ///////////////////////////////////////////////////// + @ACL(accessType = SecurityChecker.AccessType.OperateEntry) + @Parameter(name= ApiConstants.VPC_ID, type=CommandType.UUID, entityType = VpcResponse.class, + required=true, description = "the ID of the vpc") + protected Long id; + + @Parameter(name = ApiConstants.VPC_OFF_ID, type = CommandType.UUID, entityType = VpcOfferingResponse.class, required=true, description = "vpc offering ID") + private Long vpcOfferingId; + + @Parameter(name = ApiConstants.TIER_NETWORK_OFFERINGS, type = CommandType.MAP, description = "network offering ids for each network in the vpc. Example: tierNetworkOfferings[0].networkId=networkId1&tierNetworkOfferings[0].networkOfferingId=newNetworkofferingId1&tierNetworkOfferings[1].networkId=networkId2&tierNetworkOfferings[1].networkOfferingId=newNetworkofferingId2") + private Map> tierNetworkOfferings; + + @Parameter(name = ApiConstants.RESUME, type = CommandType.BOOLEAN, description = "true if previous network migration cmd failed") + private Boolean resume; + + ///////////////////////////////////////////////////// + /////////////////// Accessors /////////////////////// + ///////////////////////////////////////////////////// + + public Long getId() { + return id; + } + + public Long getVpcOfferingId() { + return vpcOfferingId; + } + + public Boolean getResume() { + return resume == null ? false : resume; + } + + public Map getTierNetworkOfferings() { + HashMap flatMap = new HashMap<>(); + + if (tierNetworkOfferings == null) { + return flatMap; + } + + for (HashMap map : tierNetworkOfferings.values()) { + flatMap.put(map.get("networkid"), map.get("networkofferingid")); + } + + return flatMap; + } + + ///////////////////////////////////////////////////// + /////////////// API Implementation/////////////////// + ///////////////////////////////////////////////////// + + @Override + public String getCommandName() { + return s_name; + } + + @Override + public void execute() { + User callerUser = _accountService.getActiveUser(CallContext.current().getCallingUserId()); + Account callerAccount = _accountService.getActiveAccountById(callerUser.getAccountId()); + + Vpc result = + _networkService.migrateVpcNetwork(getId(), getVpcOfferingId(), getTierNetworkOfferings(), callerAccount, callerUser, getResume()); + + if (result != null) { + VpcResponse response = _responseGenerator.createVpcResponse(ResponseObject.ResponseView.Restricted, result); + response.setResponseName(getCommandName()); + setResponseObject(response); + } else { + throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, "Failed to migrate vpc"); + } + } + + @Override + public String getEventDescription() { return "Migrating vpc: " + getId() + " to new vpc offering (" + vpcOfferingId + ")"; } + + @Override + public String getEventType() { + return EventTypes.EVENT_NETWORK_MIGRATE; + } + + @Override + public String getSyncObjType() { + return BaseAsyncCmd.networkSyncObject; + } + + @Override + public Long getSyncObjId() { + return id; + } + + @Override + public long getEntityOwnerId() { + return CallContext.current().getCallingAccount().getId(); + } + +} diff --git a/api/src/org/apache/cloudstack/api/command/admin/network/UpdateNetworkOfferingCmd.java b/api/src/org/apache/cloudstack/api/command/admin/network/UpdateNetworkOfferingCmd.java index 411da4fd36b5..5c58530fe1f9 100644 --- a/api/src/org/apache/cloudstack/api/command/admin/network/UpdateNetworkOfferingCmd.java +++ b/api/src/org/apache/cloudstack/api/command/admin/network/UpdateNetworkOfferingCmd.java @@ -69,6 +69,9 @@ public class UpdateNetworkOfferingCmd extends BaseCmd { description = "maximum number of concurrent connections supported by the network offering") private Integer maxConnections; + @Parameter(name = ApiConstants.TAGS, type = CommandType.STRING, description = "the tags for the network offering.", length = 4096) + private String tags; + ///////////////////////////////////////////////////// /////////////////// Accessors /////////////////////// ///////////////////////////////////////////////////// @@ -105,6 +108,10 @@ public Boolean getKeepAliveEnabled() { return keepAliveEnabled; } + public String getTags() { + return tags; + } + ///////////////////////////////////////////////////// /////////////// API Implementation/////////////////// ///////////////////////////////////////////////////// diff --git a/core/src/com/cloud/agent/api/ReplugNicAnswer.java b/core/src/com/cloud/agent/api/ReplugNicAnswer.java new file mode 100644 index 000000000000..7de39c3fa330 --- /dev/null +++ b/core/src/com/cloud/agent/api/ReplugNicAnswer.java @@ -0,0 +1,29 @@ +// +// 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 ReplugNicAnswer extends Answer { + public ReplugNicAnswer() { + } + + public ReplugNicAnswer(ReplugNicCommand cmd, boolean success, String result) { + super(cmd, success, result); + } +} diff --git a/core/src/com/cloud/agent/api/ReplugNicCommand.java b/core/src/com/cloud/agent/api/ReplugNicCommand.java new file mode 100644 index 000000000000..1c61f0a649e3 --- /dev/null +++ b/core/src/com/cloud/agent/api/ReplugNicCommand.java @@ -0,0 +1,70 @@ +// +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. +// + +package com.cloud.agent.api; + +import java.util.Map; + +import com.cloud.agent.api.to.NicTO; +import com.cloud.vm.VirtualMachine; + +public class ReplugNicCommand extends Command { + + NicTO nic; + String instanceName; + VirtualMachine.Type vmType; + Map details; + + public NicTO getNic() { + return nic; + } + + @Override + public boolean executeInSequence() { + return true; + } + + protected ReplugNicCommand() { + } + + public ReplugNicCommand(NicTO nic, String instanceName, VirtualMachine.Type vmtype) { + this.nic = nic; + this.instanceName = instanceName; + this.vmType = vmtype; + } + + public ReplugNicCommand(NicTO nic, String instanceName, VirtualMachine.Type vmtype, Map details) { + this.nic = nic; + this.instanceName = instanceName; + this.vmType = vmtype; + this.details = details; + } + + public String getVmName() { + return instanceName; + } + + public VirtualMachine.Type getVMType() { + return vmType; + } + + public Map getDetails() { + return this.details; + } +} diff --git a/engine/api/src/com/cloud/vm/VirtualMachineManager.java b/engine/api/src/com/cloud/vm/VirtualMachineManager.java index 14fead7a057c..a20fc7b88d74 100644 --- a/engine/api/src/com/cloud/vm/VirtualMachineManager.java +++ b/engine/api/src/com/cloud/vm/VirtualMachineManager.java @@ -193,6 +193,9 @@ NicProfile addVmToNetwork(VirtualMachine vm, Network network, NicProfile request */ VirtualMachineTO toVmTO(VirtualMachineProfile profile); + boolean replugNic(Network network, NicTO nic, VirtualMachineTO vm, ReservationContext context, DeployDestination dest) throws ConcurrentOperationException, + ResourceUnavailableException, InsufficientCapacityException; + VirtualMachine reConfigureVm(String vmUuid, ServiceOffering newServiceOffering, boolean sameHost) throws ResourceUnavailableException, ConcurrentOperationException, InsufficientServerCapacityException; diff --git a/engine/api/src/org/apache/cloudstack/engine/orchestration/service/NetworkOrchestrationService.java b/engine/api/src/org/apache/cloudstack/engine/orchestration/service/NetworkOrchestrationService.java index e2a471fef6a2..86a8fe91a36a 100644 --- a/engine/api/src/org/apache/cloudstack/engine/orchestration/service/NetworkOrchestrationService.java +++ b/engine/api/src/org/apache/cloudstack/engine/orchestration/service/NetworkOrchestrationService.java @@ -129,6 +129,11 @@ void prepare(VirtualMachineProfile profile, DeployDestination dest, ReservationC Map getExtraDhcpOptions(long nicId); + /** + * Returns all extra dhcp options which are set on the provided nic + * @param nicId + * @return map which maps the dhcp value on it's option code + */ /** * prepares vm nic change for migration * @@ -275,4 +280,6 @@ void implementNetworkElementsAndResources(DeployDestination dest, ReservationCon int getResourceCount(Network network); void finalizeUpdateInSequence(Network network, boolean success); + + List getNetworkGurus(); } diff --git a/engine/orchestration/src/com/cloud/vm/VirtualMachineManagerImpl.java b/engine/orchestration/src/com/cloud/vm/VirtualMachineManagerImpl.java index 74927b9d4654..da13b7ac20a5 100755 --- a/engine/orchestration/src/com/cloud/vm/VirtualMachineManagerImpl.java +++ b/engine/orchestration/src/com/cloud/vm/VirtualMachineManagerImpl.java @@ -38,8 +38,8 @@ import javax.inject.Inject; import javax.naming.ConfigurationException; -import org.apache.cloudstack.framework.jobs.impl.JobSerializerHelper; -import com.cloud.agent.api.AttachOrDettachConfigDriveCommand; +import org.apache.log4j.Logger; + import org.apache.cloudstack.affinity.dao.AffinityGroupVMMapDao; import org.apache.cloudstack.ca.CAManager; import org.apache.cloudstack.context.CallContext; @@ -59,6 +59,7 @@ import org.apache.cloudstack.framework.jobs.Outcome; import org.apache.cloudstack.framework.jobs.dao.VmWorkJobDao; import org.apache.cloudstack.framework.jobs.impl.AsyncJobVO; +import org.apache.cloudstack.framework.jobs.impl.JobSerializerHelper; import org.apache.cloudstack.framework.jobs.impl.OutcomeImpl; import org.apache.cloudstack.framework.jobs.impl.VmWorkJobVO; import org.apache.cloudstack.framework.messagebus.MessageBus; @@ -70,13 +71,13 @@ import org.apache.cloudstack.storage.datastore.db.StoragePoolVO; import org.apache.cloudstack.storage.to.VolumeObjectTO; import org.apache.cloudstack.utils.identity.ManagementServerNode; -import org.apache.log4j.Logger; import com.cloud.agent.AgentManager; import com.cloud.agent.Listener; import com.cloud.agent.api.AgentControlAnswer; import com.cloud.agent.api.AgentControlCommand; import com.cloud.agent.api.Answer; +import com.cloud.agent.api.AttachOrDettachConfigDriveCommand; import com.cloud.agent.api.CheckVirtualMachineAnswer; import com.cloud.agent.api.CheckVirtualMachineCommand; import com.cloud.agent.api.ClusterVMMetaDataSyncAnswer; @@ -89,6 +90,8 @@ import com.cloud.agent.api.PrepareForMigrationCommand; import com.cloud.agent.api.RebootAnswer; import com.cloud.agent.api.RebootCommand; +import com.cloud.agent.api.ReplugNicAnswer; +import com.cloud.agent.api.ReplugNicCommand; import com.cloud.agent.api.RestoreVMSnapshotAnswer; import com.cloud.agent.api.RestoreVMSnapshotCommand; import com.cloud.agent.api.ScaleVmCommand; @@ -3635,6 +3638,36 @@ private void orchestrateMigrateForScale(final String vmUuid, final long srcHostI } } + @Override + public boolean replugNic(final Network network, final NicTO nic, final VirtualMachineTO vm, final ReservationContext context, final DeployDestination dest) throws ConcurrentOperationException, + ResourceUnavailableException, InsufficientCapacityException { + boolean result = true; + + final VMInstanceVO router = _vmDao.findById(vm.getId()); + if (router.getState() == State.Running) { + try { + final ReplugNicCommand replugNicCmd = new ReplugNicCommand(nic, vm.getName(), vm.getType(), vm.getDetails()); + final Commands cmds = new Commands(Command.OnError.Stop); + cmds.addCommand("replugnic", replugNicCmd); + _agentMgr.send(dest.getHost().getId(), cmds); + final ReplugNicAnswer replugNicAnswer = cmds.getAnswer(ReplugNicAnswer.class); + if (replugNicAnswer == null || !replugNicAnswer.getResult()) { + s_logger.warn("Unable to replug nic for vm " + vm.getName()); + result = false; + } + } catch (final OperationTimedoutException e) { + throw new AgentUnavailableException("Unable to plug nic for router " + vm.getName() + " in network " + network, dest.getHost().getId(), e); + } + } else { + s_logger.warn("Unable to apply ReplugNic, vm " + router + " is not in the right state " + router.getState()); + + throw new ResourceUnavailableException("Unable to apply ReplugNic on the backend," + " vm " + vm + " is not in the right state", DataCenter.class, + router.getDataCenterId()); + } + + return result; + } + public boolean plugNic(final Network network, final NicTO nic, final VirtualMachineTO vm, final ReservationContext context, final DeployDestination dest) throws ConcurrentOperationException, ResourceUnavailableException, InsufficientCapacityException { boolean result = true; @@ -3647,7 +3680,7 @@ public boolean plugNic(final Network network, final NicTO nic, final VirtualMach cmds.addCommand("plugnic", plugNicCmd); _agentMgr.send(dest.getHost().getId(), cmds); final PlugNicAnswer plugNicAnswer = cmds.getAnswer(PlugNicAnswer.class); - if (!(plugNicAnswer != null && plugNicAnswer.getResult())) { + if (plugNicAnswer == null || !plugNicAnswer.getResult()) { s_logger.warn("Unable to plug nic for vm " + vm.getName()); result = false; } @@ -3683,7 +3716,7 @@ public boolean unplugNic(final Network network, final NicTO nic, final VirtualMa _agentMgr.send(dest.getHost().getId(), cmds); final UnPlugNicAnswer unplugNicAnswer = cmds.getAnswer(UnPlugNicAnswer.class); - if (!(unplugNicAnswer != null && unplugNicAnswer.getResult())) { + if (unplugNicAnswer == null || !unplugNicAnswer.getResult()) { s_logger.warn("Unable to unplug nic from router " + router); result = false; } diff --git a/engine/orchestration/src/org/apache/cloudstack/engine/orchestration/NetworkOrchestrator.java b/engine/orchestration/src/org/apache/cloudstack/engine/orchestration/NetworkOrchestrator.java index 1ddff84f9913..e819248f3e9d 100644 --- a/engine/orchestration/src/org/apache/cloudstack/engine/orchestration/NetworkOrchestrator.java +++ b/engine/orchestration/src/org/apache/cloudstack/engine/orchestration/NetworkOrchestrator.java @@ -292,7 +292,7 @@ public class NetworkOrchestrator extends ManagerBase implements NetworkOrchestra List networkGurus; - + @Override public List getNetworkGurus() { return networkGurus; } @@ -1156,7 +1156,7 @@ public void implementNetworkElementsAndResources(final DeployDestination dest, f } if (s_logger.isDebugEnabled()) { - s_logger.debug("Asking " + element.getName() + " to implemenet " + network); + s_logger.debug("Asking " + element.getName() + " to implement " + network); } if (!element.implement(network, offering, dest, context)) { @@ -2644,7 +2644,9 @@ public boolean destroyNetwork(final long networkId, final ReservationContext con public void doInTransactionWithoutResult(final TransactionStatus status) { final NetworkGuru guru = AdapterBase.getAdapterByName(networkGurus, networkFinal.getGuruName()); - guru.trash(networkFinal, _networkOfferingDao.findById(networkFinal.getNetworkOfferingId())); + if (!guru.trash(networkFinal, _networkOfferingDao.findById(networkFinal.getNetworkOfferingId()))) { + throw new CloudRuntimeException("Failed to trash network."); + } if (!deleteVlansInNetwork(networkFinal.getId(), context.getCaller().getId(), callerAccount)) { s_logger.warn("Failed to delete network " + networkFinal + "; was unable to cleanup corresponding ip ranges"); diff --git a/engine/schema/src/com/cloud/network/dao/NetworkVO.java b/engine/schema/src/com/cloud/network/dao/NetworkVO.java index 08a326a0e1ef..f87178807730 100644 --- a/engine/schema/src/com/cloud/network/dao/NetworkVO.java +++ b/engine/schema/src/com/cloud/network/dao/NetworkVO.java @@ -318,6 +318,10 @@ public long getRelated() { return related; } + public void setRelated(long related) { + this.related = related; + } + @Override public long getId() { return id; diff --git a/engine/schema/src/com/cloud/network/dao/RouterNetworkVO.java b/engine/schema/src/com/cloud/network/dao/RouterNetworkVO.java index db86cfa26b07..5808af3a3e82 100644 --- a/engine/schema/src/com/cloud/network/dao/RouterNetworkVO.java +++ b/engine/schema/src/com/cloud/network/dao/RouterNetworkVO.java @@ -68,6 +68,10 @@ public Network.GuestType getGuestType() { return guestType; } + public void setNetworkId(long networkId) { + this.networkId = networkId; + } + @Override public long getId() { return id; diff --git a/engine/schema/src/com/cloud/network/vpc/NetworkACLVO.java b/engine/schema/src/com/cloud/network/vpc/NetworkACLVO.java index b6ed5cbd2cbb..fb6a239c58d5 100644 --- a/engine/schema/src/com/cloud/network/vpc/NetworkACLVO.java +++ b/engine/schema/src/com/cloud/network/vpc/NetworkACLVO.java @@ -93,6 +93,10 @@ public void setDisplay(boolean display) { this.display = display; } + public void setVpcId(long vpcId) { + this.vpcId = vpcId; + } + @Override public boolean isDisplay() { return display; diff --git a/engine/schema/src/com/cloud/network/vpc/VpcGatewayVO.java b/engine/schema/src/com/cloud/network/vpc/VpcGatewayVO.java index 23568b492303..9919ba3bf7fb 100644 --- a/engine/schema/src/com/cloud/network/vpc/VpcGatewayVO.java +++ b/engine/schema/src/com/cloud/network/vpc/VpcGatewayVO.java @@ -220,4 +220,8 @@ public long getNetworkACLId() { public Class getEntityType() { return VpcGateway.class; } + + public void setVpcId(Long vpcId) { + this.vpcId = vpcId; + } } diff --git a/engine/schema/src/com/cloud/network/vpc/dao/NetworkACLDao.java b/engine/schema/src/com/cloud/network/vpc/dao/NetworkACLDao.java index 5e2a6f50c9c2..37ba3471ce88 100644 --- a/engine/schema/src/com/cloud/network/vpc/dao/NetworkACLDao.java +++ b/engine/schema/src/com/cloud/network/vpc/dao/NetworkACLDao.java @@ -16,8 +16,14 @@ // under the License. package com.cloud.network.vpc.dao; +import java.util.List; + import com.cloud.network.vpc.NetworkACLVO; +import com.cloud.utils.db.DB; import com.cloud.utils.db.GenericDao; public interface NetworkACLDao extends GenericDao { + + @DB + List listByVpcId(long vpcId); } diff --git a/engine/schema/src/com/cloud/network/vpc/dao/NetworkACLDaoImpl.java b/engine/schema/src/com/cloud/network/vpc/dao/NetworkACLDaoImpl.java index 00bb1d87c025..d21df1244f69 100644 --- a/engine/schema/src/com/cloud/network/vpc/dao/NetworkACLDaoImpl.java +++ b/engine/schema/src/com/cloud/network/vpc/dao/NetworkACLDaoImpl.java @@ -19,15 +19,29 @@ import org.springframework.stereotype.Component; +import java.util.List; + import com.cloud.network.vpc.NetworkACLVO; import com.cloud.utils.db.DB; import com.cloud.utils.db.GenericDaoBase; +import com.cloud.utils.db.SearchBuilder; +import com.cloud.utils.db.SearchCriteria; @Component @DB() public class NetworkACLDaoImpl extends GenericDaoBase implements NetworkACLDao { + protected final SearchBuilder AllFieldsSearch; protected NetworkACLDaoImpl() { + AllFieldsSearch = createSearchBuilder(); + AllFieldsSearch.and("vpcId", AllFieldsSearch.entity().getVpcId(), SearchCriteria.Op.EQ); + AllFieldsSearch.and("id", AllFieldsSearch.entity().getId(), SearchCriteria.Op.EQ); + AllFieldsSearch.done(); } + @Override public List listByVpcId(long vpcId) { + SearchCriteria sc = AllFieldsSearch.create(); + sc.setParameters("vpcId", vpcId); + return listBy(sc); + } } diff --git a/engine/schema/src/com/cloud/network/vpc/dao/VpcGatewayDao.java b/engine/schema/src/com/cloud/network/vpc/dao/VpcGatewayDao.java index a2a449b9930a..e6a72c870b0e 100644 --- a/engine/schema/src/com/cloud/network/vpc/dao/VpcGatewayDao.java +++ b/engine/schema/src/com/cloud/network/vpc/dao/VpcGatewayDao.java @@ -30,4 +30,6 @@ public interface VpcGatewayDao extends GenericDao { List listByVpcIdAndType(long vpcId, VpcGateway.Type type); List listByAclIdAndType(long aclId, VpcGateway.Type type); + + List listByVpcId(long vpcId); } diff --git a/engine/schema/src/com/cloud/network/vpc/dao/VpcGatewayDaoImpl.java b/engine/schema/src/com/cloud/network/vpc/dao/VpcGatewayDaoImpl.java index 284fd8884c1f..39d33192a093 100644 --- a/engine/schema/src/com/cloud/network/vpc/dao/VpcGatewayDaoImpl.java +++ b/engine/schema/src/com/cloud/network/vpc/dao/VpcGatewayDaoImpl.java @@ -82,4 +82,11 @@ public List listByAclIdAndType(long aclId, VpcGateway.Type type) { sc.setParameters("type", type); return listBy(sc); } + + @Override + public List listByVpcId(long vpcId) { + SearchCriteria sc = AllFieldsSearch.create(); + sc.setParameters("vpcId", vpcId); + return listBy(sc); + } } diff --git a/engine/schema/src/com/cloud/network/vpc/dao/VpcOfferingServiceMapDaoImpl.java b/engine/schema/src/com/cloud/network/vpc/dao/VpcOfferingServiceMapDaoImpl.java index 9679c3af7d86..9e14bb5348f6 100644 --- a/engine/schema/src/com/cloud/network/vpc/dao/VpcOfferingServiceMapDaoImpl.java +++ b/engine/schema/src/com/cloud/network/vpc/dao/VpcOfferingServiceMapDaoImpl.java @@ -97,7 +97,6 @@ public boolean areServicesSupportedByNetworkOffering(long networkOfferingId, Ser @Override public List listServicesForVpcOffering(long offId) { SearchCriteria sc = ServicesSearch.create(); - ; sc.setParameters("offeringId", offId); return customSearch(sc, null); } diff --git a/engine/schema/src/com/cloud/offerings/NetworkOfferingDetailsVO.java b/engine/schema/src/com/cloud/offerings/NetworkOfferingDetailsVO.java index c16c5ac4bf1a..d28e150da7cb 100644 --- a/engine/schema/src/com/cloud/offerings/NetworkOfferingDetailsVO.java +++ b/engine/schema/src/com/cloud/offerings/NetworkOfferingDetailsVO.java @@ -25,21 +25,20 @@ import javax.persistence.Id; import javax.persistence.Table; -import org.apache.cloudstack.api.InternalIdentity; - import com.cloud.offering.NetworkOffering; import com.cloud.offering.NetworkOffering.Detail; +import org.apache.cloudstack.api.ResourceDetail; @Entity @Table(name = "network_offering_details") -public class NetworkOfferingDetailsVO implements InternalIdentity { +public class NetworkOfferingDetailsVO implements ResourceDetail { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) @Column(name = "id") private long id; @Column(name = "network_offering_id") - private long offeringId; + private long resourceId; @Enumerated(value = EnumType.STRING) @Column(name = "name") @@ -51,8 +50,8 @@ public class NetworkOfferingDetailsVO implements InternalIdentity { public NetworkOfferingDetailsVO() { } - public NetworkOfferingDetailsVO(long offeringId, Detail detailName, String value) { - this.offeringId = offeringId; + public NetworkOfferingDetailsVO(long resourceId, Detail detailName, String value) { + this.resourceId = resourceId; this.name = detailName; this.value = value; } @@ -62,11 +61,20 @@ public long getId() { return id; } - public long getOfferingId() { - return offeringId; + @Override + public long getResourceId() { + return resourceId; + } + + public void setResourceId(long resourceId) { + this.resourceId = resourceId; + } + + public String getName() { + return name.name(); } - public NetworkOffering.Detail getName() { + public NetworkOffering.Detail getDetailName() { return name; } @@ -74,12 +82,13 @@ public String getValue() { return value; } - public void setId(long id) { - this.id = id; + @Override + public boolean isDisplay() { + return false; } - public void setOfferingId(long offeringId) { - this.offeringId = offeringId; + public void setId(long id) { + this.id = id; } public void setName(NetworkOffering.Detail name) { diff --git a/engine/schema/src/com/cloud/offerings/NetworkOfferingVO.java b/engine/schema/src/com/cloud/offerings/NetworkOfferingVO.java index f6451fb72c96..e31714356e6d 100644 --- a/engine/schema/src/com/cloud/offerings/NetworkOfferingVO.java +++ b/engine/schema/src/com/cloud/offerings/NetworkOfferingVO.java @@ -223,6 +223,10 @@ public String getTags() { return tags; } + public void setTags(String tags) { + this.tags = tags; + } + public void setName(String name) { this.name = name; } diff --git a/engine/schema/src/com/cloud/offerings/dao/NetworkOfferingDetailsDao.java b/engine/schema/src/com/cloud/offerings/dao/NetworkOfferingDetailsDao.java index 6af9c91c122d..94e5006a708f 100644 --- a/engine/schema/src/com/cloud/offerings/dao/NetworkOfferingDetailsDao.java +++ b/engine/schema/src/com/cloud/offerings/dao/NetworkOfferingDetailsDao.java @@ -21,9 +21,9 @@ import com.cloud.offering.NetworkOffering; import com.cloud.offering.NetworkOffering.Detail; import com.cloud.offerings.NetworkOfferingDetailsVO; -import com.cloud.utils.db.GenericDao; +import org.apache.cloudstack.resourcedetail.ResourceDetailsDao; -public interface NetworkOfferingDetailsDao extends GenericDao { +public interface NetworkOfferingDetailsDao extends ResourceDetailsDao { Map getNtwkOffDetails(long offeringId); diff --git a/engine/schema/src/com/cloud/offerings/dao/NetworkOfferingDetailsDaoImpl.java b/engine/schema/src/com/cloud/offerings/dao/NetworkOfferingDetailsDaoImpl.java index ea476709c2e2..786b71c17c49 100644 --- a/engine/schema/src/com/cloud/offerings/dao/NetworkOfferingDetailsDaoImpl.java +++ b/engine/schema/src/com/cloud/offerings/dao/NetworkOfferingDetailsDaoImpl.java @@ -16,6 +16,8 @@ // under the License. package com.cloud.offerings.dao; +import org.apache.cloudstack.resourcedetail.ResourceDetailsDaoBase; + import java.util.HashMap; import java.util.List; import java.util.Map; @@ -23,27 +25,27 @@ import com.cloud.offering.NetworkOffering; import com.cloud.offering.NetworkOffering.Detail; import com.cloud.offerings.NetworkOfferingDetailsVO; -import com.cloud.utils.db.GenericDaoBase; import com.cloud.utils.db.GenericSearchBuilder; import com.cloud.utils.db.SearchBuilder; import com.cloud.utils.db.SearchCriteria; import com.cloud.utils.db.SearchCriteria.Func; import com.cloud.utils.db.SearchCriteria.Op; -public class NetworkOfferingDetailsDaoImpl extends GenericDaoBase implements NetworkOfferingDetailsDao { +public class NetworkOfferingDetailsDaoImpl extends ResourceDetailsDaoBase implements NetworkOfferingDetailsDao { protected final SearchBuilder DetailSearch; private final GenericSearchBuilder ValueSearch; public NetworkOfferingDetailsDaoImpl() { DetailSearch = createSearchBuilder(); - DetailSearch.and("offeringId", DetailSearch.entity().getOfferingId(), SearchCriteria.Op.EQ); + DetailSearch.and("resourceId", DetailSearch.entity().getResourceId(), SearchCriteria.Op.EQ); DetailSearch.and("name", DetailSearch.entity().getName(), SearchCriteria.Op.EQ); + DetailSearch.and("value", DetailSearch.entity().getValue(), SearchCriteria.Op.EQ); DetailSearch.done(); ValueSearch = createSearchBuilder(String.class); ValueSearch.select(null, Func.DISTINCT, ValueSearch.entity().getValue()); - ValueSearch.and("offeringId", ValueSearch.entity().getOfferingId(), SearchCriteria.Op.EQ); + ValueSearch.and("resourceId", ValueSearch.entity().getResourceId(), SearchCriteria.Op.EQ); ValueSearch.and("name", ValueSearch.entity().getName(), Op.EQ); ValueSearch.done(); } @@ -51,12 +53,12 @@ public NetworkOfferingDetailsDaoImpl() { @Override public Map getNtwkOffDetails(long offeringId) { SearchCriteria sc = DetailSearch.create(); - sc.setParameters("offeringId", offeringId); + sc.setParameters("resourceId", offeringId); List results = search(sc, null); Map details = new HashMap(results.size()); for (NetworkOfferingDetailsVO result : results) { - details.put(result.getName(), result.getValue()); + details.put(result.getDetailName(), result.getValue()); } return details; @@ -66,7 +68,7 @@ public Map getNtwkOffDetails(long offeringId) { public String getDetail(long offeringId, Detail detailName) { SearchCriteria sc = ValueSearch.create(); sc.setParameters("name", detailName); - sc.setParameters("offeringId", offeringId); + sc.setParameters("resourceId", offeringId); List results = customSearch(sc, null); if (results.isEmpty()) { return null; @@ -75,4 +77,7 @@ public String getDetail(long offeringId, Detail detailName) { } } + @Override public void addDetail(long resourceId, String key, String value, boolean display) { + persist(new NetworkOfferingDetailsVO(resourceId, Detail.valueOf(key), value)); + } } diff --git a/engine/schema/src/com/cloud/tags/dao/ResourceTagDao.java b/engine/schema/src/com/cloud/tags/dao/ResourceTagDao.java index b7884342a53f..bacb09b98793 100644 --- a/engine/schema/src/com/cloud/tags/dao/ResourceTagDao.java +++ b/engine/schema/src/com/cloud/tags/dao/ResourceTagDao.java @@ -29,15 +29,35 @@ public interface ResourceTagDao extends GenericDao { /** - * @param resourceId - * @param resourceType - * @return + * Remove a resourceTag based on the resourceId and type + * @param resourceId the id of the resource you want to remove + * @param resourceType the resource type + * @return true if successful */ boolean removeByIdAndType(long resourceId, ResourceObjectType resourceType); List listBy(long resourceId, ResourceObjectType resourceType); + /** + * Find a resource tag based on the resource id, resource type and key + * @param resourceId the id of the resource you want to find + * @param resourceType the resource type (e.g. VPC) + * @param key the key value + * @return the ResourceTag matching the search criteria + */ + ResourceTag findByKey(long resourceId, ResourceObjectType resourceType, String key); + void updateResourceId(long srcId, long destId, ResourceObjectType resourceType); Map> listTags(); + + /** + * remove a resource tag based on the resource id, resource type and key + * @param resourceId the id of the resource you want to remove + * @param resourceType the resource type (e.g. VPC) + * @param key the key value + */ + void removeByResourceIdAndKey(long resourceId, ResourceObjectType resourceType, String key); + + List listByResourceUuid(String resourceUuid); } diff --git a/engine/schema/src/com/cloud/tags/dao/ResourceTagsDaoImpl.java b/engine/schema/src/com/cloud/tags/dao/ResourceTagsDaoImpl.java index d5578a8fded7..cc9d99e6ab16 100644 --- a/engine/schema/src/com/cloud/tags/dao/ResourceTagsDaoImpl.java +++ b/engine/schema/src/com/cloud/tags/dao/ResourceTagsDaoImpl.java @@ -42,6 +42,8 @@ public ResourceTagsDaoImpl() { AllFieldsSearch.and("resourceId", AllFieldsSearch.entity().getResourceId(), Op.EQ); AllFieldsSearch.and("uuid", AllFieldsSearch.entity().getResourceUuid(), Op.EQ); AllFieldsSearch.and("resourceType", AllFieldsSearch.entity().getResourceType(), Op.EQ); + AllFieldsSearch.and("key", AllFieldsSearch.entity().getKey(), Op.EQ); + AllFieldsSearch.and("resourceUuid", AllFieldsSearch.entity().getResourceUuid(), Op.EQ); AllFieldsSearch.done(); } @@ -62,6 +64,15 @@ public List listBy(long resourceId, ResourceObjectType re return listBy(sc); } + @Override + public ResourceTag findByKey(long resourceId, ResourceObjectType resourceType, String key) { + SearchCriteria sc = AllFieldsSearch.create(); + sc.setParameters("resourceId", resourceId); + sc.setParameters("resourceType", resourceType); + sc.setParameters("key", key); + return findOneBy(sc); + } + @Override public void updateResourceId(long srcId, long destId, ResourceObjectType resourceType) { SearchCriteria sc = AllFieldsSearch.create(); sc.setParameters("resourceId", srcId); @@ -93,4 +104,20 @@ public Map> listTags() { } return resourceTagMap; } + + @Override + public void removeByResourceIdAndKey(long resourceId, ResourceObjectType resourceType, String key) { + SearchCriteria sc = AllFieldsSearch.create(); + sc.setParameters("resourceId", resourceId); + sc.setParameters("resourceType", resourceType); + sc.setParameters("key", key); + remove(sc); + } + + @Override + public List listByResourceUuid(String resourceUuid) { + SearchCriteria sc = AllFieldsSearch.create(); + sc.setParameters("resourceUuid", resourceUuid); + return listBy(sc); + } } diff --git a/engine/schema/src/com/cloud/vm/dao/NicIpAliasDao.java b/engine/schema/src/com/cloud/vm/dao/NicIpAliasDao.java index b79c101b8286..40e7e4072416 100644 --- a/engine/schema/src/com/cloud/vm/dao/NicIpAliasDao.java +++ b/engine/schema/src/com/cloud/vm/dao/NicIpAliasDao.java @@ -58,4 +58,5 @@ public interface NicIpAliasDao extends GenericDao { List listByNetworkIdAndState(long networkId, NicIpAlias.State state); + int moveIpAliases(long fromNicId, long toNicId); } \ No newline at end of file diff --git a/engine/schema/src/com/cloud/vm/dao/NicIpAliasDaoImpl.java b/engine/schema/src/com/cloud/vm/dao/NicIpAliasDaoImpl.java index 48cc6621b444..d1453aa46307 100644 --- a/engine/schema/src/com/cloud/vm/dao/NicIpAliasDaoImpl.java +++ b/engine/schema/src/com/cloud/vm/dao/NicIpAliasDaoImpl.java @@ -172,4 +172,14 @@ public Integer countAliasIps(long id) { List list = listBy(sc); return list.size(); } + + @Override + public int moveIpAliases(long fromNicId, long toNicId) { + SearchCriteria sc = AllFieldsSearch.create(); + sc.setParameters("nicId", fromNicId); + + NicIpAliasVO update = createForUpdate(); + update.setNicId(toNicId); + return update(update, sc); + } } diff --git a/engine/schema/src/com/cloud/vm/dao/NicSecondaryIpDao.java b/engine/schema/src/com/cloud/vm/dao/NicSecondaryIpDao.java index ef8df516e9f3..96b80b84dd7e 100644 --- a/engine/schema/src/com/cloud/vm/dao/NicSecondaryIpDao.java +++ b/engine/schema/src/com/cloud/vm/dao/NicSecondaryIpDao.java @@ -57,4 +57,6 @@ public interface NicSecondaryIpDao extends GenericDao { Long countByNicId(long nicId); List listSecondaryIpUsingKeyword(long nicId, String keyword); + + int moveSecondaryIps(long fromNicId, long toNicId); } diff --git a/engine/schema/src/com/cloud/vm/dao/NicSecondaryIpDaoImpl.java b/engine/schema/src/com/cloud/vm/dao/NicSecondaryIpDaoImpl.java index 50733de002d1..01f53bc99fb3 100644 --- a/engine/schema/src/com/cloud/vm/dao/NicSecondaryIpDaoImpl.java +++ b/engine/schema/src/com/cloud/vm/dao/NicSecondaryIpDaoImpl.java @@ -172,4 +172,15 @@ public List listSecondaryIpUsingKeyword(long nicId, String key sc.setParameters("address", "%" + keyword + "%"); return listBy(sc); } + + @Override + public int moveSecondaryIps(long fromNicId, long toNicId) { + NicSecondaryIpVO update = createForUpdate(); + update.setNicId(toNicId); + + SearchCriteria sc = AllFieldsSearch.create(); + sc.setParameters("nicId", fromNicId); + + return update(update, sc); + } } diff --git a/engine/schema/src/com/cloud/vm/dao/NicSecondaryIpVO.java b/engine/schema/src/com/cloud/vm/dao/NicSecondaryIpVO.java index 23e45e8a2356..d60ac9298fcb 100644 --- a/engine/schema/src/com/cloud/vm/dao/NicSecondaryIpVO.java +++ b/engine/schema/src/com/cloud/vm/dao/NicSecondaryIpVO.java @@ -97,6 +97,11 @@ public long getNicId() { return nicId; } + @Override + public void setNicId(long nicId) { + this.nicId = nicId; + } + @Override public long getDomainId() { return domainId; diff --git a/plugins/hypervisors/kvm/src/com/cloud/hypervisor/kvm/resource/BridgeVifDriver.java b/plugins/hypervisors/kvm/src/com/cloud/hypervisor/kvm/resource/BridgeVifDriver.java index 2fab9a83cf72..11b22c494f46 100644 --- a/plugins/hypervisors/kvm/src/com/cloud/hypervisor/kvm/resource/BridgeVifDriver.java +++ b/plugins/hypervisors/kvm/src/com/cloud/hypervisor/kvm/resource/BridgeVifDriver.java @@ -20,16 +20,19 @@ package com.cloud.hypervisor.kvm.resource; import java.io.File; +import java.util.ArrayList; +import java.util.List; import java.util.Map; import java.util.regex.Matcher; import java.util.regex.Pattern; import javax.naming.ConfigurationException; -import com.google.common.base.Strings; import org.apache.log4j.Logger; import org.libvirt.LibvirtException; +import com.google.common.base.Strings; + import com.cloud.agent.api.to.NicTO; import com.cloud.exception.InternalErrorException; import com.cloud.network.Networks; @@ -54,6 +57,8 @@ public void configure(Map params) throws ConfigurationException super.configure(params); + getPifs(); + // Set the domr scripts directory params.put("domr.scripts.dir", "scripts/network/domr/kvm"); @@ -80,12 +85,125 @@ public void configure(Map params) throws ConfigurationException if (libvirtVersion == null) { libvirtVersion = 0L; } + } - try { - createControlNetwork(); - } catch (LibvirtException e) { - throw new ConfigurationException(e.getMessage()); + public void getPifs() { + final File dir = new File("/sys/devices/virtual/net"); + final File[] netdevs = dir.listFiles(); + final List bridges = new ArrayList(); + for (File netdev : netdevs) { + final File isbridge = new File(netdev.getAbsolutePath() + "/bridge"); + final String netdevName = netdev.getName(); + s_logger.debug("looking in file " + netdev.getAbsolutePath() + "/bridge"); + if (isbridge.exists()) { + s_logger.debug("Found bridge " + netdevName); + bridges.add(netdevName); + } + } + + String guestBridgeName = _libvirtComputingResource.getGuestBridgeName(); + String publicBridgeName = _libvirtComputingResource.getPublicBridgeName(); + + for (final String bridge : bridges) { + s_logger.debug("looking for pif for bridge " + bridge); + final String pif = getPif(bridge); + if (_libvirtComputingResource.isPublicBridge(bridge)) { + _pifs.put("public", pif); + } + if (guestBridgeName != null && bridge.equals(guestBridgeName)) { + _pifs.put("private", pif); + } + _pifs.put(bridge, pif); + } + + // guest(private) creates bridges on a pif, if private bridge not found try pif direct + // This addresses the unnecessary requirement of someone to create an unused bridge just for traffic label + if (_pifs.get("private") == null) { + s_logger.debug("guest(private) traffic label '" + guestBridgeName + "' not found as bridge, looking for physical interface"); + final File dev = new File("/sys/class/net/" + guestBridgeName); + if (dev.exists()) { + s_logger.debug("guest(private) traffic label '" + guestBridgeName + "' found as a physical device"); + _pifs.put("private", guestBridgeName); + } + } + + // public creates bridges on a pif, if private bridge not found try pif direct + // This addresses the unnecessary requirement of someone to create an unused bridge just for traffic label + if (_pifs.get("public") == null) { + s_logger.debug("public traffic label '" + publicBridgeName+ "' not found as bridge, looking for physical interface"); + final File dev = new File("/sys/class/net/" + publicBridgeName); + if (dev.exists()) { + s_logger.debug("public traffic label '" + publicBridgeName + "' found as a physical device"); + _pifs.put("public", publicBridgeName); + } + } + + s_logger.debug("done looking for pifs, no more bridges"); + } + + private String getPif(final String bridge) { + String pif = matchPifFileInDirectory(bridge); + final File vlanfile = new File("/proc/net/vlan/" + pif); + + if (vlanfile.isFile()) { + pif = Script.runSimpleBashScript("grep ^Device\\: /proc/net/vlan/" + pif + " | awk {'print $2'}"); } + + return pif; + } + + private String matchPifFileInDirectory(final String bridgeName) { + final File brif = new File("/sys/devices/virtual/net/" + bridgeName + "/brif"); + + if (!brif.isDirectory()) { + final File pif = new File("/sys/class/net/" + bridgeName); + if (pif.isDirectory()) { + // if bridgeName already refers to a pif, return it as-is + return bridgeName; + } + s_logger.debug("failing to get physical interface from bridge " + bridgeName + ", does " + brif.getAbsolutePath() + "exist?"); + return ""; + } + + final File[] interfaces = brif.listFiles(); + + for (File anInterface : interfaces) { + final String fname = anInterface.getName(); + s_logger.debug("matchPifFileInDirectory: file name '" + fname + "'"); + if (isInterface(fname)) { + return fname; + } + } + + s_logger.debug("failing to get physical interface from bridge " + bridgeName + ", did not find an eth*, bond*, team*, vlan*, em*, p*p*, ens*, eno*, enp*, or enx* in " + brif.getAbsolutePath()); + return ""; + } + + private static final String [] IF_NAME_PATTERNS = { + "^eth", + "^bond", + "^vlan", + "^vx", + "^em", + "^ens", + "^eno", + "^enp", + "^team", + "^enx", + "^p\\d+p\\d+" + }; + + /** + * @param fname + * @return + */ + private static boolean isInterface(final String fname) { + StringBuilder commonPattern = new StringBuilder(); + for (final String ifNamePattern : IF_NAME_PATTERNS) { + commonPattern.append("|(").append(ifNamePattern).append(".*)"); + } + + return fname.matches(commonPattern.toString()); } @Override @@ -161,6 +279,7 @@ public LibvirtVMDef.InterfaceDef plug(NicTO nic, String guestOsType, String nicA if (nic.getPxeDisable() == true) { intf.setPxeDisable(true); } + return intf; } @@ -169,6 +288,16 @@ public void unplug(LibvirtVMDef.InterfaceDef iface) { deleteVnetBr(iface.getBrName()); } + @Override + public void attach(LibvirtVMDef.InterfaceDef iface) { + Script.runSimpleBashScript("brctl addif " + iface.getBrName() + " " + iface.getDevName()); + } + + @Override + public void detach(LibvirtVMDef.InterfaceDef iface) { + Script.runSimpleBashScript("test -d /sys/class/net/" + iface.getBrName() + "/brif/" + iface.getDevName() + " && brctl delif " + iface.getBrName() + " " + iface.getDevName()); + } + private String setVnetBrName(String pifName, String vnetId) { return "br" + pifName + "-" + vnetId; } @@ -272,10 +401,6 @@ private void deleteVnetBr(String brName) { } } - private void createControlNetwork() throws LibvirtException { - createControlNetwork(_bridges.get("linklocal")); - } - private void deleteExistingLinkLocalRouteTable(String linkLocalBr) { Script command = new Script("/bin/bash", _timeout); command.add("-c"); @@ -304,16 +429,21 @@ private void deleteExistingLinkLocalRouteTable(String linkLocalBr) { } } - private void createControlNetwork(String privBrName) { + private void createControlNetwork() { + createControlNetwork(_bridges.get("linklocal")); + } + + @Override + public void createControlNetwork(String privBrName) { deleteExistingLinkLocalRouteTable(privBrName); - if (!isBridgeExists(privBrName)) { + if (!isExistingBridge(privBrName)) { Script.runSimpleBashScript("brctl addbr " + privBrName + "; ip link set " + privBrName + " up; ip address add 169.254.0.1/16 dev " + privBrName, _timeout); } - } - private boolean isBridgeExists(String bridgeName) { - File f = new File("/sys/devices/virtual/net/" + bridgeName); + @Override + public boolean isExistingBridge(String bridgeName) { + File f = new File("/sys/devices/virtual/net/" + bridgeName + "/bridge"); if (f.exists()) { return true; } else { diff --git a/plugins/hypervisors/kvm/src/com/cloud/hypervisor/kvm/resource/DirectVifDriver.java b/plugins/hypervisors/kvm/src/com/cloud/hypervisor/kvm/resource/DirectVifDriver.java index 3cc8839176c3..b8763fa8da7a 100644 --- a/plugins/hypervisors/kvm/src/com/cloud/hypervisor/kvm/resource/DirectVifDriver.java +++ b/plugins/hypervisors/kvm/src/com/cloud/hypervisor/kvm/resource/DirectVifDriver.java @@ -63,4 +63,18 @@ public void unplug(LibvirtVMDef.InterfaceDef iface) { // not needed, libvirt will cleanup } + @Override + public void attach(LibvirtVMDef.InterfaceDef iface) { + + } + + @Override + public void detach(LibvirtVMDef.InterfaceDef iface) { + + } + + @Override + public void createControlNetwork(String privBrName) { + } + } diff --git a/plugins/hypervisors/kvm/src/com/cloud/hypervisor/kvm/resource/IvsVifDriver.java b/plugins/hypervisors/kvm/src/com/cloud/hypervisor/kvm/resource/IvsVifDriver.java index 1aae2b58933c..8e73d859039f 100644 --- a/plugins/hypervisors/kvm/src/com/cloud/hypervisor/kvm/resource/IvsVifDriver.java +++ b/plugins/hypervisors/kvm/src/com/cloud/hypervisor/kvm/resource/IvsVifDriver.java @@ -74,8 +74,6 @@ public void configure(Map params) throws ConfigurationException if (libvirtVersion == null) { libvirtVersion = 0L; } - - createControlNetwork(_bridges.get("linklocal")); } @Override @@ -145,6 +143,17 @@ public InterfaceDef plug(NicTO nic, String guestOsType, String nicAdapter) throw public void unplug(InterfaceDef iface) { } + @Override + public void attach(LibvirtVMDef.InterfaceDef iface) { + Script.runSimpleBashScript("/usr/sbin/ivs-ctl add-port " + iface.getDevName()); + } + + @Override + public void detach(LibvirtVMDef.InterfaceDef iface) { + Script.runSimpleBashScript("/usr/sbin/ivs-ctl del-port " + iface.getDevName()); + } + + private void createControlNetwork() throws LibvirtException { createControlNetwork(_bridges.get("linklocal")); } @@ -268,7 +277,8 @@ private void deleteExitingLinkLocalRouteTable(String linkLocalBr) { } } - private void createControlNetwork(String privBrName) { + @Override + public void createControlNetwork(String privBrName) { deleteExitingLinkLocalRouteTable(privBrName); if (!isBridgeExists(privBrName)) { Script.runSimpleBashScript("brctl addbr " + privBrName + "; ip link set " + privBrName + " up; ip address add 169.254.0.1/16 dev " + privBrName, _timeout); diff --git a/plugins/hypervisors/kvm/src/com/cloud/hypervisor/kvm/resource/LibvirtComputingResource.java b/plugins/hypervisors/kvm/src/com/cloud/hypervisor/kvm/resource/LibvirtComputingResource.java index 51b9737312e0..9c97b3ed23f3 100644 --- a/plugins/hypervisors/kvm/src/com/cloud/hypervisor/kvm/resource/LibvirtComputingResource.java +++ b/plugins/hypervisors/kvm/src/com/cloud/hypervisor/kvm/resource/LibvirtComputingResource.java @@ -47,12 +47,6 @@ import javax.xml.parsers.DocumentBuilderFactory; import javax.xml.parsers.ParserConfigurationException; -import org.apache.cloudstack.storage.to.PrimaryDataStoreTO; -import org.apache.cloudstack.storage.to.VolumeObjectTO; -import org.apache.cloudstack.utils.hypervisor.HypervisorUtils; -import org.apache.cloudstack.utils.linux.CPUStat; -import org.apache.cloudstack.utils.linux.MemStat; -import org.apache.cloudstack.utils.qemu.QemuImg.PhysicalDiskFormat; import org.apache.commons.io.FileUtils; import org.apache.commons.io.IOUtils; import org.apache.commons.lang.ArrayUtils; @@ -76,6 +70,15 @@ import org.xml.sax.InputSource; import org.xml.sax.SAXException; +import com.google.common.base.Strings; + +import org.apache.cloudstack.storage.to.PrimaryDataStoreTO; +import org.apache.cloudstack.storage.to.VolumeObjectTO; +import org.apache.cloudstack.utils.hypervisor.HypervisorUtils; +import org.apache.cloudstack.utils.linux.CPUStat; +import org.apache.cloudstack.utils.linux.MemStat; +import org.apache.cloudstack.utils.qemu.QemuImg.PhysicalDiskFormat; + import com.cloud.agent.api.Answer; import com.cloud.agent.api.Command; import com.cloud.agent.api.HostVmStateReportEntry; @@ -167,7 +170,6 @@ import com.cloud.vm.VirtualMachine; import com.cloud.vm.VirtualMachine.PowerState; import com.cloud.vm.VmDetailConstants; -import com.google.common.base.Strings; /** * LibvirtComputingResource execute requests on the computing/routing host using @@ -968,6 +970,18 @@ public boolean configure(final String name, final Map params) th } } + final Map bridges = new HashMap(); + + params.put("libvirt.host.bridges", bridges); + params.put("libvirt.host.pifs", _pifs); + + params.put("libvirt.computing.resource", this); + params.put("libvirtVersion", _hypervisorLibvirtVersion); + + + configureVifDrivers(params); + + /* switch (_bridgeType) { case OPENVSWITCH: getOvsPifs(); @@ -977,6 +991,7 @@ public boolean configure(final String name, final Map params) th getPifs(); break; } + */ if (_pifs.get("private") == null) { s_logger.debug("Failed to get private nic name"); @@ -1027,19 +1042,13 @@ public boolean configure(final String name, final Map params) th params.put("vm.migrate.speed", String.valueOf(_migrateSpeed)); } - final Map bridges = new HashMap(); bridges.put("linklocal", _linkLocalBridgeName); bridges.put("public", _publicBridgeName); bridges.put("private", _privBridgeName); bridges.put("guest", _guestBridgeName); - params.put("libvirt.host.bridges", bridges); - params.put("libvirt.host.pifs", _pifs); - - params.put("libvirt.computing.resource", this); - params.put("libvirtVersion", _hypervisorLibvirtVersion); + getVifDriver(TrafficType.Control).createControlNetwork(_linkLocalBridgeName); - configureVifDrivers(params); configureDiskActivityChecks(params); final KVMStorageProcessor storageProcessor = new KVMStorageProcessor(_storagePoolMgr, this); @@ -1132,6 +1141,23 @@ public VifDriver getVifDriver(final TrafficType trafficType) { return vifDriver; } + public VifDriver getVifDriver(final TrafficType trafficType, final String bridgeName) { + VifDriver vifDriver = null; + + for (VifDriver driver : getAllVifDrivers()) { + if (driver.isExistingBridge(bridgeName)) { + vifDriver = driver; + break; + } + } + + if (vifDriver == null) { + vifDriver = getVifDriver(trafficType); + } + + return vifDriver; + } + public List getAllVifDrivers() { final Set vifDrivers = new HashSet(); @@ -1160,10 +1186,10 @@ private void getPifs() { for (final String bridge : bridges) { s_logger.debug("looking for pif for bridge " + bridge); final String pif = getPif(bridge); - if (_publicBridgeName != null && bridge.equals(_publicBridgeName)) { + if (isPublicBridge(bridge)) { _pifs.put("public", pif); } - if (_guestBridgeName != null && bridge.equals(_guestBridgeName)) { + if (isGuestBridge(bridge)) { _pifs.put("private", pif); } _pifs.put(bridge, pif); @@ -1194,6 +1220,10 @@ private void getPifs() { s_logger.debug("done looking for pifs, no more bridges"); } + boolean isGuestBridge(String bridge) { + return _guestBridgeName != null && bridge.equals(_guestBridgeName); + } + private void getOvsPifs() { final String cmdout = Script.runSimpleBashScript("ovs-vsctl list-br | sed '{:q;N;s/\\n/%/g;t q}'"); s_logger.debug("cmdout was " + cmdout); @@ -1204,10 +1234,10 @@ private void getOvsPifs() { // Not really interested in the pif name at this point for ovs // bridges final String pif = bridge; - if (_publicBridgeName != null && bridge.equals(_publicBridgeName)) { + if (isPublicBridge(bridge)) { _pifs.put("public", pif); } - if (_guestBridgeName != null && bridge.equals(_guestBridgeName)) { + if (isGuestBridge(bridge)) { _pifs.put("private", pif); } _pifs.put(bridge, pif); @@ -1215,6 +1245,10 @@ private void getOvsPifs() { s_logger.debug("done looking for pifs, no more bridges"); } + public boolean isPublicBridge(String bridge) { + return _publicBridgeName != null && bridge.equals(_publicBridgeName); + } + private String getPif(final String bridge) { String pif = matchPifFileInDirectory(bridge); final File vlanfile = new File("/proc/net/vlan/" + pif); @@ -1281,12 +1315,12 @@ boolean isInterface(final String fname) { return false; } - public boolean checkNetwork(final String networkName) { + public boolean checkNetwork(final TrafficType trafficType, final String networkName) { if (networkName == null) { return true; } - if (_bridgeType == BridgeType.OPENVSWITCH) { + if (getVifDriver(trafficType, networkName) instanceof OvsVifDriver) { return checkOvsNetwork(networkName); } else { return checkBridgeNetwork(networkName); @@ -1421,7 +1455,7 @@ public synchronized boolean destroyTunnelNetwork(final String bridge) { public synchronized boolean findOrCreateTunnelNetwork(final String nwName) { try { - if (checkNetwork(nwName)) { + if (checkNetwork(TrafficType.Guest, nwName)) { return true; } // if not found, create a new one @@ -2324,7 +2358,7 @@ private void createVif(final LibvirtVMDef vm, final NicTO nic, final String nicA } } - vm.getDevices().addDevice(getVifDriver(nic.getType()).plug(nic, vm.getPlatformEmulator().toString(), nicAdapter).toString()); + vm.getDevices().addDevice(getVifDriver(nic.getType(), nic.getName()).plug(nic, vm.getPlatformEmulator(), nicAdapter)); } public boolean cleanupDisk(final DiskDef disk) { diff --git a/plugins/hypervisors/kvm/src/com/cloud/hypervisor/kvm/resource/LibvirtDomainXMLParser.java b/plugins/hypervisors/kvm/src/com/cloud/hypervisor/kvm/resource/LibvirtDomainXMLParser.java index 847d77553f4a..d979d553f489 100644 --- a/plugins/hypervisors/kvm/src/com/cloud/hypervisor/kvm/resource/LibvirtDomainXMLParser.java +++ b/plugins/hypervisors/kvm/src/com/cloud/hypervisor/kvm/resource/LibvirtDomainXMLParser.java @@ -27,7 +27,7 @@ import javax.xml.parsers.DocumentBuilderFactory; import javax.xml.parsers.ParserConfigurationException; -import com.google.common.base.Strings; +import org.apache.commons.lang.StringUtils; import org.apache.log4j.Logger; import org.w3c.dom.Document; import org.w3c.dom.Element; @@ -36,7 +36,8 @@ import org.xml.sax.InputSource; import org.xml.sax.SAXException; -import com.cloud.utils.StringUtils; +import com.google.common.base.Strings; + import com.cloud.hypervisor.kvm.resource.LibvirtVMDef.ChannelDef; import com.cloud.hypervisor.kvm.resource.LibvirtVMDef.DiskDef; import com.cloud.hypervisor.kvm.resource.LibvirtVMDef.InterfaceDef; @@ -161,6 +162,8 @@ public boolean parseDomainXML(String domXML) { String mac = getAttrValue("mac", "address", nic); String dev = getAttrValue("target", "dev", nic); String model = getAttrValue("model", "type", nic); + String slot = StringUtils.removeStart(getAttrValue("address", "slot", nic), "0x"); + InterfaceDef def = new InterfaceDef(); NodeList bandwidth = nic.getElementsByTagName("bandwidth"); Integer networkRateKBps = 0; @@ -181,6 +184,11 @@ public boolean parseDomainXML(String domXML) { String scriptPath = getAttrValue("script", "path", nic); def.defEthernet(dev, mac, NicModel.valueOf(model.toUpperCase()), scriptPath, networkRateKBps); } + + if (StringUtils.isNotBlank(slot)) { + def.setSlot(Integer.parseInt(slot, 16)); + } + interfaces.add(def); } diff --git a/plugins/hypervisors/kvm/src/com/cloud/hypervisor/kvm/resource/LibvirtVMDef.java b/plugins/hypervisors/kvm/src/com/cloud/hypervisor/kvm/resource/LibvirtVMDef.java index 0f34a92d6d61..0196c85bb586 100644 --- a/plugins/hypervisors/kvm/src/com/cloud/hypervisor/kvm/resource/LibvirtVMDef.java +++ b/plugins/hypervisors/kvm/src/com/cloud/hypervisor/kvm/resource/LibvirtVMDef.java @@ -16,16 +16,17 @@ // under the License. package com.cloud.hypervisor.kvm.resource; -import com.google.common.collect.Maps; -import org.apache.commons.lang.StringEscapeUtils; -import org.apache.log4j.Logger; - import java.io.File; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; +import org.apache.commons.lang.StringEscapeUtils; +import org.apache.log4j.Logger; + +import com.google.common.collect.Maps; + public class LibvirtVMDef { private static final Logger s_logger = Logger.getLogger(LibvirtVMDef.class); @@ -890,7 +891,7 @@ public String toString() { } } - enum NicModel { + public enum NicModel { E1000("e1000"), VIRTIO("virtio"), RTL8139("rtl8139"), NE2KPCI("ne2k_pci"), VMXNET3("vmxnet3"); String _model; @@ -925,6 +926,8 @@ enum HostNicType { private String _virtualPortInterfaceId; private int _vlanTag = -1; private boolean _pxeDisable = false; + private boolean _linkStateUp = true; + private Integer _slot; public void defBridgeNet(String brName, String targetBrName, String macAddr, NicModel model) { defBridgeNet(brName, targetBrName, macAddr, model, 0); @@ -1012,6 +1015,10 @@ public String getDevName() { return _networkName; } + public void setDevName(String networkName) { + _networkName = networkName; + } + public String getMacAddress() { return _macAddr; } @@ -1044,6 +1051,22 @@ public int getVlanTag() { return _vlanTag; } + public void setSlot(Integer slot) { + _slot = slot; + } + + public Integer getSlot() { + return _slot; + } + + public void setLinkStateUp(boolean linkStateUp) { + _linkStateUp = linkStateUp; + } + + public boolean isLinkStateUp() { + return _linkStateUp; + } + @Override public String toString() { StringBuilder netBuilder = new StringBuilder(); @@ -1086,6 +1109,12 @@ public String toString() { if (_vlanTag > 0 && _vlanTag < 4095) { netBuilder.append("\n\n"); } + + netBuilder.append("\n"); + + if (_slot != null) { + netBuilder.append(String.format("
\n", _slot)); + } netBuilder.append("\n"); return netBuilder.toString(); } diff --git a/plugins/hypervisors/kvm/src/com/cloud/hypervisor/kvm/resource/OvsVifDriver.java b/plugins/hypervisors/kvm/src/com/cloud/hypervisor/kvm/resource/OvsVifDriver.java index 6462df7cf308..06cd1617b782 100644 --- a/plugins/hypervisors/kvm/src/com/cloud/hypervisor/kvm/resource/OvsVifDriver.java +++ b/plugins/hypervisors/kvm/src/com/cloud/hypervisor/kvm/resource/OvsVifDriver.java @@ -18,6 +18,8 @@ */ package com.cloud.hypervisor.kvm.resource; +import java.util.Arrays; +import java.util.List; import java.util.Map; import javax.naming.ConfigurationException; @@ -42,6 +44,8 @@ public class OvsVifDriver extends VifDriverBase { public void configure(Map params) throws ConfigurationException { super.configure(params); + getPifs(); + String networkScriptsDir = (String)params.get("network.scripts.dir"); if (networkScriptsDir == null) { networkScriptsDir = "scripts/vm/network/vnet"; @@ -49,8 +53,27 @@ public void configure(Map params) throws ConfigurationException String value = (String)params.get("scripts.timeout"); _timeout = NumbersUtil.parseInt(value, 30 * 60) * 1000; + } - createControlNetwork(_bridges.get("linklocal")); + public void getPifs() { + final String cmdout = Script.runSimpleBashScript("ovs-vsctl list-br | sed '{:q;N;s/\\n/%/g;t q}'"); + s_logger.debug("cmdout was " + cmdout); + final List bridges = Arrays.asList(cmdout.split("%")); + for (final String bridge : bridges) { + s_logger.debug("looking for pif for bridge " + bridge); + // String pif = getOvsPif(bridge); + // Not really interested in the pif name at this point for ovs + // bridges + final String pif = bridge; + if (_libvirtComputingResource.isPublicBridge(bridge)) { + _pifs.put("public", pif); + } + if (_libvirtComputingResource.isGuestBridge(bridge)) { + _pifs.put("private", pif); + } + _pifs.put(bridge, pif); + } + s_logger.debug("done looking for pifs, no more bridges"); } @Override @@ -132,6 +155,17 @@ public void unplug(InterfaceDef iface) { // Libvirt apparently takes care of this, see BridgeVifDriver unplug } + + @Override + public void attach(LibvirtVMDef.InterfaceDef iface) { + Script.runSimpleBashScript("ovs-vsctl add-port " + iface.getBrName() + " " + iface.getDevName()); + } + + @Override + public void detach(LibvirtVMDef.InterfaceDef iface) { + Script.runSimpleBashScript("ovs-vsctl port-to-br " + iface.getDevName() + " && ovs-vsctl del-port " + iface.getBrName() + " " + iface.getDevName()); + } + private void deleteExitingLinkLocalRouteTable(String linkLocalBr) { Script command = new Script("/bin/bash", _timeout); command.add("-c"); @@ -156,14 +190,16 @@ private void deleteExitingLinkLocalRouteTable(String linkLocalBr) { } } - private void createControlNetwork(String privBrName) { + @Override + public void createControlNetwork(String privBrName) { deleteExitingLinkLocalRouteTable(privBrName); - if (!isBridgeExists(privBrName)) { + if (!isExistingBridge(privBrName)) { Script.runSimpleBashScript("ovs-vsctl add-br " + privBrName + "; ip link set " + privBrName + " up; ip address add 169.254.0.1/16 dev " + privBrName, _timeout); } } - private boolean isBridgeExists(String bridgeName) { + @Override + public boolean isExistingBridge(String bridgeName) { Script command = new Script("/bin/sh", _timeout); command.add("-c"); command.add("ovs-vsctl br-exists " + bridgeName); diff --git a/plugins/hypervisors/kvm/src/com/cloud/hypervisor/kvm/resource/VifDriver.java b/plugins/hypervisors/kvm/src/com/cloud/hypervisor/kvm/resource/VifDriver.java index 5cd2d6151a4f..387a552b55e2 100644 --- a/plugins/hypervisors/kvm/src/com/cloud/hypervisor/kvm/resource/VifDriver.java +++ b/plugins/hypervisors/kvm/src/com/cloud/hypervisor/kvm/resource/VifDriver.java @@ -36,4 +36,12 @@ public interface VifDriver { public void unplug(LibvirtVMDef.InterfaceDef iface); + void attach(LibvirtVMDef.InterfaceDef iface); + + void detach(LibvirtVMDef.InterfaceDef iface); + + void createControlNetwork(String privBrName); + + boolean isExistingBridge(String bridgeName); + } diff --git a/plugins/hypervisors/kvm/src/com/cloud/hypervisor/kvm/resource/VifDriverBase.java b/plugins/hypervisors/kvm/src/com/cloud/hypervisor/kvm/resource/VifDriverBase.java index 2baec276a0fa..dad73f28c168 100644 --- a/plugins/hypervisors/kvm/src/com/cloud/hypervisor/kvm/resource/VifDriverBase.java +++ b/plugins/hypervisors/kvm/src/com/cloud/hypervisor/kvm/resource/VifDriverBase.java @@ -63,4 +63,8 @@ protected LibvirtVMDef.InterfaceDef.NicModel getGuestNicModel(String platformEmu return LibvirtVMDef.InterfaceDef.NicModel.E1000; } } + + public boolean isExistingBridge(String bridgeName) { + return false; + } } diff --git a/plugins/hypervisors/kvm/src/com/cloud/hypervisor/kvm/resource/wrapper/LibvirtCheckNetworkCommandWrapper.java b/plugins/hypervisors/kvm/src/com/cloud/hypervisor/kvm/resource/wrapper/LibvirtCheckNetworkCommandWrapper.java index 0d3df1f274c6..1ce491cd16c9 100644 --- a/plugins/hypervisors/kvm/src/com/cloud/hypervisor/kvm/resource/wrapper/LibvirtCheckNetworkCommandWrapper.java +++ b/plugins/hypervisors/kvm/src/com/cloud/hypervisor/kvm/resource/wrapper/LibvirtCheckNetworkCommandWrapper.java @@ -25,6 +25,7 @@ import com.cloud.agent.api.CheckNetworkAnswer; import com.cloud.agent.api.CheckNetworkCommand; import com.cloud.hypervisor.kvm.resource.LibvirtComputingResource; +import com.cloud.network.Networks; import com.cloud.network.PhysicalNetworkSetupInfo; import com.cloud.resource.CommandWrapper; import com.cloud.resource.ResourceWrapper; @@ -38,13 +39,13 @@ public Answer execute(final CheckNetworkCommand command, final LibvirtComputingR String errMsg = null; for (final PhysicalNetworkSetupInfo nic : phyNics) { - if (!libvirtComputingResource.checkNetwork(nic.getGuestNetworkName())) { + if (!libvirtComputingResource.checkNetwork(Networks.TrafficType.Guest, nic.getGuestNetworkName())) { errMsg = "Can not find network: " + nic.getGuestNetworkName(); break; - } else if (!libvirtComputingResource.checkNetwork(nic.getPrivateNetworkName())) { + } else if (!libvirtComputingResource.checkNetwork(Networks.TrafficType.Management, nic.getPrivateNetworkName())) { errMsg = "Can not find network: " + nic.getPrivateNetworkName(); break; - } else if (!libvirtComputingResource.checkNetwork(nic.getPublicNetworkName())) { + } else if (!libvirtComputingResource.checkNetwork(Networks.TrafficType.Public, nic.getPublicNetworkName())) { errMsg = "Can not find network: " + nic.getPublicNetworkName(); break; } diff --git a/plugins/hypervisors/kvm/src/com/cloud/hypervisor/kvm/resource/wrapper/LibvirtPlugNicCommandWrapper.java b/plugins/hypervisors/kvm/src/com/cloud/hypervisor/kvm/resource/wrapper/LibvirtPlugNicCommandWrapper.java index 018d6a784c34..2ee9b953d84e 100644 --- a/plugins/hypervisors/kvm/src/com/cloud/hypervisor/kvm/resource/wrapper/LibvirtPlugNicCommandWrapper.java +++ b/plugins/hypervisors/kvm/src/com/cloud/hypervisor/kvm/resource/wrapper/LibvirtPlugNicCommandWrapper.java @@ -19,13 +19,6 @@ package com.cloud.hypervisor.kvm.resource.wrapper; -import java.util.List; - -import org.apache.log4j.Logger; -import org.libvirt.Connect; -import org.libvirt.Domain; -import org.libvirt.LibvirtException; - import com.cloud.agent.api.Answer; import com.cloud.agent.api.PlugNicAnswer; import com.cloud.agent.api.PlugNicCommand; @@ -36,6 +29,12 @@ import com.cloud.hypervisor.kvm.resource.VifDriver; import com.cloud.resource.CommandWrapper; import com.cloud.resource.ResourceWrapper; +import org.apache.log4j.Logger; +import org.libvirt.Connect; +import org.libvirt.Domain; +import org.libvirt.LibvirtException; + +import java.util.List; @ResourceWrapper(handles = PlugNicCommand.class) public final class LibvirtPlugNicCommandWrapper extends CommandWrapper { @@ -61,7 +60,7 @@ public Answer execute(final PlugNicCommand command, final LibvirtComputingResour } nicnum++; } - final VifDriver vifDriver = libvirtComputingResource.getVifDriver(nic.getType()); + final VifDriver vifDriver = libvirtComputingResource.getVifDriver(nic.getType(), nic.getName()); final InterfaceDef interfaceDef = vifDriver.plug(nic, "Other PV", ""); vm.attachDevice(interfaceDef.toString()); diff --git a/plugins/hypervisors/kvm/src/com/cloud/hypervisor/kvm/resource/wrapper/LibvirtPrepareForMigrationCommandWrapper.java b/plugins/hypervisors/kvm/src/com/cloud/hypervisor/kvm/resource/wrapper/LibvirtPrepareForMigrationCommandWrapper.java index 2dfca5d6fc41..940a0a727ef2 100644 --- a/plugins/hypervisors/kvm/src/com/cloud/hypervisor/kvm/resource/wrapper/LibvirtPrepareForMigrationCommandWrapper.java +++ b/plugins/hypervisors/kvm/src/com/cloud/hypervisor/kvm/resource/wrapper/LibvirtPrepareForMigrationCommandWrapper.java @@ -19,12 +19,6 @@ package com.cloud.hypervisor.kvm.resource.wrapper; -import java.net.URISyntaxException; - -import org.apache.log4j.Logger; -import org.libvirt.Connect; -import org.libvirt.LibvirtException; - import com.cloud.agent.api.Answer; import com.cloud.agent.api.PrepareForMigrationAnswer; import com.cloud.agent.api.PrepareForMigrationCommand; @@ -37,6 +31,11 @@ import com.cloud.resource.CommandWrapper; import com.cloud.resource.ResourceWrapper; import com.cloud.storage.Volume; +import org.apache.log4j.Logger; +import org.libvirt.Connect; +import org.libvirt.LibvirtException; + +import java.net.URISyntaxException; @ResourceWrapper(handles = PrepareForMigrationCommand.class) public final class LibvirtPrepareForMigrationCommandWrapper extends CommandWrapper { @@ -60,7 +59,7 @@ public Answer execute(final PrepareForMigrationCommand command, final LibvirtCom final Connect conn = libvirtUtilitiesHelper.getConnectionByVmName(vm.getName()); for (final NicTO nic : nics) { - libvirtComputingResource.getVifDriver(nic.getType()).plug(nic, null, ""); + libvirtComputingResource.getVifDriver(nic.getType(), nic.getName()).plug(nic, null, ""); } /* setup disks, e.g for iso */ diff --git a/plugins/hypervisors/kvm/src/com/cloud/hypervisor/kvm/resource/wrapper/LibvirtReplugNicCommandWrapper.java b/plugins/hypervisors/kvm/src/com/cloud/hypervisor/kvm/resource/wrapper/LibvirtReplugNicCommandWrapper.java new file mode 100644 index 000000000000..8c20a33e608a --- /dev/null +++ b/plugins/hypervisors/kvm/src/com/cloud/hypervisor/kvm/resource/wrapper/LibvirtReplugNicCommandWrapper.java @@ -0,0 +1,133 @@ +// 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 java.util.List; + +import org.apache.log4j.Logger; +import org.libvirt.Connect; +import org.libvirt.Domain; +import org.libvirt.LibvirtException; + +import com.cloud.agent.api.Answer; +import com.cloud.agent.api.ReplugNicAnswer; +import com.cloud.agent.api.ReplugNicCommand; +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.LibvirtVMDef.InterfaceDef; +import com.cloud.hypervisor.kvm.resource.VifDriver; +import com.cloud.resource.CommandWrapper; +import com.cloud.resource.ResourceWrapper; + +@ResourceWrapper(handles = ReplugNicCommand.class) +public final class LibvirtReplugNicCommandWrapper extends CommandWrapper { + + private static final Logger s_logger = Logger.getLogger(LibvirtReplugNicCommandWrapper.class); + public enum DomainAffect { + CURRENT(0), LIVE(1), CONFIG(2), BOTH(3); + + private int value; + DomainAffect(int value) { + this.value = value; + } + + public int getValue() { + return value; + } + } + + @Override + public Answer execute(final ReplugNicCommand command, final LibvirtComputingResource libvirtComputingResource) { + final NicTO nic = command.getNic(); + final String vmName = command.getVmName(); + Domain vm = null; + try { + final LibvirtUtilitiesHelper libvirtUtilitiesHelper = libvirtComputingResource.getLibvirtUtilitiesHelper(); + final Connect conn = libvirtUtilitiesHelper.getConnectionByVmName(vmName); + vm = libvirtComputingResource.getDomain(conn, vmName); + + InterfaceDef oldPluggedNic = findPluggedNic(libvirtComputingResource, nic, vmName, conn); + + final VifDriver newVifDriver = libvirtComputingResource.getVifDriver(nic.getType(), nic.getName()); + final InterfaceDef interfaceDef = newVifDriver.plug(nic, "Other PV", oldPluggedNic.getModel().toString()); + + interfaceDef.setSlot(oldPluggedNic.getSlot()); + interfaceDef.setDevName(oldPluggedNic.getDevName()); + interfaceDef.setLinkStateUp(false); + + oldPluggedNic.setSlot(null); + + int i = 0; + do { + i++; + s_logger.debug("ReplugNic: Detaching interface" + oldPluggedNic + " (Attempt: " + i + ")"); + vm.detachDevice(oldPluggedNic.toString()); + } while (findPluggedNic(libvirtComputingResource, nic, vmName, conn) != null && i <= 10); + + s_logger.debug("ReplugNic: Attaching interface" + interfaceDef); + vm.attachDevice(interfaceDef.toString()); + + interfaceDef.setLinkStateUp(true); + s_logger.debug("ReplugNic: Updating interface" + interfaceDef); + vm.updateDeviceFlags(interfaceDef.toString(), DomainAffect.LIVE.getValue()); + + /* + // Manual replug + for (final VifDriver vifDriver : libvirtComputingResource.getAllVifDrivers()) { + vifDriver.detach(oldPluggedNic); + } + newVifDriver.attach(interfaceDef); + */ + + // 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); + } + + return new ReplugNicAnswer(command, true, "success"); + } catch (final LibvirtException | InternalErrorException e) { + final String msg = " Plug Nic failed due to " + e.toString(); + s_logger.warn(msg, e); + return new ReplugNicAnswer(command, false, msg); + } finally { + if (vm != null) { + try { + vm.free(); + } catch (final LibvirtException l) { + s_logger.trace("Ignoring libvirt error.", l); + } + } + } + } + + private InterfaceDef findPluggedNic(LibvirtComputingResource libvirtComputingResource, NicTO nic, String vmName, Connect conn) { + InterfaceDef oldPluggedNic = null; + + final List pluggedNics = libvirtComputingResource.getInterfaces(conn, vmName); + + for (final InterfaceDef pluggedNic : pluggedNics) { + if (pluggedNic.getMacAddress().equalsIgnoreCase(nic.getMac())) { + oldPluggedNic = pluggedNic; + } + } + + return oldPluggedNic; + } +} \ No newline at end of file diff --git a/plugins/hypervisors/kvm/test/com/cloud/hypervisor/kvm/resource/LibvirtComputingResourceTest.java b/plugins/hypervisors/kvm/test/com/cloud/hypervisor/kvm/resource/LibvirtComputingResourceTest.java index b3f853003213..2fd7692df58e 100644 --- a/plugins/hypervisors/kvm/test/com/cloud/hypervisor/kvm/resource/LibvirtComputingResourceTest.java +++ b/plugins/hypervisors/kvm/test/com/cloud/hypervisor/kvm/resource/LibvirtComputingResourceTest.java @@ -19,16 +19,6 @@ package com.cloud.hypervisor.kvm.resource; -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertNotNull; -import static org.junit.Assert.assertTrue; -import static org.junit.Assert.fail; -import static org.mockito.Mockito.doNothing; -import static org.mockito.Mockito.doThrow; -import static org.mockito.Mockito.times; -import static org.mockito.Mockito.verify; -import static org.mockito.Mockito.when; - import java.io.ByteArrayInputStream; import java.io.File; import java.io.IOException; @@ -48,11 +38,6 @@ import javax.xml.xpath.XPathExpressionException; import javax.xml.xpath.XPathFactory; -import org.apache.cloudstack.storage.command.AttachAnswer; -import org.apache.cloudstack.storage.command.AttachCommand; -import org.apache.cloudstack.utils.linux.CPUStat; -import org.apache.cloudstack.utils.linux.MemStat; -import org.apache.cloudstack.utils.qemu.QemuImg.PhysicalDiskFormat; import org.apache.commons.lang.SystemUtils; import org.joda.time.Duration; import org.junit.Assert; @@ -78,6 +63,12 @@ import org.w3c.dom.Document; import org.xml.sax.SAXException; +import org.apache.cloudstack.storage.command.AttachAnswer; +import org.apache.cloudstack.storage.command.AttachCommand; +import org.apache.cloudstack.utils.linux.CPUStat; +import org.apache.cloudstack.utils.linux.MemStat; +import org.apache.cloudstack.utils.qemu.QemuImg.PhysicalDiskFormat; + import com.cloud.agent.api.Answer; import com.cloud.agent.api.AttachIsoCommand; import com.cloud.agent.api.BackupSnapshotCommand; @@ -178,6 +169,16 @@ import com.cloud.vm.VirtualMachine.PowerState; import com.cloud.vm.VirtualMachine.Type; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertTrue; +import static org.junit.Assert.fail; +import static org.mockito.Mockito.doNothing; +import static org.mockito.Mockito.doThrow; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + @RunWith(MockitoJUnitRunner.class) public class LibvirtComputingResourceTest { @@ -1021,7 +1022,7 @@ public void testPrepareForMigrationCommand() { when(nicTO.getType()).thenReturn(TrafficType.Guest); when(diskTO.getType()).thenReturn(Volume.Type.ISO); - when(libvirtComputingResource.getVifDriver(nicTO.getType())).thenReturn(vifDriver); + when(libvirtComputingResource.getVifDriver(nicTO.getType(), nicTO.getName())).thenReturn(vifDriver); when(libvirtComputingResource.getStoragePoolMgr()).thenReturn(storagePoolManager); final LibvirtRequestWrapper wrapper = LibvirtRequestWrapper.getInstance(); @@ -1069,7 +1070,7 @@ public void testPrepareForMigrationCommandMigration() { when(nicTO.getType()).thenReturn(TrafficType.Guest); when(diskTO.getType()).thenReturn(Volume.Type.ISO); - when(libvirtComputingResource.getVifDriver(nicTO.getType())).thenReturn(vifDriver); + when(libvirtComputingResource.getVifDriver(nicTO.getType(), nicTO.getName())).thenReturn(vifDriver); when(libvirtComputingResource.getStoragePoolMgr()).thenReturn(storagePoolManager); when(storagePoolManager.connectPhysicalDisksViaVmSpec(vm)).thenReturn(true); @@ -1161,7 +1162,7 @@ public void testPrepareForMigrationCommandURISyntaxException() { when(nicTO.getType()).thenReturn(TrafficType.Guest); when(volume.getType()).thenReturn(Volume.Type.ISO); - when(libvirtComputingResource.getVifDriver(nicTO.getType())).thenReturn(vifDriver); + when(libvirtComputingResource.getVifDriver(nicTO.getType(), nicTO.getName())).thenReturn(vifDriver); when(libvirtComputingResource.getStoragePoolMgr()).thenReturn(storagePoolManager); try { when(libvirtComputingResource.getVolumePath(conn, volume)).thenThrow(URISyntaxException.class); @@ -1213,7 +1214,7 @@ public void testPrepareForMigrationCommandInternalErrorException() { when(vm.getNics()).thenReturn(new NicTO[]{nicTO}); when(nicTO.getType()).thenReturn(TrafficType.Guest); - when(libvirtComputingResource.getVifDriver(nicTO.getType())).thenThrow(InternalErrorException.class); + when(libvirtComputingResource.getVifDriver(nicTO.getType(), nicTO.getName())).thenThrow(InternalErrorException.class); when(libvirtComputingResource.getStoragePoolMgr()).thenReturn(storagePoolManager); try { when(libvirtComputingResource.getVolumePath(conn, volume)).thenReturn("/path"); @@ -2550,9 +2551,9 @@ public void testCheckNetworkCommand() { final CheckNetworkCommand command = new CheckNetworkCommand(networkInfoList); - when(libvirtComputingResource.checkNetwork(nic.getGuestNetworkName())).thenReturn(true); - when(libvirtComputingResource.checkNetwork(nic.getPrivateNetworkName())).thenReturn(true); - when(libvirtComputingResource.checkNetwork(nic.getPublicNetworkName())).thenReturn(true); + when(libvirtComputingResource.checkNetwork(TrafficType.Guest, nic.getGuestNetworkName())).thenReturn(true); + when(libvirtComputingResource.checkNetwork(TrafficType.Management, nic.getPrivateNetworkName())).thenReturn(true); + when(libvirtComputingResource.checkNetwork(TrafficType.Public, nic.getPublicNetworkName())).thenReturn(true); final LibvirtRequestWrapper wrapper = LibvirtRequestWrapper.getInstance(); assertNotNull(wrapper); @@ -2560,9 +2561,9 @@ public void testCheckNetworkCommand() { final Answer answer = wrapper.execute(command, libvirtComputingResource); assertTrue(answer.getResult()); - verify(libvirtComputingResource, times(3)).checkNetwork(nic.getGuestNetworkName()); - verify(libvirtComputingResource, times(3)).checkNetwork(nic.getPrivateNetworkName()); - verify(libvirtComputingResource, times(3)).checkNetwork(nic.getPublicNetworkName()); + verify(libvirtComputingResource, times(1)).checkNetwork(TrafficType.Guest, nic.getGuestNetworkName()); + verify(libvirtComputingResource, times(1)).checkNetwork(TrafficType.Management, nic.getPrivateNetworkName()); + verify(libvirtComputingResource, times(1)).checkNetwork(TrafficType.Public, nic.getPublicNetworkName()); } @Test @@ -2574,7 +2575,7 @@ public void testCheckNetworkCommandFail1() { final CheckNetworkCommand command = new CheckNetworkCommand(networkInfoList); - when(libvirtComputingResource.checkNetwork(networkSetupInfo.getGuestNetworkName())).thenReturn(false); + when(libvirtComputingResource.checkNetwork(TrafficType.Guest, networkSetupInfo.getGuestNetworkName())).thenReturn(false); final LibvirtRequestWrapper wrapper = LibvirtRequestWrapper.getInstance(); assertNotNull(wrapper); @@ -2582,7 +2583,7 @@ public void testCheckNetworkCommandFail1() { final Answer answer = wrapper.execute(command, libvirtComputingResource); assertFalse(answer.getResult()); - verify(libvirtComputingResource, times(1)).checkNetwork(networkSetupInfo.getGuestNetworkName()); + verify(libvirtComputingResource, times(1)).checkNetwork(TrafficType.Guest, networkSetupInfo.getGuestNetworkName()); } @Test @@ -2594,8 +2595,8 @@ public void testCheckNetworkCommandFail2() { final CheckNetworkCommand command = new CheckNetworkCommand(networkInfoList); - when(libvirtComputingResource.checkNetwork(networkSetupInfo.getGuestNetworkName())).thenReturn(true); - when(libvirtComputingResource.checkNetwork(networkSetupInfo.getPrivateNetworkName())).thenReturn(false); + when(libvirtComputingResource.checkNetwork(TrafficType.Guest, networkSetupInfo.getGuestNetworkName())).thenReturn(true); + when(libvirtComputingResource.checkNetwork(TrafficType.Management, networkSetupInfo.getPrivateNetworkName())).thenReturn(false); final LibvirtRequestWrapper wrapper = LibvirtRequestWrapper.getInstance(); assertNotNull(wrapper); @@ -2603,8 +2604,8 @@ public void testCheckNetworkCommandFail2() { final Answer answer = wrapper.execute(command, libvirtComputingResource); assertFalse(answer.getResult()); - verify(libvirtComputingResource, times(1)).checkNetwork(networkSetupInfo.getGuestNetworkName()); - verify(libvirtComputingResource, times(1)).checkNetwork(networkSetupInfo.getPrivateNetworkName()); + verify(libvirtComputingResource, times(1)).checkNetwork(TrafficType.Guest, networkSetupInfo.getGuestNetworkName()); + verify(libvirtComputingResource, times(1)).checkNetwork(TrafficType.Management, networkSetupInfo.getPrivateNetworkName()); } @Test @@ -2616,9 +2617,9 @@ public void testCheckNetworkCommandFail3() { final CheckNetworkCommand command = new CheckNetworkCommand(networkInfoList); - when(libvirtComputingResource.checkNetwork(networkSetupInfo.getGuestNetworkName())).thenReturn(true); - when(libvirtComputingResource.checkNetwork(networkSetupInfo.getPrivateNetworkName())).thenReturn(true); - when(libvirtComputingResource.checkNetwork(networkSetupInfo.getPublicNetworkName())).thenReturn(false); + when(libvirtComputingResource.checkNetwork(TrafficType.Guest, networkSetupInfo.getGuestNetworkName())).thenReturn(true); + when(libvirtComputingResource.checkNetwork(TrafficType.Management, networkSetupInfo.getPrivateNetworkName())).thenReturn(true); + when(libvirtComputingResource.checkNetwork(TrafficType.Public, networkSetupInfo.getPublicNetworkName())).thenReturn(false); final LibvirtRequestWrapper wrapper = LibvirtRequestWrapper.getInstance(); assertNotNull(wrapper); @@ -2626,8 +2627,8 @@ public void testCheckNetworkCommandFail3() { final Answer answer = wrapper.execute(command, libvirtComputingResource); assertFalse(answer.getResult()); - verify(libvirtComputingResource, times(1)).checkNetwork(networkSetupInfo.getGuestNetworkName()); - verify(libvirtComputingResource, times(1)).checkNetwork(networkSetupInfo.getPrivateNetworkName()); + verify(libvirtComputingResource, times(1)).checkNetwork(TrafficType.Guest, networkSetupInfo.getGuestNetworkName()); + verify(libvirtComputingResource, times(1)).checkNetwork(TrafficType.Management, networkSetupInfo.getPrivateNetworkName()); } @Test @@ -3151,12 +3152,13 @@ public void testPlugNicCommandNoMatchMack() { when(intDef.getMacAddress()).thenReturn("00:00:00:00"); when(nic.getMac()).thenReturn("00:00:00:01"); + when(nic.getName()).thenReturn("br0"); try { when(libvirtUtilitiesHelper.getConnectionByVmName(command.getVmName())).thenReturn(conn); when(libvirtComputingResource.getDomain(conn, instanceName)).thenReturn(vm); - when(libvirtComputingResource.getVifDriver(nic.getType())).thenReturn(vifDriver); + when(libvirtComputingResource.getVifDriver(nic.getType(), nic.getName())).thenReturn(vifDriver); when(vifDriver.plug(nic, "Other PV", "")).thenReturn(interfaceDef); when(interfaceDef.toString()).thenReturn("Interface"); @@ -3180,7 +3182,7 @@ public void testPlugNicCommandNoMatchMack() { try { verify(libvirtUtilitiesHelper, times(1)).getConnectionByVmName(command.getVmName()); verify(libvirtComputingResource, times(1)).getDomain(conn, instanceName); - verify(libvirtComputingResource, times(1)).getVifDriver(nic.getType()); + verify(libvirtComputingResource, times(1)).getVifDriver(nic.getType(), nic.getName()); verify(vifDriver, times(1)).plug(nic, "Other PV", ""); } catch (final LibvirtException e) { fail(e.getMessage()); @@ -3253,7 +3255,7 @@ public void testPlugNicCommandInternalError() { when(libvirtUtilitiesHelper.getConnectionByVmName(command.getVmName())).thenReturn(conn); when(libvirtComputingResource.getDomain(conn, instanceName)).thenReturn(vm); - when(libvirtComputingResource.getVifDriver(nic.getType())).thenReturn(vifDriver); + when(libvirtComputingResource.getVifDriver(nic.getType(), nic.getName())).thenReturn(vifDriver); when(vifDriver.plug(nic, "Other PV", "")).thenThrow(InternalErrorException.class); @@ -3273,7 +3275,7 @@ public void testPlugNicCommandInternalError() { try { verify(libvirtUtilitiesHelper, times(1)).getConnectionByVmName(command.getVmName()); verify(libvirtComputingResource, times(1)).getDomain(conn, instanceName); - verify(libvirtComputingResource, times(1)).getVifDriver(nic.getType()); + verify(libvirtComputingResource, times(1)).getVifDriver(nic.getType(), nic.getName()); verify(vifDriver, times(1)).plug(nic, "Other PV", ""); } catch (final LibvirtException e) { fail(e.getMessage()); diff --git a/plugins/hypervisors/kvm/test/com/cloud/hypervisor/kvm/resource/LibvirtDomainXMLParserTest.java b/plugins/hypervisors/kvm/test/com/cloud/hypervisor/kvm/resource/LibvirtDomainXMLParserTest.java index 78c4e868f2c1..9197013af20b 100644 --- a/plugins/hypervisors/kvm/test/com/cloud/hypervisor/kvm/resource/LibvirtDomainXMLParserTest.java +++ b/plugins/hypervisors/kvm/test/com/cloud/hypervisor/kvm/resource/LibvirtDomainXMLParserTest.java @@ -19,14 +19,15 @@ package com.cloud.hypervisor.kvm.resource; -import junit.framework.TestCase; - import java.io.File; import java.util.List; + +import junit.framework.TestCase; + +import com.cloud.hypervisor.kvm.resource.LibvirtVMDef.ChannelDef; import com.cloud.hypervisor.kvm.resource.LibvirtVMDef.DiskDef; import com.cloud.hypervisor.kvm.resource.LibvirtVMDef.InterfaceDef; import com.cloud.hypervisor.kvm.resource.LibvirtVMDef.RngDef; -import com.cloud.hypervisor.kvm.resource.LibvirtVMDef.ChannelDef; import com.cloud.hypervisor.kvm.resource.LibvirtVMDef.WatchDogDef; public class LibvirtDomainXMLParserTest extends TestCase { @@ -227,6 +228,8 @@ public void testDomainXMLParser() { for (int i = 0; i < ifs.size(); i++) { assertEquals(ifModel, ifs.get(i).getModel()); assertEquals(ifType, ifs.get(i).getNetType()); + assertEquals(Integer.valueOf(i + 3), ifs.get(i).getSlot()); + assertEquals("vnet" + i, ifs.get(i).getDevName()); } List rngs = parser.getRngs(); diff --git a/plugins/hypervisors/kvm/test/com/cloud/hypervisor/kvm/resource/LibvirtVMDefTest.java b/plugins/hypervisors/kvm/test/com/cloud/hypervisor/kvm/resource/LibvirtVMDefTest.java index e758dece1662..006562c213ec 100644 --- a/plugins/hypervisors/kvm/test/com/cloud/hypervisor/kvm/resource/LibvirtVMDefTest.java +++ b/plugins/hypervisors/kvm/test/com/cloud/hypervisor/kvm/resource/LibvirtVMDefTest.java @@ -19,14 +19,15 @@ package com.cloud.hypervisor.kvm.resource; +import java.io.File; + import junit.framework.TestCase; -import com.cloud.hypervisor.kvm.resource.LibvirtVMDef.DiskDef; + import com.cloud.hypervisor.kvm.resource.LibvirtVMDef.ChannelDef; +import com.cloud.hypervisor.kvm.resource.LibvirtVMDef.DiskDef; import com.cloud.hypervisor.kvm.resource.LibvirtVMDef.SCSIDef; import com.cloud.utils.Pair; -import java.io.File; - public class LibvirtVMDefTest extends TestCase { public void testInterfaceEtehrnet() { @@ -34,8 +35,12 @@ public void testInterfaceEtehrnet() { ifDef.defEthernet("targetDeviceName", "00:11:22:aa:bb:dd", LibvirtVMDef.InterfaceDef.NicModel.VIRTIO); String expected = - "\n" + "\n" + "\n" + "\n" - + "\n"; + "\n" + + "\n" + + "\n" + + "\n" + + "\n" + + "\n"; assertEquals(expected, ifDef.toString()); } @@ -45,8 +50,44 @@ public void testInterfaceDirectNet() { ifDef.defDirectNet("targetDeviceName", null, "00:11:22:aa:bb:dd", LibvirtVMDef.InterfaceDef.NicModel.VIRTIO, "private"); String expected = - "\n" + "\n" + - "\n" + "\n" + "\n"; + "\n" + + "\n" + + "\n" + + "\n" + + "\n" + + "\n"; + + assertEquals(expected, ifDef.toString()); + } + + public void testInterfaceBridgeSlot() { + LibvirtVMDef.InterfaceDef ifDef = new LibvirtVMDef.InterfaceDef(); + ifDef.defBridgeNet("targetDeviceName", null, "00:11:22:aa:bb:dd", LibvirtVMDef.InterfaceDef.NicModel.VIRTIO); + ifDef.setSlot(16); + + String expected = + "\n" + + "\n" + + "\n" + + "\n" + + "\n" + + "
\n" + + "\n"; + + assertEquals(expected, ifDef.toString()); + + ifDef.setLinkStateUp(false); + ifDef.setDevName("vnet11"); + + expected = + "\n" + + "\n" + + "\n" + + "\n" + + "\n" + + "\n" + + "
\n" + + "\n"; assertEquals(expected, ifDef.toString()); } diff --git a/plugins/hypervisors/kvm/test/com/cloud/hypervisor/kvm/resource/wrapper/LibvirtReplugNicCommandWrapperTest.java b/plugins/hypervisors/kvm/test/com/cloud/hypervisor/kvm/resource/wrapper/LibvirtReplugNicCommandWrapperTest.java new file mode 100644 index 000000000000..86e455be2712 --- /dev/null +++ b/plugins/hypervisors/kvm/test/com/cloud/hypervisor/kvm/resource/wrapper/LibvirtReplugNicCommandWrapperTest.java @@ -0,0 +1,274 @@ +// 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 static org.mockito.AdditionalMatchers.not; +import static org.mockito.Matchers.anyString; +import static org.mockito.Matchers.eq; +import static org.mockito.Mockito.doNothing; +import static org.mockito.Mockito.doReturn; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.spy; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import java.util.Arrays; +import java.util.HashMap; +import java.util.Map; + +import javax.naming.ConfigurationException; + +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.libvirt.Connect; +import org.libvirt.Domain; +import org.libvirt.LibvirtException; +import org.mockito.BDDMockito; +import org.powermock.api.mockito.PowerMockito; +import org.powermock.core.classloader.annotations.PrepareForTest; +import org.powermock.modules.junit4.PowerMockRunner; + +import com.cloud.agent.api.Answer; +import com.cloud.agent.api.ReplugNicCommand; +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.OvsVifDriver; +import com.cloud.network.Networks; +import com.cloud.utils.script.Script; +import com.cloud.vm.VirtualMachine; +@RunWith(PowerMockRunner.class) +@PrepareForTest(Script.class) +public class LibvirtReplugNicCommandWrapperTest { + private static final String part_1 = + "\n" + + " i-85-285-VM\n" + + " 8825b180-468f-4227-beb7-6b06fd342116\n" + + " CentOS 5.5 (64-bit)\n" + + " 262144\n" + + " 262144\n" + + " 1\n" + + " \n" + + " 256\n" + + " \n" + + " \n" + + " \n" + + " Apache Software Foundation\n" + + " CloudStack KVM Hypervisor\n" + + " 8825b180-468f-4227-beb7-6b06fd342116\n" + + " \n" + + " \n" + + " \n" + + " hvm\n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " destroy\n" + + " restart\n" + + " destroy\n" + + " \n" + + " /usr/libexec/qemu-kvm\n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " 0c4aae6926524a04b460\n" + + " \n" + + "
\n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + "
\n" + + " \n" + + " \n" + + " \n" + + "
\n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + "
\n" + + " \n" + + " \n" + + " \n" + + "
\n" + + " \n"; + private static final String part_2 = + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + "
\n" + + " \n"; + private static final String part_3 = + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + "
\n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + "