37 #include <boost/algorithm/string.hpp>
38 #include <boost/tokenizer.hpp>
45 using namespace boost;
50 const char *builtin_task_name[] = {
51 "CLUSTER_BUILTIN_display_line",
57 const string determine_trailing_indentation(
const string &text) {
60 const char *base = text.c_str();
61 const char *ptr = base + (text.length() - 1);
63 while (ptr >= base && isspace(*ptr) && *ptr !=
'\n' && *ptr !=
'\r') {
67 indentation.append(ptr+1);
70 if (indentation.empty())
71 indentation.append(
" ");
75 bool translate_ssh_statement(
const char *base,
const char *end,
76 const string &indentation,
77 const char **nextp,
string &ssh_command,
84 const char *open_curly = base;
85 while (open_curly < end && *open_curly !=
'{')
88 const char *close_curly;
90 errmsg.append(
"curly brace not found");
97 string text(base, open_curly-base);
98 char_separator<char> sep(
" \t\n\r");
99 tokenizer<char_separator<char>> tokens(text, sep);
100 for (
const auto& token : tokens) {
101 if (!strncmp(token.c_str(),
"random-start-delay=", 19)) {
103 errmsg.append(
"invalid random-start-delay argument");
106 options.append(
" --");
107 options.append(token.substr(0, 18));
109 options.append(token.substr(19));
111 else if (token.compare(
"in-series") == 0) {
112 options.append(
" --");
113 options.append(token);
127 string content(open_curly, close_curly-open_curly);
130 string escaped_content;
131 escaped_content.reserve(content.length()+32);
132 for (
const char *ptr = content.c_str(); *ptr; ptr++) {
134 escaped_content.append(1,
'\\');
135 escaped_content.append(1, *ptr);
138 ssh_command.append(escaped_content);
139 ssh_command.append(
"\"\n");
140 ssh_command.append(indentation);
141 ssh_command.append(
"if [ $? -ne 0 ]; then\n");
142 ssh_command.append(indentation);
143 ssh_command.append(
" exit 1\n");
144 ssh_command.append(indentation);
145 ssh_command.append(
"fi");
147 *nextp = close_curly + 1;
158 string translated_text;
161 const char *base = m_text.c_str();
167 end = strchr(base,
'\n');
172 line.append(base, end-base);
175 if (line[0] ==
'#') {
176 line = line.substr(1);
178 if (description.empty())
179 description.append(line);
180 else if (description[description.length()-1] ==
'.') {
181 description.append(
" ");
182 description.append(line);
185 description.append(
" ");
186 description.append(line);
194 base += strlen(base);
206 (
int)m_lineno, m_fname.c_str());
209 if ((ptr = strchr(base,
'{')) == 0)
211 (
int)m_lineno, m_fname.c_str());
215 "Missing terminating '}' in task definition on line %d of '%s'",
216 (
int)m_lineno, m_fname.c_str());
218 string text(base, ptr-base);
220 vector<string> words;
222 char_separator<char> sep(
" ");
223 tokenizer<char_separator<char>> tokens(text, sep);
224 for (
const auto& t : tokens)
228 if (words.size() < 2 || words.size() == 3 || words[0].compare(
"task:") != 0 ||
229 (words.size() >= 3 && words[2].compare(
"roles:") != 0))
231 (
int)m_lineno, m_fname.c_str());
233 string task_name(words[1]);
235 for (
size_t i=0; builtin_task_name[i]; i++) {
236 if (task_name.compare(builtin_task_name[i]) == 0)
238 "Task name '%s' conflicts with built-in on line %d of '%s'",
239 task_name.c_str(), (int)m_lineno, m_fname.c_str());
244 task_name.c_str(), (int)m_lineno, m_fname.c_str());
246 translated_text.append(task_name);
247 translated_text.append(
" () {\n local _SSH_HOSTS=\"");;
250 if (words.size() > 3) {
252 for (
size_t i=3; i<words.size(); i++) {
253 text.append(words[i]);
256 char_separator<char> sep(
" ,");
257 tokenizer<char_separator<char>> tokens(text, sep);
259 for (
const auto& t : tokens) {
260 if (context.
roles.count(t) == 0)
262 t.c_str(), (int)m_lineno, m_fname.c_str());
266 translated_text.append(
" + ");
267 translated_text.append(
"(${ROLE_");
268 translated_text.append(t);
269 translated_text.append(
"})");
270 if (!task_roles.empty())
271 task_roles.append(
", ");
272 task_roles.append(t);
277 for (
auto & role : context.
roles) {
281 translated_text.append(
" + ");
282 translated_text.append(
"(${ROLE_");
283 translated_text.append(role);
284 translated_text.append(
"})");
287 translated_text.append(
"\"\n");
288 translated_text.append(
" if [ $# -gt 0 ] && [ $1 == \"on\" ]; then\n");
289 translated_text.append(
" shift\n");
290 translated_text.append(
" if [ $# -eq 0 ]; then\n");
291 translated_text.append(
" echo \"Missing host specification in 'on' argument\"\n");
292 translated_text.append(
" exit 1\n");
293 translated_text.append(
" else\n");
294 translated_text.append(
" _SSH_HOSTS=\"$1\"\n");
295 translated_text.append(
" shift\n");
296 translated_text.append(
" fi\n");
297 translated_text.append(
" fi\n");
298 translated_text.append(
" echo \"");
299 translated_text.append(task_name);
300 translated_text.append(
" $@\"\n ");
306 while (base < end && isspace(*base)) {
322 ptr = base + offset + 3;
323 while (*ptr && isspace(*ptr))
325 if (*ptr ==
'{' || *ptr == 0 || !strncmp(ptr,
"random-start-delay=", 19) ||
326 !strncmp(ptr,
"in-series", 9))
328 (
int)tmp_lineno, m_fname.c_str());
333 task_body.append(base, offset);
335 string indentation = determine_trailing_indentation(task_body);
336 if (!translate_ssh_statement(base, end, indentation, &ptr, ssh_command, error_msg))
338 error_msg.c_str(), (int)lineno, m_fname.c_str());
340 task_body.append(ssh_command);
345 string content(base, end-base);
347 task_body.append(content);
350 translated_text.append(task_body);
352 translated_text.append(
"\n}\n");
354 context.
tasks[task_name] = description;
357 return translated_text;
Declarations for TranslatorTask.
Retrieves system information (hardware, installation directory, etc)
map< string, string > task_roles
String format(const char *fmt,...)
Returns a String using printf like format facilities Vanilla snprintf is about 1.5x faster than this...
set< string > roles
Set of role names.
Logging routines and macros.
const string translate(TranslationContext &context) override
Translates a task definition.
Compatibility Macros for C/C++.
map< string, string > symbols
Map of variable names to default values.
#define HT_THROWF(_code_, _fmt_,...)
static String install_dir
The installation directory.
A String class based on std::string.
Context used to translate cluster definition statements.
Cluster definition file translation definitions.
Error codes, Exception handling, error logging.
map< string, string > tasks
Map of tasks to descriptive text.