Start VM => run rsync => stop VM

chudak

Well-Known Member
May 11, 2019
322
16
58
Hello all

I am looking for suggestions or maybe an ready to use script to accomplish this scenario:
Start a VM on schedule => run rsync (may take awhile) => stop VM

Thx in advance.
 
Don't get what you try to achieve exactly. Can you provide more details?
 
@tburger

i have a pve server with say one VM (say named OMV) that is responsible for rsync backups

Instead I want to run this rsync from the host pve by
start OMV
run rsync
shutdown OMV

Main question - is it possible to execute in a pve host shell one liner rsync jobs that actually is on the OMV VM?

The overall goal not to have OMV VM running all the time, but only when is needed.

Thx
 
My approach would be the following:
Create a shellscript which runs via crown on PVE.

This would need to do the following steps.
1. Check if OMV VM is running
2. If not: power on via CLI command (qm start ...)
3. Wait for the VM to start (periodically check via ping and ssh-connect)
4. Once started run a ssh-command (rsync) from PVE to OMV. (Maybe wait / repeat if return code of rsync is not 0)
5. Once completed sleep a few seconds
6. Shutdown VM from PVE via qm command or via ssh shutdown command again

Done :)
 
But that does not change the general challenge that the backup server should be powered off normally
 
My approach would be the following:
Create a shellscript which runs via crown on PVE.

This would need to do the following steps.
1. Check if OMV VM is running
2. If not: power on via CLI command (qm start ...)
3. Wait for the VM to start (periodically check via ping and ssh-connect)
4. Once started run a ssh-command (rsync) from PVE to OMV. (Maybe wait / repeat if return code of rsync is not 0)
5. Once completed sleep a few seconds
6. Shutdown VM from PVE via qm command or via ssh shutdown command again

Done :)

Sounds you answered my main question and I can execute a VM command on a pve host via ssh-command

Now I need to look around for a ready to use script, I am sure people use something similar.

Thanks !
 
Not sure you will find something "ready to use". Are you familar with shell scripting?
I have a few of those steps in my backpocket but nothing which will run end 2 end...
Obviously you will need to add SSH keybased authentication as well on OMV, but that shouldn't be much of a problem...
 
Not sure you will find something "ready to use". Are you familar with shell scripting?
I have a few of those steps in my backpocket but nothing which will run end 2 end...
Obviously you will need to add SSH keybased authentication as well on OMV, but that shouldn't be much of a problem...

If you have pls share an example on how to run a rsync command from PVE in VM
 
first you need to enable public-key authentication in your VM for whichever account you run your script under on your PVE host.
Here is a guide:
https://serverpilot.io/docs/how-to-use-ssh-public-key-authentication/
But there are tons of other out there. It was just the first I found and from a quick scan-read it seemed to be ok.

The actual call of rsync in the ssh session is quite simple. The only thing which might give you some trouble is blanks/space characters in your folder structure.
The command will look similar to this:
Code:
ssh -l <yourUser> <yourSSHServer> -p <yourSSHPort> <yourRsyncCommand>
some other examples can be found here: https://zaiste.net/posts/few-ways-to-execute-commands-remotely-ssh/

to list your existing VMs on PVE do a
Code:
sudo qm list

to get the current status of the vm you can do a
Code:
sudo qm status 1012 #assuming 1012 is the VM ID of your server
#status: stopped

Start the VM by
Code:
sudo qm start 1012

And to shut the VM down
Code:
sudo qm shutdown 1012

To force a power off
Code:
sudo qm stop 1012

