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