diff --git a/norg/borg/borg.nim b/norg/borg/borg.nim index 5acdc60..1d42578 100644 --- a/norg/borg/borg.nim +++ b/norg/borg/borg.nim @@ -1,27 +1,29 @@ import ../model/config_type import ../model/state_type +import ../model/borg_type import ../notifier/notifier -import strutils, sequtils +import ../utils/actions +import strutils +import strformat +import sequtils import osproc import times import os import nativesockets -const BORG_BIN = "borg" proc genArchiveName(): string = let hostname = getHostname() let ts = getTime().format("yyyy-MM-dd'T'HH:mm:ss'.'ffffff") - return hostname & "-" & ts + return fmt"{hostname}-{ts}" proc genCommand(cmd: string, repo: string, others: seq[string]): string = - var str = BORG_BIN & " " & cmd & " " - str &= " " & repo & " " - str &= others.join(" ") - return str + let args = others.join(" ") + let cmd = fmt"{BORG_BIN} {cmd} {repo} {args}" + return cmd proc run(cmd: string): int = - echo "Trying to run : ", cmd + echo fmt"Trying to run : {cmd}" try: let res = execProcess(cmd) echo res @@ -29,7 +31,7 @@ proc run(cmd: string): int = echo getCurrentExceptionMsg() proc runDiscard(cmd: string): int = - echo "Trying to run : ", cmd + echo fmt"Trying to run : {cmd}" try: let res = execCmd(cmd) return res @@ -40,13 +42,23 @@ proc runDiscard(cmd: string): int = proc initRepo(nc: NorgConfig, repo: Repository): int = return runDiscard genCommand(cmd = "init", repo = repo.path, others = nc.args.others) +proc createArchive(nc: NorgConfig, repo: Repository, archivename: string, retry: int = 0): int = + let others = concat(nc.source_directories, nc.args.others) + let res = run genCommand(cmd = "create", repo = archivename, others = others) + if res == 1: + sleep 15 * 1000 # 15 seconds + if retry == nc.retries: + return 1 + else: + return createArchive(nc, repo, archivename, retry + 1) + return res + proc backupSources(nc: NorgConfig, repo: Repository): int = let start_time = now() notify(nc.notifiers, state=Running) - let others = concat(nc.source_dirs, nc.args.others) - let archivename = repo.path & "::" & genArchiveName() - let res = run genCommand(cmd = "create", repo = archivename, others = others) let end_time = now() + let archivename = repo.path & "::" & genArchiveName() + let res = createArchive(nc, repo, archivename) let total = (end_time - start_time).inMilliSeconds() case res of 0: @@ -55,6 +67,7 @@ proc backupSources(nc: NorgConfig, repo: Repository): int = notify(nc.notifiers, state=Failure, runtime=total) else: notify(nc.notifiers, state=Failure, runtime=total) + return res proc listArchives(nc: NorgConfig, repo: Repository): int = @@ -65,7 +78,7 @@ proc mountArchive(nc: NorgConfig, repo: Repository): int = let others = nc.args.others[1..^1] let ok = runDiscard genCommand(cmd = "mount", repo = archive, others = others) if ok == 0: - echo "Mounted ", archive, " at ", others[0] + echo fmt"Mounted {archive} at {others[0]}" else: echo "Failed to mount ", archive @@ -83,7 +96,7 @@ proc isEmpty(dir: string): bool = return count == 0 proc extractArchive(nc: NorgConfig, repo: Repository): int = - let archive = repo.path & "::" & nc.args.others[0] + let archive = fmt"{repo.path}::{nc.args.others[0]}" var others = nc.args.others[1..^1] if nc.args.extract_destination != "": discard existsOrCreateDir(nc.args.extract_destination) @@ -96,22 +109,51 @@ proc extractArchive(nc: NorgConfig, repo: Repository): int = else: echo "Not restoring to non-empty destination\r\nPlease use the --destination flag" +proc pruneRepo(nc: NorgConfig, repo: Repository): int = + echo "Not Yet Implemented." + discard + +proc compactRepo(nc: NorgConfig, repo: Repository): int = + echo "Not Yet Implemented." + discard + +proc checkRepo(nc: NorgConfig, repo: Repository): int = + echo "Not Yet Implemented." + discard + proc execute*(nc: NorgConfig) = - putEnv("BORG_PASSPHRASE", nc.getEncryptionPassword()) + run_actions(norg_config.actions.before_everything) for repo in nc.repositories: + run_actions(norg_config.actions.before_actions) case nc.args.borg_cmd - of "init": + of INIT: discard initRepo(nc, repo) - of "backup": + of CREATE: + run_actions(norg_config.actions.before_backup) discard backupSources(nc, repo) - of "create": - discard backupSources(nc, repo) - of "list": + run_actions(norg_config.actions.after_backup) + of LIST: discard listArchives(nc, repo) - of "mount": + of MOUNT: discard mountArchive(nc, repo) - of "umount": + of UMOUNT: discard unmountArchive(nc) - of "extract": + of EXTRACT: + run_actions(norg_config.actions.before_extract) discard extractArchive(nc, repo) - delEnv("BORG_PASSPHRASE") + run_actions(norg_config.actions.after_extract) + of PRUNE: + run_actions(norg_config.actions.before_prune) + discard pruneRepo(nc, repo) + run_actions(norg_config.actions.after_prune) + of COMPACT: + run_actions(norg_config.actions.before_compact) + discard compactRepo(nc, repo) + run_actions(norg_config.actions.after_compact) + of CHECK: + run_actions(norg_config.actions.before_check) + discard checkRepo(nc, repo) + run_actions(norg_config.actions.after_check) + run_actions(norg_config.actions.after_actions) + delEncryptionPassphraseInfo() + run_actions(norg_config.actions.after_everything) diff --git a/norg/config/args.nim b/norg/config/args.nim index da3dcf4..02008db 100644 --- a/norg/config/args.nim +++ b/norg/config/args.nim @@ -1,10 +1,11 @@ import argparse +import ../model/borg_type type NorgArgs* = object config_file*: string extract_destination*: string - borg_cmd*: string + borg_cmd*: BorgCommand others*: seq[string] var norg_args*: NorgArgs = NorgArgs() @@ -20,7 +21,7 @@ proc parseArgs*() = var opts = p.parse(commandLineParams()) norg_args.config_file = opts.config norg_args.extract_destination = opts.destination - norg_args.borg_cmd = opts.borg_cmd + norg_args.borg_cmd = opts.borg_cmd.toBorgCommand() norg_args.others = opts.others except ShortCircuit as err: diff --git a/norg/config/init.nim b/norg/config/init.nim index 0cfe2a3..77b2a8c 100644 --- a/norg/config/init.nim +++ b/norg/config/init.nim @@ -4,18 +4,34 @@ import ../model/config_type import notifier_config export config_type -proc parseConfigFile*(file: string): NorgConfig = - norg_config = newNorgConfig() - let in_conf = parsetoml.parseFile(file) +proc parseSourceDirectories*(in_conf: TomlValueRef): seq[string] = + var src_dirs: seq[string] = @[] + for dir in in_conf{"source_directories"}.getElems(): + src_dirs.add(dir.getStr()) + # this is here for backwards compatibiliy for dir in in_conf{"source_dirs"}.getElems(): - norg_config.source_dirs.add(dir.getStr()) - for r in in_conf{"repositories"}.getElems(): + norg_config.source_directories.add(dir.getStr()) + return src_dirs + +proc parseEncryption*(enc_conf: TomlValueRef) = + norg_config.setEncryptionPassphrase(enc_conf{"encryption_passphrase"}.getStr("")) + +proc parseRepositories*(rep_conf: TomlValueRef): seq[Repository] = + var repos: seq[Repository] = @[] + for r in rep_conf.getElems(): let rtable = r.getTable() var repo = Repository() repo.path = rtable["path"].getStr() repo.label = rtable["label"].getStr() - norg_config.repositories.add(repo) - norg_config.setEncryptionPassword(in_conf{"encryption_password"}.getStr("")) + repos.add(repo) + return repos + +proc parseConfigFile*(file: string): NorgConfig = + norg_config = newNorgConfig() + let in_conf = parsetoml.parseFile(file) + norg_config.source_directories = parseSourceDirectories(in_conf) + parseEncryption(in_conf{"encryption"}) + norg_config.repositories = parseRepositories(in_conf{"repositories"}) norg_config.notifiers = parseNotifiers(in_conf) return norg_config diff --git a/norg/config/notifier_config.nim b/norg/config/notifier_config.nim index 521c87b..fede6bd 100644 --- a/norg/config/notifier_config.nim +++ b/norg/config/notifier_config.nim @@ -18,5 +18,4 @@ proc parseNotifiers*(in_conf: TomlValueRef): Notifiers = if in_conf.hasKey("uptimekuma"): let u = parseUptimeKumaConf(in_conf["uptimekuma"]) notifiers.uptimekuma = u - echo notifiers return notifiers diff --git a/norg/model/actions_type.nim b/norg/model/actions_type.nim new file mode 100644 index 0000000..6079e1f --- /dev/null +++ b/norg/model/actions_type.nim @@ -0,0 +1,24 @@ + +type + Actions* = object + # Before and after everything for all repositories + before_everything*: seq[string] + after_everything*: seq[string] + # before and after each reposistory + before_actions*: seq[string] + after_actions*: seq[string] + # before and after a backup per respository + before_backup*: seq[string] + after_backup*: seq[string] + # before and after a prune per repository + before_prune*: seq[string] + after_prune*: seq[string] + # before and after an extract per repository + before_extract*: seq[string] + after_extract*: seq[string] + # before and after an compact per repository + before_compact*: seq[string] + after_compact*: seq[string] + # before and after an check per repository + before_check*: seq[string] + after_check*: seq[string] diff --git a/norg/model/borg_type.nim b/norg/model/borg_type.nim new file mode 100644 index 0000000..dcb57ed --- /dev/null +++ b/norg/model/borg_type.nim @@ -0,0 +1,19 @@ +const BORG_BIN* = "borg" + +type + BorgCommand* = enum + LIST = "list" + INIT = "init", + CREATE = "create", + EXTRACT = "extract", + MOUNT = "mount", + UMOUNT = "umount", + PRUNE = "prune", + CHECK = "check", + COMPACT = "compact" + +proc toBorgCommand*(str: string): BorgCommand = + for cmd in BorgCommand.items: + if str == $cmd: return cmd + + diff --git a/norg/model/config_type.nim b/norg/model/config_type.nim index 70bd19f..fef8050 100644 --- a/norg/model/config_type.nim +++ b/norg/model/config_type.nim @@ -1,44 +1,54 @@ import repository_type import notifier_types +import actions_type import ../config/args +import os export repository_type export notifier_types +export actions_type type NorgConfig* = ref object - source_dirs*: seq[string] + retries*: int + source_directories*: seq[string] repositories*: seq[Repository] + maintenance*: Maintenance + #checks*: Check - see borgmatic + notifiers*: Notifiers + actions*: Actions + args*: NorgArgs + Maintenance* = object keep_daily*: int keep_weekly*: int keep_monthly*: int - #checks*: Check - see borgmatic - notifiers*: Notifiers - args*: NorgArgs - encryption_password: string var norg_config*: NorgConfig +proc newMaintenance*(): Maintenance = + var m = Maintenance() + m.keep_daily = 7 + m.keep_weekly = 4 + m.keep_monthly = 6 + return m + proc newNorgConfig*(): NorgConfig = var nc = NorgConfig() - nc.keep_daily = 7 - nc.keep_weekly = 4 - nc.keep_monthly = 6 + nc.maintenance = newMaintenance() return nc -proc `$`*(c: NorgConfig): string = - var msg: string = "Source Dirs: " & $c.source_dirs & "\r\n" - msg &= "Repositories: " & $c.repositories & "\r\n" - msg &= "Keep Daily: " & $c.keep_daily & "\r\n" - msg &= "Keep Weekly: " & $c.keep_weekly & "\r\n" - msg &= "Keep Monthly: " & $c.keep_monthly & "\r\n" - msg &= "Notifiers: " & $c.notifiers & "\r\n" - return msg +#proc `$`*(c: NorgConfig): string = +# var msg: string = "Source Dirs: " & $c.source_directories & "\r\n" +# msg &= "Repositories: " & $c.repositories & "\r\n" +# msg &= "Notifiers: " & $c.notifiers & "\r\n" +# return msg + +proc setEncryptionPassphrase*(nc: var NorgConfig, pw: string) = + putEnv("BORG_PASSPHRASE", pw) + +proc delEncryptionPassphraseInfo*() = + delEnv("BORG_PASSPHRASE") -proc setEncryptionPassword*(nc: var NorgConfig, pw: string) = - nc.encryption_password = pw -proc getEncryptionPassword*(nc: NorgConfig): string = - return nc.encryption_password diff --git a/norg/norg.nim b/norg/norg.nim index 34b6018..7198ff8 100644 --- a/norg/norg.nim +++ b/norg/norg.nim @@ -6,7 +6,7 @@ proc start() = parseArgs() norg_config = parseConfigFile(norg_args.config_file) norg_config.args = norg_args - if norg_config.source_dirs.len > 0 and norg_config.repositories.len > 0: + if norg_config.source_directories.len > 0 and norg_config.repositories.len > 0: borg.execute(norg_config) when isMainModule: diff --git a/norg/utils/actions.nim b/norg/utils/actions.nim new file mode 100644 index 0000000..707a401 --- /dev/null +++ b/norg/utils/actions.nim @@ -0,0 +1,6 @@ +import osproc + +proc run_actions*(actions: seq[string]): int {.discardable.} = + for action in actions: + echo execCmd(action) +