this is a function I have built to check if a system is available through ICMP echo requests. It is called ICMP-Ping:
Bash:
icmpPing()
{
    # Initialisierung der Variablen
    local myTarget=$null
    local icmpPingRetryCount=$null
    local waitIntervalInSeconds=30
    local icmpPing="success"
    local icmpPingRetryCount=1
    # Auswertung der Übergabeparameter
    while [ $# -gt 0 ]
        do
            case "$1" in
                # Option -h für "SSH Zielsystem"
                -h)     local myTarget="$2"; shift;;
                -pc)    local icmpPingRetryCount=$2; shift;;
                # Option -pc für den zu verwendenden PingCount
                -wi)    local waitIntervalInSeconds=$2; shift;;
                # für alle anderen Optionen:
                -*)
                        echo >&2 \
                        "usage: icmpPing [-h hostnameOrIP] [-pc icmpPingRetryCount] [-wi IntegerWaitIntervalInSeconds] )"
                        exit 1;;
                # terminate while loop
                *)  break;;
                esac
            shift
    done
    ### Überprüfung der Übergabeparameter
    # Wenn kein Host übergeben wird, dann brechen wir hier ab!
    if [[ -z $myTarget ]] ;
        then
            #writeLogInformation -m "--- icmpPing: Missing parameter. At least >mySshTarget< and >mySshUser< must be specified." -t -tc "red" -f -fts -fn "$myLogFileAbsolutePath" -lm "$myLogMode"
            icmpPing="failed"
    fi
    
    ### THE DOING
    if [[ "$icmpPing" == "success" ]]
        then
            local counter=0
            #writeLogInformation -m "||| Checking availability of system >$myTarget<." -t -f -fts -fn "$myLogFileAbsolutePath" -lm "$myLogMode"
            while [ $counter -lt $icmpPingRetryCount ]
                do
                    local myPingResult=$null
                    local myPingResult=`ping $myTarget -c 3`
                    if [[ "$myPingResult" =~ ", 0% packet loss," ]] ;
                        then
                            # AUSGABE ERGEBNIS (grün)
                            #writeLogInformation -m "+++ icmpPing: Ping successful after try no. $retryCount." -t -tc "green" -f -fts -fn "$myLogFileAbsolutePath" -lm "$myLogMode"
                            local icmpPing="success"
                            # Aus dem Loop rausspringen
                            break
                        else
                            # AUSGABE ERGEBNIS (rot)
                            #writeLogInformation -m "--- icmpPing: Ping failed after try $retryCount." -t -tc "red" -f -fts -fn "$myLogFileAbsolutePath"
                            #writeLogInformation -m "--- icmpPing:     Probably a (dynamic) name resolution problem or system is not available." -t -tc "red" -f -fts -fn "$myLogFileAbsolutePath" -lm "$myLogMode"
                            local counter=$[$counter+1]
                            local icmpPing="failed"
                            # Interval abwarten
                            sleep $waitIntervalInSeconds
                    fi
            done
    fi
    
    # Rückgabe-Wert
    echo "$icmpPing"   
}

#  Example Call:
icmpPing -h $myDestinationHost


