Line data Source code
1 : /*
2 : * Copyright (c) 2013 Juniper Networks, Inc. All rights reserved.
3 : */
4 :
5 : #include <sstream>
6 : #include <exception>
7 : #include <cstdlib>
8 : #include <algorithm>
9 : #include <string>
10 : #include <boost/lexical_cast.hpp>
11 : #include <boost/assign/list_of.hpp>
12 : #include <boost/tuple/tuple.hpp>
13 : #include <base/util.h>
14 : #include <boost/ptr_container/ptr_vector.hpp>
15 : #include <base/logging.h>
16 : #include <sandesh/sandesh_trace.h>
17 : #include <sandesh/sandesh_message_builder.h>
18 : #include <sandesh/protocol/TXMLProtocol.h>
19 : #include "ruleparser/ruleglob.h"
20 : #include "db_handler.h"
21 : #include "OpServerProxy.h"
22 : #include <analytics/collector_uve_types.h>
23 : #include <analytics/viz_constants.h>
24 : #include "ruleeng.h"
25 : #include "stat_walker.h"
26 :
27 : using std::string;
28 : using std::vector;
29 : using std::map;
30 : using std::pair;
31 : using std::make_pair;
32 : using boost::tuple;
33 : using boost::tuples::make_tuple;
34 : using boost::ptr_vector;
35 :
36 : using namespace contrail::sandesh::protocol;
37 :
38 : int Ruleeng::RuleBuilderID = 0;
39 : int Ruleeng::RuleWorkerID = 0;
40 :
41 : SandeshTraceBufferPtr UVETraceBuf(SandeshTraceBufferCreate("UveTrace", 25000));
42 :
43 0 : Ruleeng::Ruleeng(DbHandlerPtr db_handler, OpServerProxy *osp) :
44 0 : db_handler_(db_handler), osp_(osp), rulelist_(new t_rulelist()) {
45 0 : }
46 :
47 0 : Ruleeng::~Ruleeng() {
48 0 : delete rulelist_;
49 0 : }
50 :
51 0 : void Ruleeng::Init() {
52 0 : LOG(DEBUG, "Ruleeng::" << __func__ << " Begin");
53 0 : DbHandler::RuleMap rulemap;
54 :
55 0 : if (db_handler_) {
56 0 : db_handler_->GetRuleMap(rulemap);
57 0 : DbHandler::RuleMap::iterator iter;
58 :
59 0 : for (iter = rulemap.begin(); iter != rulemap.end(); iter++) {
60 0 : Buildrules((*iter).first, (*iter).second);
61 : }
62 : }
63 0 : LOG(DEBUG, "Ruleeng::" << __func__ << " Done");
64 0 : }
65 :
66 0 : bool Ruleeng::Buildrules(const std::string& rulesrc, const std::string& rulebuf) {
67 0 : Builder *task = new Builder(this, rulesrc, rulebuf);
68 0 : TaskScheduler *scheduler = TaskScheduler::GetInstance();
69 0 : scheduler->Enqueue(task);
70 :
71 0 : return true;
72 : }
73 :
74 0 : bool Ruleeng::Parserules(char *base, size_t sz) {
75 0 : parse(rulelist_, base, sz);
76 :
77 0 : return true;
78 : }
79 :
80 0 : bool Ruleeng::Parserules(const char *bytes, int len) {
81 0 : parse(rulelist_, bytes, len);
82 :
83 0 : return true;
84 : }
85 :
86 0 : bool Ruleeng::rule_present(const VizMsg *vmsgp) {
87 0 : t_rulemsgtype msgtype(vmsgp->msg->GetMessageType());
88 :
89 0 : const SandeshHeader &header(vmsgp->msg->GetHeader());
90 0 : if (header.__isset.Context) {
91 0 : msgtype.has_context_ = true;
92 0 : msgtype.context_ = header.Context;
93 : }
94 :
95 0 : return rulelist_->rule_present(msgtype);
96 0 : }
97 :
98 :
99 0 : void Ruleeng::remove_identifier(const pugi::xml_node &parent) {
100 0 : for (pugi::xml_node node = parent.first_child(); node;
101 0 : node = node.next_sibling()) {
102 0 : node.remove_attribute("identifier");
103 0 : remove_identifier(node);
104 : }
105 0 : }
106 :
107 0 : static bool ParseNodeImpl(DbHandler::Var& sample,
108 : const string& attype, const pugi::xml_node& node) {
109 0 : if (attype == "string") {
110 0 : std::string val(node.child_value());
111 0 : TXMLProtocol::unescapeXMLControlChars(val);
112 0 : sample = val;
113 0 : } else if (attype == "double") {
114 0 : sample = (double) strtod(node.child_value(), NULL);
115 0 : } else if ((attype == "u16") || (attype == "u32") || (attype == "u64")) {
116 0 : sample = (uint64_t) strtoul(node.child_value(), NULL, 10);
117 0 : } else if (attype == "set" || attype == "list") {
118 0 : pugi::xml_node set;
119 0 : if (attype == "set") {
120 0 : set = node.child("set");
121 : } else {
122 0 : set = node.child("list");
123 : }
124 0 : if (strcmp(set.attribute("type").value(),"string") != 0) {
125 0 : return false;
126 : }
127 0 : std::vector<std::string> set_value;
128 0 : for (pugi::xml_node set_elem = set.first_child(); set_elem;
129 0 : set_elem = set_elem.next_sibling()) {
130 0 : std::string elem_value = set_elem.child_value();
131 0 : TXMLProtocol::unescapeXMLControlChars(elem_value);
132 0 : set_value.push_back(elem_value);
133 0 : }
134 0 : sample = set_value;
135 0 : } else if (attype == "map") {
136 0 : pugi::xml_node map_node = node.child("map");
137 0 : if (strcmp(map_node.attribute("key").value(), "string") != 0) {
138 0 : return false;
139 : }
140 0 : if (strcmp(map_node.attribute("value").value(), "string") != 0) {
141 0 : return false;
142 : }
143 0 : std::map<std::string, std::string> map_value;
144 0 : for (pugi::xml_node map_key = map_node.first_child(); map_key;
145 0 : map_key = map_key.next_sibling().next_sibling()) {
146 0 : std::ostringstream key_val;
147 : std::pair<std::string, string> map_elem =
148 0 : std::make_pair(std::string(map_key.child_value()),
149 0 : std::string(map_key.next_sibling().child_value()));
150 0 : map_value.insert(map_elem);
151 0 : }
152 0 : sample = map_value;
153 0 : } else {
154 0 : return false;
155 : }
156 0 : return true;
157 : }
158 :
159 0 : static DbHandler::Var ParseNode(const pugi::xml_node& node, bool silent = false) {
160 0 : DbHandler::Var sample;
161 :
162 0 : if (node.empty()) {
163 0 : LOG(ERROR, __func__ << "Parsing Empty node");
164 0 : return sample;
165 : }
166 0 : string attype = node.attribute("type").value();
167 0 : if (!ParseNodeImpl(sample, attype, node)) {
168 0 : if (!silent)
169 0 : LOG(ERROR, __func__ << " Bad Stat Type " << attype <<
170 : " for attr " << node.name());
171 : }
172 0 : return sample;
173 0 : }
174 :
175 : // Dom Elements are pair
176 : // First member is a variant; either xml_node (struct) or Var (basic type)
177 : // Second member is a key, for the map case
178 : typedef boost::variant<pugi::xml_node, DbHandler::Var> ElemVar;
179 : typedef std::pair<ElemVar, string> ElemT;
180 :
181 : class DomChildVisitor : public boost::static_visitor<> {
182 : public:
183 0 : void operator()(const pugi::xml_node& node) {
184 0 : parent = node;
185 0 : }
186 0 : void operator()(const DbHandler::Var& dv) {
187 0 : }
188 :
189 : pugi::xml_node parent;
190 :
191 0 : void GetResult(const string& sub, pugi::xml_node& node) {
192 0 : if (!parent) {
193 0 : node = parent;
194 : } else {
195 0 : node = parent.child(sub.c_str());
196 : }
197 0 : }
198 : };
199 :
200 0 : static bool ParseDomTags(const std::string& tstr,
201 : std::vector<std::string> *toptags,
202 : const ptr_vector<tuple<string,ElemT> >* elem_chain,
203 : StatWalker::TagMap *tagmap) {
204 : size_t pos;
205 0 : size_t npos = 0;
206 :
207 : // If the tags string is empty, there's nothing to parse
208 0 : if (tstr.empty()) return true;
209 :
210 : do {
211 0 : if (npos)
212 0 : pos = npos+1;
213 : else
214 0 : pos = 0;
215 :
216 0 : npos = tstr.find(',' , pos);
217 0 : string term;
218 0 : if (npos == string::npos)
219 0 : term = tstr.substr(pos, string::npos);
220 : else
221 0 : term = tstr.substr(pos, npos - pos);
222 :
223 : // Separating this term into a prefix and suffix
224 : // Prefix may not be present
225 0 : size_t spos = term.find(':');
226 0 : string pterm, sterm;
227 :
228 0 : if (spos == string::npos) {
229 : // Single Tag case
230 0 : sterm = term;
231 : } else {
232 : // Double Tag case
233 0 : pterm = term.substr(0,spos);
234 0 : sterm = term.substr(spos+1,string::npos);
235 : }
236 :
237 0 : if (sterm.empty()) return false;
238 :
239 0 : if (toptags) {
240 0 : assert(elem_chain==NULL);
241 0 : assert(tagmap==NULL);
242 0 : if (sterm[0] != '.') {
243 : // These are top-level tags
244 :
245 : // We do not allow prefixes with top-level tags
246 0 : if (!pterm.empty()) return false;
247 :
248 0 : toptags->push_back(sterm);
249 : }
250 :
251 0 : continue;
252 : } else {
253 0 : assert(elem_chain);
254 0 : assert(elem_chain->size()>1);
255 0 : assert(tagmap);
256 0 : if (sterm[0] != '.') {
257 : // We are not processing at the top level
258 : // Ignore top-level tags
259 0 : continue;
260 : }
261 : }
262 :
263 0 : string sname, pname;
264 :
265 : // strip out the leading "."
266 0 : sname = sterm.substr(1, string::npos);
267 0 : size_t sz = elem_chain->size();
268 0 : StatWalker::TagVal tv;
269 :
270 0 : if (sname.compare(g_viz_constants.STAT_KEY_FIELD)==0) {
271 0 : tv.val = elem_chain->at(sz-1).get<1>().second;
272 : } else {
273 : // TODO: Add support for map value as tag
274 0 : const ElemVar& ev = elem_chain->at(sz-1).get<1>().first;
275 0 : pugi::xml_node anode_s;
276 :
277 0 : DomChildVisitor dcv;
278 0 : boost::apply_visitor(dcv, ev);
279 0 : dcv.GetResult(sname, anode_s);
280 :
281 0 : if (!anode_s) return false;
282 0 : tv.val = ParseNode(anode_s);
283 : }
284 :
285 0 : if (!pterm.empty()) {
286 : // The prefix is a child of the deepest node,
287 : // or a child at the current level (2nd deepest node)
288 0 : size_t idx = ((pterm[0] == '.') ? sz-1 : sz-2);
289 0 : for (size_t ix=1; ix<=idx; ix++) {
290 0 : if (!pname.empty()) pname.append(".");
291 0 : pname.append(elem_chain->at(ix).get<0>());
292 : }
293 0 : if (pterm[0] != '.') {
294 0 : if (!pname.empty()) pname.append(".");
295 : }
296 0 : pname.append(pterm);
297 0 : size_t found = pterm.rfind('.');
298 0 : string pattr = pterm.substr(found+1, string::npos);
299 0 : DbHandler::Var pv;
300 0 : if (pattr.compare(g_viz_constants.STAT_KEY_FIELD)==0) {
301 0 : pv = elem_chain->at(idx).get<1>().second;
302 : } else {
303 : // TODO: Add support for map value as tag
304 0 : const ElemVar& ev = elem_chain->at(idx).get<1>().first;
305 0 : pugi::xml_node anode_p;
306 :
307 0 : DomChildVisitor dcv;
308 0 : boost::apply_visitor(dcv, ev);
309 0 : dcv.GetResult(pattr, anode_p);
310 :
311 0 : if (!anode_p) return false;
312 0 : pv = ParseNode(anode_p);
313 : }
314 0 : tv.prefix = make_pair(pname, pv);
315 0 : }
316 :
317 0 : tagmap->insert(make_pair(sname, tv));
318 :
319 0 : } while (npos != string::npos);
320 :
321 0 : return true;
322 : }
323 :
324 0 : static bool DomValidElems(pugi::xml_node node,
325 : ptr_vector<ElemT> &elem_list, std::string& ltype) {
326 :
327 0 : if (strcmp(node.attribute("type").value(), "list") == 0) {
328 0 : pugi::xml_node subs = node.child("list");
329 0 : ltype = subs.attribute("type").value();
330 0 : for (pugi::xml_node elem = subs.first_child(); elem;
331 0 : elem = elem.next_sibling()) {
332 0 : if (ltype == "struct") {
333 0 : elem_list.push_back(new pair<ElemVar,string>(elem,string()));
334 : } else {
335 0 : DbHandler::Var sample;
336 0 : if (ParseNodeImpl(sample, ltype, elem)) {
337 0 : elem_list.push_back(new pair<ElemVar,string>(sample, string()));
338 : } else {
339 0 : return false;
340 : }
341 0 : }
342 : }
343 0 : } else if (strcmp(node.attribute("type").value(), "struct") == 0) {
344 0 : elem_list.push_back(new pair<ElemVar, string>(
345 0 : node.first_child(),string()));
346 0 : } else if (strcmp(node.attribute("type").value(), "map") == 0) {
347 0 : pugi::xml_node subs = node.child("map");
348 0 : ltype = subs.attribute("value").value();
349 0 : string key("__UNKNOWN__");
350 0 : uint32_t idx = 0;
351 0 : for (pugi::xml_node elem = subs.first_child(); elem;
352 0 : elem = elem.next_sibling()) {
353 0 : if (idx % 2) {
354 0 : if (ltype == "struct") {
355 0 : elem_list.push_back(new pair<ElemVar, string>(elem,key));
356 : } else {
357 0 : DbHandler::Var sample;
358 0 : if (ParseNodeImpl(sample, ltype, elem)) {
359 0 : elem_list.push_back(new pair<ElemVar, string>(
360 0 : sample, key));
361 : } else {
362 0 : return false;
363 : }
364 0 : }
365 0 : key = string("__UNKNOWN__");
366 : } else {
367 0 : std::string val(elem.child_value());
368 0 : TXMLProtocol::unescapeXMLControlChars(val);
369 0 : key = val;
370 0 : }
371 0 : idx++;
372 : }
373 0 : } else {
374 0 : return false;
375 : }
376 0 : return true;
377 : }
378 :
379 : class DomStatVisitor : public boost::static_visitor<> {
380 : public:
381 0 : void operator()(const pugi::xml_node& node) {
382 0 : for (pugi::xml_node sattr = node.first_child(); sattr;
383 0 : sattr = sattr.next_sibling()) {
384 0 : DbHandler::Var sample = ParseNode(sattr, true);
385 0 : if (sample.type == DbHandler::INVALID) {
386 : // for structs or lists, look for the tags annotation
387 : // and process for stats.
388 : // Ignore any struct or list that does not have the
389 : // tags annotation
390 0 : if (sattr.attribute("tags").empty()) {
391 0 : LOG(DEBUG, __func__ << " Message: " <<
392 : " Name: " << node.name() << " Child: " <<
393 : sattr.name() << " No tags annotation ");
394 0 : continue;
395 0 : }
396 0 : string ltype;
397 0 : ptr_vector<ElemT> elem_list;
398 0 : if (!DomValidElems(sattr, elem_list, ltype)) {
399 0 : LOG(ERROR, __func__ << " Message: " <<
400 : " Name: " << node.name() << " Child: " << sattr.name() <<
401 : (ltype.empty() ? " Bad Stat type in walk" :
402 : " Bad Stat list type in walk") <<
403 : (ltype.empty() ? node.attribute("type").value() : ltype));
404 0 : continue;
405 0 : }
406 0 : elem_map[sattr.name()] = make_pair(sattr.attribute("tags").value(),
407 0 : elem_list);
408 0 : } else {
409 : // For simple attributes (strings, integers and doubles)
410 : // record the attributes in the stat tables
411 0 : attribs.insert(make_pair(sattr.name(), sample));
412 : }
413 0 : }
414 0 : }
415 0 : void operator()(const DbHandler::Var& dv) {
416 0 : attribs.insert(make_pair(
417 : g_viz_constants.STAT_VALUE_FIELD, dv));
418 0 : }
419 :
420 : DbHandler::AttribMap attribs;
421 : // For this map:
422 : // the key is the attribute name
423 : // the value is a pair of the tags string, and ElemT
424 : map<string, pair<string, ptr_vector<ElemT> > > elem_map;
425 :
426 0 : void GetResult(DbHandler::AttribMap& lattribs,
427 : map<string, pair<string, ptr_vector<ElemT> > >& lelem_map) {
428 0 : lattribs = attribs;
429 0 : lelem_map = elem_map;
430 0 : }
431 : };
432 :
433 : class DomSelfVisitor : public boost::static_visitor<> {
434 : public:
435 0 : void operator()(const pugi::xml_node& node) {
436 0 : se = node;
437 0 : }
438 0 : void operator()(const DbHandler::Var& dv) {
439 0 : }
440 :
441 : pugi::xml_node se;
442 :
443 0 : void GetResult(pugi::xml_node& node) {
444 0 : node = se;
445 0 : }
446 : };
447 : /* This function recursively walks the XML DOM
448 : for objectlog and UVE messages and processes
449 : them for stats.
450 : It is invoked after finding a top-level
451 : stats attribute
452 : */
453 0 : static bool DomStatWalker(StatWalker& sw,
454 : const std::string& tstr,
455 : ptr_vector<tuple<string,ElemT> > elem_chain) {
456 :
457 0 : pugi::xml_node object;
458 :
459 0 : DomSelfVisitor dsv;
460 0 : boost::apply_visitor(dsv, elem_chain.at(0).get<1>().first);
461 0 : dsv.GetResult(object);
462 :
463 0 : size_t sz = elem_chain.size();
464 :
465 0 : const ElemVar& ev = elem_chain.at(sz-1).get<1>().first;
466 0 : const string& node_key = elem_chain.at(sz-1).get<1>().second;
467 0 : string node_name = elem_chain.at(sz-1).get<0>();
468 0 : StatWalker::TagMap tagmap;
469 : // Parse the tags annotation to find all tags that will
470 : // be used to index stats samples
471 0 : if (ParseDomTags(tstr, NULL, &elem_chain, &tagmap)) {
472 0 : DbHandler::AttribMap attribs;
473 : // For this map:
474 : // the key is the attribute name
475 : // the value is a pair of the tags string, and ElemT
476 0 : map<string, pair<string, ptr_vector<ElemT> > > elem_map;
477 : // Load all tags and non-tags
478 :
479 0 : DomStatVisitor dsv;
480 0 : boost::apply_visitor(dsv, ev);
481 0 : dsv.GetResult(attribs, elem_map);
482 :
483 0 : if (!node_key.empty()) {
484 0 : attribs.insert(make_pair(g_viz_constants.STAT_KEY_FIELD,
485 : node_key));
486 : }
487 0 : sw.Push(node_name, tagmap, attribs);
488 :
489 0 : for (map<string, pair<string, ptr_vector<ElemT> > >::iterator ei =
490 0 : elem_map.begin(); ei != elem_map.end(); ei++) {
491 0 : ptr_vector<ElemT> & elem_list = ei->second.second;
492 0 : string & tstr_sub(ei->second.first);
493 0 : for (size_t idx=0; idx<elem_list.size(); idx++) {
494 0 : ptr_vector<tuple<string,ElemT> > elem_parent = elem_chain;
495 0 : elem_parent.push_back(new tuple<string,ElemT>(ei->first, elem_list[idx]));
496 : // recursive invokation to process stats of child
497 : // structs and lists that have the tags annotation
498 0 : if (!DomStatWalker(sw, tstr_sub, elem_parent)) {
499 0 : LOG(ERROR, __func__ <<
500 : " Name: " << object.name() << " Node: " << node_name <<
501 : " Bad element " << ei->first);
502 : }
503 0 : }
504 : }
505 0 : sw.Pop();
506 0 : } else {
507 0 : LOG(ERROR, __func__ <<
508 : " Name: " << object.name() << " Node: " << node_name <<
509 : " Bad tags " << tstr);
510 0 : return false;
511 : }
512 0 : return true;
513 0 : }
514 :
515 : /*
516 : This function is used to analyse an objectlog or UVE for stats attributes
517 : and walk the XML DOM to process the stats
518 : */
519 :
520 0 : static bool DomTopStatWalker(const pugi::xml_node& object,
521 : DbHandler *db,
522 : uint64_t timestamp,
523 : const pugi::xml_node& node,
524 : StatWalker::TagMap tmap,
525 : const std::string& source,
526 : GenDb::GenDbIf::DbAddColumnCb db_cb) {
527 :
528 0 : ptr_vector<ElemT> elem_list;
529 0 : string ltype;
530 : // We accept both structs and lists of structs for stats
531 : // For structs, we need to process a singe element.
532 : // Otherwise we process each element of the list
533 0 : if (!DomValidElems(node, elem_list, ltype)) {
534 0 : LOG(ERROR, __func__ << " Source: " << source <<
535 : " Name: " << object.name() << " Node: " << node.name() <<
536 : (ltype.empty() ? " Bad Stat type " : " Bad Stat list type") <<
537 : (ltype.empty() ? node.attribute("type").value() : ltype));
538 0 : return false;
539 : }
540 :
541 0 : std::vector<std::string> toptags;
542 :
543 0 : string tstr(node.attribute("tags").value());
544 :
545 : // Get the top-level tags for this stat attribute
546 0 : if (ParseDomTags(tstr, &toptags, NULL, NULL)) {
547 :
548 0 : StatWalker::TagMap m1 = tmap;
549 :
550 0 : for (size_t idx=0; idx < toptags.size(); idx++) {
551 : pugi::xml_node anode_p =
552 0 : object.child(toptags.at(idx).c_str());
553 0 : StatWalker::TagVal th;
554 0 : th.val = ParseNode(anode_p);
555 0 : m1.insert(make_pair(toptags.at(idx), th));
556 0 : }
557 :
558 0 : StatWalker sw(boost::bind(&DbHandler::StatTableInsert, db,
559 0 : _1, _2, _3, _4, _5, db_cb), timestamp, object.name(), m1);
560 :
561 0 : ptr_vector<tuple<string, ElemT> > parent_chain;
562 0 : parent_chain.push_back(new tuple<string, ElemT>(object.name(),
563 0 : make_pair<ElemVar, string>(object,string())));
564 :
565 : // Process all elements the next level down for stats
566 0 : for (size_t idx=0; idx<elem_list.size(); idx++) {
567 0 : ptr_vector<tuple<string, ElemT> > elem_chain = parent_chain;
568 0 : elem_chain.push_back(new tuple<string, ElemT>(node.name(), elem_list[idx]));
569 0 : if (!DomStatWalker(sw, tstr, elem_chain)) {
570 0 : LOG(ERROR, __func__ << " Source: " << source <<
571 : " Name: " << object.name() << " Node: " << node.name());
572 0 : continue;
573 0 : }
574 0 : }
575 0 : } else {
576 0 : LOG(ERROR, __func__ << " Source: " << source <<
577 : " Name: " << object.name() << " Node: " << node.name() <<
578 : " Bad tags " << tstr);
579 0 : return false;
580 : }
581 0 : return true;
582 0 : }
583 :
584 : /*
585 : * Walk the XML DOM to find keys to record this message against.
586 : * Write to the objectlog accordingly
587 : */
588 0 : static size_t DomObjectWalk(const pugi::xml_node& parent, const VizMsg *rmsg,
589 : DbHandler *db, uint64_t timestamp,
590 : DbHandler::ObjectNamesVec *object_names,
591 : GenDb::GenDbIf::DbAddColumnCb db_cb) {
592 0 : std::map<std::string, std::string> keymap;
593 0 : std::map<std::string, std::string>::iterator it;
594 : const char *table;
595 : const char *nodetype;
596 0 : std::string rowkey;
597 :
598 0 : for (pugi::xml_node node = parent.first_child(); node;
599 0 : node = node.next_sibling()) {
600 0 : table = node.attribute("key").value();
601 0 : nodetype = node.attribute("type").value();
602 0 : if (strcmp(table, "") && strcmp(nodetype, "")) {
603 : // check if Sandesh node has a map attribute;
604 : // key type of map should not be extracted,
605 : // only key value of attribute should be extracted
606 0 : rowkey = std::string(node.child_value());
607 0 : TXMLProtocol::unescapeXMLControlChars(rowkey);
608 0 : it = keymap.find(table);
609 0 : if (it != keymap.end()) {
610 0 : std::string tempstr(it->second);
611 0 : tempstr.append(":");
612 0 : tempstr.append(rowkey);
613 0 : keymap.erase(it);
614 0 : keymap.insert(std::pair<std::string, std::string>(table, tempstr));
615 0 : } else {
616 0 : keymap.insert(std::pair<std::string, std::string>(table, rowkey));
617 : }
618 : }
619 : }
620 0 : for (it = keymap.begin(); it != keymap.end(); it++) {
621 0 : db->ObjectTableInsert(it->first, it->second,
622 0 : timestamp, rmsg->unm, rmsg, db_cb);
623 0 : std::string tempstr(it->first);
624 0 : tempstr.append(":");
625 0 : tempstr.append(it->second);
626 0 : object_names->push_back(tempstr);
627 0 : }
628 0 : for (pugi::xml_node node = parent.first_child(); node;
629 0 : node = node.next_sibling()) {
630 0 : DomObjectWalk(node, rmsg, db, timestamp, object_names, db_cb);
631 : }
632 0 : return keymap.size();
633 0 : }
634 :
635 : /*
636 : * Check if any handling is needed for the message wrt to ObjectLog
637 : * Looks for the 'key' annotations for the table name and inserts
638 : * the object trace with the rowkey corresponding to the value of the
639 : * field
640 : */
641 0 : void Ruleeng::handle_object_log(const pugi::xml_node& parent, const VizMsg *rmsg,
642 : DbHandler *db, const SandeshHeader &header,
643 : DbHandler::ObjectNamesVec *object_names,
644 : GenDb::GenDbIf::DbAddColumnCb db_cb) {
645 0 : if (!(header.get_Hints() & g_sandesh_constants.SANDESH_KEY_HINT)) {
646 0 : return;
647 : }
648 0 : uint64_t timestamp(header.get_Timestamp());
649 0 : std::string source(header.get_Source());
650 0 : std::string type(rmsg->msg->GetMessageType());
651 0 : std::string module(header.get_Module());
652 0 : std::string instance_id(header.get_InstanceId());
653 0 : std::string node_type(header.get_NodeType());
654 0 : SandeshType::type sandesh_type(header.get_Type());
655 :
656 0 : DomObjectWalk(parent, rmsg, db, timestamp, object_names, db_cb);
657 :
658 : // UVE related stats are not processed here. See handle_uve_publish.
659 0 : if (sandesh_type == SandeshType::UVE) {
660 0 : return;
661 : }
662 :
663 : // All stats records the "name" field as a tag.
664 : // If no such field is present, use the source of
665 : // this message
666 0 : DbHandler::Var nkey = source;
667 :
668 0 : StatWalker::TagMap m1;
669 0 : StatWalker::TagVal h1,h2;
670 0 : h2.val = source;
671 0 : m1.insert(make_pair(g_viz_constants.STAT_SOURCE_FIELD, h2));
672 :
673 0 : pugi::xml_node object(parent);
674 0 : for (pugi::xml_node node = object.first_child(); node;
675 0 : node = node.next_sibling()) {
676 0 : if (!strcmp(node.name(), g_viz_constants.STAT_OBJECTID_FIELD.c_str())) {
677 0 : nkey = ParseNode(node);
678 : }
679 : }
680 0 : h1.val = nkey;
681 0 : m1.insert(make_pair(g_viz_constants.STAT_OBJECTID_FIELD, h1));
682 :
683 0 : for (pugi::xml_node node = object.first_child(); node;
684 0 : node = node.next_sibling()) {
685 :
686 0 : if (!node.attribute("tags").empty()) {
687 0 : DomTopStatWalker(object, db, timestamp, node,
688 : m1, source, db_cb);
689 : }
690 : }
691 0 : }
692 :
693 0 : bool Ruleeng::handle_uve_statistics(const pugi::xml_node& parent,
694 : const VizMsg *rmsg, DbHandler *db, const SandeshHeader& header,
695 : GenDb::GenDbIf::DbAddColumnCb db_cb) {
696 0 : const SandeshType::type& sandesh_type(header.get_Type());
697 0 : if ((sandesh_type != SandeshType::UVE) &&
698 0 : (sandesh_type != SandeshType::ALARM)) {
699 0 : return true;
700 : }
701 :
702 0 : std::string type(rmsg->msg->GetMessageType());
703 0 : std::string source(header.get_Source());
704 0 : std::string module(header.get_Module());
705 0 : std::string instance_id(header.get_InstanceId());
706 0 : std::string node_type(header.get_NodeType());
707 0 : int64_t ts(header.get_Timestamp());
708 :
709 0 : pugi::xml_node object(parent);
710 0 : if (!object) {
711 0 : LOG(ERROR, __func__ << " Message: " << type << " : " << source <<
712 : ":" << node_type << ":" << module << ":" << instance_id <<
713 : " object NOT PRESENT: " << rmsg->msg->ExtractMessage());
714 0 : return false;
715 : }
716 :
717 0 : object = object.child("data");
718 0 : object = object.first_child();
719 :
720 0 : for (pugi::xml_node node = object.first_child(); node;
721 0 : node = node.next_sibling()) {
722 0 : if (strcmp(node.attribute("key").value(), "")) {
723 0 : continue;
724 : }
725 : // Ignore deleted
726 0 : if (!strcmp(node.name(), "deleted")) {
727 0 : if (!strcmp(node.child_value(), "true")) {
728 0 : return true;
729 : }
730 : }
731 0 : if (!node.attribute("tags").empty()) {
732 : // For messages send during UVE Sync, stats must be ignored
733 0 : if (header.get_Hints() & g_sandesh_constants.SANDESH_SYNC_HINT) {
734 0 : continue;
735 : }
736 : pugi::xml_node anode_p =
737 0 : object.child(g_viz_constants.STAT_OBJECTID_FIELD.c_str());
738 0 : StatWalker::TagMap m1;
739 0 : StatWalker::TagVal h1,h2;
740 0 : h1.val = ParseNode(anode_p);
741 0 : m1.insert(make_pair(g_viz_constants.STAT_OBJECTID_FIELD, h1));
742 0 : h2.val = source;
743 0 : m1.insert(make_pair(g_viz_constants.STAT_SOURCE_FIELD, h2));
744 : // Process this UVE's Stat attributes.
745 : // We will always index by Source and UVE key (name)
746 : // Other indexes depend on the "tags" attribute
747 0 : if (!DomTopStatWalker(object, db, ts, node,
748 : m1, source, db_cb)) {
749 0 : continue;
750 : }
751 0 : }
752 : }
753 0 : return true;
754 0 : }
755 :
756 0 : bool Ruleeng::handle_uve_publish(const pugi::xml_node& parent,
757 : const VizMsg *rmsg, DbHandler *db, const SandeshHeader& header,
758 : GenDb::GenDbIf::DbAddColumnCb db_cb) {
759 0 : const SandeshType::type& sandesh_type(header.get_Type());
760 0 : if ((sandesh_type != SandeshType::UVE) &&
761 0 : (sandesh_type != SandeshType::ALARM)) {
762 0 : return true;
763 : }
764 :
765 0 : bool is_alarm = (sandesh_type == SandeshType::ALARM) ? true : false;
766 :
767 0 : std::string type(rmsg->msg->GetMessageType());
768 0 : std::string source(header.get_Source());
769 0 : std::string module(header.get_Module());
770 0 : std::string instance_id(header.get_InstanceId());
771 0 : std::string node_type(header.get_NodeType());
772 0 : int32_t seq(header.get_SequenceNum());
773 0 : int64_t ts(header.get_Timestamp());
774 :
775 0 : pugi::xml_node object(parent);
776 0 : if (!object) {
777 0 : LOG(ERROR, __func__ << " Message: " << type << " : " << source <<
778 : ":" << node_type << ":" << module << ":" << instance_id <<
779 : " object NOT PRESENT: " << rmsg->msg->ExtractMessage());
780 0 : return false;
781 : }
782 :
783 0 : object = object.child("data");
784 0 : object = object.first_child();
785 :
786 0 : std::string barekey;
787 : const char *tempstr;
788 0 : std::string rowkey;
789 0 : std::string table;
790 0 : std::string object_name(object.name());
791 :
792 0 : bool deleted = false;
793 0 : for (pugi::xml_node node = object.first_child(); node;
794 0 : node = node.next_sibling()) {
795 :
796 0 : tempstr = node.attribute("key").value();
797 0 : if (strcmp(tempstr, "")) {
798 0 : rowkey = std::string(node.child_value());
799 0 : TXMLProtocol::unescapeXMLControlChars(rowkey);
800 0 : if (!barekey.empty()) {
801 0 : barekey.append(":");
802 0 : barekey.append(rowkey);
803 : } else {
804 0 : table = std::string(tempstr);
805 0 : barekey.append(rowkey);
806 : }
807 : }
808 0 : if (!strcmp(node.name(), "deleted")) {
809 0 : if (!strcmp(node.child_value(), "true")) {
810 0 : deleted = true;
811 : }
812 : }
813 0 : if (!strcmp(node.name(), "proxy")) {
814 0 : object_name = object_name +
815 0 : string("-") + node.child_value();
816 : }
817 : }
818 :
819 0 : std::string key = table + ":" + barekey;
820 :
821 0 : if (table.empty()) {
822 0 : LOG(ERROR, __func__ << " Message: " << type << " : " << source <<
823 : ":" << node_type << ":" << module << ":" << instance_id <<
824 : " key NOT PRESENT");
825 0 : return false;
826 : }
827 :
828 0 : map<string,pair<string,pugi::xml_node> > vmap;
829 0 : if (deleted) {
830 0 : if (!osp_->UVEDelete(object_name, source, node_type, module,
831 : instance_id, key, seq, is_alarm)) {
832 0 : LOG(ERROR, __func__ << " Cannot Delete " << key);
833 0 : PUBLISH_UVE_DELETE_TRACE(UVETraceBuf, source, module, object_name, key,
834 : seq, false, node_type, instance_id);
835 : } else {
836 0 : PUBLISH_UVE_DELETE_TRACE(UVETraceBuf, source, module, object_name, key,
837 : seq, true, node_type, instance_id);
838 : }
839 0 : LOG(DEBUG, __func__ << " Deleted " << key);
840 0 : osp_->UVENotif(object_name,
841 : source, node_type, module, instance_id, table, barekey, vmap ,deleted);
842 0 : return true;
843 : }
844 :
845 0 : for (pugi::xml_node node = object.first_child(); node;
846 0 : node = node.next_sibling()) {
847 0 : std::ostringstream ostr;
848 0 : std::ostringstream tstr;
849 0 : node.print(ostr, "", pugi::format_raw | pugi::format_no_escapes);
850 0 : std::string agg;
851 0 : std::string atyp;
852 0 : tempstr = node.attribute("key").value();
853 0 : if (strcmp(tempstr, "")) {
854 0 : continue;
855 : }
856 : // "node" has the underlying XML node.
857 : // "ostr" is the encoded attribute for the UVE
858 0 : vmap.insert(make_pair(node.name(), make_pair(ostr.str(), node)));
859 0 : tempstr = node.attribute("aggtype").value();
860 0 : if (strcmp(tempstr, "")) {
861 0 : agg = std::string(tempstr);
862 : } else {
863 0 : agg = std::string("None");
864 : }
865 :
866 0 : if (!osp_->UVEUpdate(object_name, node.name(),
867 : source, node_type, module, instance_id,
868 0 : table, barekey, ostr.str(), seq,
869 : agg, ts,
870 : is_alarm)) {
871 0 : LOG(ERROR, __func__ << " Message: " << type << " : " << source <<
872 : ":" << node_type << ":" << module << ":" << instance_id <<
873 : " Name: " << object.name() << " UVEUpdate Failed");
874 0 : PUBLISH_UVE_UPDATE_TRACE(UVETraceBuf, source, module, object_name, key,
875 : node.name(), false, node_type, instance_id);
876 : } else {
877 0 : PUBLISH_UVE_UPDATE_TRACE(UVETraceBuf, source, module, object_name, key,
878 : node.name(), true, node_type, instance_id);
879 : }
880 0 : }
881 :
882 : // Publish on the Kafka bus that this UVE has changed
883 0 : osp_->UVENotif(object_name,
884 : source, node_type, module, instance_id, table, barekey, vmap, deleted);
885 0 : return true;
886 0 : }
887 :
888 0 : bool Ruleeng::handle_session_object(const pugi::xml_node &parent,
889 : DbHandler *db, const SandeshHeader &header,
890 : GenDb::GenDbIf::DbAddColumnCb db_cb) {
891 0 : if (header.get_Type() != SandeshType::SESSION) {
892 0 : return true;
893 : }
894 0 : if(!(db->SessionTableInsert(parent, header, db_cb))) {
895 0 : return false;
896 : }
897 0 : return true;
898 : }
899 :
900 0 : bool Ruleeng::rule_execute(const VizMsg *vmsgp, bool uveproc, DbHandler *db,
901 : GenDb::GenDbIf::DbAddColumnCb db_cb) {
902 0 : DbHandler::ObjectNamesVec object_names;
903 0 : const SandeshXMLMessage *sxmsg =
904 : static_cast<const SandeshXMLMessage *>(vmsgp->msg);
905 0 : const SandeshHeader &header(sxmsg->GetHeader());
906 0 : const pugi::xml_node &parent(sxmsg->GetMessageNode());
907 0 : remove_identifier(parent);
908 : // First publish to redis and kafka
909 0 : if (uveproc) handle_uve_publish(parent, vmsgp, db, header, db_cb);
910 : // Check if the message needs to be dropped
911 0 : if (db && db->DropMessage(header, vmsgp)) {
912 0 : return true;
913 : }
914 :
915 0 : if (db) {
916 : // 1. make entry in OBJECT_VALUE_TABLE if needed
917 : // 2. get object-type:name{1-6}
918 0 : handle_object_log(parent, vmsgp, db, header, &object_names, db_cb);
919 :
920 : // Insert into the message table
921 0 : db->MessageTableInsert(vmsgp, object_names, db_cb);
922 :
923 0 : if (uveproc) handle_uve_statistics(parent, vmsgp, db, header, db_cb);
924 :
925 0 : handle_session_object(parent, db, header, db_cb);
926 : }
927 :
928 0 : RuleMsg rmsg(vmsgp);
929 0 : rulelist_->rule_execute(rmsg);
930 0 : return true;
931 0 : }
|