1 /**
2 Copyright: Copyright (c) 2019, Joakim Brännström. All rights reserved.
3 License: $(LINK2 http://www.boost.org/LICENSE_1_0.txt, Boost Software License 1.0)
4 Author: Joakim Brännström (joakim.brannstrom@gmx.com)
5 */
6 module dsnapshot.cmdgroup.admin;
7 
8 import logger = std.experimental.logger;
9 import std.algorithm;
10 import std.array : empty, array;
11 import std.exception : collectException;
12 import std.stdio : writeln;
13 
14 import dsnapshot.backend;
15 import dsnapshot.config : Config;
16 import dsnapshot.exception;
17 import dsnapshot.process;
18 import dsnapshot.types;
19 
20 @safe:
21 
22 int cmdAdmin(SnapshotConfig[] snapshots, const Config.Admin conf) nothrow {
23     auto operateOn = () {
24         if (conf.names.empty) {
25             return snapshots;
26         }
27 
28         import dsnapshot.set : Set, toSet;
29 
30         Set!string pick = conf.names.map!(a => a.value).toSet;
31         return snapshots.filter!(a => pick.contains(a.name)).array;
32     }();
33 
34     foreach (snapshot; operateOn) {
35         try {
36             auto backend = makeSyncBackend(snapshot);
37 
38             auto crypt = makeCrypBackend(snapshot.crypt);
39             open(crypt, backend.flow);
40             scope (exit)
41                 crypt.close;
42 
43             final switch (conf.cmd) with (Config.Admin) {
44             case Cmd.list:
45                 cmdList(snapshot, backend.flow);
46                 break;
47             case Cmd.diskusage:
48                 cmdDiskUsage(snapshot, backend.flow);
49                 break;
50             }
51         } catch (SnapshotException e) {
52             e.errMsg.match!(a => a.print).collectException;
53             logger.error(e.msg).collectException;
54         } catch (Exception e) {
55             logger.error(e.msg).collectException;
56             break;
57         }
58     }
59 
60     return 0;
61 }
62 
63 private:
64 
65 void cmdList(SnapshotConfig snapshot, Flow flow) {
66     import dsnapshot.layout_utils;
67 
68     auto layout = snapshot.syncCmd.match!((None a) => snapshot.layout,
69             (RsyncConfig a) => fillLayout(snapshot.layout, a.flow, snapshot.remoteCmd));
70 
71     writeln("Snapshot config: ", snapshot.name);
72     writeln(layout);
73 }
74 
75 void cmdDiskUsage(SnapshotConfig snapshot, Flow flow) {
76     import dsnapshot.layout;
77     import dsnapshot.layout_utils;
78 
79     writeln("Snapshot config: ", snapshot.name);
80 
81     const cmdDu = snapshot.syncCmd.match!((None a) => null, (RsyncConfig a) => a.cmdDiskUsage);
82     if (cmdDu.empty) {
83         logger.errorf("cmd_du is not set for snapshot %s", snapshot.name).collectException;
84         return;
85     }
86 
87     // dfmt off
88     flow.match!((None a) => 1,
89                        (FlowRsyncToLocal a) => localDiskUsage(cmdDu, a.dst.value.Path),
90                        (FlowLocal a) => localDiskUsage(cmdDu, a.dst.value.Path),
91                        (FlowLocalToRsync a) => remoteDiskUsage(a.dst, snapshot.remoteCmd, cmdDu)
92                        );
93     // dfmt on
94 }
95 
96 int localDiskUsage(const string[] cmdDu, Path p) {
97     return spawnProcessLog(cmdDu ~ p.toString).wait;
98 }
99 
100 int remoteDiskUsage(RemoteHost host, RemoteCmd remote, const string[] cmdDu) {
101     auto cmd = remote.match!((SshRemoteCmd a) => a.rsh);
102     cmd ~= host.addr ~ cmdDu ~ host.path;
103     return spawnProcessLog(cmd).wait;
104 }