And this one does the check of an SSH-connectivity. It is called SSH-Ping:
Bash:
sshPing()
{
    # Initialisierung der Variablen
    local mySshTarget=$null
    local mySshPort=$null
    local mySshUser=$null
    local sshPingRetryCountCount=$null
    local waitIntervalInSeconds=$null
    local sshPing="fail"
    local retryCount=1
    # Auswertung der Übergabeparameter
    while [ $# -gt 0 ]
        do
            case "$1" in
                # Option -h für "SSH Zielsystem"
                -h)     local mySshTarget="$2"; shift;;
                # Option -p für den SSH-Port
                -p)     local mySshPort="$2"; shift;;
                # Option -u für den zu verwendenden User
                -u)     local mySshUser="$2"; shift;;
                # Option -pc für den zu verwendenden PingCount
                -pc)    local sshPingRetryCount=$2; shift;;
                # Option -pc für den zu verwendenden PingCount
                -wi)    local waitIntervalInSeconds=$2; shift;;
                # für alle anderen Optionen:
                -*)
                        echo >&2 \
                        "usage: sshPing [-h hostnameOrIP] [-u username] ( [-p sshPort] [-pc IntegerRetryCount] [-wi IntegerWaitIntervalInSeconds] )"
                        exit 1;;
                # terminate while loop
                *)  break;;
                esac
            shift
    done
    ### Überprüfung der Übergabeparameter
    # Wenn kein User oder kein Host übergeben wird, dann brechen wir hier ab!
    if [[ -z $mySshTarget || -z $mySshUser ]] ;
        then
            #writeLogInformation -m "--- Missing parameter. At least >mySshTarget< and >mySshUser< must be specified." -t -tc "red" -f -fts -fn "$myLogFileAbsolutePath" -lm "$myLogMode"
            # Rückgabe-Wert
            echo "$sshPing"
        # Ansonsten können wir den Check ausführen.
        else
            # Wenn kein Port übergeben wird, dann wird der Default-Port verwendet
            if [ -z $mySshPort ] ;
                then
                    local mySshPort=22
            fi
            # Wenn kein Retrycount übergeben wird, dann setzen wir einen Default-Wert von 3
            if [ -z $sshPingRetryCount ] ;
                then
                    local sshPingRetryCount=10
            fi
            # Wenn kein Warteinterval angegeben wird, dann definieren wir eines von 5 Sekunden
            if [ -z $waitIntervalInSeconds ] ;
                then
                    local waitIntervalInSeconds=5
            fi

            # Basis-SSH Connection-String erzeugen.
            local sshBasicConnectString="ssh -l $mySshUser $mySshTarget -p $mySshPort"
            #writeLogInformation -m "||| Checking availability of system >$mySshTarget< through user >$mySshUser<." -t -f -fts -fn "$myLogFileAbsolutePath" -lm "$myLogMode"
            while [ $retryCount -le $sshPingRetryCount ]
                do
                    local myWhoAmIResult=`eval "$sshBasicConnectString 'whoami'"`
                    if [ "$myWhoAmIResult" == "$mySshUser" ] ;
                        then
                            # AUSGABE ERGEBNIS (grün)
                            #writeLogInformation -m "+++ SSH-Ping successful after try no. $retryCount." -t -tc "green" -f -fts -fn "$myLogFileAbsolutePath" -lm "$myLogMode"
                            local sshPing="success"
                            # Aus dem Loop rausspringen
                            break
                        else
                            # AUSGABE ERGEBNIS (rot)
                            #writeLogInformation -m "--- SSH-Ping failed after try $retryCount." -t -tc "red" -f -fts -fn "$myLogFileAbsolutePath"
                            #writeLogInformation -m "--- Probably a (dynamic) name resolution problem or system is not available." -t -tc "red" -f -fts -fn "$myLogFileAbsolutePath" -lm "$myLogMode"
                            local retryCount=$[$retryCount+1]
                            # Interval abwarten
                            sleep $waitIntervalInSeconds
                    fi
            done
            # Rückgabe-Wert
            echo "$sshPing"
    fi
}

# Example Call
sshPing -h $myDestinationHost -u $myDestinationUser -p $myDestinationSSHPort -pc 5 -wi 5

Apologies for the German comments. I have never moved that to international (engl.)
 
@tburger

So far I am stuck to run a simply rsync via remote ssh :(

Tried:

ssh user@nas "rsync -aAXzv user@nas:/srv/dev-disk-by-label-MAIN/Public/Temp/ /srv/dev-disk-by-label-OLD/OLD_DRIVE/Public/Temp"

ssh user@nas "rsync -aAXzv --progress -e ssh user@nas:/srv/dev-disk-by-label-MAIN/Public/Temp/ /srv/dev-disk-by-label-OLD/OLD_DRIVE/Public/Temp"

Error:
Host key verification failed.
rsync: connection unexpectedly closed (0 bytes received so far) [Receiver]
rsync error: unexplained error (code 255) at io.c(235) [Receiver=3.1.3]

(wonder if I am not passing ssh key correctly?!)

Were you successful to run something similar ?
 
Last edited:
Were you successful to run something similar ?
Not really. My procedure works a little different but you experience the joy of SSH-connections through various users and key-based authentication.
Sometimes this can be a real pain as you need to think it "the right way". Have been there as well - and it drove me nuts ;)

Host key verification failed.
This actually tells you that the connection is established, but not successful because the host you are connecting to in this command has an unexpected host-identification key.
Sometimes these keys will get re-generated (I have had this after an update of the OS once) and especially in situations as you are in are a real pain, because all of the sudden things break.
So you need to find out which user has a wrong key stored in you ssh-chain.

connect via your proxmox user to "user @ nas".
Then issue the ssh command to connect from this session to the ssh target - which I find by the way odd that this is "user @ nas" as well.
This doesn't make sense to me.
This command seems unlogical:
ssh user@nas "rsync -aAXzv user@nas:/srv/dev-disk-by-label-MAIN/Public/Temp/ /srv/dev-disk-by-label-OLD/OLD_DRIVE/Public/Temp"
shouldn't it be (note the second user @ nas is gone)
Bash:
ssh user@nas "rsync -aAXzv /srv/dev-disk-by-label-MAIN/Public/Temp/ /srv/dev-disk-by-label-OLD/OLD_DRIVE/Public/Temp"

