Skip to content

Commit d2ba4f8

Browse files
committed
Removed WinRM4j dependency, added SSH support for powershell, still need to fix one client method
1 parent 2b0c2e5 commit d2ba4f8

File tree

3 files changed

+106
-88
lines changed

3 files changed

+106
-88
lines changed

plugins/backup/veeam/pom.xml

Lines changed: 0 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -28,11 +28,6 @@
2828
</parent>
2929

3030
<dependencies>
31-
<dependency>
32-
<groupId>org.apache.cxf</groupId>
33-
<artifactId>cxf-rt-transports-http-hc</artifactId>
34-
<version>${cs.cxf.version}</version>
35-
</dependency>
3631

3732
<dependency>
3833
<groupId>org.apache.cloudstack</groupId>
@@ -49,42 +44,6 @@
4944
<artifactId>commons-lang3</artifactId>
5045
<version>${cs.commons-lang3.version}</version>
5146
</dependency>
52-
<dependency>
53-
<groupId>io.cloudsoft.windows</groupId>
54-
<artifactId>winrm4j-client</artifactId>
55-
<version>${cs.winrm4j.version}</version>
56-
<exclusions>
57-
<exclusion>
58-
<groupId>org.apache.cxf</groupId>
59-
<artifactId>cxf-rt-transports-http</artifactId>
60-
</exclusion>
61-
<exclusion>
62-
<groupId>org.apache.cxf</groupId>
63-
<artifactId>cxf-rt-transports-http-hc</artifactId>
64-
</exclusion>
65-
<exclusion>
66-
<groupId>org.apache.cxf</groupId>
67-
<artifactId>cxf-rt-frontend-jaxws</artifactId>
68-
</exclusion>
69-
</exclusions>
70-
</dependency>
71-
<dependency>
72-
<groupId>io.cloudsoft.windows</groupId>
73-
<artifactId>winrm4j-service</artifactId>
74-
<version>${cs.winrm4j.version}</version>
75-
<scope>provided</scope>
76-
<!--exclusions>
77-
<exclusion>
78-
<groupId>org.apache.cxf</groupId>
79-
<artifactId>cxf-core</artifactId>
80-
</exclusion>
81-
</exclusions-->
82-
</dependency>
83-
<dependency>
84-
<groupId>io.cloudsoft.windows</groupId>
85-
<artifactId>winrm4j</artifactId>
86-
<version>${cs.winrm4j.version}</version>
87-
</dependency>
8847
<dependency>
8948
<groupId>com.github.tomakehurst</groupId>
9049
<artifactId>wiremock-standalone</artifactId>

plugins/backup/veeam/src/main/java/org/apache/cloudstack/backup/veeam/VeeamClient.java

Lines changed: 92 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -27,16 +27,18 @@
2727
import java.security.NoSuchAlgorithmException;
2828
import java.security.SecureRandom;
2929
import java.util.ArrayList;
30-
import java.util.Collections;
30+
import java.util.Arrays;
3131
import java.util.HashMap;
3232
import java.util.List;
3333
import java.util.Map;
34+
import java.util.StringJoiner;
3435
import java.util.UUID;
3536

3637
import javax.net.ssl.SSLContext;
3738
import javax.net.ssl.X509TrustManager;
3839

3940
import com.cloud.utils.Pair;
41+
import com.cloud.utils.ssh.SshHelper;
4042
import org.apache.cloudstack.api.ApiErrorCode;
4143
import org.apache.cloudstack.api.ServerApiException;
4244
import org.apache.cloudstack.backup.BackupPolicy;
@@ -66,7 +68,6 @@
6668
import org.apache.http.client.CookieStore;
6769
import org.apache.http.client.CredentialsProvider;
6870
import org.apache.http.client.HttpClient;
69-
import org.apache.http.client.config.AuthSchemes;
7071
import org.apache.http.client.config.RequestConfig;
7172
import org.apache.http.client.methods.HttpDelete;
7273
import org.apache.http.client.methods.HttpGet;
@@ -90,9 +91,6 @@
9091
import com.fasterxml.jackson.dataformat.xml.XmlMapper;
9192
import com.fasterxml.jackson.dataformat.xml.ser.ToXmlGenerator;
9293

