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