client: parse keyword file and export keywords in get_state() GUI RPC

- This adds overhead to the get_state() call,
  but this happens only once per minute with the Manager.
- rename things so that "keyword_ids" refers to lists of keyword IDs
  and "keywords" refers to full KEYWORD objects
- have boinccmd include keywords in workunit properties
This commit is contained in:
David Anderson 2017-07-30 13:12:16 -07:00
parent 093e00a07c
commit d15d0b95de
12 changed files with 111 additions and 22 deletions

View File

@ -484,6 +484,26 @@ int CLIENT_STATE::init() {
fclose(f);
}
// parse keyword file if present
//
f = fopen(KEYWORD_FILENAME, "r");
if (f) {
MIOFILE mf;
mf.init_file(f);
XML_PARSER xp(&mf);
retval = keywords.parse(xp);
if (!retval) keywords.present = true;
fclose(f);
#if 0
std::map<int, KEYWORD>::iterator it;
for (it = keywords.keywords.begin(); it != keywords.keywords.end(); it++) {
int id = it->first;
KEYWORD& kw = it->second;
printf("keyword %d: %s\n", id, kw.name.c_str());
}
#endif
}
parse_account_files();
parse_statistics_files();

View File

@ -1125,8 +1125,8 @@ int WORKUNIT::parse(XML_PARSER& xp) {
#endif
continue;
}
if (xp.parse_str("job_keywords", buf, sizeof(buf))) {
job_keywords.parse_str(buf );
if (xp.parse_str("job_keyword_ids", buf, sizeof(buf))) {
job_keyword_ids.parse_str(buf );
continue;
}
// unused stuff
@ -1176,13 +1176,13 @@ int WORKUNIT::write(MIOFILE& out, bool gui) {
input_files[i].write(out);
}
if (!job_keywords.empty()) {
if (!job_keyword_ids.empty()) {
if (gui) {
if (gstate.keywords.present) {
job_keywords.write_xml_text(out, gstate.keywords);
job_keyword_ids.write_xml_text(out, gstate.keywords);
}
} else {
job_keywords.write_xml_num(out);
job_keyword_ids.write_xml_num(out);
}
}
out.printf("</workunit>\n");

View File

@ -368,7 +368,7 @@ struct WORKUNIT {
double rsc_fpops_bound;
double rsc_memory_bound;
double rsc_disk_bound;
JOB_KEYWORDS job_keywords;
JOB_KEYWORD_IDS job_keyword_ids;
WORKUNIT(){
safe_strcpy(name, "");
@ -376,7 +376,7 @@ struct WORKUNIT {
version_num = 0;
command_line = "";
input_files.clear();
job_keywords.clear();
job_keyword_ids.clear();
project = NULL;
app = NULL;
ref_cnt = 0;

View File

@ -74,6 +74,7 @@ extern void send_log_after(const char* filename, double t, MIOFILE& mf);
#define GLOBAL_PREFS_FILE_NAME "global_prefs.xml"
#define GLOBAL_PREFS_OVERRIDE_FILE "global_prefs_override.xml"
#define JOB_LOG_BASE "job_log_"
#define KEYWORD_FILENAME "keywords.xml"
#define LOOKUP_ACCOUNT_FILENAME "lookup_account.xml"
#define LOOKUP_WEBSITE_FILENAME "lookup_website.html"
#define MASTER_BASE "master_"

View File

@ -334,6 +334,7 @@ int RPC_CLIENT::get_reply(char*& mbuf) {
if (n <= 0) return ERR_READ;
buf[n]=0;
mf.puts(buf);
printf("%s\n", buf);
if (strchr(buf, '\003')) break;
}
mf.get_buf(mbuf, n);

View File

@ -39,6 +39,7 @@
#include "common_defs.h"
#include "filesys.h"
#include "hostinfo.h"
#include "keyword.h"
#include "miofile.h"
#include "network.h"
#include "notice.h"
@ -232,6 +233,7 @@ struct WORKUNIT {
double rsc_disk_bound;
PROJECT* project;
APP* app;
JOB_KEYWORDS job_keywords;
WORKUNIT();

View File

@ -601,6 +601,10 @@ int WORKUNIT::parse(XML_PARSER& xp) {
if (xp.parse_double("rsc_fpops_bound", rsc_fpops_bound)) continue;
if (xp.parse_double("rsc_memory_bound", rsc_memory_bound)) continue;
if (xp.parse_double("rsc_disk_bound", rsc_disk_bound)) continue;
if (xp.match_tag("job_keywords")) {
job_keywords.parse(xp);
continue;
}
}
return ERR_XML_PARSE;
}

View File

@ -142,6 +142,13 @@ void WORKUNIT::print() {
printf(" FP bound: %e\n", rsc_fpops_bound);
printf(" memory bound: %.2f MB\n", rsc_memory_bound/MEGA);
printf(" disk bound: %.2f MB\n", rsc_disk_bound/MEGA);
if (!job_keywords.empty()) {
printf(" keywords:\n");
for (unsigned int i=0; i<job_keywords.keywords.size(); i++) {
KEYWORD &kw = job_keywords.keywords[i];
printf(" %s\n", kw.name.c_str());
}
}
}
void RESULT::print() {

View File

@ -27,6 +27,7 @@ int KEYWORD::parse(XML_PARSER& xp) {
if (xp.match_tag("/keyword")) {
return 0;
}
if (xp.parse_int("id", id)) continue;
if (xp.parse_string("name", name)) continue;
if (xp.parse_string("description", description)) continue;
if (xp.parse_int("parent", parent)) continue;
@ -43,16 +44,27 @@ void KEYWORD::write_xml(MIOFILE& mf) {
" <description>%s</description>\n"
" <parent>%d</parent>\n"
" <level>%d</level>\n"
" <category>%d</category>\n",
name, description, parent, level, category
" <category>%d</category>\n"
"</keyword>\n",
name.c_str(), description.c_str(), parent, level, category
);
}
int KEYWORDS::parse(XML_PARSER& xp) {
while (!xp.get_tag()) {
if (xp.match_tag("/keyword")) {
if (xp.match_tag("/keywords")) {
return 0;
}
if (xp.match_tag("keyword")) {
KEYWORD kw;
int retval = kw.parse(xp);
if (retval) {
printf("KEYWORD parse fail: %d\n", retval);
return retval;
}
keywords[kw.id] = kw;
continue;
}
}
return ERR_XML_PARSE;
}
@ -88,7 +100,7 @@ void USER_KEYWORDS::write(FILE* f) {
fprintf(f, "</user_keywords>\n");
}
void JOB_KEYWORDS::parse_str(char* buf) {
void JOB_KEYWORD_IDS::parse_str(char* buf) {
char* p = strtok(buf, " ");
if (!p) return;
ids.push_back(atoi(p));
@ -99,7 +111,9 @@ void JOB_KEYWORDS::parse_str(char* buf) {
}
}
void JOB_KEYWORDS::write_xml_text(MIOFILE& mf, KEYWORDS& k) {
// write list of full keywords
//
void JOB_KEYWORD_IDS::write_xml_text(MIOFILE& mf, KEYWORDS& k) {
mf.printf("<job_keywords>\n");
for (unsigned int i=0; i<ids.size(); i++) {
int id = ids[i];
@ -108,9 +122,11 @@ void JOB_KEYWORDS::write_xml_text(MIOFILE& mf, KEYWORDS& k) {
mf.printf("</job_keywords>\n");
}
void JOB_KEYWORDS::write_xml_num(MIOFILE& out) {
// write 1-line list of keyword IDs
//
void JOB_KEYWORD_IDS::write_xml_num(MIOFILE& out) {
bool first = true;
out.printf(" <keywords>");
out.printf(" <job_keyword_ids>");
for (unsigned int i=0; i<ids.size(); i++) {
if (first) {
out.printf("%d", ids[i]);
@ -119,5 +135,20 @@ void JOB_KEYWORDS::write_xml_num(MIOFILE& out) {
out.printf(", %d", ids[i]);
}
}
out.printf("</keywords>\n");
out.printf("</job_keyword_ids>\n");
}
int JOB_KEYWORDS::parse(XML_PARSER& xp) {
while (!xp.get_tag()) {
if (xp.match_tag("/job_keywords")) {
return 0;
}
if (xp.match_tag("keyword")) {
KEYWORD kw;
int retval = kw.parse(xp);
if (retval) return retval;
keywords.push_back(kw);
}
}
return ERR_XML_PARSE;
}

View File

@ -21,8 +21,11 @@
#define BOINC_KEYWORD_H
#include <vector>
#include <map>
#include "parse.h"
// a keyword
//
struct KEYWORD {
int id;
std::string name;
@ -35,6 +38,8 @@ struct KEYWORD {
int parse(XML_PARSER&);
};
// the set of all keywords
//
struct KEYWORDS {
std::map<int, KEYWORD> keywords;
bool present;
@ -45,6 +50,8 @@ struct KEYWORDS {
inline KEYWORD& get(int id) {return keywords[id];}
};
// a user's keyword preferences
//
struct USER_KEYWORDS {
std::vector<int> yes;
std::vector<int> no;
@ -59,7 +66,9 @@ struct USER_KEYWORDS {
}
};
struct JOB_KEYWORDS {
// the keywords IDs associated with a job (workunit)
//
struct JOB_KEYWORD_IDS {
std::vector<int> ids;
void parse_str(char*);
// parse space-separated list
@ -73,4 +82,18 @@ struct JOB_KEYWORDS {
void write_xml_num(MIOFILE&);
};
// same, but the entire keyword objects (for GUI RPC client)
//
struct JOB_KEYWORDS {
std::vector<KEYWORD> keywords;
inline bool empty() {
return keywords.empty();
}
inline void clear() {
keywords.clear();
}
int parse(XML_PARSER&);
};
#endif

View File

@ -19,7 +19,7 @@
//
// A job's keywords are stored in workunit.keywords as a char string.
// We don't want to parse that every time we score the job,
// so we maintain a list of JOB_KEYWORDs paralleling the job array.
// so we maintain a list of JOB_KEYWORD_IDS paralleling the job array.
#include <algorithm>
#include <iterator>
@ -27,13 +27,13 @@
#include "sched_main.h"
#include "keyword.h"
JOB_KEYWORDS *job_keywords_array;
JOB_KEYWORD_IDS *job_keywords_array;
// compute the score increment for the given job and user keywords
// (or -1 if the keywords are incompatible)
//
double keyword_score_aux(
USER_KEYWORDS& uks, JOB_KEYWORDS& jks
USER_KEYWORDS& uks, JOB_KEYWORD_IDS& jks
) {
double score = 0;
@ -61,7 +61,7 @@ double keyword_score(int i) {
// parse job keywords if not already done
//
JOB_KEYWORDS& jk = job_keywords_array[i];
JOB_KEYWORD_IDS& jk = job_keywords_array[i];
if (jk.empty()) {
WU_RESULT& wr = ssp->wu_results[i];
if (empty(wr.workunit.keywords)) {
@ -88,5 +88,5 @@ void keyword_sched_remove_job(int i) {
// called at CGI start to initialize job keyword array
//
void keyword_sched_init() {
job_keywords_array = new JOB_KEYWORDS[ssp->max_wu_results];
job_keywords_array = new JOB_KEYWORD_IDS[ssp->max_wu_results];
}

View File

@ -556,7 +556,7 @@ static int insert_wu_tags(WORKUNIT& wu, APP& app) {
if (!empty(wu.keywords)) {
char buf2[1024];
sprintf(buf2,
" <job_keywords>%s</job_keywords>\n",
" <job_keyword_ids>%s</job_keyword_ids>\n",
wu.keywords
);
strcat(buf, buf2);