Line data Source code
1 : /*
2 : * Copyright (c) 2013 Juniper Networks, Inc. All rights reserved.
3 : */
4 :
5 : #include "ifmap/ifmap_server.h"
6 :
7 : #include <boost/asio/io_service.hpp>
8 : #include <boost/algorithm/string.hpp>
9 :
10 : #include "base/logging.h"
11 : #include "base/regex.h"
12 : #include "base/task_annotations.h"
13 : #include "base/time_util.h"
14 : #include "config-client-mgr/config_amqp_client.h"
15 : #include "config-client-mgr/config_db_client.h"
16 : #include "config_client_show_types.h"
17 : #include "db/db.h"
18 : #include "db/db_graph.h"
19 : #include "db/db_graph_edge.h"
20 : #include "ifmap/ifmap_client.h"
21 : #include "ifmap/ifmap_exporter.h"
22 : #include "ifmap/ifmap_graph_walker.h"
23 : #include "ifmap/ifmap_link_table.h"
24 : #include "ifmap/ifmap_log.h"
25 : #include "ifmap/ifmap_node.h"
26 : #include "ifmap/ifmap_server_table.h"
27 : #include "ifmap/ifmap_server_show_types.h"
28 : #include "ifmap/ifmap_log_types.h"
29 : #include "ifmap/ifmap_table.h"
30 : #include "ifmap/ifmap_update_queue.h"
31 : #include "ifmap/ifmap_update_sender.h"
32 : #include "ifmap/ifmap_xmpp.h"
33 : #include "ifmap/ifmap_uuid_mapper.h"
34 : #include "schema/vnc_cfg_types.h"
35 :
36 : #include "control-node/sandesh/control_node_types.h"
37 :
38 : using contrail::regex;
39 : using contrail::regex_match;
40 : using contrail::regex_search;
41 : using std::make_pair;
42 :
43 : class IFMapServer::IFMapStaleEntriesCleaner : public Task {
44 : public:
45 0 : IFMapStaleEntriesCleaner(DB *db, DBGraph *graph, IFMapServer *server):
46 : Task(TaskScheduler::GetInstance()->GetTaskId("db::IFMapTable"), 0),
47 0 : db_(db), graph_(graph), ifmap_server_(server) {
48 0 : }
49 :
50 0 : bool Run() {
51 : // objects_deleted indicates the count of objects-deleted-but-not-node
52 0 : uint32_t nodes_deleted = 0, nodes_changed = 0, links_deleted = 0,
53 0 : objects_deleted = 0;
54 : uint64_t curr_seq_num =
55 0 : ifmap_server_->get_config_generation_number();
56 :
57 0 : DBGraph::edge_iterator e_next(graph_);
58 0 : for (DBGraph::edge_iterator e_iter = graph_->edge_list_begin();
59 0 : e_iter != graph_->edge_list_end(); e_iter = e_next) {
60 0 : const DBGraph::DBEdgeInfo &tuple = *e_iter;
61 : // increment only after dereferencing
62 0 : e_next = ++e_iter;
63 :
64 0 : IFMapLink *link = static_cast<IFMapLink *>(boost::get<2>(tuple));
65 0 : assert(link);
66 :
67 0 : bool exists = false;
68 : IFMapLink::LinkOriginInfo origin_info =
69 0 : link->GetOriginInfo(IFMapOrigin::CASSANDRA, &exists);
70 0 : if (exists && (origin_info.sequence_number < curr_seq_num)) {
71 : IFMapLinkTable *ltable = static_cast<IFMapLinkTable *>(
72 0 : db_->FindTable("__ifmap_metadata__.0"));
73 : // Cleanup the node and remove from the graph
74 0 : link->RemoveOriginInfo(IFMapOrigin::CASSANDRA);
75 0 : if (link->is_origin_empty()) {
76 0 : ltable->DeleteLink(link);
77 : }
78 0 : links_deleted++;
79 : }
80 : }
81 :
82 0 : DBGraph::vertex_iterator v_next(graph_);
83 0 : for (DBGraph::vertex_iterator v_iter = graph_->vertex_list_begin();
84 0 : v_iter != graph_->vertex_list_end(); v_iter = v_next) {
85 :
86 0 : IFMapNode *node = static_cast<IFMapNode *>(v_iter.operator->());
87 : // increment only after dereferencing
88 0 : v_next = ++v_iter;
89 :
90 : IFMapObject *object =
91 0 : node->Find(IFMapOrigin(IFMapOrigin::CASSANDRA));
92 : IFMapServerTable *ntable =
93 0 : static_cast<IFMapServerTable *>(node->table());
94 0 : if (object != NULL) {
95 0 : if (object->sequence_number() < curr_seq_num) {
96 0 : node->Remove(object);
97 0 : bool retb = ntable->DeleteIfEmpty(node);
98 0 : if (retb) {
99 0 : nodes_deleted++;
100 : } else {
101 0 : objects_deleted++;
102 : }
103 : } else {
104 : // There could be stale properties
105 0 : bool changed = object->ResolveStaleness();
106 0 : if (changed) {
107 0 : nodes_changed++;
108 0 : ntable->Notify(node);
109 : }
110 : }
111 : } else {
112 : // The node doesnt have any object. We should delete it if it
113 : // does not have any neighbors either.
114 0 : bool retb = ntable->DeleteIfEmpty(node);
115 0 : if (retb) {
116 0 : nodes_deleted++;
117 : }
118 : }
119 : }
120 0 : IFMAP_DEBUG(IFMapStaleEntriesCleanerInfo, curr_seq_num, nodes_deleted,
121 : nodes_changed, links_deleted, objects_deleted);
122 :
123 0 : return true;
124 : }
125 0 : std::string Description() const {
126 0 : return "IFMapServer::IFMapStaleEntriesCleaner";
127 : }
128 :
129 : private:
130 : DB *db_;
131 : DBGraph *graph_;
132 : IFMapServer *ifmap_server_;
133 : };
134 :
135 : class IFMapServer::IFMapVmSubscribe : public Task {
136 : public:
137 66 : IFMapVmSubscribe(DB *db, DBGraph *graph, IFMapServer *server,
138 : const std::string &vr_name, const std::string &vm_uuid,
139 66 : bool subscribe, bool has_vms):
140 : Task(TaskScheduler::GetInstance()->GetTaskId("db::IFMapTable"), 0),
141 66 : db_(db), ifmap_server_(server), vr_name_(vr_name),
142 66 : vm_uuid_(vm_uuid), subscribe_(subscribe), has_vms_(has_vms) {
143 66 : }
144 :
145 66 : bool Run() {
146 :
147 : IFMapServerTable *vm_table = static_cast<IFMapServerTable *>(
148 66 : db_->FindTable("__ifmap__.virtual_machine.0"));
149 66 : assert(vm_table != NULL);
150 :
151 : // We are processing a VM-sub/unsub. If the client is gone by the time
152 : // we get here, there is nothing to do.
153 66 : IFMapClient *client = ifmap_server_->FindClient(vr_name_);
154 66 : if (!client) {
155 0 : return true;
156 : }
157 :
158 : // Find the vm's node using its UUID. If the config has not added the
159 : // vm yet, treat this request as pending since we cant process it right
160 : // now. If the node is marked deleted, mark it as pending since the
161 : // node might get revived. In this case, the pending entry will get
162 : // cleaned up either via an unsub from the client or client-delete.
163 66 : IFMapNode *vm_node = ifmap_server_->GetVmNodeByUuid(vm_uuid_);
164 66 : if (vm_node && !vm_node->IsDeleted()) {
165 : IFMapServerTable *vr_table = static_cast<IFMapServerTable *>(
166 52 : db_->FindTable("__ifmap__.virtual_router.0"));
167 52 : assert(vr_table != NULL);
168 :
169 52 : std::string vm_name = vm_node->name();
170 52 : vr_table->IFMapVmSubscribe(vr_name_, vm_name, subscribe_, has_vms_);
171 :
172 52 : if (subscribe_) {
173 52 : ifmap_server_->ClientGraphDownload(client);
174 : }
175 52 : } else {
176 14 : ifmap_server_->ProcessVmRegAsPending(vm_uuid_, vr_name_,
177 14 : subscribe_);
178 : }
179 :
180 66 : return true;
181 : }
182 0 : std::string Description() const { return "IFMapServer::IFMapVmSubscribe"; }
183 :
184 : private:
185 : DB *db_;
186 : IFMapServer *ifmap_server_;
187 : std::string vr_name_;
188 : std::string vm_uuid_;
189 : bool subscribe_;
190 : bool has_vms_;
191 : };
192 :
193 179 : IFMapServer::IFMapServer(DB *db, DBGraph *graph,
194 179 : boost::asio::io_context *io_service)
195 179 : : db_(db), graph_(graph),
196 179 : queue_(new IFMapUpdateQueue(this)),
197 179 : exporter_(new IFMapExporter(this)),
198 179 : sender_(new IFMapUpdateSender(this, queue())),
199 179 : vm_uuid_mapper_(new IFMapVmUuidMapper(db_, this)),
200 179 : work_queue_(TaskScheduler::GetInstance()->GetTaskId("db::IFMapTable"),
201 : 0, boost::bind(&IFMapServer::ClientWorker, this, _1)),
202 179 : io_service_(io_service), config_manager_(NULL),
203 537 : ifmap_channel_manager_(NULL) {
204 179 : }
205 :
206 321 : IFMapServer::~IFMapServer() {
207 321 : }
208 :
209 53 : void IFMapServer::Initialize() {
210 53 : exporter_->Initialize(db_);
211 53 : vm_uuid_mapper_->Initialize();
212 53 : }
213 :
214 159 : void IFMapServer::Shutdown() {
215 159 : vm_uuid_mapper_->Shutdown();
216 159 : exporter_->Shutdown();
217 159 : }
218 :
219 74 : void IFMapServer::ClientRegister(IFMapClient *client) {
220 74 : size_t index = client_indexes_.find_first_clear();
221 74 : if (index == BitSet::npos) {
222 0 : index = client_indexes_.size();
223 : }
224 74 : client_indexes_.set(index);
225 :
226 74 : std::pair<ClientMap::iterator, bool> cm_ret;
227 74 : cm_ret = client_map_.insert(make_pair(client->identifier(), client));
228 74 : assert(cm_ret.second);
229 :
230 74 : std::pair<IndexMap::iterator, bool> im_ret;
231 74 : im_ret = index_map_.insert(make_pair(index, client));
232 74 : assert(im_ret.second);
233 :
234 74 : client->Initialize(exporter_.get(), index);
235 74 : queue_->Join(index);
236 74 : IFMAP_DEBUG(IFMapServerClientRegUnreg, "Register request for client ",
237 : client->identifier(), index);
238 74 : }
239 :
240 6 : void IFMapServer::SaveClientHistory(IFMapClient *client) {
241 6 : if (client_history_.size() >= kClientHistorySize) {
242 : // Remove the oldest entry.
243 0 : client_history_.pop_front();
244 : }
245 6 : ClientHistoryInfo info(client->identifier(), client->index(),
246 6 : client->created_at(), UTCTimestampUsec());
247 6 : client_history_.push_back(info);
248 6 : }
249 :
250 6 : void IFMapServer::ClientUnregister(IFMapClient *client) {
251 6 : IFMAP_DEBUG(IFMapServerClientRegUnreg, "Un-register request for client ",
252 : client->identifier(), client->index());
253 6 : size_t index = client->index();
254 6 : sender_->CleanupClient(index);
255 6 : queue_->Leave(index);
256 6 : ImSz_t iret = index_map_.erase(index);
257 6 : assert(iret == 1);
258 6 : CmSz_t cret = client_map_.erase(client->identifier());
259 6 : assert(cret == 1);
260 6 : client_indexes_.reset(index);
261 6 : }
262 :
263 56 : bool IFMapServer::ProcessClientWork(bool add, IFMapClient *client) {
264 56 : if (add) {
265 50 : ClientRegister(client);
266 50 : ClientExporterSetup(client);
267 50 : ClientGraphDownload(client);
268 : } else {
269 6 : RemoveSelfAddedLinksAndObjects(client);
270 6 : CleanupUuidMapper(client);
271 6 : SaveClientHistory(client);
272 6 : int index = client->index();
273 6 : ClientUnregister(client);
274 : // Exporter cleanup must happen after ClientUnregister() which does
275 : // Q-Leave which needs the config trackers in the exporters.
276 6 : ClientExporterCleanup(index);
277 : }
278 56 : return true;
279 : }
280 :
281 : // To be used only by tests.
282 50 : void IFMapServer::AddClient(IFMapClient *client) {
283 : // Let ClientWorker() do all the work in the context of the db-task
284 : QueueEntry entry;
285 50 : entry.op = ADD;
286 50 : entry.client = client;
287 50 : work_queue_.Enqueue(entry);
288 50 : }
289 :
290 : // To be used only by tests.
291 6 : void IFMapServer::DeleteClient(IFMapClient *client) {
292 : // Let ClientWorker() do all the work in the context of the db-task
293 : QueueEntry entry;
294 6 : entry.op = DEL;
295 6 : entry.client = client;
296 6 : work_queue_.Enqueue(entry);
297 6 : }
298 :
299 : // To be used only by tests.
300 0 : void IFMapServer::SimulateDeleteClient(IFMapClient *client) {
301 : QueueEntry entry;
302 0 : entry.op = DEL;
303 0 : entry.client = client;
304 0 : ClientWorker(entry);
305 0 : }
306 :
307 56 : bool IFMapServer::ClientWorker(QueueEntry work_entry) {
308 56 : bool add = (work_entry.op == ADD) ? true : false;
309 56 : IFMapClient *client = work_entry.client;
310 :
311 56 : bool done = ProcessClientWork(add, client);
312 :
313 56 : return done;
314 : }
315 :
316 : // Get the list of subscribed VMs. For each item in the list, if it exist in the
317 : // list of pending vm registration requests, remove it.
318 6 : void IFMapServer::CleanupUuidMapper(IFMapClient *client) {
319 6 : std::vector<std::string> vmlist = client->vm_list();
320 6 : for (size_t count = 0; count < vmlist.size(); ++count) {
321 0 : vm_uuid_mapper_->CleanupPendingVmRegEntry(vmlist.at(count));
322 : }
323 6 : }
324 :
325 6 : void IFMapServer::RemoveSelfAddedLinksAndObjects(IFMapClient *client) {
326 : IFMapServerTable *vr_table = static_cast<IFMapServerTable *>(
327 6 : db_->FindTable("__ifmap__.virtual_router.0"));
328 6 : assert(vr_table != NULL);
329 :
330 6 : IFMapNode *node = vr_table->FindNode(client->identifier());
331 6 : if ((node != NULL) && node->IsVertexValid()) {
332 6 : IFMapOrigin origin(IFMapOrigin::XMPP);
333 6 : for (DBGraphVertex::adjacency_iterator iter = node->begin(graph_), next;
334 18 : iter != node->end(graph_); iter = next) {
335 12 : IFMapNode *adj = static_cast<IFMapNode *>(iter.operator->());
336 12 : next = ++iter;
337 12 : if (adj->table()->name() == "__ifmap__.virtual_machine.0") {
338 6 : vr_table->IFMapRemoveVrVmLink(node, adj);
339 6 : IFMapServerTable::RemoveObjectAndDeleteNode(adj, origin);
340 : }
341 : }
342 6 : IFMapServerTable::RemoveObjectAndDeleteNode(node, origin);
343 : }
344 6 : }
345 :
346 102 : void IFMapServer::ClientGraphDownload(IFMapClient *client) {
347 102 : IFMapTable *table = IFMapTable::FindTable(db_, "virtual-router");
348 102 : assert(table);
349 :
350 102 : IFMapNode *node = table->FindNode(client->identifier());
351 102 : if ((node != NULL) && node->IsVertexValid()) {
352 84 : for (DBGraphVertex::edge_iterator iter = node->edge_list_begin(graph_);
353 252 : iter != node->edge_list_end(graph_); ++iter) {
354 168 : IFMapLink *link = static_cast<IFMapLink *>(iter.operator->());
355 168 : if (exporter_->FilterNeighbor(node, link)) {
356 2 : continue;
357 : }
358 : DBTable *link_table = static_cast<DBTable *>(
359 166 : db_->FindTable("__ifmap_metadata__.0"));
360 166 : link_table->Change(link);
361 : }
362 : }
363 102 : }
364 :
365 74 : void IFMapServer::ClientExporterSetup(IFMapClient *client) {
366 74 : exporter_->AddClientConfigTracker(client->index());
367 74 : }
368 :
369 6 : void IFMapServer::ClientExporterCleanup(int index) {
370 6 : exporter_->CleanupClientConfigTrackedEntries(index);
371 6 : exporter_->DeleteClientConfigTracker(index);
372 :
373 6 : BitSet rm_bs;
374 6 : rm_bs.set(index);
375 6 : exporter_->ResetLinkDeleteClients(rm_bs);
376 6 : }
377 :
378 254 : IFMapClient *IFMapServer::FindClient(const std::string &id) {
379 254 : ClientMap::iterator loc = client_map_.find(id);
380 254 : if (loc != client_map_.end()) {
381 212 : return loc->second;
382 : }
383 42 : return NULL;
384 : }
385 :
386 1041 : IFMapClient *IFMapServer::GetClient(int index) {
387 1041 : IndexMap::iterator loc = index_map_.find(index);
388 1041 : if (loc != index_map_.end()) {
389 1041 : return loc->second;
390 : }
391 0 : return NULL;
392 : }
393 :
394 0 : bool IFMapServer::ClientNameToIndex(const std::string &id, int *index) {
395 0 : IFMapClient *client = FindClient(id);
396 0 : if (client) {
397 0 : *index = client->index();
398 0 : return true;
399 : }
400 0 : return false;
401 : }
402 :
403 52 : void IFMapServer::ProcessVmSubscribe(std::string vr_name, std::string vm_uuid,
404 : bool subscribe, bool has_vms) {
405 : IFMapVmSubscribe *vm_sub = new IFMapVmSubscribe(db_, graph_, this,
406 : vr_name, vm_uuid,
407 52 : subscribe, has_vms);
408 52 : TaskScheduler *scheduler = TaskScheduler::GetInstance();
409 52 : scheduler->Enqueue(vm_sub);
410 52 : }
411 :
412 14 : void IFMapServer::ProcessVmSubscribe(std::string vr_name, std::string vm_uuid,
413 : bool subscribe) {
414 14 : IFMapClient *client = FindClient(vr_name);
415 14 : assert(client);
416 : IFMapVmSubscribe *vm_sub =
417 : new IFMapVmSubscribe(db_, graph_, this, vr_name, vm_uuid, subscribe,
418 14 : client->HasVms());
419 14 : TaskScheduler *scheduler = TaskScheduler::GetInstance();
420 14 : scheduler->Enqueue(vm_sub);
421 14 : }
422 :
423 14 : void IFMapServer::ProcessVmRegAsPending(std::string vm_uuid,
424 : std::string vr_name, bool subscribe) {
425 14 : IFMAP_DEBUG(IFMapServerPendingVmReg, vm_uuid, vr_name, subscribe);
426 14 : vm_uuid_mapper_->ProcessVmRegAsPending(vm_uuid, vr_name, subscribe);
427 14 : }
428 :
429 66 : IFMapNode *IFMapServer::GetVmNodeByUuid(const std::string &vm_uuid) {
430 66 : return vm_uuid_mapper_->GetVmNodeByUuid(vm_uuid);
431 : }
432 :
433 0 : void IFMapServer::FillClientMap(IFMapServerShowClientMap *out_map,
434 : const std::string &search_string) {
435 0 : regex search_expr(search_string);
436 0 : out_map->set_table_count(client_map_.size());
437 0 : for (ClientMap::const_iterator iter = client_map_.begin();
438 0 : iter != client_map_.end(); ++iter) {
439 0 : IFMapClient *client = iter->second;
440 0 : if (!regex_search(client->identifier(), search_expr)) {
441 0 : continue;
442 : }
443 0 : IFMapServerClientMapShowEntry entry;
444 0 : entry.set_client_name(client->identifier());
445 0 : entry.set_interest_tracker_entries(exporter_->ClientConfigTrackerSize(
446 : IFMapExporter::INTEREST, client->index()));
447 0 : entry.set_advertised_tracker_entries(exporter_->ClientConfigTrackerSize(
448 : IFMapExporter::ADVERTISED, client->index()));
449 0 : out_map->clients.push_back(entry);
450 0 : }
451 0 : out_map->set_print_count(out_map->clients.size());
452 0 : }
453 :
454 0 : void IFMapServer::FillIndexMap(IFMapServerShowIndexMap *out_map,
455 : const std::string &search_string) {
456 0 : regex search_expr(search_string);
457 0 : out_map->set_table_count(index_map_.size());
458 0 : for (IndexMap::const_iterator iter = index_map_.begin();
459 0 : iter != index_map_.end(); ++iter) {
460 0 : IFMapClient *client = iter->second;
461 0 : if (!regex_search(client->identifier(), search_expr)) {
462 0 : continue;
463 : }
464 0 : IFMapServerIndexMapShowEntry entry;
465 0 : entry.set_client_index(iter->first);
466 0 : entry.set_client_name(client->identifier());
467 0 : out_map->clients.push_back(entry);
468 0 : }
469 0 : out_map->set_print_count(out_map->clients.size());
470 0 : }
471 :
472 0 : const std::string IFMapServer::ClientHistoryInfo::client_created_at_str() const {
473 0 : return duration_usecs_to_string(UTCTimestampUsec() - client_created_at);
474 : }
475 :
476 0 : const std::string IFMapServer::ClientHistoryInfo::history_created_at_str() const {
477 0 : return duration_usecs_to_string(UTCTimestampUsec() - history_created_at);
478 : }
479 :
480 0 : void IFMapServer::FillClientHistory(IFMapServerClientHistoryList *out_list,
481 : const std::string &search_string) {
482 0 : regex search_expr(search_string);
483 0 : out_list->set_table_count(client_history_.size());
484 0 : for (ClientHistory::const_iterator iter = client_history_.begin();
485 0 : iter != client_history_.end(); ++iter) {
486 0 : ClientHistoryInfo info = *iter;
487 0 : if (!regex_search(info.client_name, search_expr)) {
488 0 : continue;
489 : }
490 0 : IFMapServerClientHistoryEntry entry;
491 0 : entry.set_client_name(info.client_name);
492 0 : entry.set_client_index(info.client_index);
493 0 : entry.set_creation_time_ago(info.client_created_at_str());
494 0 : entry.set_deletion_time_ago(info.history_created_at_str());
495 0 : out_list->clients.push_back(entry);
496 0 : }
497 0 : out_list->set_print_count(out_list->clients.size());
498 0 : }
499 :
500 176 : void IFMapServer::GetUIInfo(IFMapServerInfoUI *server_info) const {
501 176 : server_info->set_num_peer_clients(GetClientMapSize());
502 176 : }
503 :
504 176 : bool IFMapServer::CollectStats(BgpRouterState *state, bool first) const {
505 176 : CHECK_CONCURRENCY("bgp::ShowCommand");
506 :
507 176 : ConfigDBConnInfo db_conn_info;
508 176 : const ConfigClientManager *ccmgr = get_config_manager();
509 176 : bool change = false;
510 :
511 176 : ccmgr->config_db_client()->GetConnectionInfo(db_conn_info);
512 176 : if (first || db_conn_info != state->get_db_conn_info()) {
513 1 : state->set_db_conn_info(db_conn_info);
514 1 : change = true;
515 : }
516 :
517 176 : if (ccmgr->config_amqp_client()) {
518 176 : ConfigAmqpConnInfo amqp_conn_info;
519 176 : ccmgr->config_amqp_client()->GetConnectionInfo(amqp_conn_info);
520 176 : if (first || amqp_conn_info != state->get_amqp_conn_info()) {
521 1 : state->set_amqp_conn_info(amqp_conn_info);
522 1 : change = true;
523 : }
524 176 : }
525 :
526 176 : IFMapServerInfoUI server_info;
527 176 : GetUIInfo(&server_info);
528 176 : if (first || server_info != state->get_ifmap_server_info()) {
529 1 : state->set_ifmap_server_info(server_info);
530 1 : change = true;
531 : }
532 :
533 176 : return change;
534 176 : }
535 :
536 0 : void IFMapServer::CleanupStaleEntries() {
537 : IFMapStaleEntriesCleaner *cleaner =
538 0 : new IFMapStaleEntriesCleaner(db_, graph_, this);
539 0 : TaskScheduler *scheduler = TaskScheduler::GetInstance();
540 0 : scheduler->Enqueue(cleaner);
541 0 : }
|