Skip to content

Commit f3f3f97

Browse files
committed
Store and get IpAddresses details from backup metadata while creating instance from backup
1 parent e2fa0be commit f3f3f97

File tree

13 files changed

+230
-48
lines changed

13 files changed

+230
-48
lines changed

api/src/main/java/com/cloud/network/NetworkService.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -268,4 +268,5 @@ Network createPrivateNetwork(String networkName, String displayText, long physic
268268
InternalLoadBalancerElementService getInternalLoadBalancerElementByNetworkServiceProviderId(long networkProviderId);
269269
InternalLoadBalancerElementService getInternalLoadBalancerElementById(long providerId);
270270
List<InternalLoadBalancerElementService> getInternalLoadBalancerElements();
271+
IpAddresses getIpAddressesFromIps(String ipAddress, String ip6Address, String macAddress);
271272
}

api/src/main/java/org/apache/cloudstack/api/ApiConstants.java

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -285,6 +285,7 @@ public class ApiConstants {
285285
public static final String IP_ADDRESS = "ipaddress";
286286
public static final String IP_ADDRESSES = "ipaddresses";
287287
public static final String IP6_ADDRESS = "ip6address";
288+
public static final String IP6_ADDRESSES = "ip6addresses";
288289
public static final String IP_ADDRESS_ID = "ipaddressid";
289290
public static final String IS_2FA_ENABLED = "is2faenabled";
290291
public static final String IS_2FA_VERIFIED = "is2faverified";
@@ -333,6 +334,7 @@ public class ApiConstants {
333334
public static final String LBID = "lbruleid";
334335
public static final String LB_PROVIDER = "lbprovider";
335336
public static final String MAC_ADDRESS = "macaddress";
337+
public static final String MAC_ADDRESSES = "macaddresses";
336338
public static final String MAX = "max";
337339
public static final String MAX_SNAPS = "maxsnaps";
338340
public static final String MAX_BACKUPS = "maxbackups";
@@ -417,6 +419,7 @@ public class ApiConstants {
417419
public static final String POST_URL = "postURL";
418420
public static final String POWER_STATE = "powerstate";
419421
public static final String PRECEDENCE = "precedence";
422+
public static final String PRESERVE_IP = "preserveip";
420423
public static final String PRIVATE_INTERFACE = "privateinterface";
421424
public static final String PRIVATE_IP = "privateip";
422425
public static final String PRIVATE_PORT = "privateport";

api/src/main/java/org/apache/cloudstack/api/command/user/vm/CreateVMFromBackupCmd.java

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,9 @@ public class CreateVMFromBackupCmd extends BaseDeployVMCmd {
7272
@Parameter(name = ApiConstants.TEMPLATE_ID, type = CommandType.UUID, entityType = TemplateResponse.class, description = "the ID of the template for the virtual machine")
7373
private Long templateId;
7474

75+
@Parameter(name = ApiConstants.PRESERVE_IP, type = CommandType.BOOLEAN, description = "Use the same IP/MAC addresses as stored in the backup metadata. Works only if the original Instance is deleted and the IP/MAC address is avaialable.")
76+
private Boolean preserveIp;
77+
7578
/////////////////////////////////////////////////////
7679
/////////////////// Accessors ///////////////////////
7780
/////////////////////////////////////////////////////
@@ -88,6 +91,10 @@ public Long getTemplateId() {
8891
return templateId;
8992
}
9093

94+
public boolean getPreserveIp() {
95+
return (preserveIp != null) ? preserveIp : false;
96+
}
97+
9198
@Override
9299
public void create() {
93100
UserVm vm;

api/src/main/java/org/apache/cloudstack/backup/BackupManager.java

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@
3333
import org.apache.cloudstack.framework.config.Configurable;
3434

3535
import com.cloud.exception.ResourceUnavailableException;
36+
import com.cloud.network.Network;
3637
import com.cloud.offering.DiskOfferingInfo;
3738
import com.cloud.utils.Pair;
3839
import com.cloud.utils.component.Manager;
@@ -230,6 +231,8 @@ public interface BackupManager extends BackupService, Configurable, PluggableSer
230231
*/
231232
boolean restoreBackup(final Long backupId);
232233

234+
Map<Long, Network.IpAddresses> getIpToNetworkMapFromBackup(Backup backup, boolean preserveIps, List<Long> networkIds);
235+
233236
/**
234237
* Restore a backup to a new Instance
235238
*/

server/src/main/java/com/cloud/network/NetworkServiceImpl.java

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6304,4 +6304,20 @@ protected void checkCallerForPublicIpQuarantineAccess(PublicIpQuarantine publicI
63046304

63056305
_accountMgr.checkAccess(callingAccount, domainOfThePreviousOwner);
63066306
}
6307+
6308+
@Override
6309+
public Network.IpAddresses getIpAddressesFromIps(String ipAddress, String ip6Address, String macAddress) {
6310+
if (ip6Address != null) {
6311+
ip6Address = NetUtils.standardizeIp6Address(ip6Address);
6312+
}
6313+
if (macAddress != null) {
6314+
if (!NetUtils.isValidMac(macAddress)) {
6315+
throw new InvalidParameterValueException("Mac address is not valid: " + macAddress);
6316+
} else if (!NetUtils.isUnicastMac(macAddress)) {
6317+
throw new InvalidParameterValueException("Mac address is not unicast: " + macAddress);
6318+
}
6319+
macAddress = NetUtils.standardizeMacAddress(macAddress);
6320+
}
6321+
return new Network.IpAddresses(ipAddress, ip6Address, macAddress);
6322+
}
63076323
}

server/src/main/java/com/cloud/vm/UserVmManagerImpl.java

Lines changed: 1 addition & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -9173,22 +9173,8 @@ public UserVm allocateVMFromBackup(CreateVMFromBackupCmd cmd) throws Insufficien
91739173

91749174
Map<Long, IpAddresses> ipToNetworkMap = cmd.getIpToNetworkMap();
91759175
if (networkIds == null && ipToNetworkMap == null) {
9176-
String networkIdsString = backup.getDetail(ApiConstants.NETWORK_IDS);
9177-
if (networkIdsString == null) {
9178-
throw new CloudRuntimeException("Backup doesn't contain network information. Please specify atleast one valid network while creating instance");
9179-
}
9180-
List<String> networkUuids = List.of(networkIdsString.split(","));
9181-
ipToNetworkMap = new LinkedHashMap<Long, IpAddresses>();
91829176
networkIds = new ArrayList<Long>();
9183-
for (String networkUuid: networkUuids) {
9184-
Network network = _networkDao.findByUuid(networkUuid);
9185-
if (network == null) {
9186-
throw new CloudRuntimeException("Unable to find network with the uuid " + networkUuid + "stored in backup. Please specify a valid network id while creating the instance");
9187-
}
9188-
Long networkId = network.getId();
9189-
ipToNetworkMap.put(networkId, null);
9190-
networkIds.add(networkId);
9191-
}
9177+
ipToNetworkMap = backupManager.getIpToNetworkMapFromBackup(backup, cmd.getPreserveIp(), networkIds);
91929178
}
91939179

91949180
if (cmd.getUserData() != null) {

server/src/main/java/org/apache/cloudstack/backup/BackupManagerImpl.java

Lines changed: 77 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,8 @@
2121
import java.util.Collections;
2222
import java.util.Date;
2323
import java.util.HashMap;
24-
import java.util.HashSet;
24+
import java.util.LinkedHashMap;
25+
import java.util.LinkedHashSet;
2526
import java.util.List;
2627
import java.util.Map;
2728
import java.util.Objects;
@@ -38,6 +39,9 @@
3839
import com.cloud.capacity.CapacityVO;
3940
import com.cloud.configuration.Resource;
4041
import com.cloud.exception.ResourceAllocationException;
42+
import com.cloud.network.Network;
43+
import com.cloud.network.NetworkService;
44+
import com.cloud.network.dao.NetworkDao;
4145
import com.cloud.storage.Snapshot;
4246
import com.cloud.api.query.dao.UserVmJoinDao;
4347
import com.cloud.api.query.vo.UserVmJoinVO;
@@ -187,6 +191,10 @@ public class BackupManagerImpl extends ManagerBase implements BackupManager {
187191
@Inject
188192
private UserVmJoinDao userVmJoinDao;
189193
@Inject
194+
private NetworkDao networkDao;
195+
@Inject
196+
private NetworkService networkService;
197+
@Inject
190198
private ApiDispatcher apiDispatcher;
191199
@Inject
192200
private AsyncJobManager asyncJobManager;
@@ -318,12 +326,21 @@ public Map<String, String> getVmDetailsForBackup(VirtualMachine vm) {
318326
details.put(ApiConstants.SERVICE_OFFERING_ID, serviceOffering.getUuid());
319327
List<UserVmJoinVO> userVmJoinVOs = userVmJoinDao.searchByIds(vm.getId());
320328
if (userVmJoinVOs != null && !userVmJoinVOs.isEmpty()) {
321-
Set<String> networkIds = new HashSet<>();
329+
Set<String> networkIds = new LinkedHashSet<>();
330+
Set<String> ipAddresses = new LinkedHashSet<>();
331+
Set<String> ip6Addresses = new LinkedHashSet<>();
332+
Set<String> macAddresses = new LinkedHashSet<>();
322333
for (UserVmJoinVO userVmJoinVO : userVmJoinVOs) {
323334
networkIds.add(userVmJoinVO.getNetworkUuid());
335+
ipAddresses.add(userVmJoinVO.getIpAddress());
336+
ip6Addresses.add(userVmJoinVO.getIp6Address());
337+
macAddresses.add(userVmJoinVO.getMacAddress());
324338
}
325339
if (!networkIds.isEmpty()) {
326340
details.put(ApiConstants.NETWORK_IDS, String.join(",", networkIds));
341+
details.put(ApiConstants.IP_ADDRESSES, String.join(",", ipAddresses));
342+
details.put(ApiConstants.IP6_ADDRESS, String.join(",", ip6Addresses));
343+
details.put(ApiConstants.MAC_ADDRESSES, String.join(",", macAddresses));
327344
}
328345
}
329346
return details;
@@ -1059,6 +1076,64 @@ public List<DiskOfferingInfo> getDataDiskOfferingListFromBackup(Backup backup) {
10591076
return diskOfferingInfoList;
10601077
}
10611078

1079+
@Override
1080+
public Map<Long, Network.IpAddresses> getIpToNetworkMapFromBackup(Backup backup, boolean preserveIps, List<Long> networkIds)
1081+
{
1082+
Map<Long, Network.IpAddresses> ipToNetworkMap = new LinkedHashMap<Long, Network.IpAddresses>();
1083+
1084+
String networkIdsString = backup.getDetail(ApiConstants.NETWORK_IDS);
1085+
if (networkIdsString == null) {
1086+
throw new CloudRuntimeException("Backup doesn't contain network information. Please specify atleast one valid network while creating instance");
1087+
}
1088+
List<String> networkUuids = List.of(networkIdsString.split(","));
1089+
1090+
List<String> requestedIp = null;
1091+
List<String> requestedMac = null;
1092+
List<String> requestedIpv6 = null;
1093+
boolean preserveIpAddresses = false;
1094+
if (preserveIps) {
1095+
VMInstanceVO vm = vmInstanceDao.findById(backup.getVmId());
1096+
if (vm == null) {
1097+
preserveIpAddresses = true;
1098+
}
1099+
}
1100+
if (preserveIpAddresses) {
1101+
String ipAddressString = backup.getDetail(ApiConstants.IP_ADDRESSES);
1102+
String ip6AddressString = backup.getDetail(ApiConstants.IP6_ADDRESSES);
1103+
String macAddressString = backup.getDetail(ApiConstants.MAC_ADDRESSES);
1104+
1105+
if (ipAddressString != null) {
1106+
requestedIp = List.of(ipAddressString.split(","));
1107+
}
1108+
if (macAddressString != null) {
1109+
requestedMac = List.of(macAddressString.split(","));
1110+
}
1111+
if (ip6AddressString != null) {
1112+
requestedIpv6 = List.of(ip6AddressString.split(","));
1113+
}
1114+
}
1115+
1116+
int index = 0;
1117+
for (String networkUuid: networkUuids) {
1118+
Network network = networkDao.findByUuid(networkUuid);
1119+
if (network == null) {
1120+
throw new CloudRuntimeException("Unable to find network with the uuid " + networkUuid + "stored in backup. Please specify a valid network id while creating the instance");
1121+
}
1122+
Long networkId = network.getId();
1123+
Network.IpAddresses ipAddresses = null;
1124+
if (preserveIpAddresses) {
1125+
String ip = (requestedIp != null) ? requestedIp.get(index) : null;
1126+
String ipv6 = (requestedIpv6 != null) ? requestedIpv6.get(index) : null;
1127+
String mac = (requestedMac != null) ? requestedMac.get(index) : null;
1128+
ipAddresses = networkService.getIpAddressesFromIps(ip, ipv6, mac);
1129+
}
1130+
ipToNetworkMap.put(networkId, ipAddresses);
1131+
networkIds.add(networkId);
1132+
index += 1;
1133+
}
1134+
return ipToNetworkMap;
1135+
}
1136+
10621137
@Override
10631138
public boolean restoreBackupToVM(final Long backupId, final Long vmId) throws ResourceUnavailableException {
10641139
final BackupVO backup = backupDao.findById(backupId);

server/src/test/java/com/cloud/vm/UserVmManagerImplTest.java

Lines changed: 0 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -3187,7 +3187,6 @@ public void testAllocateVMFromBackupUsingCmdValues() throws InsufficientCapacity
31873187
when(backup.getZoneId()).thenReturn(zoneId);
31883188
when(backup.getVmId()).thenReturn(vmId);
31893189
when(backupDao.findById(backupId)).thenReturn(backup);
3190-
when(backup.getDetail(ApiConstants.NETWORK_IDS)).thenReturn("network-uuid");
31913190

31923191
UserVmVO userVmVO = new UserVmVO();
31933192
userVmVO.setTemplateId(templateId);
@@ -3196,10 +3195,6 @@ public void testAllocateVMFromBackupUsingCmdValues() throws InsufficientCapacity
31963195
when(template.getFormat()).thenReturn(Storage.ImageFormat.QCOW2);
31973196
when(templateDao.findById(templateId)).thenReturn(template);
31983197

3199-
NetworkVO network = mock(NetworkVO.class);
3200-
when(network.getId()).thenReturn(networkId);
3201-
when(_networkDao.findByUuid("network-uuid")).thenReturn(network);
3202-
32033198
Mockito.doReturn(userVmVoMock).when(userVmManagerImpl).createAdvancedVirtualMachine(any(), any(), any(), any(), any(), any(), any(),
32043199
any(), any(), any(), any(), any(), any(), any(), any(), any(), any(), any(), any(), nullable(Boolean.class), any(), any(), any(),
32053200
any(), any(), any(), any(), eq(false), any(), any());
@@ -3255,13 +3250,8 @@ public void testAllocateVMFromBackupUsingBackupValues() throws InsufficientCapac
32553250
when(serviceOffering.getDiskOfferingId()).thenReturn(rootDiskOfferingId);
32563251
when(diskOfferingDao.findById(rootDiskOfferingId)).thenReturn(diskOffering);
32573252

3258-
when(backup.getDetail(ApiConstants.NETWORK_IDS)).thenReturn("net1-uuid,net2-uuid");
32593253
NetworkVO network1 = mock(NetworkVO.class);
32603254
NetworkVO network2 = mock(NetworkVO.class);
3261-
when(_networkDao.findByUuid("net1-uuid")).thenReturn(network1);
3262-
when(_networkDao.findByUuid("net2-uuid")).thenReturn(network2);
3263-
when(network1.getId()).thenReturn(network1Id);
3264-
when(network2.getId()).thenReturn(network2Id);
32653255
when(backupManager.getDataDiskOfferingListFromBackup(backup)).thenReturn(List.of(new DiskOfferingInfo(diskOffering, 10L, 1000L, 2000L)));
32663256

32673257
Mockito.doReturn(userVmVoMock).when(userVmManagerImpl).createAdvancedVirtualMachine(any(), any(), any(), any(), any(), any(), any(),

server/src/test/java/com/cloud/vpc/MockNetworkManagerImpl.java

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1122,6 +1122,11 @@ public List<InternalLoadBalancerElementService> getInternalLoadBalancerElements(
11221122
return null;
11231123
}
11241124

1125+
@Override
1126+
public IpAddresses getIpAddressesFromIps(String ipAddress, String ip6Address, String macAddress) {
1127+
return null;
1128+
}
1129+
11251130
@Override
11261131
public void expungeLbVmRefs(List<Long> vmIds, Long batchSize) {
11271132
}

ui/public/locales/en.json

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -992,6 +992,7 @@
992992
"label.f5.ip.loadbalancer": "F5 BIG-IP load balancer.",
993993
"label.failed": "Failed",
994994
"label.featured": "Featured",
995+
"label.fetch.from.backup": "Fetch from Backup",
995996
"label.fetch.instances": "Fetch Instances",
996997
"label.fetch.latest": "Fetch latest",
997998
"label.filename": "File Name",
@@ -1965,6 +1966,8 @@
19651966
"label.restartrequired": "Restart required",
19661967
"label.restore": "Restore",
19671968
"label.restore.volume.attach": "Restore volume and attach",
1969+
"label.use.backup.ip.address": "Use IP Addresses from Backup",
1970+
"label.use.backup.ip.address.tooltip": "Use the same IP/MAC addresses as stored in the backup metadata. The command will error out if the IP/MAC addresses are not available",
19681971
"label.review": "Review",
19691972
"label.role": "Role",
19701973
"label.roleid": "Role",
@@ -2666,7 +2669,7 @@
26662669
"message.action.cancel.maintenance": "Your host has been successfully canceled for maintenance. This process can take up to several minutes.",
26672670
"message.action.cancel.maintenance.mode": "Please confirm that you want to cancel this maintenance.",
26682671
"message.action.create.snapshot.from.vmsnapshot": "Please confirm that you want to create Snapshot from Instance Snapshot",
2669-
"message.action.create.instance.from.backup": "Please confirm that you want to create a new Instance from the given Backup.<br>Click on configure to edit the parameters for the new Instance.",
2672+
"message.action.create.instance.from.backup": "Please confirm that you want to create a new Instance from the given Backup.<br>Click on configure to edit the parameters for the new Instance before creation.",
26702673
"message.action.delete.asnrange": "Please confirm the AS range that you want to delete",
26712674
"message.action.delete.autoscale.vmgroup": "Please confirm that you want to delete this autoscaling group.",
26722675
"message.action.delete.backup.offering": "Please confirm that you want to delete this backup offering?",

0 commit comments

Comments
 (0)