Skip to content
GitLab
Projects
Groups
Snippets
Help
Loading...
Help
Help
Support
Community forum
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in
Toggle navigation
B
backupCoordinator
Project overview
Project overview
Details
Activity
Releases
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Issues
0
Issues
0
List
Boards
Labels
Service Desk
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Operations
Operations
Incidents
Environments
Packages & Registries
Packages & Registries
Package Registry
Container Registry
Analytics
Analytics
CI / CD
Repository
Value Stream
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
kowis-projects
backupCoordinator
Commits
90402347
Commit
90402347
authored
Sep 08, 2018
by
David
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Getin thigns done I think
parent
b84e7c0d
Changes
3
Hide whitespace changes
Inline
Side-by-side
Showing
3 changed files
with
139 additions
and
11 deletions
+139
-11
src/main/scala/is/kow/backupcoordinator/HostBackup.scala
src/main/scala/is/kow/backupcoordinator/HostBackup.scala
+11
-0
src/main/scala/is/kow/backupcoordinator/Main.scala
src/main/scala/is/kow/backupcoordinator/Main.scala
+0
-2
src/main/scala/is/kow/backupcoordinator/actors/HostBackupActor.scala
...ala/is/kow/backupcoordinator/actors/HostBackupActor.scala
+128
-9
No files found.
src/main/scala/is/kow/backupcoordinator/HostBackup.scala
0 → 100644
View file @
90402347
package
is.kow.backupcoordinator
case
class
HostBackup
(
hostname
:
String
,
repoName
:
String
,
backupPaths
:
List
[
String
],
preBackupCommand
:
Option
[
String
]
=
None
,
postBackupCommand
:
Option
[
String
]
=
None
,
keepDaily
:
Int
=
30
,
keepMonthly
:
Int
=
3
)
src/main/scala/is/kow/backupcoordinator/Main.scala
View file @
90402347
...
...
@@ -8,8 +8,6 @@ object Main extends App {
Security
.
addProvider
(
new
BouncyCastleProvider
)
//Set up an actor system to do the work
println
(
"HELLO WORLD"
)
}
src/main/scala/is/kow/backupcoordinator/actors/HostBackupActor.scala
View file @
90402347
package
is.kow.backupcoordinator.actors
import
akka.actor.
{
Actor
,
ActorLogging
,
ActorRef
,
Props
,
ReceiveTimeout
}
import
akka.actor.
{
Actor
,
ActorLogging
,
ActorRef
,
Props
,
ReceiveTimeout
,
Terminated
}
import
is.kow.backupcoordinator.HostBackup
import
is.kow.backupcoordinator.actors.HostBackupActor.EstablishSSHSession
import
is.kow.backupcoordinator.actors.SSHCommandActor.
{
ExecuteCommand
,
ExecutionCompleted
}
import
is.kow.backupcoordinator.actors.SSHConnectionActor.Connect
import
net.schmizz.sshj.SSHClient
import
scala.concurrent.duration.Duration
object
HostBackupActor
{
def
props
(
host
name
:
String
,
statusActor
:
ActorRef
)
:
Props
=
Props
(
new
HostBackupActor
(
hostname
,
statusActor
))
def
props
(
host
Backup
:
HostBackup
,
statusActor
:
ActorRef
)
:
Props
=
Props
(
new
HostBackupActor
(
hostBackup
,
statusActor
))
case
object
EstablishSSHSession
...
...
@@ -15,7 +16,10 @@ object HostBackupActor {
}
class
HostBackupActor
(
hostname
:
String
,
statusActor
:
ActorRef
)
extends
Actor
with
ActorLogging
{
class
HostBackupActor
(
hostBackup
:
HostBackup
,
statusActor
:
ActorRef
)
extends
Actor
with
ActorLogging
{
import
scala.concurrent.duration._
/*
stages of backing up a host
1. establish SSH connection
...
...
@@ -39,20 +43,135 @@ class HostBackupActor(hostname: String, statusActor: ActorRef) extends Actor wit
case
EstablishSSHSession
=>
//Start a tiny actor to do the actual establishment, so it can fail, and we can retry
val
connectionActor
=
context
.
actorOf
(
SSHConnectionActor
.
props
)
import
scala.concurrent.duration._
connectionActor
!
Connect
(
hostBackup
.
hostname
)
//Assuming default port
context
.
setReceiveTimeout
(
3.
seconds
)
context
.
become
(
awaitingConnection
)
}
def
awaitingConnection
:
Receive
=
{
case
ReceiveTimeout
=>
log
.
error
(
"Did not receive connection in time! Retrying"
)
log
.
error
(
"Did not receive connection in time! OH NOES"
)
throw
new
Exception
(
"Did not receive connection in time!"
)
case
client
:
SSHClient
=>
case
client
:
SSHClient
=>
context
.
setReceiveTimeout
(
Duration
.
Undefined
)
//Have an ssh connection, time to send commands like make backup directory
val
mkdirActor
=
context
.
actorOf
(
SSHCommandActor
.
props
(
client
))
context
.
watch
(
mkdirActor
)
mkdirActor
!
ExecuteCommand
(
"mkdir /mnt/auto-backup"
)
context
.
become
(
awaitingMkdirComplete
(
client
,
mkdirActor
))
context
.
setReceiveTimeout
(
1.
second
)
}
def
awaitingMkdirComplete
(
client
:
SSHClient
,
mkdirActor
:
ActorRef
)
:
Receive
=
{
case
ReceiveTimeout
=>
log
.
error
(
"Unable to create directory in time! OH NOES"
)
throw
new
Exception
(
"Did not create directory in time!"
)
//TODO: need to clean up!
case
t
@Terminated
(
`client`
)
=>
log
.
error
(
s
"there was an exception and the actor died"
)
//TODO: need to clean up behind myself
case
ExecutionCompleted
(
cmd
,
output
,
status
)
=>
context
.
setReceiveTimeout
(
Duration
.
Undefined
)
log
.
info
(
s
"Command Processed ($status): $cmd -- $output"
)
//Can move onto step three
val
mountActor
=
context
.
actorOf
(
SSHCommandActor
.
props
(
client
))
context
.
watch
(
mountActor
)
mountActor
!
ExecuteCommand
(
"mount 10.10.220.92:/mnt/OldTank/backupTesting /mnt/auto-backup"
)
context
.
become
(
awaitingMountComplete
(
client
,
mountActor
))
context
.
setReceiveTimeout
(
3.
seconds
)
}
def
awaitingMountComplete
(
client
:
SSHClient
,
mountActor
:
ActorRef
)
:
Receive
=
{
case
ReceiveTimeout
=>
log
.
error
(
"Unable to mount nfs in time! OH NOES"
)
throw
new
Exception
(
"Did mount nfs in time!"
)
//TODO: need to clean up behind me
case
t
@Terminated
(
`client`
)
=>
log
.
error
(
s
"there was an exception and mounting command died"
)
case
ExecutionCompleted
(
cmd
,
output
,
status
)
=>
context
.
setReceiveTimeout
(
Duration
.
Undefined
)
//Completed the call to mount the nfs directory
log
.
info
(
s
"Command Processed ($status): $cmd -- $output"
)
//Step four!
doBackup
(
client
,
hostBackup
.
preBackupCommand
,
hostBackup
.
backupPaths
,
hostBackup
.
postBackupCommand
)
}
def
awaitingPreBackup
(
client
:
SSHClient
,
cmdActor
:
ActorRef
,
backupPaths
:
List
[
String
],
postBackup
:
Option
[
String
])
:
Receive
=
{
case
t
@Terminated
(
`cmdActor`
)
=>
log
.
error
(
"Pre-backup failed!"
)
//TODO: need to clean up hard
case
ExecutionCompleted
(
cmd
,
output
,
status
)
=>
//Completed the call to do pre-backup commands
log
.
info
(
s
"Command Processed ($status): $cmd -- $output"
)
//Can now execute the actual backup command
doBackup
(
client
,
None
,
backupPaths
,
postBackup
)
}
def
awaitingBackupDirectory
(
client
:
SSHClient
,
cmdActor
:
ActorRef
,
backupPaths
:
List
[
String
],
postBackup
:
Option
[
String
])
:
Receive
=
{
case
t
@Terminated
(
`cmdActor`
)
=>
log
.
error
(
"Directory backup failed!"
)
//TODO: need to clean up hard
case
ExecutionCompleted
(
cmd
,
output
,
status
)
=>
//A backup directory is completed!
log
.
info
(
s
"Completed a directory backup!"
)
doBackup
(
client
,
None
,
backupPaths
.
tail
,
postBackup
)
}
def
awaitingUnmount
(
client
:
SSHClient
,
cmdActor
:
ActorRef
)
:
Receive
=
{
case
t
@Terminated
(
`cmdActor`
)
=>
log
.
error
(
"unmount failed!"
)
//TODO: need to clean up hard
case
ExecutionCompleted
(
cmd
,
output
,
status
)
=>
log
.
info
(
"ALL DONE, closing down"
)
//Done with this, now we can close our ssh connection
client
.
close
()
//Boom!
context
.
stop
(
self
)
}
def
doBackup
(
client
:
SSHClient
,
preBackup
:
Option
[
String
],
backupPaths
:
List
[
String
],
postBackup
:
Option
[
String
])
:
Unit
=
{
if
(
preBackup
.
nonEmpty
)
{
//Do the pre-backup commands, and become awaitingPreBackup
val
preBackupCmd
=
context
.
actorOf
(
SSHCommandActor
.
props
(
client
))
preBackupCmd
!
ExecuteCommand
(
preBackup
.
get
)
//TODO: what's a reasonable time for stopping? Should have some kind of tick to indicate processing
context
.
become
(
awaitingPreBackup
(
client
,
preBackupCmd
,
backupPaths
,
postBackup
))
}
else
if
(
backupPaths
.
nonEmpty
)
{
//TODO: this assumes already initialized -- Need to add an initializer actor to handle that for me
//TODO: can check initialization with `borg info`
//have to build the backup command and issue one for each backup path
val
path
=
backupPaths
.
head
val
repoPath
=
s
"/mnt/auto-backup/${hostBackup.repoName}"
val
backupName
=
path
.
split
(
"/"
).
last
val
backupCmd
=
s
"""
| borg create \
| --info \
| --stats \
| --one-file-system \
| --compression zlib,7 \
| ${repoPath}::${backupName}-{now:%Y-%m-%d_%H:%M:%S} \
| ${path}
|
"""
.
stripMargin
//backup command created
val
backupDirectoryActor
=
context
.
actorOf
(
SSHCommandActor
.
props
(
client
))
backupDirectoryActor
!
ExecuteCommand
(
backupCmd
)
context
.
become
(
awaitingBackupDirectory
(
client
,
backupDirectoryActor
,
backupPaths
,
postBackup
))
}
else
if
(
postBackup
.
nonEmpty
)
{
log
.
error
(
"TODO Post backup stuff isn't actually ever run"
)
//Backup paths should be empty at this stage
doBackup
(
client
,
None
,
backupPaths
,
None
)
}
else
{
//All done doing the backup, step five!
val
cmd
=
"umount /mnt/auto-backup"
val
actor
=
context
.
actorOf
(
SSHCommandActor
.
props
(
client
))
actor
!
ExecuteCommand
(
cmd
)
context
.
become
(
awaitingUnmount
(
client
,
actor
))
}
}
}
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
.
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment