diff --git a/api/src/com/cloud/event/EventTypes.java b/api/src/com/cloud/event/EventTypes.java index 63b7cd0b32d..a7979888886 100755 --- a/api/src/com/cloud/event/EventTypes.java +++ b/api/src/com/cloud/event/EventTypes.java @@ -63,6 +63,11 @@ public class EventTypes { public static final String EVENT_FIREWALL_OPEN = "FIREWALL.OPEN"; public static final String EVENT_FIREWALL_CLOSE = "FIREWALL.CLOSE"; + //NIC Events + public static final String EVENT_NIC_CREATE = "NIC.CREATE"; + public static final String EVENT_NIC_DELETE = "NIC.DELETE"; + public static final String EVENT_NIC_UPDATE = "NIC.UPDATE"; + // Load Balancers public static final String EVENT_ASSIGN_TO_LOAD_BALANCER_RULE = "LB.ASSIGN.TO.RULE"; public static final String EVENT_REMOVE_FROM_LOAD_BALANCER_RULE = "LB.REMOVE.FROM.RULE"; diff --git a/api/src/com/cloud/vm/UserVmService.java b/api/src/com/cloud/vm/UserVmService.java index b1ebe10596d..e211692e2d2 100755 --- a/api/src/com/cloud/vm/UserVmService.java +++ b/api/src/com/cloud/vm/UserVmService.java @@ -113,6 +113,27 @@ public interface UserVmService { UserVm updateVirtualMachine(UpdateVMCmd cmd) throws ResourceUnavailableException, InsufficientCapacityException; + /** + * Adds a NIC on the given network to the virtual machine + * @param cmd the command object that defines the vm and the given network + * @return the vm object if successful, null otherwise + */ + UserVm addNicToVirtualMachine(AddNicToVMCmd cmd); + + /** + * Removes a NIC on the given network from the virtual machine + * @param cmd the command object that defines the vm and the given network + * @return the vm object if successful, null otherwise + */ + UserVm removeNicFromVirtualMachine(RemoveNicFromVMCmd cmd); + + /** + * Updates default Nic to the given network for given virtual machine + * @param cmd the command object that defines the vm and the given network + * @return the vm object if successful, null otherwise + */ + UserVm updateDefaultNicForVirtualMachine(UpdateDefaultNicForVMCmd cmd); + UserVm recoverVirtualMachine(RecoverVMCmd cmd) throws ResourceAllocationException; /** diff --git a/api/src/org/apache/cloudstack/api/ApiConstants.java b/api/src/org/apache/cloudstack/api/ApiConstants.java index d084271ca7c..d242830631b 100755 --- a/api/src/org/apache/cloudstack/api/ApiConstants.java +++ b/api/src/org/apache/cloudstack/api/ApiConstants.java @@ -224,6 +224,7 @@ public class ApiConstants { public static final String NETWORK_OFFERING_ID = "networkofferingid"; public static final String NETWORK_IDS = "networkids"; public static final String NETWORK_ID = "networkid"; + public static final String NIC_ID = "nicid"; public static final String SPECIFY_VLAN = "specifyvlan"; public static final String IS_DEFAULT = "isdefault"; public static final String IS_SYSTEM = "issystem"; diff --git a/api/src/org/apache/cloudstack/api/command/user/vm/AddNicToVMCmd.java b/api/src/org/apache/cloudstack/api/command/user/vm/AddNicToVMCmd.java new file mode 100644 index 00000000000..43340008e16 --- /dev/null +++ b/api/src/org/apache/cloudstack/api/command/user/vm/AddNicToVMCmd.java @@ -0,0 +1,121 @@ +// 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.user.vm; + +import java.util.ArrayList; +import java.util.EnumSet; + +import org.apache.log4j.Logger; + +import org.apache.cloudstack.api.*; +import org.apache.cloudstack.api.ApiConstants.VMDetails; +import org.apache.cloudstack.api.response.UserVmResponse; +import org.apache.cloudstack.api.response.NetworkResponse; +import com.cloud.event.EventTypes; +import com.cloud.exception.ConcurrentOperationException; +import com.cloud.exception.InsufficientCapacityException; +import com.cloud.exception.ResourceAllocationException; +import com.cloud.exception.ResourceUnavailableException; +import com.cloud.user.Account; +import com.cloud.user.UserContext; +import com.cloud.uservm.UserVm; + +@APICommand(name = "addNicToVirtualMachine", description="Adds VM to specified network by creating a NIC", responseObject=UserVmResponse.class) + +public class AddNicToVMCmd extends BaseAsyncCmd { + public static final Logger s_logger = Logger.getLogger(AddNicToVMCmd.class); + private static final String s_name = "addnictovirtualmachineresponse"; + + ///////////////////////////////////////////////////// + //////////////// API parameters ///////////////////// + ///////////////////////////////////////////////////// + + @Parameter(name=ApiConstants.VIRTUAL_MACHINE_ID, type=CommandType.UUID, entityType=UserVmResponse.class, + required=true, description="Virtual Machine ID") + private Long vmId; + + @Parameter(name=ApiConstants.NETWORK_ID, type=CommandType.UUID, entityType=NetworkResponse.class, + required=true, description="Network ID") + private Long netId; + + @Parameter(name=ApiConstants.IP_ADDRESS, type=CommandType.STRING, description="IP Address for the new network") + private String ipaddr; + + ///////////////////////////////////////////////////// + /////////////////// Accessors /////////////////////// + ///////////////////////////////////////////////////// + + public Long getVmId() { + return vmId; + } + + public Long getNetworkId() { + return netId; + } + + public String getIpAddress() { + return ipaddr; + } + + ///////////////////////////////////////////////////// + /////////////// API Implementation/////////////////// + ///////////////////////////////////////////////////// + + @Override + public String getCommandName() { + return s_name; + } + + public static String getResultObjectName() { + return "virtualmachine"; + } + + @Override + public String getEventType() { + return EventTypes.EVENT_NIC_CREATE; + } + + @Override + public String getEventDescription() { + return "Adding network " + getNetworkId() + " to user vm: " + getVmId(); + } + + @Override + public long getEntityOwnerId() { + UserVm vm = _responseGenerator.findUserVmById(getVmId()); + if (vm == null) { + return Account.ACCOUNT_ID_SYSTEM; // bad id given, parent this command to SYSTEM so ERROR events are tracked + } + return vm.getAccountId(); + } + + @Override + public void execute(){ + UserContext.current().setEventDetails("Vm Id: " + getVmId() + " Network Id: " + getNetworkId()); + UserVm result = _userVmService.addNicToVirtualMachine(this); + ArrayList dc = new ArrayList(); + dc.add(VMDetails.valueOf("nics")); + EnumSet details = EnumSet.copyOf(dc); + if (result != null){ + UserVmResponse response = _responseGenerator.createUserVmResponse("virtualmachine", details, result).get(0); + response.setResponseName(getCommandName()); + this.setResponseObject(response); + } else { + throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, "Failed to add NIC to vm. Refer to server logs for details."); + } + } +} diff --git a/api/src/org/apache/cloudstack/api/command/user/vm/RemoveNicFromVMCmd.java b/api/src/org/apache/cloudstack/api/command/user/vm/RemoveNicFromVMCmd.java new file mode 100644 index 00000000000..b1a870ec8f6 --- /dev/null +++ b/api/src/org/apache/cloudstack/api/command/user/vm/RemoveNicFromVMCmd.java @@ -0,0 +1,115 @@ +// 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.user.vm; + +import java.util.ArrayList; +import java.util.EnumSet; + +import org.apache.log4j.Logger; + +import org.apache.cloudstack.api.*; +import org.apache.cloudstack.api.ApiConstants.VMDetails; +import org.apache.cloudstack.api.response.UserVmResponse; +import org.apache.cloudstack.api.response.NicResponse; +import com.cloud.event.EventTypes; +import com.cloud.exception.ConcurrentOperationException; +import com.cloud.exception.InsufficientCapacityException; +import com.cloud.exception.ResourceAllocationException; +import com.cloud.exception.ResourceUnavailableException; +import com.cloud.user.Account; +import com.cloud.user.UserContext; +import com.cloud.uservm.UserVm; + +@APICommand(name = "removeNicFromVirtualMachine", description="Removes VM from specified network by deleting a NIC", responseObject=UserVmResponse.class) + +public class RemoveNicFromVMCmd extends BaseAsyncCmd { + public static final Logger s_logger = Logger.getLogger(RemoveNicFromVMCmd.class); + private static final String s_name = "removenicfromvirtualmachineresponse"; + + ///////////////////////////////////////////////////// + //////////////// API parameters ///////////////////// + ///////////////////////////////////////////////////// + + @Parameter(name=ApiConstants.VIRTUAL_MACHINE_ID, type=CommandType.UUID, entityType=UserVmResponse.class, + required=true, description="Virtual Machine ID") + private Long vmId; + + @Parameter(name=ApiConstants.NIC_ID, type=CommandType.UUID, entityType=NicResponse.class, + required=true, description="NIC ID") + private Long nicId; + + ///////////////////////////////////////////////////// + /////////////////// Accessors /////////////////////// + ///////////////////////////////////////////////////// + + public Long getVmId() { + return vmId; + } + + public Long getNicId() { + return nicId; + } + + ///////////////////////////////////////////////////// + /////////////// API Implementation/////////////////// + ///////////////////////////////////////////////////// + + @Override + public String getCommandName() { + return s_name; + } + + public static String getResultObjectName() { + return "virtualmachine"; + } + + @Override + public String getEventType() { + return EventTypes.EVENT_NIC_DELETE; + } + + @Override + public String getEventDescription() { + return "Removing NIC " + getNicId() + " from user vm: " + getVmId(); + } + + + @Override + public long getEntityOwnerId() { + UserVm vm = _responseGenerator.findUserVmById(getVmId()); + if (vm == null) { + return Account.ACCOUNT_ID_SYSTEM; // bad id given, parent this command to SYSTEM so ERROR events are tracked + } + return vm.getAccountId(); + } + + @Override + public void execute(){ + UserContext.current().setEventDetails("Vm Id: "+getVmId() + " Nic Id: " + getNicId()); + UserVm result = _userVmService.removeNicFromVirtualMachine(this); + ArrayList dc = new ArrayList(); + dc.add(VMDetails.valueOf("nics")); + EnumSet details = EnumSet.copyOf(dc); + if (result != null){ + UserVmResponse response = _responseGenerator.createUserVmResponse("virtualmachine", details, result).get(0); + response.setResponseName(getCommandName()); + this.setResponseObject(response); + } else { + throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, "Failed to remove NIC from vm, see error log for details"); + } + } +} diff --git a/api/src/org/apache/cloudstack/api/command/user/vm/UpdateDefaultNicForVMCmd.java b/api/src/org/apache/cloudstack/api/command/user/vm/UpdateDefaultNicForVMCmd.java new file mode 100644 index 00000000000..07518c90928 --- /dev/null +++ b/api/src/org/apache/cloudstack/api/command/user/vm/UpdateDefaultNicForVMCmd.java @@ -0,0 +1,115 @@ +// 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.user.vm; + +import java.util.ArrayList; +import java.util.EnumSet; + +import org.apache.log4j.Logger; + +import org.apache.cloudstack.api.*; +import org.apache.cloudstack.api.ApiConstants.VMDetails; +import org.apache.cloudstack.api.response.UserVmResponse; +import org.apache.cloudstack.api.response.NicResponse; +import com.cloud.event.EventTypes; +import com.cloud.exception.ConcurrentOperationException; +import com.cloud.exception.InsufficientCapacityException; +import com.cloud.exception.ResourceAllocationException; +import com.cloud.exception.ResourceUnavailableException; +import com.cloud.user.Account; +import com.cloud.user.UserContext; +import com.cloud.uservm.UserVm; + +@APICommand(name = "updateDefaultNicForVirtualMachine", description="Changes the default NIC on a VM", responseObject=UserVmResponse.class) + +public class UpdateDefaultNicForVMCmd extends BaseAsyncCmd { + public static final Logger s_logger = Logger.getLogger(UpdateDefaultNicForVMCmd.class); + private static final String s_name = "updatedefaultnicforvirtualmachineresponse"; + + ///////////////////////////////////////////////////// + //////////////// API parameters ///////////////////// + ///////////////////////////////////////////////////// + + @Parameter(name=ApiConstants.VIRTUAL_MACHINE_ID, type=CommandType.UUID, entityType=UserVmResponse.class, + required=true, description="Virtual Machine ID") + private Long vmId; + + @Parameter(name=ApiConstants.NIC_ID, type=CommandType.UUID, entityType=NicResponse.class, + required=true, description="NIC ID") + private Long nicId; + + ///////////////////////////////////////////////////// + /////////////////// Accessors /////////////////////// + ///////////////////////////////////////////////////// + + public Long getVmId() { + return vmId; + } + + public Long getNicId() { + return nicId; + } + + ///////////////////////////////////////////////////// + /////////////// API Implementation/////////////////// + ///////////////////////////////////////////////////// + + @Override + public String getCommandName() { + return s_name; + } + + public static String getResultObjectName() { + return "virtualmachine"; + } + + @Override + public String getEventType() { + return EventTypes.EVENT_NIC_UPDATE; + } + + @Override + public String getEventDescription() { + return "Updating NIC " + getNicId() + " on user vm: " + getVmId(); + } + + + @Override + public long getEntityOwnerId() { + UserVm vm = _responseGenerator.findUserVmById(getVmId()); + if (vm == null) { + return Account.ACCOUNT_ID_SYSTEM; // bad id given, parent this command to SYSTEM so ERROR events are tracked + } + return vm.getAccountId(); + } + + @Override + public void execute(){ + UserContext.current().setEventDetails("Vm Id: "+getVmId() + " Nic Id: " + getNicId()); + UserVm result = _userVmService.updateDefaultNicForVirtualMachine(this); + ArrayList dc = new ArrayList(); + dc.add(VMDetails.valueOf("nics")); + EnumSet details = EnumSet.copyOf(dc); + if (result != null){ + UserVmResponse response = _responseGenerator.createUserVmResponse("virtualmachine", details, result).get(0); + response.setResponseName(getCommandName()); + this.setResponseObject(response); + } else { + throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, "Failed to set default nic for VM. Refer to server logs for details."); + } + } +} diff --git a/api/src/org/apache/cloudstack/api/response/NicResponse.java b/api/src/org/apache/cloudstack/api/response/NicResponse.java index a6ca5b8232d..7e200ae1c8b 100644 --- a/api/src/org/apache/cloudstack/api/response/NicResponse.java +++ b/api/src/org/apache/cloudstack/api/response/NicResponse.java @@ -17,11 +17,14 @@ package org.apache.cloudstack.api.response; import org.apache.cloudstack.api.ApiConstants; +import com.cloud.vm.Nic; import com.cloud.serializer.Param; import com.google.gson.annotations.SerializedName; import org.apache.cloudstack.api.BaseResponse; +import org.apache.cloudstack.api.EntityReference; @SuppressWarnings("unused") +@EntityReference(value=Nic.class) public class NicResponse extends BaseResponse { @SerializedName("id") @Param(description="the ID of the nic") diff --git a/client/tomcatconf/commands.properties.in b/client/tomcatconf/commands.properties.in index 3740fb00633..8face84e4c7 100644 --- a/client/tomcatconf/commands.properties.in +++ b/client/tomcatconf/commands.properties.in @@ -320,6 +320,11 @@ listNetworks=15 restartNetwork=15 updateNetwork=15 +#### nic commands #### +addNicToVirtualMachine=15 +removeNicFromVirtualMachine=15 +updateDefaultNicForVirtualMachine=15 + #### SSH key pair commands registerSSHKeyPair=15 createSSHKeyPair=15 diff --git a/server/src/com/cloud/network/NetworkManager.java b/server/src/com/cloud/network/NetworkManager.java index 546f1bf613c..c0065ddd7ac 100755 --- a/server/src/com/cloud/network/NetworkManager.java +++ b/server/src/com/cloud/network/NetworkManager.java @@ -262,7 +262,7 @@ public interface NetworkManager { * @throws InsufficientCapacityException * @throws ResourceUnavailableException */ - NicProfile createNicForVm(Network network, NicProfile requested, ReservationContext context, VirtualMachineProfileImpl vmProfile, boolean prepare) throws InsufficientVirtualNetworkCapcityException, + NicProfile createNicForVm(Network network, NicProfile requested, ReservationContext context, VirtualMachineProfile vmProfile, boolean prepare) throws InsufficientVirtualNetworkCapcityException, InsufficientAddressCapacityException, ConcurrentOperationException, InsufficientCapacityException, ResourceUnavailableException; diff --git a/server/src/com/cloud/network/NetworkManagerImpl.java b/server/src/com/cloud/network/NetworkManagerImpl.java index bb60dcfcdc8..2dd681876fd 100755 --- a/server/src/com/cloud/network/NetworkManagerImpl.java +++ b/server/src/com/cloud/network/NetworkManagerImpl.java @@ -3355,20 +3355,19 @@ public class NetworkManagerImpl implements NetworkManager, Manager, Listener { } @Override - public NicProfile createNicForVm(Network network, NicProfile requested, ReservationContext context, VirtualMachineProfileImpl vmProfile, boolean prepare) + public NicProfile createNicForVm(Network network, NicProfile requested, ReservationContext context, VirtualMachineProfile vmProfile, boolean prepare) throws InsufficientVirtualNetworkCapcityException, InsufficientAddressCapacityException, ConcurrentOperationException, InsufficientCapacityException, ResourceUnavailableException { VirtualMachine vm = vmProfile.getVirtualMachine(); - NetworkVO networkVO = _networksDao.findById(network.getId()); DataCenter dc = _configMgr.getZone(network.getDataCenterId()); Host host = _hostDao.findById(vm.getHostId()); DeployDestination dest = new DeployDestination(dc, null, null, host); NicProfile nic = getNicProfileForVm(network, requested, vm); - //1) allocate nic (if needed) - if (nic == null) { + //1) allocate nic (if needed) Always allocate if it is a user vm + if (nic == null || (vmProfile.getType() == VirtualMachine.Type.User)) { int deviceId = _nicDao.countNics(vm.getId()); nic = allocateNic(requested, network, false, @@ -3383,6 +3382,7 @@ public class NetworkManagerImpl implements NetworkManager, Manager, Listener { //2) prepare nic if (prepare) { + NetworkVO networkVO = _networksDao.findById(network.getId()); nic = prepareNic(vmProfile, dest, context, nic.getId(), networkVO); s_logger.debug("Nic is prepared successfully for vm " + vm + " in network " + network); } diff --git a/server/src/com/cloud/network/NetworkModelImpl.java b/server/src/com/cloud/network/NetworkModelImpl.java index a99e9c50cbe..ce53d6b2f5e 100644 --- a/server/src/com/cloud/network/NetworkModelImpl.java +++ b/server/src/com/cloud/network/NetworkModelImpl.java @@ -1692,6 +1692,9 @@ public class NetworkModelImpl implements NetworkModel, Manager{ } else { nic = _nicDao.findByInstanceIdAndNetworkId(networkId, vm.getId()); } + if (nic == null) { + return null; + } NetworkVO network = _networksDao.findById(networkId); Integer networkRate = getNetworkRate(network.getId(), vm.getId()); @@ -1836,4 +1839,4 @@ public class NetworkModelImpl implements NetworkModel, Manager{ return offering.isInline(); } -} \ No newline at end of file +} diff --git a/server/src/com/cloud/network/element/VirtualRouterElement.java b/server/src/com/cloud/network/element/VirtualRouterElement.java index 2b54ae0fe36..cc64c15fd28 100755 --- a/server/src/com/cloud/network/element/VirtualRouterElement.java +++ b/server/src/com/cloud/network/element/VirtualRouterElement.java @@ -860,8 +860,8 @@ public class VirtualRouterElement extends AdapterBase implements VirtualRouterEl // for Basic zone, add all Running routers - we have to send Dhcp/vmData/password info to them when // network.dns.basiczone.updates is set to "all" - Long podId = dest.getPod().getId(); if (isPodBased && _routerMgr.getDnsBasicZoneUpdate().equalsIgnoreCase("all")) { + Long podId = dest.getPod().getId(); List allRunningRoutersOutsideThePod = _routerDao.findByNetworkOutsideThePod(network.getId(), podId, State.Running, Role.VIRTUAL_ROUTER); routers.addAll(allRunningRoutersOutsideThePod); diff --git a/server/src/com/cloud/vm/UserVmManagerImpl.java b/server/src/com/cloud/vm/UserVmManagerImpl.java index ecf124228d4..82a8e25c5a6 100644 --- a/server/src/com/cloud/vm/UserVmManagerImpl.java +++ b/server/src/com/cloud/vm/UserVmManagerImpl.java @@ -17,11 +17,16 @@ package com.cloud.vm; import com.cloud.agent.AgentManager; +import com.cloud.agent.AgentManager.OnError; import com.cloud.agent.api.*; import com.cloud.agent.api.storage.CreatePrivateTemplateAnswer; import com.cloud.agent.api.to.NicTO; import com.cloud.agent.api.to.VirtualMachineTO; import com.cloud.agent.api.to.VolumeTO; +import com.cloud.agent.api.PlugNicAnswer; +import com.cloud.agent.api.PlugNicCommand; +import com.cloud.agent.api.UnPlugNicAnswer; +import com.cloud.agent.api.UnPlugNicCommand; import com.cloud.agent.manager.Commands; import com.cloud.alert.AlertManager; import com.cloud.api.ApiDBUtils; @@ -916,6 +921,237 @@ public class UserVmManagerImpl implements UserVmManager, UserVmService, Manager return _vmDao.findById(vmInstance.getId()); } + @Override + public UserVm addNicToVirtualMachine(AddNicToVMCmd cmd) throws InvalidParameterValueException, PermissionDeniedException, CloudRuntimeException { + Long vmId = cmd.getVmId(); + Long networkId = cmd.getNetworkId(); + String ipAddress = cmd.getIpAddress(); + Account caller = UserContext.current().getCaller(); + + UserVmVO vmInstance = _vmDao.findById(vmId); + if(vmInstance == null) { + throw new InvalidParameterValueException("unable to find a virtual machine with id " + vmId); + } + NetworkVO network = _networkDao.findById(networkId); + if(network == null) { + throw new InvalidParameterValueException("unable to find a network with id " + networkId); + } + NicProfile profile = new NicProfile(null); + if(ipAddress != null) { + profile = new NicProfile(ipAddress); + } + + // Perform permission check on VM + _accountMgr.checkAccess(caller, null, true, vmInstance); + + // Verify that zone is not Basic + DataCenterVO dc = _dcDao.findById(vmInstance.getDataCenterIdToDeployIn()); + if (dc.getNetworkType() == DataCenter.NetworkType.Basic) { + throw new CloudRuntimeException("Zone " + vmInstance.getDataCenterIdToDeployIn() + ", has a NetworkType of Basic. Can't add a new NIC to a VM on a Basic Network"); + } + + // Perform account permission check on network + if (network.getGuestType() != Network.GuestType.Shared) { + // Check account permissions + List networkMap = _networkDao.listBy(caller.getId(), network.getId()); + if ((networkMap == null || networkMap.isEmpty() ) && caller.getType() != Account.ACCOUNT_TYPE_ADMIN) { + throw new PermissionDeniedException("Unable to modify a vm using network with id " + network.getId() + ", permission denied"); + } + } + + //ensure network belongs in zone + if (network.getDataCenterId() != vmInstance.getDataCenterIdToDeployIn()) { + throw new CloudRuntimeException(vmInstance + " is in zone:" + vmInstance.getDataCenterIdToDeployIn() + " but " + network + " is in zone:" + network.getDataCenterId()); + } + + if(_networkModel.getNicInNetwork(vmInstance.getId(),network.getId()) != null){ + s_logger.debug(vmInstance + " already in " + network + " going to add another NIC"); + } else { + //* get all vms hostNames in the network + List hostNames = _vmInstanceDao.listDistinctHostNames(network.getId()); + //* verify that there are no duplicates + if (hostNames.contains(vmInstance.getHostName())) { + throw new CloudRuntimeException(network + " already has a vm with host name: '" + vmInstance.getHostName()); + } + } + + NicProfile guestNic = null; + + try { + guestNic = _itMgr.addVmToNetwork(vmInstance, network, profile); + } catch (ResourceUnavailableException e) { + throw new CloudRuntimeException("Unable to add NIC to " + vmInstance + ": " + e); + } catch (InsufficientCapacityException e) { + throw new CloudRuntimeException("Insufficient capacity when adding NIC to " + vmInstance + ": " + e); + } catch (ConcurrentOperationException e) { + throw new CloudRuntimeException("Concurrent operations on adding NIC to " + vmInstance + ": " +e); + } + if (guestNic == null) { + throw new CloudRuntimeException("Unable to add NIC to " + vmInstance); + } + + s_logger.debug("Successful addition of " + network + " from " + vmInstance); + return _vmDao.findById(vmInstance.getId()); + } + + @Override + public UserVm removeNicFromVirtualMachine(RemoveNicFromVMCmd cmd) throws InvalidParameterValueException, PermissionDeniedException, CloudRuntimeException { + Long vmId = cmd.getVmId(); + Long nicId = cmd.getNicId(); + Account caller = UserContext.current().getCaller(); + + UserVmVO vmInstance = _vmDao.findById(vmId); + if(vmInstance == null) { + throw new InvalidParameterValueException("unable to find a virtual machine with id " + vmId); + } + NicVO nic = _nicDao.findById(nicId); + if (nic == null){ + throw new InvalidParameterValueException("unable to find a nic with id " + nicId); + } + NetworkVO network = _networkDao.findById(nic.getNetworkId()); + if(network == null) { + throw new InvalidParameterValueException("unable to find a network with id " + nic.getNetworkId()); + } + + // Perform permission check on VM + _accountMgr.checkAccess(caller, null, true, vmInstance); + + // Verify that zone is not Basic + DataCenterVO dc = _dcDao.findById(vmInstance.getDataCenterIdToDeployIn()); + if (dc.getNetworkType() == DataCenter.NetworkType.Basic) { + throw new CloudRuntimeException("Zone " + vmInstance.getDataCenterIdToDeployIn() + ", has a NetworkType of Basic. Can't remove a NIC from a VM on a Basic Network"); + } + + //check to see if nic is attached to VM + if (nic.getInstanceId() != vmId) { + throw new InvalidParameterValueException(nic + " is not a nic on " + vmInstance); + } + + // Perform account permission check on network + if (network.getGuestType() != Network.GuestType.Shared) { + // Check account permissions + List networkMap = _networkDao.listBy(caller.getId(), network.getId()); + if ((networkMap == null || networkMap.isEmpty() ) && caller.getType() != Account.ACCOUNT_TYPE_ADMIN) { + throw new PermissionDeniedException("Unable to modify a vm using network with id " + network.getId() + ", permission denied"); + } + } + + boolean nicremoved = false; + + try { + nicremoved = _itMgr.removeNicFromVm(vmInstance, nic); + } catch (ResourceUnavailableException e) { + throw new CloudRuntimeException("Unable to remove " + network + " from " + vmInstance +": " + e); + + } catch (ConcurrentOperationException e) { + throw new CloudRuntimeException("Concurrent operations on removing " + network + " from " + vmInstance + ": " + e); + } + + if (!nicremoved) { + throw new CloudRuntimeException("Unable to remove " + network + " from " + vmInstance ); + } + + s_logger.debug("Successful removal of " + network + " from " + vmInstance); + return _vmDao.findById(vmInstance.getId()); + + + } + + @Override + public UserVm updateDefaultNicForVirtualMachine(UpdateDefaultNicForVMCmd cmd) throws InvalidParameterValueException, CloudRuntimeException { + Long vmId = cmd.getVmId(); + Long nicId = cmd.getNicId(); + Account caller = UserContext.current().getCaller(); + + UserVmVO vmInstance = _vmDao.findById(vmId); + if (vmInstance == null){ + throw new InvalidParameterValueException("unable to find a virtual machine with id " + vmId); + } + NicVO nic = _nicDao.findById(nicId); + if (nic == null){ + throw new InvalidParameterValueException("unable to find a nic with id " + nicId); + } + NetworkVO network = _networkDao.findById(nic.getNetworkId()); + if (network == null){ + throw new InvalidParameterValueException("unable to find a network with id " + nic.getNetworkId()); + } + + // Perform permission check on VM + _accountMgr.checkAccess(caller, null, true, vmInstance); + + // Verify that zone is not Basic + DataCenterVO dc = _dcDao.findById(vmInstance.getDataCenterIdToDeployIn()); + if (dc.getNetworkType() == DataCenter.NetworkType.Basic) { + throw new CloudRuntimeException("Zone " + vmInstance.getDataCenterIdToDeployIn() + ", has a NetworkType of Basic. Can't change default NIC on a Basic Network"); + } + + // no need to check permissions for network, we'll enumerate the ones they already have access to + Network existingdefaultnet = _networkModel.getDefaultNetworkForVm(vmId); + + //check to see if nic is attached to VM + if (nic.getInstanceId() != vmId) { + throw new InvalidParameterValueException(nic + " is not a nic on " + vmInstance); + } + // if current default equals chosen new default, Throw an exception + if (nic.isDefaultNic()){ + throw new CloudRuntimeException("refusing to set default nic because chosen nic is already the default"); + } + + //make sure the VM is Running or Stopped + if ((vmInstance.getState() != State.Running) && (vmInstance.getState() != State.Stopped)) { + throw new CloudRuntimeException("refusing to set default " + vmInstance + " is not Running or Stopped"); + } + + NicProfile existing = null; + List nicProfiles = _networkMgr.getNicProfiles(vmInstance); + for (NicProfile nicProfile : nicProfiles) { + if(nicProfile.isDefaultNic() && nicProfile.getNetworkId() == existingdefaultnet.getId()){ + existing = nicProfile; + continue; + } + } + + if (existing == null){ + s_logger.warn("Failed to update default nic, no nic profile found for existing default network"); + throw new CloudRuntimeException("Failed to find a nic profile for the existing default network. This is bad and probably means some sort of configuration corruption"); + } + + NicVO existingVO = _nicDao.findById(existing.id); + Integer chosenID = nic.getDeviceId(); + Integer existingID = existing.getDeviceId(); + + nic.setDefaultNic(true); + nic.setDeviceId(existingID); + existingVO.setDefaultNic(false); + existingVO.setDeviceId(chosenID); + + nic = _nicDao.persist(nic); + existingVO = _nicDao.persist(existingVO); + + Network newdefault = null; + newdefault = _networkModel.getDefaultNetworkForVm(vmId); + + if (newdefault == null){ + nic.setDefaultNic(false); + nic.setDeviceId(chosenID); + existingVO.setDefaultNic(true); + existingVO.setDeviceId(existingID); + + nic = _nicDao.persist(nic); + existingVO = _nicDao.persist(existingVO); + + newdefault = _networkModel.getDefaultNetworkForVm(vmId); + if (newdefault.getId() == existingdefaultnet.getId()) { + throw new CloudRuntimeException("Setting a default nic failed, and we had no default nic, but we were able to set it back to the original"); + } + throw new CloudRuntimeException("Failed to change default nic to " + nic + " and now we have no default"); + } else if (newdefault.getId() == nic.getNetworkId()) { + s_logger.debug("successfully set default network to " + network + " for " + vmInstance); + return _vmDao.findById(vmInstance.getId()); + } + + throw new CloudRuntimeException("something strange happened, new default network(" + newdefault.getId() + ") is not null, and is not equal to the network(" + nic.getNetworkId() + ") of the chosen nic"); + } @Override public HashMap getVirtualMachineStatistics(long hostId, String hostName, List vmIds) throws CloudRuntimeException { @@ -3671,16 +3907,58 @@ public class UserVmManagerImpl implements UserVmManager, UserVmService, Manager public boolean plugNic(Network network, NicTO nic, VirtualMachineTO vm, ReservationContext context, DeployDestination dest) throws ConcurrentOperationException, ResourceUnavailableException, InsufficientCapacityException { - //not supported - throw new UnsupportedOperationException("Plug nic is not supported for vm of type " + vm.getType()); + UserVmVO vmVO = _vmDao.findById(vm.getId()); + if (vmVO.getState() == State.Running) { + try { + PlugNicCommand plugNicCmd = new PlugNicCommand(nic,vm.getName()); + Commands cmds = new Commands(OnError.Stop); + cmds.addCommand("plugnic",plugNicCmd); + _agentMgr.send(dest.getHost().getId(),cmds); + PlugNicAnswer plugNicAnswer = cmds.getAnswer(PlugNicAnswer.class); + if (!(plugNicAnswer != null && plugNicAnswer.getResult())) { + s_logger.warn("Unable to plug nic for " + vmVO); + return false; + } + } catch (OperationTimedoutException e) { + throw new AgentUnavailableException("Unable to plug nic for " + vmVO + " in network " + network, dest.getHost().getId(), e); + } + } else if (vmVO.getState() == State.Stopped || vmVO.getState() == State.Stopping) { + s_logger.warn(vmVO + " is Stopped, not sending PlugNicCommand. Currently " + vmVO.getState()); + } else { + s_logger.warn("Unable to plug nic, " + vmVO + " is not in the right state " + vmVO.getState()); + throw new ResourceUnavailableException("Unable to plug nic on the backend," + + vmVO + " is not in the right state", DataCenter.class, vmVO.getDataCenterIdToDeployIn()); + } + return true; } @Override public boolean unplugNic(Network network, NicTO nic, VirtualMachineTO vm, ReservationContext context, DeployDestination dest) throws ConcurrentOperationException, ResourceUnavailableException { - //not supported - throw new UnsupportedOperationException("Unplug nic is not supported for vm of type " + vm.getType()); + UserVmVO vmVO = _vmDao.findById(vm.getId()); + if (vmVO.getState() == State.Running) { + try { + UnPlugNicCommand unplugNicCmd = new UnPlugNicCommand(nic,vm.getName()); + Commands cmds = new Commands(OnError.Stop); + cmds.addCommand("unplugnic",unplugNicCmd); + _agentMgr.send(dest.getHost().getId(),cmds); + UnPlugNicAnswer unplugNicAnswer = cmds.getAnswer(UnPlugNicAnswer.class); + if (!(unplugNicAnswer != null && unplugNicAnswer.getResult())) { + s_logger.warn("Unable to unplug nic for " + vmVO); + return false; + } + } catch (OperationTimedoutException e) { + throw new AgentUnavailableException("Unable to unplug nic for " + vmVO + " in network " + network, dest.getHost().getId(), e); + } + } else if (vmVO.getState() == State.Stopped || vmVO.getState() == State.Stopping) { + s_logger.warn(vmVO + " is Stopped, not sending UnPlugNicCommand. Currently " + vmVO.getState()); + } else { + s_logger.warn("Unable to unplug nic, " + vmVO + " is not in the right state " + vmVO.getState()); + throw new ResourceUnavailableException("Unable to unplug nic on the backend," + + vmVO + " is not in the right state", DataCenter.class, vmVO.getDataCenterIdToDeployIn()); + } + return true; } @Override diff --git a/server/src/com/cloud/vm/VirtualMachineManager.java b/server/src/com/cloud/vm/VirtualMachineManager.java index 4f04617baba..53bd442f33b 100644 --- a/server/src/com/cloud/vm/VirtualMachineManager.java +++ b/server/src/com/cloud/vm/VirtualMachineManager.java @@ -152,6 +152,15 @@ public interface VirtualMachineManager extends Manager { NicProfile addVmToNetwork(VirtualMachine vm, Network network, NicProfile requested) throws ConcurrentOperationException, ResourceUnavailableException, InsufficientCapacityException; + /** + * @param vm + * @param nic + * @return + * @throws ResourceUnavailableException + * @throws ConcurrentOperationException + */ + boolean removeNicFromVm(VirtualMachine vm, NicVO nic) throws ConcurrentOperationException, ResourceUnavailableException; + /** * @param vm * @param network diff --git a/server/src/com/cloud/vm/VirtualMachineManagerImpl.java b/server/src/com/cloud/vm/VirtualMachineManagerImpl.java index e0647bbe67f..19756addd5f 100755 --- a/server/src/com/cloud/vm/VirtualMachineManagerImpl.java +++ b/server/src/com/cloud/vm/VirtualMachineManagerImpl.java @@ -2462,7 +2462,12 @@ public class VirtualMachineManagerImpl implements VirtualMachineManager, Listene ResourceUnavailableException, InsufficientCapacityException { s_logger.debug("Adding vm " + vm + " to network " + network + "; requested nic profile " + requested); - VMInstanceVO vmVO = _vmDao.findById(vm.getId()); + VMInstanceVO vmVO; + if (vm.getType() == VirtualMachine.Type.User) { + vmVO = _userVmDao.findById(vm.getId()); + } else { + vmVO = _vmDao.findById(vm.getId()); + } ReservationContext context = new ReservationContextImpl(null, null, _accountMgr.getActiveUser(User.UID_SYSTEM), _accountMgr.getAccount(Account.ACCOUNT_ID_SYSTEM)); @@ -2506,7 +2511,6 @@ public class VirtualMachineManagerImpl implements VirtualMachineManager, Listene } } - @Override public NicTO toNicTO(NicProfile nic, HypervisorType hypervisorType) { HypervisorGuru hvGuru = _hvGuruMgr.getGuru(hypervisorType); @@ -2515,6 +2519,61 @@ public class VirtualMachineManagerImpl implements VirtualMachineManager, Listene return nicTO; } + @Override + public boolean removeNicFromVm(VirtualMachine vm, NicVO nic) throws ConcurrentOperationException, ResourceUnavailableException { + VMInstanceVO vmVO = _vmDao.findById(vm.getId()); + NetworkVO network = _networkDao.findById(nic.getNetworkId()); + ReservationContext context = new ReservationContextImpl(null, null, _accountMgr.getActiveUser(User.UID_SYSTEM), + _accountMgr.getAccount(Account.ACCOUNT_ID_SYSTEM)); + + VirtualMachineProfileImpl vmProfile = new VirtualMachineProfileImpl(vmVO, null, + null, null, null); + + DataCenter dc = _configMgr.getZone(network.getDataCenterId()); + Host host = _hostDao.findById(vm.getHostId()); + DeployDestination dest = new DeployDestination(dc, null, null, host); + VirtualMachineGuru vmGuru = getVmGuru(vmVO); + HypervisorGuru hvGuru = _hvGuruMgr.getGuru(vmProfile.getVirtualMachine().getHypervisorType()); + VirtualMachineTO vmTO = hvGuru.implement(vmProfile); + + // don't delete default NIC on a user VM + if (nic.isDefaultNic() && vm.getType() == VirtualMachine.Type.User ) { + s_logger.warn("Failed to remove nic from " + vm + " in " + network + ", nic is default."); + throw new CloudRuntimeException("Failed to remove nic from " + vm + " in " + network + ", nic is default."); + } + + NicProfile nicProfile = new NicProfile(nic, network, nic.getBroadcastUri(), nic.getIsolationUri(), + _networkModel.getNetworkRate(network.getId(), vm.getId()), + _networkModel.isSecurityGroupSupportedInNetwork(network), + _networkModel.getNetworkTag(vmProfile.getVirtualMachine().getHypervisorType(), network)); + + //1) Unplug the nic + if (vm.getState() == State.Running) { + NicTO nicTO = toNicTO(nicProfile, vmProfile.getVirtualMachine().getHypervisorType()); + s_logger.debug("Un-plugging nic " + nic + " for vm " + vm + " from network " + network); + boolean result = vmGuru.unplugNic(network, nicTO, vmTO, context, dest); + if (result) { + s_logger.debug("Nic is unplugged successfully for vm " + vm + " in network " + network ); + } else { + s_logger.warn("Failed to unplug nic for the vm " + vm + " from network " + network); + return false; + } + } else if (vm.getState() != State.Stopped) { + s_logger.warn("Unable to remove vm " + vm + " from network " + network); + throw new ResourceUnavailableException("Unable to remove vm " + vm + " from network, is not in the right state", + DataCenter.class, vm.getDataCenterIdToDeployIn()); + } + + //2) Release the nic + _networkMgr.releaseNic(vmProfile, nic); + s_logger.debug("Successfully released nic " + nic + "for vm " + vm); + + //3) Remove the nic + _networkMgr.removeNic(vmProfile, nic); + _nicsDao.expunge(nic.getId()); + return true; + } + @Override public boolean removeVmFromNetwork(VirtualMachine vm, Network network, URI broadcastUri) throws ConcurrentOperationException, ResourceUnavailableException { VMInstanceVO vmVO = _vmDao.findById(vm.getId()); @@ -2538,21 +2597,38 @@ public class VirtualMachineManagerImpl implements VirtualMachineManager, Listene } else { nic = _networkModel.getNicInNetwork(vm.getId(), network.getId()); } + + if (nic == null){ + s_logger.warn("Could not get a nic with " + network); + return false; + } + // don't delete default NIC on a user VM + if (nic.isDefaultNic() && vm.getType() == VirtualMachine.Type.User ) { + s_logger.warn("Failed to remove nic from " + vm + " in " + network + ", nic is default."); + throw new CloudRuntimeException("Failed to remove nic from " + vm + " in " + network + ", nic is default."); + } + NicProfile nicProfile = new NicProfile(nic, network, nic.getBroadcastUri(), nic.getIsolationUri(), _networkModel.getNetworkRate(network.getId(), vm.getId()), _networkModel.isSecurityGroupSupportedInNetwork(network), _networkModel.getNetworkTag(vmProfile.getVirtualMachine().getHypervisorType(), network)); //1) Unplug the nic - NicTO nicTO = toNicTO(nicProfile, vmProfile.getVirtualMachine().getHypervisorType()); - s_logger.debug("Un-plugging nic for vm " + vm + " from network " + network); - boolean result = vmGuru.unplugNic(network, nicTO, vmTO, context, dest); - if (result) { - s_logger.debug("Nic is unplugged successfully for vm " + vm + " in network " + network ); - } else { - s_logger.warn("Failed to unplug nic for the vm " + vm + " from network " + network); - return false; + if (vm.getState() == State.Running) { + NicTO nicTO = toNicTO(nicProfile, vmProfile.getVirtualMachine().getHypervisorType()); + s_logger.debug("Un-plugging nic for vm " + vm + " from network " + network); + boolean result = vmGuru.unplugNic(network, nicTO, vmTO, context, dest); + if (result) { + s_logger.debug("Nic is unplugged successfully for vm " + vm + " in network " + network ); + } else { + s_logger.warn("Failed to unplug nic for the vm " + vm + " from network " + network); + return false; + } + } else if (vm.getState() != State.Stopped) { + s_logger.warn("Unable to remove vm " + vm + " from network " + network); + throw new ResourceUnavailableException("Unable to remove vm " + vm + " from network, is not in the right state", + DataCenter.class, vm.getDataCenterIdToDeployIn()); } //2) Release the nic @@ -2561,7 +2637,7 @@ public class VirtualMachineManagerImpl implements VirtualMachineManager, Listene //3) Remove the nic _networkMgr.removeNic(vmProfile, nic); - return result; + return true; } } diff --git a/server/test/com/cloud/network/MockNetworkManagerImpl.java b/server/test/com/cloud/network/MockNetworkManagerImpl.java index ef5b9c9f9b7..c9446bb49ab 100755 --- a/server/test/com/cloud/network/MockNetworkManagerImpl.java +++ b/server/test/com/cloud/network/MockNetworkManagerImpl.java @@ -733,7 +733,7 @@ public class MockNetworkManagerImpl implements NetworkManager, Manager, NetworkS */ @Override public NicProfile createNicForVm(Network network, NicProfile requested, ReservationContext context, - VirtualMachineProfileImpl vmProfile, boolean prepare) + VirtualMachineProfile vmProfile, boolean prepare) throws InsufficientVirtualNetworkCapcityException, InsufficientAddressCapacityException, ConcurrentOperationException, InsufficientCapacityException, ResourceUnavailableException { // TODO Auto-generated method stub diff --git a/server/test/com/cloud/vm/MockUserVmManagerImpl.java b/server/test/com/cloud/vm/MockUserVmManagerImpl.java index 27508b14c5b..f424e65e5ff 100644 --- a/server/test/com/cloud/vm/MockUserVmManagerImpl.java +++ b/server/test/com/cloud/vm/MockUserVmManagerImpl.java @@ -55,6 +55,8 @@ import com.cloud.exception.ResourceAllocationException; import com.cloud.exception.ResourceUnavailableException; import com.cloud.exception.StorageUnavailableException; import com.cloud.exception.VirtualMachineMigrationException; +import com.cloud.exception.InvalidParameterValueException; +import com.cloud.exception.PermissionDeniedException; import com.cloud.host.Host; import com.cloud.hypervisor.Hypervisor.HypervisorType; import com.cloud.network.Network; @@ -69,6 +71,7 @@ import com.cloud.uservm.UserVm; import com.cloud.utils.Pair; import com.cloud.utils.component.Manager; import com.cloud.utils.exception.ExecutionException; +import com.cloud.utils.exception.CloudRuntimeException; @Local(value = { UserVmManager.class, UserVmService.class }) public class MockUserVmManagerImpl implements UserVmManager, UserVmService, Manager { @@ -276,6 +279,24 @@ public class MockUserVmManagerImpl implements UserVmManager, UserVmService, Mana return null; } + @Override + public UserVm addNicToVirtualMachine(AddNicToVMCmd cmd) throws InvalidParameterValueException, PermissionDeniedException, CloudRuntimeException { + // TODO Auto-generated method stub + return null; + } + + @Override + public UserVm removeNicFromVirtualMachine(RemoveNicFromVMCmd cmd) throws InvalidParameterValueException, PermissionDeniedException, CloudRuntimeException { + // TODO Auto-generated method stub + return null; + } + + @Override + public UserVm updateDefaultNicForVirtualMachine(UpdateDefaultNicForVMCmd cmd) throws InvalidParameterValueException, CloudRuntimeException { + // TODO Auto-generated method stub + return null; + } + @Override public UserVm recoverVirtualMachine(RecoverVMCmd cmd) throws ResourceAllocationException { // TODO Auto-generated method stub diff --git a/server/test/com/cloud/vm/MockVirtualMachineManagerImpl.java b/server/test/com/cloud/vm/MockVirtualMachineManagerImpl.java index 6723198b81a..862bb251cb0 100755 --- a/server/test/com/cloud/vm/MockVirtualMachineManagerImpl.java +++ b/server/test/com/cloud/vm/MockVirtualMachineManagerImpl.java @@ -270,6 +270,15 @@ public class MockVirtualMachineManagerImpl implements VirtualMachineManager { return null; } + /* (non-Javadoc) + * @see com.cloud.vm.VirtualMachineManager#removeVmFromNetwork(com.cloud.vm.VirtualMachine, com.cloud.network.Network, java.net.URI) + */ + @Override + public boolean removeNicFromVm(VirtualMachine vm, NicVO nic) throws ConcurrentOperationException, ResourceUnavailableException { + // TODO Auto-generated method stub + return false; + } + /* (non-Javadoc) * @see com.cloud.vm.VirtualMachineManager#removeVmFromNetwork(com.cloud.vm.VirtualMachine, com.cloud.network.Network, java.net.URI) */ diff --git a/server/test/com/cloud/vpc/MockNetworkManagerImpl.java b/server/test/com/cloud/vpc/MockNetworkManagerImpl.java index b4e17948a43..a5a993836d1 100644 --- a/server/test/com/cloud/vpc/MockNetworkManagerImpl.java +++ b/server/test/com/cloud/vpc/MockNetworkManagerImpl.java @@ -1123,11 +1123,11 @@ public class MockNetworkManagerImpl implements NetworkManager, NetworkService, M /* (non-Javadoc) - * @see com.cloud.network.NetworkManager#createNicForVm(com.cloud.network.Network, com.cloud.vm.NicProfile, com.cloud.vm.ReservationContext, com.cloud.vm.VirtualMachineProfileImpl, boolean) + * @see com.cloud.network.NetworkManager#createNicForVm(com.cloud.network.Network, com.cloud.vm.NicProfile, com.cloud.vm.ReservationContext, com.cloud.vm.VirtualMachineProfileImpl, boolean, boolean) */ @Override public NicProfile createNicForVm(Network network, NicProfile requested, ReservationContext context, - VirtualMachineProfileImpl vmProfile, boolean prepare) + VirtualMachineProfile vmProfile, boolean prepare) throws InsufficientVirtualNetworkCapcityException, InsufficientAddressCapacityException, ConcurrentOperationException, InsufficientCapacityException, ResourceUnavailableException { // TODO Auto-generated method stub diff --git a/test/integration/smoke/test_nic.py b/test/integration/smoke/test_nic.py new file mode 100644 index 00000000000..6cf73acd593 --- /dev/null +++ b/test/integration/smoke/test_nic.py @@ -0,0 +1,335 @@ +""" NIC tests for VM """ +import marvin +from marvin.cloudstackTestCase import * +from marvin.cloudstackAPI import * +from marvin.remoteSSHClient import remoteSSHClient +from marvin.integration.lib.utils import * +from marvin.integration.lib.base import * +from marvin.integration.lib.common import * +from nose.plugins.attrib import attr + +import signal +import sys +import time + +class Services: + def __init__(self): + self.services = { + "disk_offering":{ + "displaytext": "Small", + "name": "Small", + "disksize": 1 + }, + "account": { + "email": "test@test.com", + "firstname": "Test", + "lastname": "User", + "username": "test", + # Random characters are appended in create account to + # ensure unique username generated each time + "password": "password", + }, + # Create a small virtual machine instance with disk offering + "small": { + "displayname": "testserver", + "username": "root", # VM creds for SSH + "password": "password", + "ssh_port": 22, + "hypervisor": 'XenServer', + "privateport": 22, + "publicport": 22, + "protocol": 'TCP', + }, + "service_offerings": { + "tiny": { + "name": "Tiny Instance", + "displaytext": "Tiny Instance", + "cpunumber": 1, + "cpuspeed": 100, # in MHz + "memory": 128, # In MBs + }, + }, + "network_offering": { + "name": 'Test Network offering', + "displaytext": 'Test Network offering', + "guestiptype": 'Isolated', + "supportedservices": 'Dhcp,Dns,SourceNat,PortForwarding', + "traffictype": 'GUEST', + "availability": 'Optional', + "serviceProviderList" : { + "Dhcp": 'VirtualRouter', + "Dns": 'VirtualRouter', + "SourceNat": 'VirtualRouter', + "PortForwarding": 'VirtualRouter', + }, + }, + "network": { + "name": "Test Network", + "displaytext": "Test Network", + "acltype": "Account", + }, + # ISO settings for Attach/Detach ISO tests + "iso": { + "displaytext": "Test ISO", + "name": "testISO", + "url": "http://iso.linuxquestions.org/download/504/1819/http/gd4.tuwien.ac.at/dsl-4.4.10.iso", + # Source URL where ISO is located + "ostype": 'CentOS 5.3 (64-bit)', + "mode": 'HTTP_DOWNLOAD', # Downloading existing ISO + }, + "template": { + "displaytext": "Cent OS Template", + "name": "Cent OS Template", + "passwordenabled": True, + }, + "diskdevice": '/dev/xvdd', + # Disk device where ISO is attached to instance + "mount_dir": "/mnt/tmp", + "sleep": 60, + "timeout": 10, + #Migrate VM to hostid + "ostype": 'CentOS 5.3 (64-bit)', + # CentOS 5.3 (64-bit) + } + +class TestDeployVM(cloudstackTestCase): + + def setUp(self): + self.cleanup = [] + self.cleaning_up = 0 + + def signal_handler(signal, frame): + self.tearDown() + sys.exit(0) + + # assign the signal handler immediately + signal.signal(signal.SIGINT, signal_handler) + + try: + self.apiclient = self.testClient.getApiClient() + self.dbclient = self.testClient.getDbConnection() + self.services = Services().services + + # Get Zone, Domain and templates + domain = get_domain(self.apiclient, self.services) + zone = get_zone(self.apiclient, self.services) + self.services['mode'] = zone.networktype + + if self.services['mode'] != 'Advanced': + self.debug("Cannot run this test with a basic zone, please use advanced!") + return + + #if local storage is enabled, alter the offerings to use localstorage + #this step is needed for devcloud + if zone.localstorageenabled == True: + self.services["service_offerings"]["tiny"]["storagetype"] = 'local' + + template = get_template( + self.apiclient, + zone.id, + self.services["ostype"] + ) + # Set Zones and disk offerings + self.services["small"]["zoneid"] = zone.id + self.services["small"]["template"] = template.id + + self.services["iso"]["zoneid"] = zone.id + self.services["network"]["zoneid"] = zone.id + + # Create Account, VMs, NAT Rules etc + self.account = Account.create( + self.apiclient, + self.services["account"], + domainid=domain.id + ) + self.cleanup.insert(0, self.account) + + self.service_offering = ServiceOffering.create( + self.apiclient, + self.services["service_offerings"]["tiny"] + ) + self.cleanup.insert(0, self.service_offering) + + #################### + ### Network offering + self.network_offering = NetworkOffering.create( + self.apiclient, + self.services["network_offering"], + ) + self.cleanup.insert(0, self.network_offering) + self.network_offering.update(self.apiclient, state='Enabled') # Enable Network offering + self.services["network"]["networkoffering"] = self.network_offering.id + + ################ + ### Test Network + self.test_network = Network.create( + self.apiclient, + self.services["network"], + self.account.account.name, + self.account.account.domainid, + ) + self.cleanup.insert(0, self.test_network) + except Exception as ex: + self.debug("Exception during NIC test SETUP!: " + str(ex)) + self.assertEqual(True, False, "Exception during NIC test SETUP!: " + str(ex)) + + @attr(tags = ["devcloud", "advanced", "advancedns", "smoke", "basic", "sg"]) + def test_01_nic(self): + if self.services['mode'] != 'Advanced': + self.debug("Cannot run this test with a basic zone, please use advanced!") + return + try: + self.virtual_machine = VirtualMachine.create( + self.apiclient, + self.services["small"], + accountid=self.account.account.name, + domainid=self.account.account.domainid, + serviceofferingid=self.service_offering.id, + mode=self.services['mode'] + ) + self.cleanup.insert(0, self.virtual_machine) + + list_vm_response = list_virtual_machines( + self.apiclient, + id=self.virtual_machine.id + ) + + self.debug( + "Verify listVirtualMachines response for virtual machine: %s" \ + % self.virtual_machine.id + ) + + self.assertEqual( + isinstance(list_vm_response, list), + True, + "Check list response returns a valid list" + ) + + self.assertNotEqual( + len(list_vm_response), + 0, + "Check VM available in List Virtual Machines" + ) + vm_response = list_vm_response[0] + + self.assertEqual( + + vm_response.id, + self.virtual_machine.id, + "Check virtual machine id in listVirtualMachines" + ) + + self.assertEqual( + vm_response.name, + self.virtual_machine.name, + "Check virtual machine name in listVirtualMachines" + ) + + self.assertEqual( + len(vm_response.nic), + 1, + "Verify we only start with one nic" + ) + + self.assertEqual( + vm_response.nic[0].isdefault, + True, + "Verify initial adapter is set to default" + ) + existing_nic_ip = vm_response.nic[0].ipaddress + existing_nic_id = vm_response.nic[0].id + + # 1. add a nic + add_response = self.virtual_machine.add_nic(self.apiclient, self.test_network.id) + + time.sleep(5) + # now go get the vm list? + + list_vm_response = list_virtual_machines( + self.apiclient, + id=self.virtual_machine.id + ) + + self.assertEqual( + len(list_vm_response[0].nic), + 2, + "Verify we have 2 NIC's now" + ) + + new_nic_id = "" + for nc in list_vm_response[0].nic: + if nc.ipaddress != existing_nic_ip: + new_nic_id = nc.id + + self.virtual_machine.update_default_nic(self.apiclient, new_nic_id) + + time.sleep(5) + + list_vm_response = list_virtual_machines( + self.apiclient, + id=self.virtual_machine.id + ) + + # iterate as we don't know for sure what order our NIC's will be returned to us. + for nc in list_vm_response[0].nic: + if nc.ipaddress == existing_nic_ip: + self.assertEqual( + nc.isdefault, + False, + "Verify initial adapter is NOT set to default" + ) + else: + self.assertEqual( + nc.isdefault, + True, + "Verify second adapter is set to default" + ) + + sawException = False + try: + self.virtual_machine.remove_nic(self.apiclient, new_nic_id) + except Exception as ex: + sawException = True + + self.assertEqual(sawException, True, "Make sure we cannot delete the default NIC") + + self.virtual_machine.remove_nic(self.apiclient, existing_nic_id) + time.sleep(5) + + list_vm_response = list_virtual_machines( + self.apiclient, + id=self.virtual_machine.id + ) + + self.assertEqual( + len(list_vm_response[0].nic), + 1, + "Verify we are back to a signle NIC" + ) + + return + except Exception as ex: + self.debug("Exception during NIC test!: " + str(ex)) + self.assertEqual(True, False, "Exception during NIC test!: " + str(ex)) + + def tearDown(self): + if self.services['mode'] != 'Advanced': + self.debug("Cannot run this test with a basic zone, please use advanced!") + return + + if self.cleaning_up == 1: + return + + self.cleaning_up = 1 + try: + for obj in self.cleanup: + try: + obj.delete(self.apiclient) + time.sleep(10) + except Exception as ex: + self.debug("Error deleting: " + str(obj) + ", exception: " + str(ex)) + + except Exception as e: + self.debug("Warning! Exception in tearDown: %s" % e) + self.cleaning_up = 0 + diff --git a/tools/marvin/marvin/integration/lib/base.py b/tools/marvin/marvin/integration/lib/base.py index 87b0bbb7bbc..830914c0b4c 100644 --- a/tools/marvin/marvin/integration/lib/base.py +++ b/tools/marvin/marvin/integration/lib/base.py @@ -389,6 +389,27 @@ class VirtualMachine: cmd.id = volume.id return apiclient.detachVolume(cmd) + def add_nic(self, apiclient, networkId): + """Add a NIC to a VM""" + cmd = addNicToVirtualMachine.addNicToVirtualMachineCmd(); + cmd.virtualmachineid = self.id + cmd.networkid = networkId + return apiclient.addNicToVirtualMachine(cmd) + + def remove_nic(self, apiclient, nicId): + """Remove a NIC to a VM""" + cmd = removeNicFromVirtualMachine.removeNicFromVirtualMachineCmd() + cmd.nicid = nicId + cmd.virtualmachineid = self.id + return apiclient.removeNicFromVirtualMachine(cmd) + + def update_default_nic(self, apiclient, nicId): + """Set a NIC to be the default network adapter for a VM""" + cmd = updateDefaultNicForVirtualMachine.updateDefaultNicForVirtualMachineCmd() + cmd.nicid = nicId + cmd.virtualmachineid = self.id + return apiclient.updateDefaultNicForVirtualMachine(cmd) + @classmethod def list(cls, apiclient, **kwargs): """List all VMs matching criteria"""