2727import java .security .NoSuchAlgorithmException ;
2828import java .security .SecureRandom ;
2929import java .util .ArrayList ;
30- import java .util .Collections ;
30+ import java .util .Arrays ;
3131import java .util .HashMap ;
3232import java .util .List ;
3333import java .util .Map ;
34+ import java .util .StringJoiner ;
3435import java .util .UUID ;
3536
3637import javax .net .ssl .SSLContext ;
3738import javax .net .ssl .X509TrustManager ;
3839
3940import com .cloud .utils .Pair ;
41+ import com .cloud .utils .ssh .SshHelper ;
4042import org .apache .cloudstack .api .ApiErrorCode ;
4143import org .apache .cloudstack .api .ServerApiException ;
4244import org .apache .cloudstack .backup .BackupPolicy ;
6668import org .apache .http .client .CookieStore ;
6769import org .apache .http .client .CredentialsProvider ;
6870import org .apache .http .client .HttpClient ;
69- import org .apache .http .client .config .AuthSchemes ;
7071import org .apache .http .client .config .RequestConfig ;
7172import org .apache .http .client .methods .HttpDelete ;
7273import org .apache .http .client .methods .HttpGet ;
9091import com .fasterxml .jackson .dataformat .xml .XmlMapper ;
9192import com .fasterxml .jackson .dataformat .xml .ser .ToXmlGenerator ;
9293
93- import io .cloudsoft .winrm4j .winrm .WinRmTool ;
94- import io .cloudsoft .winrm4j .winrm .WinRmToolResponse ;
95-
9694public 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}
0 commit comments