93-
import io.cloudsoft.winrm4j.winrm.WinRmTool;
94-
import io.cloudsoft.winrm4j.winrm.WinRmToolResponse;
95-
9694
public class VeeamClient {
9795
private static final Logger LOG = Logger.getLogger(VeeamClient.class);
9896

@@ -101,7 +99,11 @@ public class VeeamClient {
10199
private final HttpClient httpClient;
102100
private final HttpClientContext httpContext = HttpClientContext.create();
103101
private final CookieStore httpCookieStore = new BasicCookieStore();
104-
private final WinRmTool winRmTool;
102+
103+
private String veeamServerIp;
104+
private String veeamServerUsername;
105+
private String veeamServerPassword;
106+
private final int veeamServerPort = 22;
105107

106108
public VeeamClient(final String url, final String username, final String password, final boolean validateCertificate, final int timeout) throws URISyntaxException, NoSuchAlgorithmException, KeyManagementException {
107109
this.apiURI = new URI(url);
@@ -148,14 +150,13 @@ public VeeamClient(final String url, final String username, final String passwor
148150
throw new CloudRuntimeException("Failed to authenticate Veeam API service due to:" + e.getMessage());
149151
}
150152

151-
final WinRmTool.Builder builder = WinRmTool.Builder.builder(apiURI.getHost(), username, password);
152-
builder.useHttps(true);
153-
builder.disableCertificateChecks(true);
154-
builder.setAuthenticationScheme(AuthSchemes.NTLM);
155-
builder.port(WinRmTool.DEFAULT_WINRM_HTTPS_PORT);
156-
winRmTool = builder.build();
157-
winRmTool.setOperationTimeout(timeout * 1000L);
158-
winRmTool.setRetriesForConnectionFailures(1);
153+
setVeeamSshCredentials(this.apiURI.getHost(), username, password);
154+
}
155+
156+
protected void setVeeamSshCredentials(String hostIp, String username, String password) {
157+
this.veeamServerIp = hostIp;
158+
this.veeamServerUsername = username;
159+
this.veeamServerPassword = password;
159160
}
160161

161162
private void checkAuthFailure(final HttpResponse response) {
@@ -515,20 +516,70 @@ public boolean restoreFullVM(final String vmwareInstanceName, final String resto
515516
//////////////// Public Veeam PS based APIs /////////////////////
516517
/////////////////////////////////////////////////////////////////
517518

519+
/**
520+
* Generate a single command to be passed through SSH
521+
*/
522+
protected String getSingleCommandFromList(List<String> cmds) {
523+
String enableVeeamCdletsCmd = "PowerShell asnp veeampssnapin";
524+
StringJoiner joiner = new StringJoiner(";");
525+
joiner.add(enableVeeamCdletsCmd);
526+
for (String cmd : cmds) {
527+
joiner.add(cmd);
528+
}
529+
return joiner.toString();
530+
}
531+
532+
/**
533+
* Execute a list of commands in a single call on PowerShell through SSH
534+
*/
535+
protected Pair<Boolean, String> executePowerShellCommands(List<String> cmds) {
536+
try {
537+
Pair<Boolean, String> pairResult = SshHelper.sshExecute(veeamServerIp, veeamServerPort,
538+
veeamServerUsername, null, veeamServerPassword,
539+
getSingleCommandFromList(cmds));
540+
return pairResult;
541+
} catch (Exception e) {
542+
throw new CloudRuntimeException("Error while executing PowerShell commands due to: " + e.getMessage());
543+
}
544+
}
545+
518546
public boolean deleteJobAndBackup(final String jobName) {
519-
final WinRmToolResponse response = winRmTool.executePs(
520-
Collections.singletonList(
521-
String.format(
522-
"Add-PSSnapin VeeamPSSnapin\n" +
523-
"Get-VBRBackup -Name \"%s\" | Remove-VBRBackup -FromDisk -Confirm:$false\n" +
524-
"Get-VBRJob -Name \"%s\" | Remove-VBRJob -Confirm:$false\n" +
525-
"Get-VBRBackupRepository | Sync-VBRBackupRepository", jobName, jobName)
526-
));
527-
return response.getStatusCode() == 0 && !response.getStdErr().contains("Failed to delete");
547+
Pair<Boolean, String> result = executePowerShellCommands(Arrays.asList(
548+
String.format("Get-VBRBackup -Name \"%s\"", jobName),
549+
"Remove-VBRBackup -FromDisk -Confirm:$false",
550+
String.format("Get-VBRJob -Name \"%s\"", jobName),
551+
"Remove-VBRJob -Confirm:$false",
552+
"Get-VBRBackupRepository",
553+
"Sync-VBRBackupRepository"
554+
));
555+
return result.first() && !result.second().contains("Failed to delete");
528556
}
529557

558+
//TODO: Fix this one
530559
public Map<String, VMBackup.Metric> getBackupMetrics() {
531-
final List<String> psScript = Collections.singletonList(
560+
final List<String> cmds = Arrays.asList(
561+
"$backups = Get-VBRBackup",
562+
"foreach ($backup in $backups) { " +
563+
"$backup.JobName " +
564+
"$storageGroups = $backup.GetStorageGroups() " +
565+
"foreach ($group in $storageGroups) { " +
566+
"$usedSize = 0 " +
567+
"$dataSize = 0 " +
568+
"$sizePerStorage = $group.GetStorages().Stats.BackupSize " +
569+
"$dataPerStorage = $group.GetStorages().Stats.DataSize " +
570+
"foreach ($size in $sizePerStorage) { " +
571+
"$usedSize += $size " +
572+
"} " +
573+
"foreach ($size in $dataPerStorage) { " +
574+
"$dataSize += $size " +
575+
"} " +
576+
"$usedSize " +
577+
"$dataSize " +
578+
"} " +
579+
"echo \";\" " +
580+
"}"
581+
);
582+
/*final List<String> psScript = Collections.singletonList(
532583
"Add-PSSnapin VeeamPSSnapin\n" +
533584
"$backups = Get-VBRBackup\n" +
534585
"foreach ($backup in $backups) {\n" +
@@ -552,10 +603,10 @@ public Map<String, VMBackup.Metric> getBackupMetrics() {
552603
" $dataSize\n" +
553604
"}\n" +
554605
"echo \";\"" +
555-
"}\n");
556-
final WinRmToolResponse response = winRmTool.executePs(psScript);
606+
"}\n");*/
607+
Pair<Boolean, String> response = executePowerShellCommands(cmds);
557608
final Map<String, VMBackup.Metric> sizes = new HashMap<>();
558-
for (final String block : response.getStdOut().split(";\r\n")) {
609+
for (final String block : response.second().split(";\r\n")) {
559610
final String[] parts = block.split("\r\n");
560611
if (parts.length != 3) {
561612
continue;
@@ -589,19 +640,16 @@ private VMBackup.RestorePoint getRestorePointFromBlock(String[] parts) {
589640
}
590641

591642
public List<VMBackup.RestorePoint> listRestorePoints(String backupName, String vmInternalName) {
592-
final List<String> psScript = Collections.singletonList(
593-
String.format(
594-
"Add-PSSnapin VeeamPSSnapin\n" +
595-
"$backup = Get-VBRBackup -Name \"%s\"\n" +
596-
"Get-VBRRestorePoint -Backup:$backup -Name \"%s\"",
597-
backupName, vmInternalName)
643+
final List<String> cmds = Arrays.asList(
644+
String.format("$backup = Get-VBRBackup -Name \"%s\"", backupName),
645+
String.format("Get-VBRRestorePoint -Backup:$backup -Name \"%s\"", vmInternalName)
598646
);
599-
final WinRmToolResponse response = winRmTool.executePs(psScript);
600-
if (response == null) {
647+
Pair<Boolean, String> response = executePowerShellCommands(cmds);
648+
if (response == null || !response.first()) {
601649
throw new CloudRuntimeException("Failed to list restore points");
602650
}
603651
final List<VMBackup.RestorePoint> restorePoints = new ArrayList<>();
604-
for (final String block : response.getStdOut().split("\r\n\r\n")) {
652+
for (final String block : response.second().split("\r\n\r\n")) {
605653
if (block.isEmpty()) {
606654
continue;
607655
}
@@ -614,19 +662,16 @@ public List<VMBackup.RestorePoint> listRestorePoints(String backupName, String v
614662
public Pair<Boolean, String> restoreVMToDifferentLocation(String restorePointId, String hostIp, String dataStoreUuid) {
615663
final String restoreLocation = "CS-RSTR-" + UUID.randomUUID().toString();
616664
final String datastoreId = dataStoreUuid.replace("-","");
617-
final List<String> psScript = Collections.singletonList(
618-
String.format(
619-
"Add-PSSnapin VeeamPSSnapin\n" +
620-
"$point = Get-VBRRestorePoint | where {$_.Id -eq \"%s\"}\n" +
621-
"$server = Get-VBRServer -Name \"%s\"\n" +
622-
"$ds = Find-VBRViDatastore -Server:$server -Name \"%s\"\n" +
623-
"Start-VBRRestoreVM -RestorePoint:$point -Server:$server -Datastore:$ds -VMName \"%s\"",
624-
restorePointId, hostIp, datastoreId, restoreLocation)
665+
final List<String> cmds = Arrays.asList(
666+
String.format("$point = Get-VBRRestorePoint | where {$_.Id -eq \"%s\"}", restorePointId),
667+
String.format("$server = Get-VBRServer -Name \"%s\"", hostIp),
668+
String.format("$ds = Find-VBRViDatastore -Server:$server -Name \"%s\"", datastoreId),
669+
String.format("Start-VBRRestoreVM -RestorePoint:$point -Server:$server -Datastore:$ds -VMName \"%s\"", restoreLocation)
625670
);
626-
final WinRmToolResponse response = winRmTool.executePs(psScript);
627-
if (response == null) {
671+
Pair<Boolean, String> result = executePowerShellCommands(cmds);
672+
if (result == null || !result.first()) {
628673
throw new CloudRuntimeException("Failed to restore VM to location " + restoreLocation);
629674
}
630-
return new Pair<>(response.getStatusCode() == 0, restoreLocation);
675+
return new Pair<>(result.first(), restoreLocation);
631676
}
632677
}

plugins/backup/veeam/src/test/java/org/apache/cloudstack/backup/veeam/VeeamClientTest.java

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -102,4 +102,18 @@ public void test() {
102102
String a = "Id :asdasd";
103103
System.out.println(a.matches("Id(/s)*"));
104104
}
105+
106+
@Test
107+
public void testListRestorePoints() {
108+
client.setVeeamSshCredentials("10.2.2.89", "administrator", "P@ssword123");
109+
String backupName = "i-2-28-VM-CSBKP-9f7caf54-a3f0-45e7-928c-870234165892";
110+
String vmName = "i-2-28-VM";
111+
List<VMBackup.RestorePoint> restorePoints = client.listRestorePoints(backupName, vmName);
112+
}
113+
114+
@Test
115+
public void testGetVmBackups() {
116+
client.setVeeamSshCredentials("10.2.2.89", "administrator", "P@ssword123");
117+
Map<String, VMBackup.Metric> backupMetrics = client.getBackupMetrics();
118+
}
105119
}

0 commit comments

Comments
 (0)