You are executing the command locally on "nas" already as you connect to it via SSH.
So I don't see the point in adding this again - unless my assumptions are completely wrong ;)

My "ssh remote commands" for rsync look a little different. Not sure if that really makes a difference as I am doing my backup from my client to another server.
Code:
/usr/bin/rsync /home/myuser/ backup:/backup/2020-12-30_21-00 -v --exclude-from=exclude.list --rsh="ssh -l UserOnBackupServer -p 22" --links --copy-dirlinks --times --delete --delete-excluded --delete-after --ignore-errors --hard-links --bwlimit=8192 --ipv4 --link-dest=../2020-12-30_15-00 --recursive --progress --exclude 'backupIsReferringTo' --exclude 'backupFailure' --exclude 'backupSuccess'

# lets dissassemble this
# /usr/bin/rsync                          # The binary - i have good experience with full paths
# /home/myuser/                           # the source
# backup:/backup/2020-12-30_21-00         # the target (servername is backup)
# -v                                      # Verbose output
# --exclude-from=exclude.list             # exclude transferred content from file containing patterns
# --rsh="ssh -l UserOnBackupServer -p 22" # This is the actual SSH-Connection which will be used by rsync
# --links                                 # Include links
# --copy-dirlinks                         # copy dirlinks
# --times                                 # preserve timestamps
# --delete                                # delete (e.g. mirror directory content)
# --delete-excluded                       # delete excluded files on target
# --delete-after                          # delete after transfer of files
# --ignore-errors                         # ignore errors and continue if something happens
# --hard-links                            # also sync hard-links
# --bwlimit=8192                          # use a bandwidth limit (only makes sense when having network transfers)
# --ipv4                                  # enforce use IPv4 - not IPv6
# --link-dest=../2020-12-30_15-00         # This is the (existing) backup which will be the parent - so the next one only transfers changed files
# --recursive                             # go recursive through all folders
# --progress                              # show progress
# --exclude 'backupIsReferringTo'         # exclude some special file
# --exclude 'backupFailure'               # and another one
# --exclude 'backupSuccess'               # yet another

I could issue the above command from a remote-system on my client by doing it like this:
Code:
ssh -l userOnClient clientComputer -p 22 "/usr/bin/rsync /home/myuser/ backup:/backup/2020-12-30_21-00 -v --exclude-from=exclude.list --rsh="ssh -l UserOnBackupServer -p 22" --links --copy-dirlinks --times --delete --delete-excluded --delete-after --ignore-errors --hard-links --bwlimit=8192 --ipv4 --link-dest=../2020-12-30_15-00 --recursive --progress --exclude 'backupIsReferringTo' --exclude 'backupFailure' --exclude 'backupSuccess'"

does this make sense?
 
  • Like
Reactions: chudak
Solved that, so the issue was that =>

All host has to be able to do password less ssh
host -> VM1 and source -> target the same ssh identify will be used.
 
@tburger I did not see your reply but we were thinking alone the same lines :) Thx !


I wonder, the functionality to be able to execute command from PVE host on VM(s) on schedule basis might be useful to many users.

Why is it not implemented on the GUI level ?!

What do you think ?
 
cool. let me know if you need further help on this.

/edit: it could be a very cool feature - and a security nightmare!
I have learned / read the other day that you can pass keys / commands through to the vm. but again. I see this highly risky...
 
Last edited:
completed full "round trip" with simple sleeps for the time being:

Code:
qm start 103 && sleep 16 && ssh user@server1 'rsync -aAXzv --progress -e ssh user@server2:/srv/dev-disk-by-label-MAIN/Public/Temp/ /srv/dev-disk-by-label-OLD/OLD_DRIVE/Public/Temp' && sleep 5  && qm shutdown 103
 

About

The Proxmox community has been around for many years and offers help and support for Proxmox VE, Proxmox Backup Server, and Proxmox Mail Gateway.
We think our community is one of the best thanks to people like you!

Get your subscription!

The Proxmox team works very hard to make sure you are running the best software and getting stable updates and security enhancements, as well as quick enterprise support. Tens of thousands of happy customers have a Proxmox subscription. Get yours easily in our online shop.

Buy now!