diff --git a/api/src/com/cloud/network/IpAddress.java b/api/src/com/cloud/network/IpAddress.java index 47df4d6523b..fce8f38c2f2 100644 --- a/api/src/com/cloud/network/IpAddress.java +++ b/api/src/com/cloud/network/IpAddress.java @@ -87,4 +87,7 @@ public interface IpAddress extends ControlledEntity, Identity, InternalIdentity * @param vpcId */ void setVpcId(Long vpcId); + String getVmIp(); + void setVmIp(String vmIp); + } diff --git a/api/src/com/cloud/network/NetworkService.java b/api/src/com/cloud/network/NetworkService.java index 95bcc42b17a..ab6d7bfd882 100755 --- a/api/src/com/cloud/network/NetworkService.java +++ b/api/src/com/cloud/network/NetworkService.java @@ -19,9 +19,10 @@ package com.cloud.network; import java.util.List; import org.apache.cloudstack.api.command.admin.usage.ListTrafficTypeImplementorsCmd; +import org.apache.cloudstack.api.command.user.network.RestartNetworkCmd; import org.apache.cloudstack.api.command.user.network.CreateNetworkCmd; import org.apache.cloudstack.api.command.user.network.ListNetworksCmd; -import org.apache.cloudstack.api.command.user.network.RestartNetworkCmd; +import org.apache.cloudstack.api.command.user.vm.ListNicsCmd; import com.cloud.exception.ConcurrentOperationException; import com.cloud.exception.InsufficientAddressCapacityException; @@ -33,6 +34,8 @@ import com.cloud.network.Networks.TrafficType; import com.cloud.user.Account; import com.cloud.user.User; import com.cloud.utils.Pair; +import com.cloud.vm.Nic; +import com.cloud.vm.NicSecondaryIp; /** * The NetworkService interface is the "public" api to entities that make requests to the orchestration engine @@ -153,5 +156,13 @@ public interface NetworkService { Network createPrivateNetwork(String networkName, String displayText, long physicalNetworkId, String vlan, String startIp, String endIP, String gateway, String netmask, long networkOwnerId, Long vpcId) throws ResourceAllocationException, ConcurrentOperationException, InsufficientCapacityException; - + + /* Requests an IP address for the guest nic */ + String allocateSecondaryGuestIP(Account account, long zoneId, Long nicId, + Long networkId, String ipaddress) throws InsufficientAddressCapacityException; + + boolean releaseSecondaryIpFromNic(long ipAddressId); + + /* lists the nic informaton */ + List listNics(ListNicsCmd listNicsCmd); } diff --git a/api/src/com/cloud/network/rules/RulesService.java b/api/src/com/cloud/network/rules/RulesService.java index 921a86e865f..d47b38f9d43 100644 --- a/api/src/com/cloud/network/rules/RulesService.java +++ b/api/src/com/cloud/network/rules/RulesService.java @@ -25,6 +25,7 @@ import com.cloud.exception.NetworkRuleConflictException; import com.cloud.exception.ResourceUnavailableException; import com.cloud.user.Account; import com.cloud.utils.Pair; +import com.cloud.utils.net.Ip; public interface RulesService { Pair, Integer> searchStaticNatRules(Long ipId, Long id, Long vmId, Long start, Long size, String accountName, Long domainId, Long projectId, boolean isRecursive, boolean listAll); @@ -43,7 +44,7 @@ public interface RulesService { * @throws NetworkRuleConflictException * if conflicts in the network rules are detected. */ - PortForwardingRule createPortForwardingRule(PortForwardingRule rule, Long vmId, boolean openFirewall) throws NetworkRuleConflictException; + PortForwardingRule createPortForwardingRule(PortForwardingRule rule, Long vmId, Ip vmIp, boolean openFirewall) throws NetworkRuleConflictException; /** * Revokes a port forwarding rule @@ -66,7 +67,7 @@ public interface RulesService { boolean applyPortForwardingRules(long ipAdddressId, Account caller) throws ResourceUnavailableException; - boolean enableStaticNat(long ipAddressId, long vmId, long networkId, boolean isSystemVm) throws NetworkRuleConflictException, ResourceUnavailableException; + boolean enableStaticNat(long ipAddressId, long vmId, long networkId, boolean isSystemVm, String vmGuestIp) throws NetworkRuleConflictException, ResourceUnavailableException; PortForwardingRule getPortForwardigRule(long ruleId); diff --git a/api/src/com/cloud/vm/Nic.java b/api/src/com/cloud/vm/Nic.java index 9d21130327a..b2f6976240c 100644 --- a/api/src/com/cloud/vm/Nic.java +++ b/api/src/com/cloud/vm/Nic.java @@ -151,4 +151,5 @@ public interface Nic extends Identity, InternalIdentity { String getIp6Cidr(); String getIp6Address(); + boolean getSecondaryIp(); } diff --git a/api/src/com/cloud/vm/NicSecondaryIp.java b/api/src/com/cloud/vm/NicSecondaryIp.java new file mode 100644 index 00000000000..655d172b33f --- /dev/null +++ b/api/src/com/cloud/vm/NicSecondaryIp.java @@ -0,0 +1,36 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. +package com.cloud.vm; + +import org.apache.cloudstack.acl.ControlledEntity; +import org.apache.cloudstack.api.Identity; +import org.apache.cloudstack.api.InternalIdentity; + + +/** + * Nic represents one nic on the VM. + */ +public interface NicSecondaryIp extends ControlledEntity, Identity, InternalIdentity { + /** + * @return id in the CloudStack database + */ + long getId(); + long getNicId(); + String getIp4Address(); + long getNetworkId(); + long getVmId(); +} diff --git a/api/src/org/apache/cloudstack/api/ApiConstants.java b/api/src/org/apache/cloudstack/api/ApiConstants.java index 4fdc91edef2..1b544fd1641 100755 --- a/api/src/org/apache/cloudstack/api/ApiConstants.java +++ b/api/src/org/apache/cloudstack/api/ApiConstants.java @@ -458,6 +458,7 @@ public class ApiConstants { public static final String UCS_PROFILE_DN = "profiledn"; public static final String UCS_BLADE_DN = "bladedn"; public static final String UCS_BLADE_ID = "bladeid"; + public static final String VM_GUEST_IP = "vmguestip"; public enum HostDetails { all, capacity, events, stats, min; diff --git a/api/src/org/apache/cloudstack/api/ResponseGenerator.java b/api/src/org/apache/cloudstack/api/ResponseGenerator.java index 267238af37b..a6025149846 100644 --- a/api/src/org/apache/cloudstack/api/ResponseGenerator.java +++ b/api/src/org/apache/cloudstack/api/ResponseGenerator.java @@ -53,6 +53,8 @@ import org.apache.cloudstack.api.response.LoadBalancerResponse; import org.apache.cloudstack.api.response.NetworkACLResponse; import org.apache.cloudstack.api.response.NetworkOfferingResponse; import org.apache.cloudstack.api.response.NetworkResponse; +import org.apache.cloudstack.api.response.NicResponse; +import org.apache.cloudstack.api.response.NicSecondaryIpResponse; import org.apache.cloudstack.api.response.PhysicalNetworkResponse; import org.apache.cloudstack.api.response.PodResponse; import org.apache.cloudstack.api.response.PrivateGatewayResponse; @@ -163,6 +165,8 @@ import com.cloud.user.User; import com.cloud.user.UserAccount; import com.cloud.uservm.UserVm; import com.cloud.vm.InstanceGroup; +import com.cloud.vm.Nic; +import com.cloud.vm.NicSecondaryIp; import com.cloud.vm.VirtualMachine; import com.cloud.vm.snapshot.VMSnapshot; @@ -385,4 +389,7 @@ public interface ResponseGenerator { TrafficMonitorResponse createTrafficMonitorResponse(Host trafficMonitor); VMSnapshotResponse createVMSnapshotResponse(VMSnapshot vmSnapshot); + NicSecondaryIpResponse createSecondaryIPToNicResponse(String ip, + Long nicId, Long networkId); + public NicResponse createNicResponse(Nic result); } diff --git a/api/src/org/apache/cloudstack/api/command/user/firewall/CreatePortForwardingRuleCmd.java b/api/src/org/apache/cloudstack/api/command/user/firewall/CreatePortForwardingRuleCmd.java index 39ab812909d..40128526ce0 100644 --- a/api/src/org/apache/cloudstack/api/command/user/firewall/CreatePortForwardingRuleCmd.java +++ b/api/src/org/apache/cloudstack/api/command/user/firewall/CreatePortForwardingRuleCmd.java @@ -94,6 +94,9 @@ public class CreatePortForwardingRuleCmd extends BaseAsyncCreateCmd implements P description="The network of the vm the Port Forwarding rule will be created for. " + "Required when public Ip address is not associated with any Guest network yet (VPC case)") private Long networkId; + @Parameter(name = ApiConstants.VM_GUEST_IP, type = CommandType.STRING, required = false, + description = "VM guest nic Secondary ip address for the port forwarding rule") + private String vmSecondaryIp; // /////////////////////////////////////////////////// // ///////////////// Accessors /////////////////////// @@ -104,6 +107,13 @@ public class CreatePortForwardingRuleCmd extends BaseAsyncCreateCmd implements P return ipAddressId; } + public Ip getVmSecondaryIp() { + if (vmSecondaryIp == null) { + return null; + } + return new Ip(vmSecondaryIp); + } + @Override public String getProtocol() { return protocol.trim(); @@ -300,8 +310,15 @@ public class CreatePortForwardingRuleCmd extends BaseAsyncCreateCmd implements P throw new InvalidParameterValueException("Parameter cidrList is deprecated; if you need to open firewall rule for the specific cidr, please refer to createFirewallRule command"); } + Ip privateIp = getVmSecondaryIp(); + if (privateIp != null) { + if ( !privateIp.isIp4()) { + throw new InvalidParameterValueException("Invalid vm ip address"); + } + } + try { - PortForwardingRule result = _rulesService.createPortForwardingRule(this, virtualMachineId, getOpenFirewall()); + PortForwardingRule result = _rulesService.createPortForwardingRule(this, virtualMachineId, privateIp, getOpenFirewall()); setEntityId(result.getId()); setEntityUuid(result.getUuid()); } catch (NetworkRuleConflictException ex) { diff --git a/api/src/org/apache/cloudstack/api/command/user/nat/EnableStaticNatCmd.java b/api/src/org/apache/cloudstack/api/command/user/nat/EnableStaticNatCmd.java index ce6ea1663b9..a0ec68ef5dd 100644 --- a/api/src/org/apache/cloudstack/api/command/user/nat/EnableStaticNatCmd.java +++ b/api/src/org/apache/cloudstack/api/command/user/nat/EnableStaticNatCmd.java @@ -59,6 +59,9 @@ public class EnableStaticNatCmd extends BaseCmd{ description="The network of the vm the static nat will be enabled for." + " Required when public Ip address is not associated with any Guest network yet (VPC case)") private Long networkId; + @Parameter(name = ApiConstants.VM_GUEST_IP, type = CommandType.STRING, required = false, + description = "VM guest nic Secondary ip address for the port forwarding rule") + private String vmSecondaryIp; ///////////////////////////////////////////////////// /////////////////// Accessors /////////////////////// @@ -72,6 +75,13 @@ public class EnableStaticNatCmd extends BaseCmd{ return virtualMachineId; } + public String getVmSecondaryIp() { + if (vmSecondaryIp == null) { + return null; + } + return vmSecondaryIp; + } + public long getNetworkId() { IpAddress ip = _entityMgr.findById(IpAddress.class, getIpAddressId()); Long ntwkId = null; @@ -110,7 +120,7 @@ public class EnableStaticNatCmd extends BaseCmd{ @Override public void execute() throws ResourceUnavailableException{ try { - boolean result = _rulesService.enableStaticNat(ipAddressId, virtualMachineId, getNetworkId(), false); + boolean result = _rulesService.enableStaticNat(ipAddressId, virtualMachineId, getNetworkId(), false, getVmSecondaryIp()); if (result) { SuccessResponse response = new SuccessResponse(getCommandName()); this.setResponseObject(response); diff --git a/api/src/org/apache/cloudstack/api/command/user/vm/AddIpToVmNicCmd.java b/api/src/org/apache/cloudstack/api/command/user/vm/AddIpToVmNicCmd.java new file mode 100644 index 00000000000..0f992743f6d --- /dev/null +++ b/api/src/org/apache/cloudstack/api/command/user/vm/AddIpToVmNicCmd.java @@ -0,0 +1,176 @@ +// 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 org.apache.log4j.Logger; + +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.ServerApiException; +import org.apache.cloudstack.api.response.NicResponse; +import org.apache.cloudstack.api.response.NicSecondaryIpResponse; + +import com.cloud.async.AsyncJob; +import com.cloud.event.EventTypes; +import com.cloud.exception.ConcurrentOperationException; +import com.cloud.exception.InsufficientAddressCapacityException; +import com.cloud.exception.InsufficientCapacityException; +import com.cloud.exception.InvalidParameterValueException; +import com.cloud.exception.ResourceAllocationException; +import com.cloud.exception.ResourceUnavailableException; +import com.cloud.network.Network; +import com.cloud.user.Account; +import com.cloud.user.UserContext; +import com.cloud.utils.net.NetUtils; +import com.cloud.vm.Nic; + +@APICommand(name = "addIpToNic", description = "Assigns secondary IP to NIC", responseObject = NicSecondaryIpResponse.class) +public class AddIpToVmNicCmd extends BaseAsyncCmd { + public static final Logger s_logger = Logger.getLogger(AddIpToVmNicCmd.class.getName()); + private static final String s_name = "addiptovmnicresponse"; + + ///////////////////////////////////////////////////// + //////////////// API parameters ///////////////////// + ///////////////////////////////////////////////////// + @Parameter(name=ApiConstants.NIC_ID, type=CommandType.UUID, entityType = NicResponse.class, required = true, + description="the ID of the nic to which you want to assign private IP") + private Long nicId; + + @Parameter(name = ApiConstants.IP_ADDRESS, type = CommandType.STRING, required = false, + description = "Secondary IP Address") + private String ipAddr; + + ///////////////////////////////////////////////////// + /////////////////// Accessors /////////////////////// + ///////////////////////////////////////////////////// + + public String getEntityTable() { + return "nic_secondary_ips"; + } + + public String getAccountName() { + return UserContext.current().getCaller().getAccountName(); + } + + public long getDomainId() { + return UserContext.current().getCaller().getDomainId(); + } + + private long getZoneId() { + Network ntwk = _entityMgr.findById(Network.class, getNetworkId()); + if (ntwk == null) { + throw new InvalidParameterValueException("Can't find zone id for specified"); + } + return ntwk.getDataCenterId(); + } + + public Long getNetworkId() { + Nic nic = _entityMgr.findById(Nic.class, nicId); + Long networkId = nic.getNetworkId(); + return networkId; + } + + public Long getNicId() { + return nicId; + } + + public String getIpaddress () { + if (ipAddr != null) { + return ipAddr; + } else { + return null; + } + } + @Override + public long getEntityOwnerId() { + Account caller = UserContext.current().getCaller(); + return caller.getAccountId(); + } + + @Override + public String getEventType() { + return EventTypes.EVENT_NET_IP_ASSIGN; + } + + @Override + public String getEventDescription() { + return "associating ip to nic id: " + getNetworkId() + " in zone " + getZoneId(); + } + + ///////////////////////////////////////////////////// + /////////////// API Implementation/////////////////// + ///////////////////////////////////////////////////// + + + @Override + public String getCommandName() { + return s_name; + } + + public static String getResultObjectName() { + return "addressinfo"; + } + + @Override + public void execute() throws ResourceUnavailableException, ResourceAllocationException, + ConcurrentOperationException, InsufficientCapacityException { + + UserContext.current().setEventDetails("Nic Id: " + getNicId() ); + String ip; + String SecondaryIp = null; + if ((ip = getIpaddress()) != null) { + if (!NetUtils.isValidIp(ip)) { + throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, "Invalid ip address " + ip); + } + } + + try { + SecondaryIp = _networkService.allocateSecondaryGuestIP(_accountService.getAccount(getEntityOwnerId()), getZoneId(), getNicId(), getNetworkId(), getIpaddress()); + } catch (InsufficientAddressCapacityException e) { + throw new InvalidParameterValueException("Allocating guest ip for nic failed"); + } + + if (SecondaryIp != null) { + s_logger.info("Associated ip address to NIC : " + SecondaryIp); + NicSecondaryIpResponse response = new NicSecondaryIpResponse(); + response = _responseGenerator.createSecondaryIPToNicResponse(ip, getNicId(), getNetworkId()); + response.setResponseName(getCommandName()); + this.setResponseObject(response); + } else { + throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, "Failed to assign secondary ip to nic"); + } + } + + @Override + public String getSyncObjType() { + return BaseAsyncCmd.networkSyncObject; + } + + @Override + public Long getSyncObjId() { + return getNetworkId(); + } + + @Override + public AsyncJob.Type getInstanceType() { + return AsyncJob.Type.IpAddress; + } + +} diff --git a/api/src/org/apache/cloudstack/api/command/user/vm/ListNicsCmd.java b/api/src/org/apache/cloudstack/api/command/user/vm/ListNicsCmd.java new file mode 100644 index 00000000000..9af044ebb70 --- /dev/null +++ b/api/src/org/apache/cloudstack/api/command/user/vm/ListNicsCmd.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 org.apache.cloudstack.api.command.user.vm; + +import java.util.ArrayList; +import java.util.List; + +import org.apache.log4j.Logger; + +import org.apache.cloudstack.api.APICommand; +import org.apache.cloudstack.api.ApiConstants; +import org.apache.cloudstack.api.ApiErrorCode; +import org.apache.cloudstack.api.BaseListCmd; +import org.apache.cloudstack.api.Parameter; +import org.apache.cloudstack.api.ServerApiException; +import org.apache.cloudstack.api.response.ListResponse; +import org.apache.cloudstack.api.response.NicResponse; +import org.apache.cloudstack.api.response.UserVmResponse; + +import com.cloud.async.AsyncJob; +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.vm.Nic; +import com.cloud.vm.NicSecondaryIp; + +@APICommand(name = "listNics", description = "list the vm nics IP to NIC", responseObject = NicResponse.class) +public class ListNicsCmd extends BaseListCmd { + public static final Logger s_logger = Logger.getLogger(ListNicsCmd.class.getName()); + private static final String s_name = "listnics"; + + ///////////////////////////////////////////////////// + //////////////// API parameters ///////////////////// + ///////////////////////////////////////////////////// + + @Parameter(name=ApiConstants.NIC_ID, type=CommandType.UUID, entityType = NicResponse.class, required = false, + description="the ID of the nic to to list IPs") + private Long nicId; + + @Parameter(name=ApiConstants.VIRTUAL_MACHINE_ID, type=CommandType.UUID, entityType = UserVmResponse.class, required = true, + description="the ID of the vm") + private Long vmId; + + ///////////////////////////////////////////////////// + /////////////////// Accessors /////////////////////// + ///////////////////////////////////////////////////// + + public String getEntityTable() { + return "nics"; + } + + public String getAccountName() { + return UserContext.current().getCaller().getAccountName(); + } + + public long getDomainId() { + return UserContext.current().getCaller().getDomainId(); + } + + public Long getNicId() { + return nicId; + } + + public Long getVmId() { + return vmId; + } + + @Override + public long getEntityOwnerId() { + Account caller = UserContext.current().getCaller(); + return caller.getAccountId(); + } + + ///////////////////////////////////////////////////// + /////////////// API Implementation/////////////////// + ///////////////////////////////////////////////////// + + + @Override + public String getCommandName() { + return s_name; + } + + public static String getResultObjectName() { + return "addressinfo"; + } + + @Override + public void execute() throws ResourceUnavailableException, ResourceAllocationException, + ConcurrentOperationException, InsufficientCapacityException { + + try { + List results = _networkService.listNics(this); + ListResponse response = new ListResponse(); + List resList = new ArrayList(results.size()); + for (Nic r : results) { + NicResponse resp = _responseGenerator.createNicResponse(r); + resp.setObjectName("nic"); + resList.add(resp); + } + response.setResponses(resList); + response.setResponseName(getCommandName()); + this.setResponseObject(response); + + } catch (Exception e) { + s_logger.warn("Failed to list secondary ip address per nic "); + throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, e.getMessage()); + } + } + + @Override + public AsyncJob.Type getInstanceType() { + return AsyncJob.Type.IpAddress; + } + +} diff --git a/api/src/org/apache/cloudstack/api/command/user/vm/RemoveIpFromVmNicCmd.java b/api/src/org/apache/cloudstack/api/command/user/vm/RemoveIpFromVmNicCmd.java new file mode 100644 index 00000000000..cb5e0855f64 --- /dev/null +++ b/api/src/org/apache/cloudstack/api/command/user/vm/RemoveIpFromVmNicCmd.java @@ -0,0 +1,123 @@ +// 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 org.apache.log4j.Logger; + +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.ServerApiException; +import org.apache.cloudstack.api.response.NicSecondaryIpResponse; +import org.apache.cloudstack.api.response.SuccessResponse; +import com.cloud.async.AsyncJob; +import com.cloud.event.EventTypes; +import com.cloud.exception.InvalidParameterValueException; +import com.cloud.user.Account; +import com.cloud.user.UserContext; + +@APICommand(name = "removeIpFromNic", description="Assigns secondary IP to NIC.", responseObject=SuccessResponse.class) +public class RemoveIpFromVmNicCmd extends BaseAsyncCmd { + public static final Logger s_logger = Logger.getLogger(RemoveIpFromVmNicCmd.class.getName()); + private static final String s_name = "unassignsecondaryipaddrtonicresponse"; + + ///////////////////////////////////////////////////// + //////////////// API parameters ///////////////////// + ///////////////////////////////////////////////////// + + @Parameter(name=ApiConstants.ID, type=CommandType.UUID, required = true, entityType = NicSecondaryIpResponse.class, + description="the ID of the secondary ip address to nic") + private long id; + + // unexposed parameter needed for events logging + @Parameter(name=ApiConstants.ACCOUNT_ID, type=CommandType.UUID, expose=false) + private Long ownerId; + + ///////////////////////////////////////////////////// + /////////////////// Accessors /////////////////////// + ///////////////////////////////////////////////////// + + public String getEntityTable() { + return "nic_secondary_ips"; + } + + public long getIpAddressId() { + return id; + } + + public String getAccountName() { + return UserContext.current().getCaller().getAccountName(); + } + + public long getDomainId() { + return UserContext.current().getCaller().getDomainId(); + } + + @Override + public long getEntityOwnerId() { + Account caller = UserContext.current().getCaller(); + return caller.getAccountId(); + } + + @Override + public String getEventType() { + return EventTypes.EVENT_NET_IP_ASSIGN; + } + + @Override + public String getEventDescription() { + return ("Disassociating ip address with id=" + id); + } + + ///////////////////////////////////////////////////// + /////////////// API Implementation/////////////////// + ///////////////////////////////////////////////////// + + @Override + public String getCommandName() { + return s_name; + } + + public static String getResultObjectName() { + return "addressinfo"; + } + + @Override + public void execute() throws InvalidParameterValueException { + UserContext.current().setEventDetails("Ip Id: " + getIpAddressId()); + boolean result = _networkService.releaseSecondaryIpFromNic(getIpAddressId()); + if (result) { + SuccessResponse response = new SuccessResponse(getCommandName()); + this.setResponseObject(response); + } else { + throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, "Failed to remove secondary ip address for the nic"); + } + } + + @Override + public String getSyncObjType() { + return BaseAsyncCmd.networkSyncObject; + } + + @Override + public AsyncJob.Type getInstanceType() { + return AsyncJob.Type.IpAddress; + } + +} diff --git a/api/src/org/apache/cloudstack/api/response/AddIpToVmNicResponse.java b/api/src/org/apache/cloudstack/api/response/AddIpToVmNicResponse.java new file mode 100644 index 00000000000..9af20b23871 --- /dev/null +++ b/api/src/org/apache/cloudstack/api/response/AddIpToVmNicResponse.java @@ -0,0 +1,85 @@ +// 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.response; +import java.util.Date; +import java.util.List; + +import org.apache.cloudstack.api.ApiConstants; +import org.apache.cloudstack.api.BaseResponse; + +import com.cloud.serializer.Param; +import com.google.gson.annotations.SerializedName; + +@SuppressWarnings("unused") +public class AddIpToVmNicResponse extends BaseResponse { + @SerializedName(ApiConstants.ID) @Param(description="the ID of the secondary private IP addr") + private Long id; + + @SerializedName(ApiConstants.IP_ADDRESS) @Param(description="Secondary IP address") + private String ipAddr; + + @SerializedName(ApiConstants.NIC_ID) @Param(description="the ID of the nic") + private Long nicId; + + @SerializedName(ApiConstants.NETWORK_ID) @Param(description="the ID of the network") + private Long nwId; + + @SerializedName(ApiConstants.VIRTUAL_MACHINE_ID) @Param(description="the ID of the vm") + private Long vmId; + + public Long getId() { + return id; + } + + public String getIpAddr() { + return ipAddr; + } + + public void setIpAddr(String ipAddr) { + this.ipAddr = ipAddr; + } + + public Long getNicId() { + return nicId; + } + + public void setNicId(Long nicId) { + this.nicId = nicId; + } + + public Long getNwId() { + return nwId; + } + + public void setNwId(Long nwId) { + this.nwId = nwId; + } + + public Long getVmId() { + return vmId; + } + + public void setVmId(Long vmId) { + this.vmId = vmId; + } + + public Long setId(Long id) { + return id; + } + + +} diff --git a/api/src/org/apache/cloudstack/api/response/IPAddressResponse.java b/api/src/org/apache/cloudstack/api/response/IPAddressResponse.java index 251b2dd09e8..cede84f931e 100644 --- a/api/src/org/apache/cloudstack/api/response/IPAddressResponse.java +++ b/api/src/org/apache/cloudstack/api/response/IPAddressResponse.java @@ -82,6 +82,10 @@ public class IPAddressResponse extends BaseResponse implements ControlledEntityR @SerializedName(ApiConstants.VIRTUAL_MACHINE_ID) @Param(description="virutal machine id the ip address is assigned to (not null only for static nat Ip)") private String virtualMachineId; + @SerializedName("vmipaddress") @Param(description="virutal machine (dnat) ip address (not null only for static nat Ip)") + private String virtualMachineIp; + + @SerializedName("virtualmachinename") @Param(description="virutal machine name the ip address is assigned to (not null only for static nat Ip)") private String virtualMachineName; @@ -185,6 +189,10 @@ public class IPAddressResponse extends BaseResponse implements ControlledEntityR this.virtualMachineId = virtualMachineId; } + public void setVirtualMachineIp(String virtualMachineIp) { + this.virtualMachineIp = virtualMachineIp; + } + public void setVirtualMachineName(String virtualMachineName) { this.virtualMachineName = virtualMachineName; } diff --git a/api/src/org/apache/cloudstack/api/response/NicResponse.java b/api/src/org/apache/cloudstack/api/response/NicResponse.java index a7d1a0d068e..a1ceaf63798 100644 --- a/api/src/org/apache/cloudstack/api/response/NicResponse.java +++ b/api/src/org/apache/cloudstack/api/response/NicResponse.java @@ -16,6 +16,8 @@ // under the License. package org.apache.cloudstack.api.response; +import java.util.List; + import org.apache.cloudstack.api.ApiConstants; import org.apache.cloudstack.api.BaseResponse; @@ -75,7 +77,10 @@ public class NicResponse extends BaseResponse { @SerializedName(ApiConstants.IP6_ADDRESS) @Param(description="the IPv6 address of network") private String ip6Address; - + + @SerializedName("secondaryip") @Param(description="the Secondary ipv4 addr of nic") + private List secondaryIps; + public String getId() { return id; } @@ -167,4 +172,9 @@ public class NicResponse extends BaseResponse { return false; return true; } + + public void setSecondaryIps(List ipList) { + this.secondaryIps = ipList; + } + } diff --git a/api/src/org/apache/cloudstack/api/response/NicSecondaryIpResponse.java b/api/src/org/apache/cloudstack/api/response/NicSecondaryIpResponse.java new file mode 100644 index 00000000000..3464a63540e --- /dev/null +++ b/api/src/org/apache/cloudstack/api/response/NicSecondaryIpResponse.java @@ -0,0 +1,85 @@ +// 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.response; +import java.util.Date; +import java.util.List; + +import org.apache.cloudstack.api.ApiConstants; +import org.apache.cloudstack.api.BaseResponse; + +import com.cloud.serializer.Param; +import com.google.gson.annotations.SerializedName; + +@SuppressWarnings("unused") +public class NicSecondaryIpResponse extends BaseResponse { + @SerializedName(ApiConstants.ID) @Param(description="the ID of the secondary private IP addr") + private Long id; + + @SerializedName(ApiConstants.IP_ADDRESS) @Param(description="Secondary IP address") + private String ipAddr; + + @SerializedName(ApiConstants.NIC_ID) @Param(description="the ID of the nic") + private Long nicId; + + @SerializedName(ApiConstants.NETWORK_ID) @Param(description="the ID of the network") + private Long nwId; + + @SerializedName(ApiConstants.VIRTUAL_MACHINE_ID) @Param(description="the ID of the vm") + private Long vmId; + + public Long getId() { + return id; + } + + public String getIpAddr() { + return ipAddr; + } + + public void setIpAddr(String ipAddr) { + this.ipAddr = ipAddr; + } + + public Long getNicId() { + return nicId; + } + + public void setNicId(Long nicId) { + this.nicId = nicId; + } + + public Long getNwId() { + return nwId; + } + + public void setNwId(Long nwId) { + this.nwId = nwId; + } + + public Long getVmId() { + return vmId; + } + + public void setVmId(Long vmId) { + this.vmId = vmId; + } + + public Long setId(Long id) { + return id; + } + + +} diff --git a/api/test/org/apache/cloudstack/api/command/test/AddIpToVmNicTest.java b/api/test/org/apache/cloudstack/api/command/test/AddIpToVmNicTest.java new file mode 100644 index 00000000000..106589d10cc --- /dev/null +++ b/api/test/org/apache/cloudstack/api/command/test/AddIpToVmNicTest.java @@ -0,0 +1,132 @@ +// 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.test; + +import junit.framework.Assert; +import junit.framework.TestCase; + +import org.apache.cloudstack.api.ResponseGenerator; +import org.apache.cloudstack.api.command.user.vm.AddIpToVmNicCmd; +import org.apache.cloudstack.api.command.user.vm.RemoveIpFromVmNicCmd; +import org.apache.cloudstack.api.response.NicSecondaryIpResponse; +import org.apache.cloudstack.api.response.SuccessResponse; + + + +import org.junit.Before; +import org.junit.Rule; +import org.junit.Test; +import org.junit.rules.ExpectedException; +import org.mockito.Mockito; + +import com.cloud.exception.ConcurrentOperationException; +import com.cloud.exception.InsufficientAddressCapacityException; +import com.cloud.exception.InsufficientCapacityException; +import com.cloud.exception.InvalidParameterValueException; +import com.cloud.exception.ResourceAllocationException; +import com.cloud.exception.ResourceUnavailableException; +import com.cloud.network.NetworkService; +import com.cloud.user.Account; + +public class AddIpToVmNicTest extends TestCase { + + private AddIpToVmNicCmd addIpToVmNicCmd; + private RemoveIpFromVmNicCmd removeIpFromVmNicCmd; + private ResponseGenerator responseGenerator; + private SuccessResponse successResponseGenerator; + + @Rule + public ExpectedException expectedException = ExpectedException.none(); + + @Before + public void setUp() { + addIpToVmNicCmd = new AddIpToVmNicCmd() { + }; + removeIpFromVmNicCmd = new RemoveIpFromVmNicCmd(); + } + + @Test + public void testCreateSuccess() throws ResourceAllocationException, ResourceUnavailableException, ConcurrentOperationException, InsufficientCapacityException { + + NetworkService networkService = Mockito.mock(NetworkService.class); + AddIpToVmNicCmd ipTonicCmd = Mockito.mock(AddIpToVmNicCmd.class); + + Mockito.when( + networkService.allocateSecondaryGuestIP(Mockito.any(Account.class), Mockito.anyLong(), Mockito.anyLong(), Mockito.anyLong(), Mockito.anyString())).thenReturn("10.1.1.2"); + + ipTonicCmd._networkService = networkService; + responseGenerator = Mockito.mock(ResponseGenerator.class); + + NicSecondaryIpResponse ipres = Mockito.mock(NicSecondaryIpResponse.class); + Mockito.when(responseGenerator.createSecondaryIPToNicResponse(Mockito.anyString(), Mockito.anyLong(), Mockito.anyLong())).thenReturn(ipres); + + ipTonicCmd._responseGenerator = responseGenerator; + ipTonicCmd.execute(); + } + + @Test + public void testCreateFailure() throws ResourceAllocationException, ResourceUnavailableException, ConcurrentOperationException, InsufficientCapacityException { + + NetworkService networkService = Mockito.mock(NetworkService.class); + AddIpToVmNicCmd ipTonicCmd = Mockito.mock(AddIpToVmNicCmd.class); + + Mockito.when( + networkService.allocateSecondaryGuestIP(Mockito.any(Account.class), Mockito.anyLong(), Mockito.anyLong(), Mockito.anyLong(), Mockito.anyString())).thenReturn(null); + + ipTonicCmd._networkService = networkService; + + try { + ipTonicCmd.execute(); + } catch (InsufficientAddressCapacityException e) { + throw new InvalidParameterValueException("Allocating guest ip for nic failed"); + } + } + + @Test + public void testRemoveIpFromVmNicSuccess() throws ResourceAllocationException, ResourceUnavailableException, ConcurrentOperationException, InsufficientCapacityException { + + NetworkService networkService = Mockito.mock(NetworkService.class); + RemoveIpFromVmNicCmd removeIpFromNic = Mockito.mock(RemoveIpFromVmNicCmd.class); + + Mockito.when( + networkService.releaseSecondaryIpFromNic(Mockito.anyInt())).thenReturn(true); + + removeIpFromNic._networkService = networkService; + successResponseGenerator = Mockito.mock(SuccessResponse.class); + + removeIpFromNic.execute(); + } + + @Test + public void testRemoveIpFromVmNicFailure() throws InsufficientAddressCapacityException { + NetworkService networkService = Mockito.mock(NetworkService.class); + RemoveIpFromVmNicCmd removeIpFromNic = Mockito.mock(RemoveIpFromVmNicCmd.class); + + Mockito.when( + networkService.releaseSecondaryIpFromNic(Mockito.anyInt())).thenReturn(false); + + removeIpFromNic._networkService = networkService; + successResponseGenerator = Mockito.mock(SuccessResponse.class); + + try { + removeIpFromNic.execute(); + } catch (InvalidParameterValueException exception) { + Assert.assertEquals("Failed to remove secondary ip address for the nic", + exception.getLocalizedMessage()); + } + } +} diff --git a/client/tomcatconf/commands.properties.in b/client/tomcatconf/commands.properties.in index f03e8d50adb..bbb3d3078ec 100644 --- a/client/tomcatconf/commands.properties.in +++ b/client/tomcatconf/commands.properties.in @@ -326,6 +326,11 @@ addNicToVirtualMachine=15 removeNicFromVirtualMachine=15 updateDefaultNicForVirtualMachine=15 +#### +addIpToNic=15; +removeIpFromNic=15; +listNics=15; + #### SSH key pair commands registerSSHKeyPair=15 createSSHKeyPair=15 diff --git a/server/src/com/cloud/api/ApiDBUtils.java b/server/src/com/cloud/api/ApiDBUtils.java index ffee22fa1ae..95879ee0238 100755 --- a/server/src/com/cloud/api/ApiDBUtils.java +++ b/server/src/com/cloud/api/ApiDBUtils.java @@ -201,6 +201,7 @@ import com.cloud.vm.DomainRouterVO; import com.cloud.vm.InstanceGroup; import com.cloud.vm.InstanceGroupVO; import com.cloud.vm.NicProfile; +import com.cloud.vm.NicSecondaryIp; import com.cloud.vm.UserVmDetailVO; import com.cloud.vm.UserVmManager; import com.cloud.vm.UserVmVO; @@ -209,6 +210,8 @@ import com.cloud.vm.VirtualMachine; import com.cloud.vm.VmStats; import com.cloud.vm.dao.ConsoleProxyDao; import com.cloud.vm.dao.DomainRouterDao; +import com.cloud.vm.dao.NicSecondaryIpDao; +import com.cloud.vm.dao.NicSecondaryIpVO; import com.cloud.vm.dao.UserVmDao; import com.cloud.vm.dao.UserVmDetailsDao; import com.cloud.vm.dao.VMInstanceDao; @@ -319,6 +322,7 @@ public class ApiDBUtils { static HostDetailsDao _hostDetailsDao; static VMSnapshotDao _vmSnapshotDao; static ClusterDetailsDao _clusterDetailsDao; + static NicSecondaryIpDao _nicSecondaryIpDao; @Inject private ManagementServer ms; @Inject public AsyncJobManager asyncMgr; @@ -421,6 +425,7 @@ public class ApiDBUtils { @Inject private HostDetailsDao hostDetailsDao; @Inject private ClusterDetailsDao clusterDetailsDao; @Inject private VMSnapshotDao vmSnapshotDao; + @Inject private NicSecondaryIpDao nicSecondaryIpDao; @PostConstruct void init() { _ms = ms; @@ -521,6 +526,7 @@ public class ApiDBUtils { _hostDetailsDao = hostDetailsDao; _clusterDetailsDao = clusterDetailsDao; _vmSnapshotDao = vmSnapshotDao; + _nicSecondaryIpDao = nicSecondaryIpDao; // Note: stats collector should already have been initialized by this time, otherwise a null instance is returned _statsCollector = StatsCollector.getInstance(); } @@ -1519,4 +1525,8 @@ public class ApiDBUtils { public static Map findHostDetailsById(long hostId){ return _hostDetailsDao.findDetails(hostId); } + + public static List findNicSecondaryIps(long nicId) { + return _nicSecondaryIpDao.listByNicId(nicId); + } } diff --git a/server/src/com/cloud/api/ApiResponseHelper.java b/server/src/com/cloud/api/ApiResponseHelper.java index eafee8a2a4a..fbfc9554111 100755 --- a/server/src/com/cloud/api/ApiResponseHelper.java +++ b/server/src/com/cloud/api/ApiResponseHelper.java @@ -19,6 +19,7 @@ package com.cloud.api; import com.cloud.api.query.ViewResponseHelper; import com.cloud.api.query.vo.*; import com.cloud.api.response.ApiResponseSerializer; + import org.apache.cloudstack.api.response.AsyncJobResponse; import org.apache.cloudstack.api.response.AutoScalePolicyResponse; import org.apache.cloudstack.api.response.AutoScaleVmGroupResponse; @@ -52,6 +53,7 @@ import org.apache.cloudstack.api.response.LoadBalancerResponse; import org.apache.cloudstack.api.response.NetworkACLResponse; import org.apache.cloudstack.api.response.NetworkOfferingResponse; import org.apache.cloudstack.api.response.NetworkResponse; +import org.apache.cloudstack.api.response.NicResponse; import org.apache.cloudstack.api.response.PhysicalNetworkResponse; import org.apache.cloudstack.api.response.PodResponse; import org.apache.cloudstack.api.response.PrivateGatewayResponse; @@ -180,10 +182,14 @@ import com.cloud.utils.StringUtils; import com.cloud.utils.net.NetUtils; import com.cloud.vm.ConsoleProxyVO; import com.cloud.vm.InstanceGroup; +import com.cloud.vm.Nic; import com.cloud.vm.NicProfile; import com.cloud.vm.VMInstanceVO; +import com.cloud.vm.NicSecondaryIp; import com.cloud.vm.VirtualMachine; import com.cloud.vm.VirtualMachine.Type; +import com.cloud.vm.dao.NicSecondaryIpVO; + import org.apache.cloudstack.acl.ControlledEntity; import org.apache.cloudstack.acl.ControlledEntity.ACLType; import org.apache.cloudstack.api.ApiConstants.HostDetails; @@ -551,6 +557,9 @@ public class ApiResponseHelper implements ResponseGenerator { } } } + if (ipAddr.getVmIp() != null) { + ipResponse.setVirtualMachineIp(ipAddr.getVmIp()); + } if (ipAddr.getAssociatedWithNetworkId() != null) { Network ntwk = ApiDBUtils.findNetworkById(ipAddr.getAssociatedWithNetworkId()); @@ -3435,4 +3444,51 @@ public class ApiResponseHelper implements ResponseGenerator { response.setTimeout(tmDetails.get("timeout")); return response; } + + public NicSecondaryIpResponse createSecondaryIPToNicResponse(String ipAddr, Long nicId, Long networkId) { + NicSecondaryIpResponse response = new NicSecondaryIpResponse(); + response.setIpAddr(ipAddr); + response.setNicId(nicId); + response.setNwId(networkId); + response.setObjectName("nicsecondaryip"); + return response; + } + + public NicResponse createNicResponse(Nic result) { + NicResponse response = new NicResponse(); + response.setId(result.getUuid()); + response.setIpaddress(result.getIp4Address()); + + if (result.getSecondaryIp()) { + List secondaryIps = ApiDBUtils.findNicSecondaryIps(result.getId()); + if (secondaryIps != null) { + List ipList = new ArrayList(); + for (NicSecondaryIpVO ip: secondaryIps) { + NicSecondaryIpResponse ipRes = new NicSecondaryIpResponse(); + ipRes.setId(ip.getId()); + ipRes.setIpAddr(ip.getIp4Address()); + ipList.add(ipRes); + } + response.setSecondaryIps(ipList); + } + } + + response.setGateway(result.getGateway()); + response.setId(result.getUuid()); + response.setGateway(result.getGateway()); + response.setNetmask(result.getNetmask()); + response.setMacAddress(result.getMacAddress()); + if (result.getBroadcastUri() != null) { + response.setBroadcastUri(result.getBroadcastUri().toString()); + } + if (result.getIsolationUri() != null) { + response.setIsolationUri(result.getIsolationUri().toString()); + } + if (result.getIp6Address() != null) { + response.setId(result.getIp6Address()); + } + + response.setIsDefault(result.isDefaultNic()); + return response; + } } diff --git a/server/src/com/cloud/network/NetworkManager.java b/server/src/com/cloud/network/NetworkManager.java index 2904183911e..8b6bf9a7402 100755 --- a/server/src/com/cloud/network/NetworkManager.java +++ b/server/src/com/cloud/network/NetworkManager.java @@ -49,6 +49,7 @@ import com.cloud.user.User; import com.cloud.utils.Pair; import com.cloud.vm.Nic; import com.cloud.vm.NicProfile; +import com.cloud.vm.NicSecondaryIp; import com.cloud.vm.ReservationContext; import com.cloud.vm.VMInstanceVO; import com.cloud.vm.VirtualMachine; @@ -331,4 +332,15 @@ public interface NetworkManager { int getRuleCountForIp(Long addressId, FirewallRule.Purpose purpose, FirewallRule.State state); LoadBalancingServiceProvider getLoadBalancingProviderForNetwork(Network network); + + + boolean isSecondaryIpSetForNic(long nicId); + + public String allocateGuestIP(Account ipOwner, boolean isSystem, long zoneId, Long networkId, String requestedIp) + throws InsufficientAddressCapacityException; + + boolean removeVmSecondaryIps(long vmId); + + List listVmNics(Long vmId, Long nicId); + } diff --git a/server/src/com/cloud/network/NetworkManagerImpl.java b/server/src/com/cloud/network/NetworkManagerImpl.java index f527b73d481..a575183a152 100755 --- a/server/src/com/cloud/network/NetworkManagerImpl.java +++ b/server/src/com/cloud/network/NetworkManagerImpl.java @@ -140,8 +140,19 @@ import com.cloud.utils.fsm.StateMachine2; import com.cloud.utils.net.Ip; import com.cloud.utils.net.NetUtils; import com.cloud.vm.*; +import com.cloud.vm.Nic; +import com.cloud.vm.NicProfile; +import com.cloud.vm.NicSecondaryIp; +import com.cloud.vm.NicVO; +import com.cloud.vm.ReservationContext; +import com.cloud.vm.ReservationContextImpl; +import com.cloud.vm.UserVmVO; +import com.cloud.vm.VMInstanceVO; +import com.cloud.vm.VirtualMachine; import com.cloud.vm.VirtualMachine.Type; import com.cloud.vm.dao.NicDao; +import com.cloud.vm.dao.NicSecondaryIpDao; +import com.cloud.vm.dao.NicSecondaryIpVO; import com.cloud.vm.dao.UserVmDao; import com.cloud.vm.dao.VMInstanceDao; @@ -240,6 +251,8 @@ public class NetworkManagerImpl extends ManagerBase implements NetworkManager, L UsageEventDao _usageEventDao; @Inject NetworkModel _networkModel; + @Inject + NicSecondaryIpDao _nicSecondaryIpDao; @Inject UserIpv6AddressDao _ipv6Dao; @Inject @@ -1750,6 +1763,14 @@ public class NetworkManagerImpl extends ManagerBase implements NetworkManager, L guru.deallocate(network, profile, vm); _nicDao.remove(nic.getId()); s_logger.debug("Removed nic id=" + nic.getId()); + //remove the secondary ip addresses corresponding to to this nic + List secondaryIps = _nicSecondaryIpDao.listByNicId(nic.getId()); + if (secondaryIps != null) { + for (NicSecondaryIpVO ip : secondaryIps) { + _nicSecondaryIpDao.remove(ip.getId()); + } + s_logger.debug("Removed nic " + nic.getId() + " secondary ip addreses"); + } } @Override @@ -2792,6 +2813,33 @@ public class NetworkManagerImpl extends ManagerBase implements NetworkManager, L Random _rand = new Random(System.currentTimeMillis()); + public List listVmNics(Long vmId, Long nicId) { + List result = null; + if (nicId == null) { + result = _nicDao.listByVmId(vmId); + } else { + result = _nicDao.listByVmIdAndNicId(vmId, nicId); + } + return result; + } + + public String allocateGuestIP(Account ipOwner, boolean isSystem, long zoneId, Long networkId, String requestedIp) + throws InsufficientAddressCapacityException { + String ipaddr = null; + Account caller = UserContext.current().getCaller(); + long callerUserId = UserContext.current().getCallerUserId(); + // check permissions + DataCenter zone = _configMgr.getZone(zoneId); + Network network = _networksDao.findById(networkId); + + _accountMgr.checkAccess(caller, null, false, network); + + //return acquireGuestIpAddress(network, requestedIp); + ipaddr = acquireGuestIpAddress(network, requestedIp); + return ipaddr; + } + + @Override @DB public String acquireGuestIpAddress(Network network, String requestedIp) { @@ -2802,7 +2850,7 @@ public class NetworkManagerImpl extends ManagerBase implements NetworkManager, L Set availableIps = _networkModel.getAvailableIps(network, requestedIp); - if (availableIps.isEmpty()) { + if (availableIps == null || availableIps.isEmpty()) { return null; } @@ -3032,9 +3080,9 @@ public class NetworkManagerImpl extends ManagerBase implements NetworkManager, L throw new InvalidParameterValueException("Source ip address of the rule id=" + firewallStaticNatRule.getId() + " is not static nat enabled"); } - String dstIp = _networkModel.getIpInNetwork(ip.getAssociatedWithVmId(), firewallStaticNatRule.getNetworkId()); + //String dstIp = _networkModel.getIpInNetwork(ip.getAssociatedWithVmId(), firewallStaticNatRule.getNetworkId()); ruleVO.setState(FirewallRule.State.Revoke); - staticNatRules.add(new StaticNatRuleImpl(ruleVO, dstIp)); + staticNatRules.add(new StaticNatRuleImpl(ruleVO, ip.getVmIp())); } try { @@ -3598,4 +3646,26 @@ public class NetworkManagerImpl extends ManagerBase implements NetworkManager, L } return rules.size(); } + + @Override + public boolean isSecondaryIpSetForNic(long nicId) { + NicVO nic = _nicDao.findById(nicId); + return nic.getSecondaryIp(); + } + + @Override + public boolean removeVmSecondaryIps(long vmId) { + Transaction txn = Transaction.currentTxn(); + txn.start(); + List ipList = _nicSecondaryIpDao.listByVmId(vmId); + if (ipList != null) { + for (NicSecondaryIpVO ip: ipList) { + _nicSecondaryIpDao.remove(ip.getId()); + } + s_logger.debug("Revoving nic secondary ip entry ..."); + } + txn.commit(); + return true; + } + } diff --git a/server/src/com/cloud/network/NetworkModelImpl.java b/server/src/com/cloud/network/NetworkModelImpl.java index ac1bc874f9c..05258884db9 100644 --- a/server/src/com/cloud/network/NetworkModelImpl.java +++ b/server/src/com/cloud/network/NetworkModelImpl.java @@ -109,6 +109,7 @@ import com.cloud.vm.VMInstanceVO; import com.cloud.vm.VirtualMachine; import com.cloud.vm.VirtualMachine.Type; import com.cloud.vm.dao.NicDao; +import com.cloud.vm.dao.NicSecondaryIpDao; import com.cloud.vm.dao.VMInstanceDao; @Component @@ -170,6 +171,8 @@ public class NetworkModelImpl extends ManagerBase implements NetworkModel { PrivateIpDao _privateIpDao; @Inject UserIpv6AddressDao _ipv6Dao; + @Inject + NicSecondaryIpDao _nicSecondaryIpDao;; private final HashMap _systemNetworks = new HashMap(5); @@ -1624,6 +1627,8 @@ public class NetworkModelImpl extends ManagerBase implements NetworkModel { public Set getAvailableIps(Network network, String requestedIp) { String[] cidr = network.getCidr().split("/"); List ips = _nicDao.listIpAddressInNetwork(network.getId()); + List secondaryIps = _nicSecondaryIpDao.listSecondaryIpAddressInNetwork(network.getId()); + ips.addAll(secondaryIps); Set allPossibleIps = NetUtils.getAllIpsFromCidr(cidr[0], Integer.parseInt(cidr[1])); Set usedIps = new TreeSet(); diff --git a/server/src/com/cloud/network/NetworkServiceImpl.java b/server/src/com/cloud/network/NetworkServiceImpl.java index 1708224b47e..4c61aec284f 100755 --- a/server/src/com/cloud/network/NetworkServiceImpl.java +++ b/server/src/com/cloud/network/NetworkServiceImpl.java @@ -37,23 +37,28 @@ import javax.inject.Inject; import javax.naming.ConfigurationException; import org.apache.cloudstack.acl.ControlledEntity.ACLType; +import org.apache.cloudstack.acl.SecurityChecker.AccessType; import org.apache.cloudstack.api.command.admin.usage.ListTrafficTypeImplementorsCmd; import org.apache.cloudstack.api.command.user.network.CreateNetworkCmd; import org.apache.cloudstack.api.command.user.network.ListNetworksCmd; import org.apache.cloudstack.api.command.user.network.RestartNetworkCmd; import org.apache.log4j.Logger; import org.springframework.stereotype.Component; +import org.apache.cloudstack.api.command.user.vm.ListNicsCmd; +import org.bouncycastle.util.IPAddress; import com.cloud.configuration.Config; import com.cloud.configuration.ConfigurationManager; import com.cloud.configuration.dao.ConfigurationDao; import com.cloud.dc.DataCenter; +import com.cloud.dc.Pod; import com.cloud.dc.DataCenter.NetworkType; import com.cloud.dc.DataCenterVO; import com.cloud.dc.Vlan.VlanType; import com.cloud.dc.VlanVO; import com.cloud.dc.dao.AccountVlanMapDao; import com.cloud.dc.dao.DataCenterDao; +import com.cloud.dc.dao.HostPodDao; import com.cloud.dc.dao.VlanDao; import com.cloud.deploy.DeployDestination; import com.cloud.domain.Domain; @@ -65,6 +70,8 @@ import com.cloud.event.UsageEventUtils; import com.cloud.event.dao.EventDao; import com.cloud.event.dao.UsageEventDao; import com.cloud.exception.*; +import com.cloud.host.Host; +import com.cloud.host.dao.HostDao; import com.cloud.network.IpAddress.State; import com.cloud.vm.Nic; import com.cloud.network.Network.Capability; @@ -82,7 +89,9 @@ import com.cloud.network.element.VirtualRouterElement; import com.cloud.network.element.VpcVirtualRouterElement; import com.cloud.network.guru.NetworkGuru; import com.cloud.network.rules.FirewallRule.Purpose; +import com.cloud.network.rules.dao.PortForwardingRulesDao; import com.cloud.network.rules.FirewallRuleVO; +import com.cloud.network.rules.PortForwardingRuleVO; import com.cloud.network.rules.RulesManager; import com.cloud.network.vpc.PrivateIpVO; import com.cloud.network.vpc.VpcManager; @@ -112,6 +121,8 @@ import com.cloud.utils.exception.CloudRuntimeException; import com.cloud.utils.net.NetUtils; import com.cloud.vm.*; import com.cloud.vm.dao.NicDao; +import com.cloud.vm.dao.NicSecondaryIpDao; +import com.cloud.vm.dao.NicSecondaryIpVO; import com.cloud.vm.dao.UserVmDao; import com.cloud.vm.dao.VMInstanceDao; import java.util.*; @@ -203,6 +214,16 @@ public class NetworkServiceImpl extends ManagerBase implements NetworkService { @Inject NetworkModel _networkModel; + @Inject + NicSecondaryIpDao _nicSecondaryIpDao; + + @Inject + PortForwardingRulesDao _portForwardingDao; + @Inject + HostDao _hostDao; + @Inject + HostPodDao _hostPodDao; + int _cidrLimit; boolean _allowSubdomainNetworkAccess; @@ -449,6 +470,138 @@ public class NetworkServiceImpl extends ManagerBase implements NetworkService { } + public String allocateSecondaryGuestIP (Account ipOwner, long zoneId, Long nicId, Long networkId, String requestedIp) throws InsufficientAddressCapacityException { + + Long accountId = null; + Long domainId = null; + Long vmId = null; + String ipaddr = null; + + if (networkId == null) { + throw new InvalidParameterValueException("Invalid network id is given"); + } + + Network network = _networksDao.findById(networkId); + if (network == null) { + throw new InvalidParameterValueException("Invalid network id is given"); + } + accountId = network.getAccountId(); + domainId = network.getDomainId(); + + // verify permissions + _accountMgr.checkAccess(ipOwner, null, true, network); + + //check whether the nic belongs to user vm. + NicVO nicVO = _nicDao.findById(nicId); + if (nicVO == null) { + throw new InvalidParameterValueException("There is no nic for the " + nicId); + } + + if (nicVO.getVmType() != VirtualMachine.Type.User) { + throw new InvalidParameterValueException("The nic is not belongs to user vm"); + } + + DataCenter dc = _dcDao.findById(network.getDataCenterId()); + Long id = nicVO.getInstanceId(); + + DataCenter zone = _configMgr.getZone(zoneId); + if (zone == null) { + throw new InvalidParameterValueException("Invalid zone Id is given"); + } + + s_logger.debug("Calling the ip allocation ..."); + if (dc.getNetworkType() == NetworkType.Advanced && network.getGuestType() == Network.GuestType.Isolated) { + try { + ipaddr = _networkMgr.allocateGuestIP(ipOwner, false, zoneId, networkId, requestedIp); + } catch (InsufficientAddressCapacityException e) { + throw new InvalidParameterValueException("Allocating guest ip for nic failed"); + } + } else { + throw new InvalidParameterValueException("AddIpToVMNic is not supported in this network..."); + } + + if (ipaddr != null) { + // we got the ip addr so up the nics table and secodary ip + Transaction txn = Transaction.currentTxn(); + txn.start(); + + boolean nicSecondaryIpSet = nicVO.getSecondaryIp(); + if (!nicSecondaryIpSet) { + nicVO.setSecondaryIp(true); + // commit when previously set ?? + s_logger.debug("Setting nics table ..."); + _nicDao.update(nicId, nicVO); + } + + s_logger.debug("Setting nic_secondary_ip table ..."); + vmId = nicVO.getInstanceId(); + NicSecondaryIpVO secondaryIpVO = new NicSecondaryIpVO(nicId, ipaddr, vmId, accountId, domainId, networkId); + _nicSecondaryIpDao.persist(secondaryIpVO); + txn.commit(); + } + return ipaddr; + } + + @DB + public boolean releaseSecondaryIpFromNic (long ipAddressId) { + Account caller = UserContext.current().getCaller(); + boolean success = false; + + // Verify input parameters + NicSecondaryIpVO ipVO= _nicSecondaryIpDao.findById(ipAddressId); + if (ipVO == null) { + throw new InvalidParameterValueException("Unable to find ip address by id"); + } + + Network network = _networksDao.findById(ipVO.getNetworkId()); + + // verify permissions + _accountMgr.checkAccess(caller, null, true, network); + + Long nicId = ipVO.getNicId(); + s_logger.debug("ip id and nic id" + ipAddressId + "..." + nicId); + //check is this the last secondary ip for NIC + List ipList = _nicSecondaryIpDao.listByNicId(nicId); + boolean lastIp = false; + if (ipList.size() == 1) { + // this is the last secondary ip to nic + lastIp = true; + } + //check PF or static NAT is configured on this ip address + String secondaryIp = ipVO.getIp4Address(); + List pfRuleList = _portForwardingDao.listByDestIpAddr(secondaryIp); + if (pfRuleList.size() != 0) { + s_logger.debug("VM nic IP " + secondaryIp + " is associated with the port forwarding rule"); + throw new InvalidParameterValueException("Can't remove the secondary ip " + secondaryIp + " is associate with the port forwarding rule"); + } + //check if the secondary ip associated with any static nat rule + IPAddressVO publicIpVO = _ipAddressDao.findByVmIp(secondaryIp); + if (publicIpVO != null) { + s_logger.debug("VM nic IP " + secondaryIp + " is associated with the static NAT rule public IP address id " + publicIpVO.getId()); + throw new InvalidParameterValueException("Can' remove the ip " + secondaryIp + "is associate with static NAT rule public IP address id " + publicIpVO.getId()); + } + success = removeNicSecondaryIP(ipVO, lastIp); + return success; + } + + boolean removeNicSecondaryIP(NicSecondaryIpVO ipVO, boolean lastIp) { + Transaction txn = Transaction.currentTxn(); + long nicId = ipVO.getNicId(); + NicVO nic = _nicDao.findById(nicId); + + txn.start(); + + if (lastIp) { + nic.setSecondaryIp(false); + s_logger.debug("Setting nics secondary ip to false ..."); + _nicDao.update(nicId, nic); + } + + s_logger.debug("Revoving nic secondary ip entry ..."); + _nicSecondaryIpDao.remove(ipVO.getId()); + txn.commit(); + return true; + } @Override @DB @@ -3025,4 +3178,21 @@ public class NetworkServiceImpl extends ManagerBase implements NetworkService { public Network getNetwork(String networkUuid) { return _networksDao.findByUuid(networkUuid); } + + @Override + public List listNics(ListNicsCmd cmd) { + Account caller = UserContext.current().getCaller(); + Long nicId = cmd.getNicId(); + Long vmId = cmd.getVmId(); + + UserVmVO userVm = _userVmDao.findById(vmId); + + if (userVm == null) { + InvalidParameterValueException ex = new InvalidParameterValueException("Virtual mahine id does not exist"); + ex.addProxyObject(userVm, vmId, "vmId"); + throw ex; + } + _accountMgr.checkAccess(caller, null, true, userVm); + return _networkMgr.listVmNics(vmId, nicId); + } } diff --git a/server/src/com/cloud/network/addr/PublicIp.java b/server/src/com/cloud/network/addr/PublicIp.java index 61351a64050..8217e4e47b9 100644 --- a/server/src/com/cloud/network/addr/PublicIp.java +++ b/server/src/com/cloud/network/addr/PublicIp.java @@ -211,18 +211,28 @@ public class PublicIp implements PublicIpAddress { _addr.setVpcId(vpcId); } - @Override - public String getIp6Gateway() { - return _vlan.getIp6Gateway(); - } + @Override + public String getIp6Gateway() { + return _vlan.getIp6Gateway(); + } - @Override - public String getIp6Cidr() { - return _vlan.getIp6Cidr(); - } + @Override + public String getIp6Cidr() { + return _vlan.getIp6Cidr(); + } - @Override - public String getIp6Range() { - return _vlan.getIp6Range(); - } + @Override + public String getIp6Range() { + return _vlan.getIp6Range(); + } + + @Override + public String getVmIp() { + return _addr.getVmIp(); + } + + @Override + public void setVmIp(String vmIp) { + _addr.setVmIp(vmIp); + } } diff --git a/server/src/com/cloud/network/dao/IPAddressDao.java b/server/src/com/cloud/network/dao/IPAddressDao.java index 9cdb975d208..1052639ebf2 100755 --- a/server/src/com/cloud/network/dao/IPAddressDao.java +++ b/server/src/com/cloud/network/dao/IPAddressDao.java @@ -62,5 +62,8 @@ public interface IPAddressDao extends GenericDao { long countFreePublicIPs(); - long countFreeIPsInNetwork(long networkId); -} + long countFreeIPsInNetwork(long networkId); + IPAddressVO findByVmIp(String vmIp); + + IPAddressVO findByAssociatedVmIdAndVmIp(long vmId, String vmIp); +} diff --git a/server/src/com/cloud/network/dao/IPAddressDaoImpl.java b/server/src/com/cloud/network/dao/IPAddressDaoImpl.java index e7067d98156..691e46047de 100755 --- a/server/src/com/cloud/network/dao/IPAddressDaoImpl.java +++ b/server/src/com/cloud/network/dao/IPAddressDaoImpl.java @@ -80,6 +80,7 @@ public class IPAddressDaoImpl extends GenericDaoBase implemen AllFieldsSearch.and("sourcenetwork", AllFieldsSearch.entity().getSourceNetworkId(), Op.EQ); AllFieldsSearch.and("physicalNetworkId", AllFieldsSearch.entity().getPhysicalNetworkId(), Op.EQ); AllFieldsSearch.and("vpcId", AllFieldsSearch.entity().getVpcId(), Op.EQ); + AllFieldsSearch.and("associatedVmIp", AllFieldsSearch.entity().getVmIp(), Op.EQ); AllFieldsSearch.done(); VlanDbIdSearchUnallocated = createSearchBuilder(); @@ -232,6 +233,12 @@ public class IPAddressDaoImpl extends GenericDaoBase implemen return findOneBy(sc); } + @Override + public IPAddressVO findByVmIp(String vmIp) { + SearchCriteria sc = AllFieldsSearch.create(); + sc.setParameters("associatedVmIp", vmIp); + return findOneBy(sc); + } @Override public int countIPs(long dcId, long vlanId, boolean onlyCountAllocated) { SearchCriteria sc = onlyCountAllocated ? AllocatedIpCount.create() : AllIpCount.create(); @@ -347,5 +354,13 @@ public class IPAddressDaoImpl extends GenericDaoBase implemen boolean result = super.remove(id); txn.commit(); return result; - } + } + + @Override + public IPAddressVO findByAssociatedVmIdAndVmIp(long vmId, String vmIp) { + SearchCriteria sc = AllFieldsSearch.create(); + sc.setParameters("associatedWithVmId", vmId); + sc.setParameters("associatedVmIp", vmIp); + return findOneBy(sc); + } } diff --git a/server/src/com/cloud/network/dao/IPAddressVO.java b/server/src/com/cloud/network/dao/IPAddressVO.java index 00da5eb9a39..8ce8d3382b2 100644 --- a/server/src/com/cloud/network/dao/IPAddressVO.java +++ b/server/src/com/cloud/network/dao/IPAddressVO.java @@ -112,6 +112,10 @@ public class IPAddressVO implements IpAddress { @Column(name="vpc_id") private Long vpcId; + @Column(name="dnat_vmip") + private String vmIp; + + protected IPAddressVO() { this.uuid = UUID.randomUUID().toString(); } @@ -288,8 +292,18 @@ public class IPAddressVO implements IpAddress { return vpcId; } - @Override + @Override public void setVpcId(Long vpcId) { this.vpcId = vpcId; } + + @Override + public String getVmIp() { + return vmIp; + } + + @Override + public void setVmIp(String vmIp) { + this.vmIp = vmIp; + } } diff --git a/server/src/com/cloud/network/lb/LoadBalancingRulesManagerImpl.java b/server/src/com/cloud/network/lb/LoadBalancingRulesManagerImpl.java index 531a42805b6..9230f6e0ac3 100755 --- a/server/src/com/cloud/network/lb/LoadBalancingRulesManagerImpl.java +++ b/server/src/com/cloud/network/lb/LoadBalancingRulesManagerImpl.java @@ -623,7 +623,7 @@ public class LoadBalancingRulesManagerImpl extends ManagerBase implements Set mappedInstanceIds = new HashSet(); for (LoadBalancerVMMapVO mappedInstance : mappedInstances) { mappedInstanceIds.add(Long.valueOf(mappedInstance.getInstanceId())); - } + } List vmsToAdd = new ArrayList(); diff --git a/server/src/com/cloud/network/router/VirtualNetworkApplianceManagerImpl.java b/server/src/com/cloud/network/router/VirtualNetworkApplianceManagerImpl.java index 0e2eb63793c..b387ab5934c 100755 --- a/server/src/com/cloud/network/router/VirtualNetworkApplianceManagerImpl.java +++ b/server/src/com/cloud/network/router/VirtualNetworkApplianceManagerImpl.java @@ -2318,8 +2318,7 @@ public class VirtualNetworkApplianceManagerImpl extends ManagerBase implements V if (_networkModel.isProviderSupportServiceInNetwork(guestNetworkId, Service.StaticNat, provider)) { if (ip.isOneToOneNat()) { - String dstIp = _networkModel.getIpInNetwork(ip.getAssociatedWithVmId(), guestNetworkId); - StaticNatImpl staticNat = new StaticNatImpl(ip.getAccountId(), ip.getDomainId(), guestNetworkId, ip.getId(), dstIp, false); + StaticNatImpl staticNat = new StaticNatImpl(ip.getAccountId(), ip.getDomainId(), guestNetworkId, ip.getId(), ip.getVmIp(), false); staticNats.add(staticNat); } } diff --git a/server/src/com/cloud/network/rules/RulesManagerImpl.java b/server/src/com/cloud/network/rules/RulesManagerImpl.java index 614d30820b4..29ed5f36d5e 100755 --- a/server/src/com/cloud/network/rules/RulesManagerImpl.java +++ b/server/src/com/cloud/network/rules/RulesManagerImpl.java @@ -78,10 +78,13 @@ import com.cloud.utils.db.Transaction; import com.cloud.utils.exception.CloudRuntimeException; import com.cloud.utils.net.Ip; import com.cloud.vm.Nic; +import com.cloud.vm.NicSecondaryIp; import com.cloud.vm.UserVmVO; import com.cloud.vm.VirtualMachine; import com.cloud.vm.VirtualMachine.Type; import com.cloud.vm.dao.NicDao; +import com.cloud.vm.dao.NicSecondaryIpDao; +import com.cloud.vm.dao.NicSecondaryIpVO; import com.cloud.vm.dao.UserVmDao; @Component @@ -123,6 +126,8 @@ public class RulesManagerImpl extends ManagerBase implements RulesManager, Rules ResourceTagDao _resourceTagDao; @Inject VpcManager _vpcMgr; + @Inject + NicSecondaryIpDao _nicSecondaryDao; @Override public void checkIpAndUserVm(IpAddress ipAddress, UserVm userVm, Account caller) { @@ -172,7 +177,7 @@ public class RulesManagerImpl extends ManagerBase implements RulesManager, Rules @Override @DB @ActionEvent(eventType = EventTypes.EVENT_NET_RULE_ADD, eventDescription = "creating forwarding rule", create = true) - public PortForwardingRule createPortForwardingRule(PortForwardingRule rule, Long vmId, boolean openFirewall) + public PortForwardingRule createPortForwardingRule(PortForwardingRule rule, Long vmId, Ip vmIp, boolean openFirewall) throws NetworkRuleConflictException { UserContext ctx = UserContext.current(); Account caller = ctx.getCaller(); @@ -192,6 +197,7 @@ public class RulesManagerImpl extends ManagerBase implements RulesManager, Rules Network network = _networkModel.getNetwork(networkId); //associate ip address to network (if needed) boolean performedIpAssoc = false; + Nic guestNic; if (ipAddress.getAssociatedWithNetworkId() == null) { boolean assignToVpcNtwk = network.getVpcId() != null && ipAddress.getVpcId() != null && ipAddress.getVpcId().longValue() == network.getVpcId(); @@ -244,13 +250,26 @@ public class RulesManagerImpl extends ManagerBase implements RulesManager, Rules // Verify that vm has nic in the network Ip dstIp = rule.getDestinationIpAddress(); - Nic guestNic = _networkModel.getNicInNetwork(vmId, networkId); + guestNic = _networkModel.getNicInNetwork(vmId, networkId); if (guestNic == null || guestNic.getIp4Address() == null) { throw new InvalidParameterValueException("Vm doesn't belong to network associated with ipAddress"); } else { dstIp = new Ip(guestNic.getIp4Address()); } + if (vmIp != null) { + //vm ip is passed so it can be primary or secondary ip addreess. + if (!dstIp.equals(vmIp)) { + //the vm ip is secondary ip to the nic. + // is vmIp is secondary ip or not + NicSecondaryIp secondaryIp = _nicSecondaryDao.findByIp4AddressAndNicId(vmIp.toString(), guestNic.getId()); + if (secondaryIp == null) { + throw new InvalidParameterValueException("IP Address is not in the VM nic's network "); + } + dstIp = vmIp; + } + } + //if start port and end port are passed in, and they are not equal to each other, perform the validation boolean validatePortRange = false; if (rule.getSourcePortStart().intValue() != rule.getSourcePortEnd().intValue() @@ -350,8 +369,8 @@ public class RulesManagerImpl extends ManagerBase implements RulesManager, Rules throw new InvalidParameterValueException("Can't create ip forwarding rules for the network where elasticIP service is enabled"); } - String dstIp = _networkModel.getIpInNetwork(ipAddress.getAssociatedWithVmId(), networkId); - + //String dstIp = _networkModel.getIpInNetwork(ipAddress.getAssociatedWithVmId(), networkId); + String dstIp = ipAddress.getVmIp(); Transaction txn = Transaction.currentTxn(); txn.start(); @@ -397,14 +416,13 @@ public class RulesManagerImpl extends ManagerBase implements RulesManager, Rules @Override @ActionEvent(eventType = EventTypes.EVENT_ENABLE_STATIC_NAT, eventDescription = "enabling static nat") - public boolean enableStaticNat(long ipId, long vmId, long networkId, boolean isSystemVm) + public boolean enableStaticNat(long ipId, long vmId, long networkId, boolean isSystemVm, String vmGuestIp) throws NetworkRuleConflictException, ResourceUnavailableException { UserContext ctx = UserContext.current(); Account caller = ctx.getCaller(); UserContext.current().setEventDetails("Ip Id: " + ipId); // Verify input parameters - IPAddressVO ipAddress = _ipAddressDao.findById(ipId); if (ipAddress == null) { throw new InvalidParameterValueException("Unable to find ip address by id " + ipId); @@ -414,6 +432,10 @@ public class RulesManagerImpl extends ManagerBase implements RulesManager, Rules boolean performedIpAssoc = false; boolean isOneToOneNat = ipAddress.isOneToOneNat(); Long associatedWithVmId = ipAddress.getAssociatedWithVmId(); + Nic guestNic; + NicSecondaryIpVO nicSecIp = null; + String dstIp = null; + try { Network network = _networkModel.getNetwork(networkId); if (network == null) { @@ -421,11 +443,11 @@ public class RulesManagerImpl extends ManagerBase implements RulesManager, Rules } // Check that vm has a nic in the network - Nic guestNic = _networkModel.getNicInNetwork(vmId, networkId); + guestNic = _networkModel.getNicInNetwork(vmId, networkId); if (guestNic == null) { throw new InvalidParameterValueException("Vm doesn't belong to the network with specified id"); } - + dstIp = guestNic.getIp4Address(); if (!_networkModel.areServicesSupportedInNetwork(network.getId(), Service.StaticNat)) { throw new InvalidParameterValueException("Unable to create static nat rule; StaticNat service is not " + @@ -466,13 +488,36 @@ public class RulesManagerImpl extends ManagerBase implements RulesManager, Rules // Check permissions checkIpAndUserVm(ipAddress, vm, caller); + //is static nat is for vm secondary ip + //dstIp = guestNic.getIp4Address(); + if (vmGuestIp != null) { + //dstIp = guestNic.getIp4Address(); + + if (!dstIp.equals(vmGuestIp)) { + //check whether the secondary ip set to the vm or not + boolean secondaryIpSet = _networkMgr.isSecondaryIpSetForNic(guestNic.getId()); + if (!secondaryIpSet) { + throw new InvalidParameterValueException("VM ip " + vmGuestIp + " address not belongs to the vm"); + } + //check the ip belongs to the vm or not + nicSecIp = _nicSecondaryDao.findByIp4AddressAndNicId(vmGuestIp, guestNic.getId()); + if (nicSecIp == null) { + throw new InvalidParameterValueException("VM ip " + vmGuestIp + " address not belongs to the vm"); + } + dstIp = nicSecIp.getIp4Address(); + // Set public ip column with the vm ip + } + } + // Verify ip address parameter - isIpReadyForStaticNat(vmId, ipAddress, caller, ctx.getCallerUserId()); + // checking vm id is not sufficient, check for the vm ip + isIpReadyForStaticNat(vmId, ipAddress, dstIp, caller, ctx.getCallerUserId()); } ipAddress.setOneToOneNat(true); ipAddress.setAssociatedWithVmId(vmId); + ipAddress.setVmIp(dstIp); if (_ipAddressDao.update(ipAddress.getId(), ipAddress)) { // enable static nat on the backend s_logger.trace("Enabling static nat for ip address " + ipAddress + " and vm id=" + vmId + " on the backend"); @@ -483,6 +528,7 @@ public class RulesManagerImpl extends ManagerBase implements RulesManager, Rules s_logger.warn("Failed to enable static nat rule for ip address " + ipId + " on the backend"); ipAddress.setOneToOneNat(isOneToOneNat); ipAddress.setAssociatedWithVmId(associatedWithVmId); + ipAddress.setVmIp(null); _ipAddressDao.update(ipAddress.getId(), ipAddress); } } else { @@ -490,16 +536,17 @@ public class RulesManagerImpl extends ManagerBase implements RulesManager, Rules } } finally { - if (performedIpAssoc) { - //if the rule is the last one for the ip address assigned to VPC, unassign it from the network - IpAddress ip = _ipAddressDao.findById(ipAddress.getId()); - _vpcMgr.unassignIPFromVpcNetwork(ip.getId(), networkId); + if (performedIpAssoc) { + //if the rule is the last one for the ip address assigned to VPC, unassign it from the network + IpAddress ip = _ipAddressDao.findById(ipAddress.getId()); + _vpcMgr.unassignIPFromVpcNetwork(ip.getId(), networkId); } } return false; } - protected void isIpReadyForStaticNat(long vmId, IPAddressVO ipAddress, Account caller, long callerUserId) + protected void isIpReadyForStaticNat(long vmId, IPAddressVO ipAddress, + String vmIp, Account caller, long callerUserId) throws NetworkRuleConflictException, ResourceUnavailableException { if (ipAddress.isSourceNat()) { throw new InvalidParameterValueException("Can't enable static, ip address " + ipAddress + " is a sourceNat ip address"); @@ -519,7 +566,8 @@ public class RulesManagerImpl extends ManagerBase implements RulesManager, Rules throw new NetworkRuleConflictException("Failed to enable static for the ip address " + ipAddress + " and vm id=" + vmId + " as it's already assigned to antoher vm"); } - IPAddressVO oldIP = _ipAddressDao.findByAssociatedVmId(vmId); + //check wether the vm ip is alreday associated with any public ip address + IPAddressVO oldIP = _ipAddressDao.findByAssociatedVmIdAndVmIp(vmId, vmIp); if (oldIP != null) { // If elasticIP functionality is supported in the network, we always have to disable static nat on the old @@ -538,9 +586,9 @@ public class RulesManagerImpl extends ManagerBase implements RulesManager, Rules if (!reassignStaticNat) { throw new InvalidParameterValueException("Failed to enable static nat for the ip address id=" + ipAddress.getId() + " as vm id=" + vmId + " is already associated with ip id=" + oldIP.getId()); } - // unassign old static nat rule - s_logger.debug("Disassociating static nat for ip " + oldIP); - if (!disableStaticNat(oldIP.getId(), caller, callerUserId, true)) { + // unassign old static nat rule + s_logger.debug("Disassociating static nat for ip " + oldIP); + if (!disableStaticNat(oldIP.getId(), caller, callerUserId, true)) { throw new CloudRuntimeException("Failed to disable old static nat rule for vm id=" + vmId + " and ip " + oldIP); } } @@ -890,8 +938,8 @@ public class RulesManagerImpl extends ManagerBase implements RulesManager, Rules List staticNats = new ArrayList(); for (IPAddressVO ip : ips) { // Get nic IP4 address - String dstIp = _networkModel.getIpInNetwork(ip.getAssociatedWithVmId(), networkId); - StaticNatImpl staticNat = new StaticNatImpl(ip.getAllocatedToAccountId(), ip.getAllocatedInDomainId(), networkId, ip.getId(), dstIp, false); + //String dstIp = _networkModel.getIpInNetwork(ip.getAssociatedWithVmId(), networkId); + StaticNatImpl staticNat = new StaticNatImpl(ip.getAllocatedToAccountId(), ip.getAllocatedInDomainId(), networkId, ip.getId(), ip.getVmIp(), false); staticNats.add(staticNat); } @@ -1209,6 +1257,7 @@ public class RulesManagerImpl extends ManagerBase implements RulesManager, Rules boolean isIpSystem = ipAddress.getSystem(); ipAddress.setOneToOneNat(false); ipAddress.setAssociatedWithVmId(null); + ipAddress.setVmIp(null); if (isIpSystem && !releaseIpIfElastic) { ipAddress.setSystem(false); } @@ -1248,11 +1297,11 @@ public class RulesManagerImpl extends ManagerBase implements RulesManager, Rules throw ex; } - String dstIp; - if (forRevoke) { - dstIp = _networkModel.getIpInNetworkIncludingRemoved(ip.getAssociatedWithVmId(), rule.getNetworkId()); - } else { - dstIp = _networkModel.getIpInNetwork(ip.getAssociatedWithVmId(), rule.getNetworkId()); + String dstIp = ip.getVmIp(); + if (dstIp == null) { + InvalidParameterValueException ex = new InvalidParameterValueException("VM ip address of the specified public ip is not set "); + ex.addProxyObject(ruleVO, rule.getId(), "ruleId"); + throw ex; } return new StaticNatRuleImpl(ruleVO, dstIp); @@ -1333,12 +1382,16 @@ public class RulesManagerImpl extends ManagerBase implements RulesManager, Rules // create new static nat rule // Get nic IP4 address + Nic guestNic = _networkModel.getNicInNetwork(vm.getId(), networkId); + if (guestNic == null) { + throw new InvalidParameterValueException("Vm doesn't belong to the network with specified id"); + } String dstIp; - if (forRevoke) { - dstIp = _networkModel.getIpInNetworkIncludingRemoved(sourceIp.getAssociatedWithVmId(), networkId); - } else { - dstIp = _networkModel.getIpInNetwork(sourceIp.getAssociatedWithVmId(), networkId); + + dstIp = sourceIp.getVmIp(); + if (dstIp == null) { + throw new InvalidParameterValueException("Vm ip is not set as dnat ip for this public ip"); } StaticNatImpl staticNat = new StaticNatImpl(sourceIp.getAllocatedToAccountId(), sourceIp.getAllocatedInDomainId(), @@ -1373,7 +1426,7 @@ public class RulesManagerImpl extends ManagerBase implements RulesManager, Rules boolean isSystemVM = (vm.getType() == Type.ConsoleProxy || vm.getType() == Type.SecondaryStorageVm); try { - success = enableStaticNat(ip.getId(), vm.getId(), guestNetwork.getId(), isSystemVM); + success = enableStaticNat(ip.getId(), vm.getId(), guestNetwork.getId(), isSystemVM, null); } catch (NetworkRuleConflictException ex) { s_logger.warn("Failed to enable static nat as a part of enabling elasticIp and staticNat for vm " + vm + " in guest network " + guestNetwork + " due to exception ", ex); diff --git a/server/src/com/cloud/network/rules/dao/PortForwardingRulesDao.java b/server/src/com/cloud/network/rules/dao/PortForwardingRulesDao.java index 91f08e7a8ff..682a941856c 100644 --- a/server/src/com/cloud/network/rules/dao/PortForwardingRulesDao.java +++ b/server/src/com/cloud/network/rules/dao/PortForwardingRulesDao.java @@ -41,5 +41,7 @@ public interface PortForwardingRulesDao extends GenericDao listByNetwork(long networkId); List listByAccount(long accountId); + + List listByDestIpAddr(String ip4Address); } diff --git a/server/src/com/cloud/network/rules/dao/PortForwardingRulesDaoImpl.java b/server/src/com/cloud/network/rules/dao/PortForwardingRulesDaoImpl.java index 5406ab624e0..1d2e991ab39 100644 --- a/server/src/com/cloud/network/rules/dao/PortForwardingRulesDaoImpl.java +++ b/server/src/com/cloud/network/rules/dao/PortForwardingRulesDaoImpl.java @@ -32,6 +32,7 @@ import com.cloud.utils.db.GenericDaoBase; import com.cloud.utils.db.SearchBuilder; import com.cloud.utils.db.SearchCriteria; import com.cloud.utils.db.SearchCriteria.Op; +import com.cloud.vm.dao.NicSecondaryIpVO; @Component @Local(value=PortForwardingRulesDao.class) @@ -55,6 +56,7 @@ public class PortForwardingRulesDaoImpl extends GenericDaoBase listByDestIpAddr(String ip4Address) { + SearchCriteria sc = AllFieldsSearch.create(); + sc.setParameters("address", ip4Address); + return listBy(sc); + } } diff --git a/server/src/com/cloud/server/ManagementServerImpl.java b/server/src/com/cloud/server/ManagementServerImpl.java index e80d48c6512..95b2973a384 100755 --- a/server/src/com/cloud/server/ManagementServerImpl.java +++ b/server/src/com/cloud/server/ManagementServerImpl.java @@ -2165,6 +2165,9 @@ public class ManagementServerImpl extends ManagerBase implements ManagementServe cmdList.add(CreateVMSnapshotCmd.class); cmdList.add(RevertToSnapshotCmd.class); cmdList.add(DeleteVMSnapshotCmd.class); + cmdList.add(AddIpToVmNicCmd.class); + cmdList.add(RemoveIpFromVmNicCmd.class); + cmdList.add(ListNicsCmd.class); return cmdList; } diff --git a/server/src/com/cloud/vm/NicVO.java b/server/src/com/cloud/vm/NicVO.java index 8e2edda40aa..987596cff32 100644 --- a/server/src/com/cloud/vm/NicVO.java +++ b/server/src/com/cloud/vm/NicVO.java @@ -122,6 +122,9 @@ public class NicVO implements Nic { @Column(name = "uuid") String uuid = UUID.randomUUID().toString(); + @Column(name = "secondary_ip") + boolean secondaryIp; + public NicVO(String reserver, Long instanceId, long configurationId, VirtualMachine.Type vmType) { this.reserver = reserver; this.instanceId = instanceId; @@ -349,4 +352,12 @@ public class NicVO implements Nic { public void setIp6Cidr(String ip6Cidr) { this.ip6Cidr = ip6Cidr; } + + public boolean getSecondaryIp() { + return secondaryIp; + } + + public void setSecondaryIp(boolean secondaryIp) { + this.secondaryIp = secondaryIp; + } } diff --git a/server/src/com/cloud/vm/UserVmManagerImpl.java b/server/src/com/cloud/vm/UserVmManagerImpl.java index ce53c4579fd..ca9c13fd1c4 100755 --- a/server/src/com/cloud/vm/UserVmManagerImpl.java +++ b/server/src/com/cloud/vm/UserVmManagerImpl.java @@ -360,6 +360,7 @@ public class UserVmManagerImpl extends ManagerBase implements UserVmManager, Use protected ProjectManager _projectMgr; @Inject protected ResourceManager _resourceMgr; + @Inject protected NetworkServiceMapDao _ntwkSrvcDao; @Inject @@ -1359,6 +1360,13 @@ public class UserVmManagerImpl extends ManagerBase implements UserVmManager, Use + " as a part of vm id=" + vmId + " expunge because resource is unavailable", e); } + //remove vm secondary ip addresses + if (_networkMgr.removeVmSecondaryIps(vmId)) { + s_logger.debug("Removed vm " + vmId + " secondary ip address of the VM Nics as a part of expunge process"); + } else { + success = false; + s_logger.warn("Fail to remove secondary ip address of vm " + vmId + " Nics as a part of expunge process"); + } return success; } diff --git a/server/src/com/cloud/vm/dao/NicDao.java b/server/src/com/cloud/vm/dao/NicDao.java index 762048b65bf..794bacc6e8b 100644 --- a/server/src/com/cloud/vm/dao/NicDao.java +++ b/server/src/com/cloud/vm/dao/NicDao.java @@ -58,4 +58,6 @@ public interface NicDao extends GenericDao { NicVO findByNetworkIdInstanceIdAndBroadcastUri(long networkId, long instanceId, String broadcastUri); NicVO findByIp4AddressAndNetworkIdAndInstanceId(long networkId, long instanceId, String ip4Address); + + List listByVmIdAndNicId(Long vmId, Long nicId); } diff --git a/server/src/com/cloud/vm/dao/NicDaoImpl.java b/server/src/com/cloud/vm/dao/NicDaoImpl.java index 5cf152f9f90..44911740e96 100644 --- a/server/src/com/cloud/vm/dao/NicDaoImpl.java +++ b/server/src/com/cloud/vm/dao/NicDaoImpl.java @@ -53,8 +53,10 @@ public class NicDaoImpl extends GenericDaoBase implements NicDao { AllFieldsSearch.and("address", AllFieldsSearch.entity().getIp4Address(), Op.EQ); AllFieldsSearch.and("isDefault", AllFieldsSearch.entity().isDefaultNic(), Op.EQ); AllFieldsSearch.and("broadcastUri", AllFieldsSearch.entity().getBroadcastUri(), Op.EQ); + AllFieldsSearch.and("secondaryip", AllFieldsSearch.entity().getSecondaryIp(), Op.EQ); + AllFieldsSearch.and("nicid", AllFieldsSearch.entity().getId(), Op.EQ); AllFieldsSearch.done(); - + IpSearch = createSearchBuilder(String.class); IpSearch.select(null, Func.DISTINCT, IpSearch.entity().getIp4Address()); IpSearch.and("network", IpSearch.entity().getNetworkId(), Op.EQ); @@ -202,4 +204,12 @@ public class NicDaoImpl extends GenericDaoBase implements NicDao { sc.setParameters("address", ip4Address); return findOneBy(sc); } + + @Override + public List listByVmIdAndNicId(Long vmId, Long nicId) { + SearchCriteria sc = AllFieldsSearch.create(); + sc.setParameters("instance", vmId); + sc.setParameters("nicid", nicId); + return listBy(sc); + } } diff --git a/server/src/com/cloud/vm/dao/NicSecondaryIp.java b/server/src/com/cloud/vm/dao/NicSecondaryIp.java new file mode 100644 index 00000000000..e69de29bb2d diff --git a/server/src/com/cloud/vm/dao/NicSecondaryIpDao.java b/server/src/com/cloud/vm/dao/NicSecondaryIpDao.java new file mode 100644 index 00000000000..da96df43e83 --- /dev/null +++ b/server/src/com/cloud/vm/dao/NicSecondaryIpDao.java @@ -0,0 +1,53 @@ +// 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.vm.dao; + +import java.util.List; +import com.cloud.utils.db.GenericDao; + +public interface NicSecondaryIpDao extends GenericDao { + List listByVmId(long instanceId); + + List listSecondaryIpAddressInNetwork(long networkConfigId); + List listByNetworkId(long networkId); + + NicSecondaryIpVO findByInstanceIdAndNetworkId(long networkId, long instanceId); + + // void removeNicsForInstance(long instanceId); + // void removeSecondaryIpForNic(long nicId); + + NicSecondaryIpVO findByIp4AddressAndNetworkId(String ip4Address, long networkId); + + /** + * @param networkId + * @param instanceId + * @return + */ + + List getSecondaryIpAddressesForVm(long vmId); + + List listByNicId(long nicId); + + List listByNicIdAndVmid(long nicId, long vmId); + + NicSecondaryIpVO findByIp4AddressAndNicId(String ip4Address, long nicId); + + NicSecondaryIpVO findByIp4AddressAndNetworkIdAndInstanceId(long networkId, + Long vmId, String vmIp); + + List getSecondaryIpAddressesForNic(long nicId); +} diff --git a/server/src/com/cloud/vm/dao/NicSecondaryIpDaoImpl.java b/server/src/com/cloud/vm/dao/NicSecondaryIpDaoImpl.java new file mode 100644 index 00000000000..3befaf70529 --- /dev/null +++ b/server/src/com/cloud/vm/dao/NicSecondaryIpDaoImpl.java @@ -0,0 +1,138 @@ +// 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.vm.dao; + +import java.util.ArrayList; +import java.util.List; + +import javax.ejb.Local; + +import org.springframework.stereotype.Component; + +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; + +@Component +@Local(value=NicSecondaryIpDao.class) +public class NicSecondaryIpDaoImpl extends GenericDaoBase implements NicSecondaryIpDao { + private final SearchBuilder AllFieldsSearch; + private final GenericSearchBuilder IpSearch; + + protected NicSecondaryIpDaoImpl() { + super(); + AllFieldsSearch = createSearchBuilder(); + AllFieldsSearch.and("instanceId", AllFieldsSearch.entity().getVmId(), Op.EQ); + AllFieldsSearch.and("network", AllFieldsSearch.entity().getNetworkId(), Op.EQ); + AllFieldsSearch.and("address", AllFieldsSearch.entity().getIp4Address(), Op.EQ); + AllFieldsSearch.and("nicId", AllFieldsSearch.entity().getNicId(), Op.EQ); + AllFieldsSearch.done(); + + IpSearch = createSearchBuilder(String.class); + IpSearch.select(null, Func.DISTINCT, IpSearch.entity().getIp4Address()); + IpSearch.and("network", IpSearch.entity().getNetworkId(), Op.EQ); + IpSearch.and("address", IpSearch.entity().getIp4Address(), Op.NNULL); + IpSearch.done(); + } + + @Override + public List listByVmId(long instanceId) { + SearchCriteria sc = AllFieldsSearch.create(); + sc.setParameters("instanceId", instanceId); + return listBy(sc); + } + + @Override + public List listByNicId(long nicId) { + SearchCriteria sc = AllFieldsSearch.create(); + sc.setParameters("nicId", nicId); + return listBy(sc); + } + + @Override + public List listSecondaryIpAddressInNetwork(long networkId) { + SearchCriteria sc = IpSearch.create(); + sc.setParameters("network", networkId); + return customSearch(sc, null); + } + + @Override + public List listByNetworkId(long networkId) { + SearchCriteria sc = AllFieldsSearch.create(); + sc.setParameters("network", networkId); + return listBy(sc); + } + + @Override + public List listByNicIdAndVmid(long nicId, long vmId) { + SearchCriteria sc = AllFieldsSearch.create(); + sc.setParameters("nicId", nicId); + sc.setParameters("instanceId", vmId); + return listBy(sc); + } + + @Override + public List getSecondaryIpAddressesForVm(long vmId) { + SearchCriteria sc = AllFieldsSearch.create(); + sc.setParameters("instanceId", vmId); + return listBy(sc); + } + + @Override + public List getSecondaryIpAddressesForNic(long nicId) { + SearchCriteria sc = AllFieldsSearch.create(); + sc.setParameters("nicId", nicId); + List results = search(sc, null); + List ips = new ArrayList(results.size()); + for (NicSecondaryIpVO result : results) { + ips.add(result.getIp4Address()); + } + return ips; + } + + @Override + public NicSecondaryIpVO findByInstanceIdAndNetworkId(long networkId, long instanceId) { + // TODO Auto-generated method stub + return null; + } + + @Override + public NicSecondaryIpVO findByIp4AddressAndNetworkId(String ip4Address, long networkId) { + // TODO Auto-generated method stub + return null; + } + @Override + public NicSecondaryIpVO findByIp4AddressAndNicId(String ip4Address, long nicId) { + SearchCriteria sc = AllFieldsSearch.create(); + sc.setParameters("address", ip4Address); + sc.setParameters("nicId", nicId); + return findOneBy(sc); + } + + @Override + public NicSecondaryIpVO findByIp4AddressAndNetworkIdAndInstanceId( + long networkId, Long vmId, String vmIp) { + SearchCriteria sc = AllFieldsSearch.create(); + sc.setParameters("network", networkId); + sc.setParameters("instanceId", vmId); + sc.setParameters("address", vmIp); + return findOneBy(sc); + } +} diff --git a/server/src/com/cloud/vm/dao/NicSecondaryIpVO.java b/server/src/com/cloud/vm/dao/NicSecondaryIpVO.java new file mode 100644 index 00000000000..770e188ad83 --- /dev/null +++ b/server/src/com/cloud/vm/dao/NicSecondaryIpVO.java @@ -0,0 +1,160 @@ +// 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.vm.dao; + +import java.util.Date; +import java.util.UUID; + +import javax.persistence.Column; +import javax.persistence.Entity; +import javax.persistence.GeneratedValue; +import javax.persistence.GenerationType; +import javax.persistence.Id; +import javax.persistence.Table; + +import com.cloud.utils.db.GenericDao; +import com.cloud.vm.NicSecondaryIp; + +@Entity +@Table(name = "nic_secondary_ips") +public class NicSecondaryIpVO implements NicSecondaryIp { + + public NicSecondaryIpVO(Long nicId, String ipaddr, Long vmId, + Long accountId, Long domainId, Long networkId) { + this.nicId = nicId; + this.vmId = vmId; + this.ip4Address = ipaddr; + this.accountId = accountId; + this.domainId = domainId; + this.networkId = networkId; + } + + protected NicSecondaryIpVO() { + } + + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + @Column(name = "id") + long id; + + @Column(name = "nicId") + long nicId; + + @Column(name="domain_id", updatable=false) + long domainId; + + @Column(name="account_id", updatable=false) + private Long accountId; + + @Column(name = "ip4_address") + String ip4Address; + + @Column(name = "ip6_address") + String ip6Address; + + @Column(name = "network_id") + long networkId; + + @Column(name = GenericDao.CREATED_COLUMN) + Date created; + + @Column(name = "uuid") + String uuid = UUID.randomUUID().toString(); + + @Column(name = "vmId") + Long vmId; + + public long getId() { + return id; + } + + public void setId(long id) { + this.id = id; + } + + public long getNicId() { + return nicId; + } + + public void setNicId(long nicId) { + this.nicId = nicId; + } + + public long getDomainId() { + return domainId; + } + + public void setDomainId(Long domainId) { + this.domainId = domainId; + } + + public long getAccountId() { + return accountId; + } + + public void setAccountId(Long accountId) { + this.accountId = accountId; + } + + public String getIp4Address() { + return ip4Address; + } + + public void setIp4Address(String ip4Address) { + this.ip4Address = ip4Address; + } + + public String getIp6Address() { + return ip6Address; + } + + public void setIp6Address(String ip6Address) { + this.ip6Address = ip6Address; + } + + public long getNetworkId() { + return networkId; + } + + public void setNetworkId(long networkId) { + this.networkId = networkId; + } + + public Date getCreated() { + return created; + } + + public void setCreated(Date created) { + this.created = created; + } + + public String getUuid() { + return uuid; + } + + public void setUuid(String uuid) { + this.uuid = uuid; + } + + public long getVmId() { + return vmId; + } + + public void setVmId(Long vmId) { + this.vmId = vmId; + } +} diff --git a/server/test/com/cloud/network/MockNetworkManagerImpl.java b/server/test/com/cloud/network/MockNetworkManagerImpl.java index 3568da57b71..80043102648 100755 --- a/server/test/com/cloud/network/MockNetworkManagerImpl.java +++ b/server/test/com/cloud/network/MockNetworkManagerImpl.java @@ -29,6 +29,7 @@ import org.apache.cloudstack.api.command.user.network.CreateNetworkCmd; import org.apache.cloudstack.api.command.user.network.ListNetworksCmd; import org.apache.cloudstack.api.command.user.network.RestartNetworkCmd; import org.springframework.stereotype.Component; +import org.apache.cloudstack.api.command.user.vm.ListNicsCmd; import com.cloud.dc.DataCenter; import com.cloud.dc.Vlan.VlanType; @@ -64,6 +65,7 @@ import com.cloud.utils.component.Manager; import com.cloud.utils.component.ManagerBase; import com.cloud.vm.Nic; import com.cloud.vm.NicProfile; +import com.cloud.vm.NicSecondaryIp; import com.cloud.vm.ReservationContext; import com.cloud.vm.VMInstanceVO; import com.cloud.vm.VirtualMachine; @@ -824,4 +826,49 @@ public class MockNetworkManagerImpl extends ManagerBase implements NetworkManage // TODO Auto-generated method stub return null; } + + @Override + public boolean isSecondaryIpSetForNic(long nicId) { + // TODO Auto-generated method stub + return false; + } + + @Override + public boolean releaseSecondaryIpFromNic(long ipAddressId) { + // TODO Auto-generated method stub + return false; + } + + @Override + public String allocateSecondaryGuestIP(Account account, long zoneId, + Long nicId, Long networkId, String ipaddress) { + // TODO Auto-generated method stub + return null; + } + + @Override + public String allocateGuestIP(Account ipOwner, boolean isSystem, + long zoneId, Long networkId, String requestedIp) + throws InsufficientAddressCapacityException { + // TODO Auto-generated method stub + return null; + } + + @Override + public boolean removeVmSecondaryIps(long vmId) { + // TODO Auto-generated method stub + return false; + } + + @Override + public List listVmNics(Long vmId, Long nicId) { + // TODO Auto-generated method stub + return null; + } + + @Override + public List listNics(ListNicsCmd listNicsCmd) { + // TODO Auto-generated method stub + return null; + } } diff --git a/server/test/com/cloud/network/MockRulesManagerImpl.java b/server/test/com/cloud/network/MockRulesManagerImpl.java index ba3dd413cc3..e5a6894d76d 100644 --- a/server/test/com/cloud/network/MockRulesManagerImpl.java +++ b/server/test/com/cloud/network/MockRulesManagerImpl.java @@ -39,6 +39,7 @@ import com.cloud.uservm.UserVm; import com.cloud.utils.Pair; import com.cloud.utils.component.Manager; import com.cloud.utils.component.ManagerBase; +import com.cloud.utils.net.Ip; import com.cloud.vm.VirtualMachine; @Local(value = {RulesManager.class, RulesService.class}) @@ -53,14 +54,6 @@ public class MockRulesManagerImpl extends ManagerBase implements RulesManager, R return null; } - @Override - public PortForwardingRule createPortForwardingRule(PortForwardingRule rule, - Long vmId, boolean openFirewall) - throws NetworkRuleConflictException { - // TODO Auto-generated method stub - return null; - } - @Override public boolean revokePortForwardingRule(long ruleId, boolean apply) { // TODO Auto-generated method stub @@ -83,7 +76,7 @@ public class MockRulesManagerImpl extends ManagerBase implements RulesManager, R @Override public boolean enableStaticNat(long ipAddressId, long vmId, long networkId, - boolean isSystemVm) throws NetworkRuleConflictException, + boolean isSystemVm, String ipAddr) throws NetworkRuleConflictException, ResourceUnavailableException { // TODO Auto-generated method stub return false; @@ -310,4 +303,12 @@ public class MockRulesManagerImpl extends ManagerBase implements RulesManager, R return "MockRulesManagerImpl"; } + @Override + public PortForwardingRule createPortForwardingRule(PortForwardingRule rule, + Long vmId, Ip vmIp, boolean openFirewall) + throws NetworkRuleConflictException { + // TODO Auto-generated method stub + return null; + } + } diff --git a/server/test/com/cloud/vpc/MockNetworkManagerImpl.java b/server/test/com/cloud/vpc/MockNetworkManagerImpl.java index 828a555a539..63ef8744be8 100644 --- a/server/test/com/cloud/vpc/MockNetworkManagerImpl.java +++ b/server/test/com/cloud/vpc/MockNetworkManagerImpl.java @@ -29,6 +29,7 @@ import org.apache.cloudstack.api.command.admin.usage.ListTrafficTypeImplementors import org.apache.cloudstack.api.command.user.network.CreateNetworkCmd; import org.apache.cloudstack.api.command.user.network.ListNetworksCmd; import org.apache.cloudstack.api.command.user.network.RestartNetworkCmd; +import org.apache.cloudstack.api.command.user.vm.ListNicsCmd; import org.apache.log4j.Logger; import org.springframework.stereotype.Component; @@ -80,6 +81,7 @@ import com.cloud.utils.component.Manager; import com.cloud.utils.component.ManagerBase; import com.cloud.vm.Nic; import com.cloud.vm.NicProfile; +import com.cloud.vm.NicSecondaryIp; import com.cloud.vm.ReservationContext; import com.cloud.vm.VMInstanceVO; import com.cloud.vm.VirtualMachine; @@ -1317,4 +1319,75 @@ public class MockNetworkManagerImpl extends ManagerBase implements NetworkManage // TODO Auto-generated method stub return null; } + + + + + + @Override + public boolean isSecondaryIpSetForNic(long nicId) { + // TODO Auto-generated method stub + return false; + } + + @Override + public String allocateSecondaryGuestIP(Account account, long zoneId, + Long nicId, Long networkId, String ipaddress) { + // TODO Auto-generated method stub + return null; + } + + + + + + + + @Override + public boolean releaseSecondaryIpFromNic(long ipAddressId) { + // TODO Auto-generated method stub + return false; + } + + + + + + @Override + public String allocateGuestIP(Account ipOwner, boolean isSystem, + long zoneId, Long networkId, String requestedIp) + throws InsufficientAddressCapacityException { + // TODO Auto-generated method stub + return null; + } + + + + + + @Override + public boolean removeVmSecondaryIps(long vmId) { + // TODO Auto-generated method stub + return false; + } + + + + + + @Override + public List listVmNics(Long vmId, Long nicId) { + // TODO Auto-generated method stub + return null; + } + + + + + + @Override + public List listNics(ListNicsCmd listNicsCmd) { + // TODO Auto-generated method stub + return null; + } } diff --git a/setup/db/db/schema-410to420.sql b/setup/db/db/schema-410to420.sql index 4349bd08fc8..6f68e120ac1 100644 --- a/setup/db/db/schema-410to420.sql +++ b/setup/db/db/schema-410to420.sql @@ -112,3 +112,24 @@ INSERT IGNORE INTO `cloud`.`configuration` VALUES ('Advanced', 'DEFAULT', 'UserV -- Re-enable foreign key checking, at the end of the upgrade path SET foreign_key_checks = 1; + + +CREATE TABLE nic_secondary_ips ( + `id` bigint unsigned NOT NULL UNIQUE AUTO_INCREMENT, + `uuid` varchar(40), + `vmId` bigint unsigned COMMENT 'vm instance id', + `nicId` bigint unsigned NOT NULL, + `ip4_address` char(40) COMMENT 'ip4 address', + `ip6_address` char(40) COMMENT 'ip6 address', + `network_id` bigint unsigned NOT NULL COMMENT 'network configuration id', + `created` datetime NOT NULL COMMENT 'date created', + `account_id` bigint unsigned NOT NULL COMMENT 'owner. foreign key to account table', + `domain_id` bigint unsigned NOT NULL COMMENT 'the domain that the owner belongs to', + PRIMARY KEY (`id`), + CONSTRAINT `fk_nic_secondary_ip__vmId` FOREIGN KEY `fk_nic_secondary_ip__vmId`(`vmId`) REFERENCES `vm_instance`(`id`) ON DELETE CASCADE, + CONSTRAINT `fk_nic_secondary_ip__networks_id` FOREIGN KEY `fk_nic_secondary_ip__networks_id`(`network_id`) REFERENCES `networks`(`id`), + CONSTRAINT `uc_nic_secondary_ip__uuid` UNIQUE (`uuid`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8; + +ALTER TABLE `cloud`.`nics` ADD COLUMN secondary_ip SMALLINT DEFAULT '0' COMMENT 'secondary ips configured for the nic'; +ALTER TABLE `cloud`.`user_ip_address` ADD COLUMN dnat_vmip VARCHAR(40);