Wire Sysio Wire Sysion 1.0.0
Loading...
Searching...
No Matches
file_space_handler.hpp
Go to the documentation of this file.
1#pragma once
2
3#include <boost/filesystem.hpp>
4#include <boost/asio.hpp>
5
8
9namespace bfs = boost::filesystem;
10
12 template<typename SpaceProvider>
14 public:
15 file_space_handler(SpaceProvider&& space_provider, boost::asio::io_context& ctx)
16 :space_provider(std::move(space_provider)),
17 timer{ctx}
18 {
19 }
20
21 void set_sleep_time(uint32_t sleep_time) {
22 sleep_time_in_secs = sleep_time;
23 }
24
25 // warning_threshold must be less than shutdown_threshold.
26 // set them together so it is simpler to check.
27 void set_threshold(uint32_t new_threshold, uint32_t new_warning_threshold) {
28 SYS_ASSERT(new_warning_threshold < new_threshold, chain::plugin_config_exception,
29 "warning_threshold ${new_warning_threshold} must be less than threshold ${new_threshold}", ("new_warning_threshold", new_warning_threshold) ("new_threshold", new_threshold));
30
31 shutdown_threshold = new_threshold;
32 warning_threshold = new_warning_threshold;
33 }
34
35 void set_shutdown_on_exceeded(bool new_shutdown_on_exceeded) {
36 shutdown_on_exceeded = new_shutdown_on_exceeded;
37 }
38
39 void set_warning_interval(uint32_t new_warning_interval) {
40 warning_interval = new_warning_interval;
41 }
42
44 // Go over each monitored file system
45 for (auto& fs: filesystems) {
46 boost::system::error_code ec;
47 auto info = space_provider.get_space(fs.path_name, ec);
48 if ( ec ) {
49 // As the system is running and this plugin is not a critical
50 // part of the system, we should not exit.
51 // Just report the failure and continue;
52 wlog( "Unable to get space info for ${path_name}: [code: ${ec}] ${message}. Ignore this failure.",
53 ("path_name", fs.path_name.string())
54 ("ec", ec.value())
55 ("message", ec.message()));
56
57 continue;
58 }
59
60 if ( info.available < fs.shutdown_available ) {
61 if (output_threshold_warning) {
62 elog("Space usage warning: ${path}'s file system exceeded threshold ${threshold}%, available: ${available}, Capacity: ${capacity}, shutdown_available: ${shutdown_available}",
63 ("path", fs.path_name.string())("threshold", shutdown_threshold)("available", info.available)("capacity", info.capacity)("shutdown_available", fs.shutdown_available));
64 }
65 return true;
66 } else if ( info.available < fs.warning_available && output_threshold_warning ) {
67 wlog("Space usage warning: ${path}'s file system approaching threshold. available: ${available}, warning_available: ${warning_available}", ("path", fs.path_name.string()) ("available", info.available) ("warning_available", fs.warning_available));
68 if ( shutdown_on_exceeded) {
69 wlog("nodeop will shutdown when space usage exceeds threshold ${threshold}%", ("threshold", shutdown_threshold));
70 }
71 }
72 }
73
74 return false;
75 }
76
77 void add_file_system(const bfs::path& path_name) {
78 // Get detailed information of the path
79 struct stat statbuf;
80 auto status = space_provider.get_stat(path_name.string().c_str(), &statbuf);
81 SYS_ASSERT(status == 0, chain::plugin_config_exception,
82 "Failed to run stat on ${path} with status ${status}", ("path", path_name.string())("status", status));
83
84 dlog("${path_name}'s file system to be monitored", ("path_name", path_name.string()));
85
86 // If the file system containing the path is already
87 // in the filesystem list, do not add it again
88 for (auto& fs: filesystems) {
89 if (statbuf.st_dev == fs.st_dev) { // Two files belong to the same file system if their device IDs are the same.
90 dlog("${path_name}'s file system already monitored", ("path_name", path_name.string()));
91
92 return;
93 }
94 }
95
96 // For efficiency, precalculate threshold values to avoid calculating it
97 // everytime we check space usage. Since bfs::space returns
98 // available amount, we use minimum available amount as threshold.
99 boost::system::error_code ec;
100 auto info = space_provider.get_space(path_name, ec);
101 SYS_ASSERT(!ec, chain::plugin_config_exception,
102 "Unable to get space info for ${path_name}: [code: ${ec}] ${message}",
103 ("path_name", path_name.string())
104 ("ec", ec.value())
105 ("message", ec.message()));
106
107 auto shutdown_available = (100 - shutdown_threshold) * (info.capacity / 100); // (100 - shutdown_threshold)/100 is the percentage of minimum number of available bytes the file system must maintain
108 auto warning_available = (100 - warning_threshold) * (info.capacity / 100);
109
110 // Add to the list
111 filesystems.emplace_back(statbuf.st_dev, shutdown_available, path_name, warning_available);
112
113 ilog("${path_name}'s file system monitored. shutdown_available: ${shutdown_available}, capacity: ${capacity}, threshold: ${threshold}", ("path_name", path_name.string()) ("shutdown_available", shutdown_available) ("capacity", info.capacity) ("threshold", shutdown_threshold) );
114 }
115
117 if ( is_threshold_exceeded() && shutdown_on_exceeded ) {
118 elog("Shutting down, file system exceeded threshold");
119 appbase::app().quit(); // This will gracefully stop Nodeop
120 return;
121 }
122 update_warning_interval_counter();
123
124 timer.expires_from_now( boost::posix_time::seconds( sleep_time_in_secs ));
125
126 timer.async_wait([this](auto& ec) {
127 if ( ec ) {
128 wlog("Exit due to error: ${ec}, message: ${message}",
129 ("ec", ec.value())
130 ("message", ec.message()));
131 return;
132 } else {
133 // Loop over
135 }
136 });
137 }
138
139 private:
140 SpaceProvider space_provider;
141
142 boost::asio::deadline_timer timer;
143
144 uint32_t sleep_time_in_secs {2};
145 uint32_t shutdown_threshold {90};
146 uint32_t warning_threshold {85};
147 bool shutdown_on_exceeded {true};
148
149 struct filesystem_info {
150 dev_t st_dev; // device id of file system containing "file_path"
151 uintmax_t shutdown_available {0}; // minimum number of available bytes the file system must maintain
152 bfs::path path_name;
153 uintmax_t warning_available {0}; // warning is issued when availabla number of bytese drops below warning_available
154
155 filesystem_info(dev_t dev, uintmax_t available, const bfs::path& path, uintmax_t warning)
156 : st_dev(dev),
157 shutdown_available(available),
158 path_name(path),
159 warning_available(warning)
160 {
161 }
162 };
163
164 // Stores file systems to be monitored. Duplicate
165 // file systems are not stored.
166 std::vector<filesystem_info> filesystems;
167
168 uint32_t warning_interval {1};
169 uint32_t warning_interval_counter {1};
170 bool output_threshold_warning {true};
171
172 void update_warning_interval_counter() {
173 if ( warning_interval_counter == warning_interval ) {
174 output_threshold_warning = true;
175 warning_interval_counter = 1;
176 } else {
177 output_threshold_warning = false;
178 ++warning_interval_counter;
179 }
180 }
181 };
182}
#define SYS_ASSERT(expr, exc_type, FORMAT,...)
Definition exceptions.hpp:7
file_space_handler(SpaceProvider &&space_provider, boost::asio::io_context &ctx)
void set_threshold(uint32_t new_threshold, uint32_t new_warning_threshold)
void set_warning_interval(uint32_t new_warning_interval)
void add_file_system(const bfs::path &path_name)
void set_shutdown_on_exceeded(bool new_shutdown_on_exceeded)
#define wlog(FORMAT,...)
Definition logger.hpp:124
#define dlog(FORMAT,...)
Definition logger.hpp:101
#define ilog(FORMAT,...)
Definition logger.hpp:118
#define elog(FORMAT,...)
Definition logger.hpp:130
application & app()
Definition name.hpp:106
uint64_t uintmax_t
Definition stdint.h:170
unsigned int uint32_t
Definition stdint.h:126