Line data Source code
1 : /*
2 : * Copyright (c) 2016 Juniper Networks, Inc. All rights reserved.
3 : * Copyright Contributors to the OpenSDN project
4 : */
5 :
6 : #include "http_parser/http_parser.h"
7 :
8 : #include <boost/uuid/uuid_io.hpp>
9 : #include <boost/algorithm/string/case_conv.hpp>
10 :
11 : #include <cmn/agent_cmn.h>
12 :
13 : #include <vnc_cfg_types.h>
14 : #include <agent_types.h>
15 :
16 : #include <init/agent_param.h>
17 : #include <cfg/cfg_init.h>
18 :
19 : #include <ifmap/ifmap_node.h>
20 : #include <cmn/agent_cmn.h>
21 : #include <oper/ifmap_dependency_manager.h>
22 : #include <oper/config_manager.h>
23 : #include <oper/agent_sandesh.h>
24 : #include <oper/instance_task.h>
25 : #include <oper/interface_common.h>
26 : #include <oper/metadata_ip.h>
27 : #include <oper/health_check.h>
28 :
29 : #include <oper/vn.h>
30 : #include <oper/vrf.h>
31 :
32 : #include "mac_learning/mac_learning_proto.h"
33 :
34 : SandeshTraceBufferPtr
35 : HealthCheckTraceBuf(SandeshTraceBufferCreate("HealthCheck", 5000));
36 :
37 : const std::string HealthCheckInstanceTask::kHealthCheckCmd
38 : ("/usr/bin/contrail-vrouter-agent-health-check.py");
39 :
40 : ////////////////////////////////////////////////////////////////////////////////
41 :
42 0 : HealthCheckInstanceBase::HealthCheckInstanceBase(HealthCheckService *service,
43 : MetaDataIpAllocator *allocator,
44 : VmInterface *intf,
45 0 : bool ignore_status_event) :
46 0 : service_(NULL), intf_(intf),
47 0 : ip_(new MetaDataIp(allocator, intf, MetaDataIp::HEALTH_CHECK, service->IsInstanceTaskBased())),
48 0 : last_update_time_("-"), deleted_(false),
49 0 : ignore_status_event_(ignore_status_event) {
50 : // start with health check instance state as active, unless reported
51 : // down by the attached health check service, so that the existing
52 : // running traffic is not affected by attaching health check service
53 0 : active_ = true;
54 0 : ip_->set_active(true);
55 0 : if (!service->IsVnIpListHealthCheckService()) {
56 0 : intf->InsertHealthCheckInstance(this);
57 : }
58 0 : ResyncTarget(service);
59 0 : }
60 :
61 0 : HealthCheckInstanceBase::~HealthCheckInstanceBase() {
62 0 : VmInterface *intf = static_cast<VmInterface *>(intf_.get());
63 0 : if (!service_.get()->IsVnIpListHealthCheckService()) {
64 0 : intf->DeleteHealthCheckInstance(this);
65 : }
66 0 : ResyncTarget(service_.get());
67 0 : }
68 :
69 0 : void HealthCheckInstanceBase::EnqueueResync(const HealthCheckService *service,
70 : Interface *itf) const {
71 0 : DBRequest req;
72 0 : req.oper = DBRequest::DB_ENTRY_ADD_CHANGE;
73 0 : req.key.reset(new VmInterfaceKey(AgentKey::RESYNC, itf->GetUuid(), ""));
74 0 : req.data.reset(new VmInterfaceHealthCheckData());
75 0 : service->table()->agent()->interface_table()->Enqueue(&req);
76 0 : }
77 :
78 0 : void HealthCheckInstanceBase::ResyncTarget(const HealthCheckService *service)
79 : const {
80 0 : EnqueueResync(service, intf_.get());
81 0 : }
82 :
83 0 : void HealthCheckInstanceBase::set_service(HealthCheckService *service) {
84 : // It is possible that the instance is queued for deletion at this point
85 : // since this can be called in the Agent::heathCheck Task, don't need to
86 : // set the service in this case because it is not needed.
87 0 : if (deleted_)
88 0 : return;
89 0 : if (service_ == service) {
90 0 : UpdateInstanceTask();
91 0 : return;
92 : }
93 : // The instance is not expected to be associated with different instance
94 : // during its lifetime. Adding a check to make sure that service is null
95 : // in this case.
96 0 : assert(service_ == NULL);
97 0 : service_ = service;
98 0 : CreateInstanceTask();
99 : }
100 :
101 0 : std::string HealthCheckInstanceBase::to_string() {
102 0 : std::string str("Instance for service ");
103 0 : str += service_->name();
104 0 : str += " interface " + intf_->name();
105 0 : return str;
106 0 : }
107 :
108 0 : void HealthCheckInstanceBase::OnRead(const std::string &data) {
109 : HealthCheckInstanceEvent *event =
110 0 : new HealthCheckInstanceEvent(this, service_.get(),
111 : HealthCheckInstanceEvent::MESSAGE_READ,
112 0 : data);
113 0 : service_->table()->InstanceEventEnqueue(event);
114 0 : }
115 :
116 0 : void HealthCheckInstanceBase::OnExit(const boost::system::error_code &ec) {
117 : HealthCheckInstanceEvent *event =
118 0 : new HealthCheckInstanceEvent(this, service_.get(),
119 0 : HealthCheckInstanceEvent::TASK_EXIT, "");
120 0 : service_->table()->InstanceEventEnqueue(event);
121 0 : }
122 :
123 0 : void HealthCheckInstanceBase::SetService(HealthCheckService *service) {
124 : HealthCheckInstanceEvent *event =
125 : new HealthCheckInstanceEvent(this, service,
126 0 : HealthCheckInstanceEvent::SET_SERVICE, "");
127 0 : service->table()->InstanceEventEnqueue(event);
128 0 : }
129 :
130 0 : void HealthCheckInstanceBase::StopTask(HealthCheckService *service) {
131 : HealthCheckInstanceEvent *event =
132 : new HealthCheckInstanceEvent(this, service,
133 0 : HealthCheckInstanceEvent::STOP_TASK, "");
134 0 : service->table()->InstanceEventEnqueue(event);
135 0 : }
136 :
137 0 : IpAddress HealthCheckInstanceBase::update_source_ip() {
138 0 : if (source_ip_.is_unspecified()) {
139 0 : VmInterface *itf = static_cast<VmInterface *>(interface().get());
140 0 : if (itf) {
141 0 : source_ip_ = itf->GetGatewayIp(itf->primary_ip_addr());
142 : }
143 : }
144 0 : return source_ip_;
145 : }
146 :
147 0 : IpAddress HealthCheckInstanceBase::source_ip() const {
148 0 : if (source_ip_.is_unspecified() && ip_)
149 0 : return ip_->service_ip();
150 0 : return source_ip_;
151 : }
152 :
153 0 : IpAddress HealthCheckInstanceBase::destination_ip() const {
154 0 : if (destination_ip_.is_unspecified() && ip_)
155 0 : return ip_->destination_ip();
156 0 : return destination_ip_;
157 : }
158 :
159 0 : void HealthCheckInstanceBase::set_destination_ip(const IpAddress &ip) {
160 0 : ip_->set_destination_ip(ip);
161 0 : destination_ip_ = ip;
162 0 : }
163 :
164 : ////////////////////////////////////////////////////////////////////////////////
165 :
166 0 : HealthCheckInstanceTask::HealthCheckInstanceTask(HealthCheckService *service,
167 : MetaDataIpAllocator *allocator,
168 : VmInterface *intf,
169 0 : bool ignore_status_event) :
170 : HealthCheckInstanceBase(service, allocator, intf, ignore_status_event),
171 0 : task_(NULL) {
172 0 : }
173 :
174 0 : HealthCheckInstanceTask::~HealthCheckInstanceTask() {
175 0 : }
176 :
177 0 : bool HealthCheckInstanceTask::CreateInstanceTask() {
178 0 : if (task_.get() != NULL) {
179 0 : return false;
180 : }
181 :
182 0 : assert(deleted_ == false);
183 :
184 0 : HEALTH_CHECK_TRACE(Trace, "Starting " + this->to_string());
185 :
186 0 : task_.reset(new HeathCheckProcessInstance("HealthCheckInstance", "", 0,
187 0 : service_->table()->agent()->event_manager()));
188 0 : if (task_.get() != NULL) {
189 0 : task_->set_pipe_stdout(true);
190 0 : task_->set_on_data_cb(
191 : boost::bind(&HealthCheckInstanceBase::OnRead, this, _2));
192 0 : task_->set_on_exit_cb(
193 : boost::bind(&HealthCheckInstanceBase::OnExit, this, _2));
194 0 : return RunInstanceTask();
195 : }
196 :
197 0 : return false;
198 : }
199 :
200 0 : bool HealthCheckInstanceTask::DestroyInstanceTask() {
201 0 : if (deleted_) {
202 0 : return true;
203 : }
204 :
205 0 : if (task_.get() == NULL) {
206 0 : return false;
207 : }
208 :
209 0 : HEALTH_CHECK_TRACE(Trace, "Deleting " + this->to_string());
210 0 : deleted_ = true;
211 0 : StopInstanceTask();
212 0 : return true;
213 : }
214 :
215 0 : bool HealthCheckInstanceTask::RunInstanceTask() {
216 0 : UpdateInstanceTaskCommand();
217 0 : return task_->Run();
218 : }
219 :
220 0 : bool HealthCheckInstanceTask::StopInstanceTask() {
221 0 : task_->Stop();
222 0 : return true;
223 : }
224 :
225 0 : void HealthCheckInstanceTask::UpdateInstanceTaskCommand() {
226 0 : if (service_->table()->agent()->test_mode()) {
227 : // in test mode, set task instance to run no-op shell
228 0 : task_->set_cmd("sleep 1");
229 0 : return;
230 : }
231 :
232 0 : std::stringstream cmd_str;
233 0 : cmd_str << kHealthCheckCmd << " -m " << service_->monitor_type();
234 0 : cmd_str << " -d " << ip_->GetLinkLocalIp().to_string();
235 0 : cmd_str << " -t " << service_->timeout() +
236 0 : service_->timeout_usecs() / 1000000;
237 0 : cmd_str << " -r " << service_->max_retries();
238 0 : cmd_str << " -i " << service_->delay() +
239 0 : service_->delay_usecs() / 1000000;
240 :
241 0 : if (service_->monitor_type().find("HTTP") != std::string::npos &&
242 0 : !service_->url_path().empty()) {
243 : // append non empty url string to script for HTTP
244 0 : cmd_str << " -u " << service_->url_path();
245 : }
246 :
247 0 : task_->set_cmd(cmd_str.str());
248 0 : }
249 :
250 0 : bool HealthCheckInstanceTask::IsRunning() const {
251 0 : return (task_.get() != NULL ? task_->is_running(): false);
252 : }
253 :
254 : ////////////////////////////////////////////////////////////////////////////////
255 :
256 0 : HealthCheckInstanceService::HealthCheckInstanceService(
257 : HealthCheckService *service, MetaDataIpAllocator *allocator,
258 : VmInterface *intf, VmInterface *other_intf, bool ignore_status_event,
259 0 : bool multi_hop) :
260 : HealthCheckInstanceBase(service, allocator, intf, ignore_status_event),
261 0 : other_intf_(other_intf), multi_hop_(multi_hop), bfd_up_(false) {
262 0 : if (service->IsSegmentHealthCheckService() && other_intf) {
263 0 : other_intf->InsertHealthCheckInstance(this);
264 : }
265 0 : }
266 :
267 0 : void HealthCheckInstanceService::BfdStateChange(bool up) {
268 0 : if (up) {
269 0 : bfd_up_ = true;
270 0 : return;
271 : }
272 :
273 0 : if (!bfd_up_) {
274 : // BFD reports its initial state as Down when the session is created.
275 : // Do not tear down the BGP flow until this session has been Up.
276 0 : return;
277 : }
278 :
279 0 : bfd_up_ = false;
280 0 : if (!bfd_down_callback_.empty()) {
281 0 : bfd_down_callback_();
282 : }
283 : }
284 :
285 0 : HealthCheckInstanceService::~HealthCheckInstanceService() {
286 0 : if (service()->IsSegmentHealthCheckService() && other_intf_.get()) {
287 0 : VmInterface *vmi = static_cast<VmInterface *>(other_intf_.get());
288 0 : vmi->DeleteHealthCheckInstance(this);
289 0 : EnqueueResync(service(), other_intf_.get());
290 : }
291 0 : }
292 :
293 0 : bool HealthCheckInstanceService::CreateInstanceTask() {
294 0 : assert(deleted_ == false);
295 0 : HealthCheckService::HealthCheckType type = service_->health_check_type();
296 0 : HEALTH_CHECK_TRACE(Trace, "Starting " + this->to_string());
297 0 : assert(type == HealthCheckService::SEGMENT ||
298 : type == HealthCheckService::BFD);
299 0 : if (service_->table()->health_check_service_callback(type).empty() ||
300 0 : service_->table()->health_check_service_callback(type)
301 0 : (HealthCheckTable::CREATE_SERVICE, this) == false) {
302 0 : HEALTH_CHECK_TRACE(Trace, "Failed to start " + this->to_string());
303 0 : return false;
304 : }
305 0 : return true;
306 : }
307 :
308 0 : bool HealthCheckInstanceService::DestroyInstanceTask() {
309 0 : if (deleted_) {
310 0 : return true;
311 : }
312 :
313 0 : HealthCheckService::HealthCheckType type = service_->health_check_type();
314 0 : if (!service_->table()->health_check_service_callback(type).empty()) {
315 0 : HEALTH_CHECK_TRACE(Trace, "Deleting " + this->to_string());
316 0 : service_->table()->health_check_service_callback(type)
317 0 : (HealthCheckTable::DELETE_SERVICE, this);
318 : }
319 :
320 0 : deleted_ = true;
321 0 : return false;
322 : }
323 :
324 0 : bool HealthCheckInstanceService::RunInstanceTask() {
325 0 : HealthCheckService::HealthCheckType type = service_->health_check_type();
326 0 : if (!service_->table()->health_check_service_callback(type).empty()) {
327 0 : HEALTH_CHECK_TRACE(Trace, "Run Instance " + this->to_string());
328 0 : assert(type == HealthCheckService::SEGMENT ||
329 : type == HealthCheckService::BFD);
330 0 : return service_->table()->health_check_service_callback(type)
331 0 : (HealthCheckTable::RUN_SERVICE, this);
332 : }
333 0 : HEALTH_CHECK_TRACE(Trace, "Run Instance failed " + this->to_string());
334 0 : return false;
335 : }
336 :
337 0 : bool HealthCheckInstanceService::StopInstanceTask() {
338 0 : HealthCheckService::HealthCheckType type = service_->health_check_type();
339 0 : if (!service_->table()->health_check_service_callback(type).empty()) {
340 0 : HEALTH_CHECK_TRACE(Trace, "Stop Instance " + this->to_string());
341 0 : return service_->table()->health_check_service_callback(type)
342 0 : (HealthCheckTable::STOP_SERVICE, this);
343 : }
344 0 : return false;
345 : }
346 :
347 0 : bool HealthCheckInstanceService::UpdateInstanceTask() {
348 0 : bool success = false;
349 0 : HealthCheckService::HealthCheckType type = service_->health_check_type();
350 0 : if (!service_->table()->health_check_service_callback(type).empty()) {
351 0 : HEALTH_CHECK_TRACE(Trace, "Updating " + this->to_string());
352 0 : assert(type == HealthCheckService::SEGMENT ||
353 : type == HealthCheckService::BFD);
354 0 : success = service_->table()->health_check_service_callback(type)
355 0 : (HealthCheckTable::UPDATE_SERVICE, this);
356 : }
357 0 : if (!success) {
358 0 : HEALTH_CHECK_TRACE(Trace, "Failed to Update " + this->to_string());
359 : }
360 0 : return success;
361 : }
362 :
363 0 : void HealthCheckInstanceService::ResyncTarget(const HealthCheckService
364 : *service) const {
365 0 : HealthCheckInstanceBase::ResyncTarget(service);
366 0 : if (service->IsSegmentHealthCheckService() && other_intf_.get()) {
367 0 : EnqueueResync(service, other_intf_.get());
368 : }
369 0 : }
370 :
371 0 : HealthCheckMacIpInstanceService::HealthCheckMacIpInstanceService(
372 : HealthCheckService *service, MetaDataIpAllocator *allocator,
373 : VmInterface *intf, VmInterface *other_intf, bool ignore_status_event,
374 0 : bool multi_hop) :
375 : HealthCheckInstanceService(service, allocator, intf, other_intf,
376 0 : ignore_status_event, multi_hop) {
377 :
378 : // Override this value done in the base class to prevent mac-ip
379 : // learnt entry from being deleted on bfd startup
380 0 : active_ = false;
381 0 : }
382 :
383 0 : HealthCheckMacIpInstanceService::~HealthCheckMacIpInstanceService() {
384 0 : }
385 :
386 0 : void HealthCheckMacIpInstanceService::ResyncTarget(const HealthCheckService
387 : *service) const {
388 0 : if (!active_) {
389 0 : if (!service_->table()->health_check_notify_callback().empty()) {
390 0 : service_->table()->health_check_notify_callback()(this);
391 : }
392 : }
393 0 : }
394 :
395 : ////////////////////////////////////////////////////////////////////////////////
396 :
397 0 : HealthCheckInstanceEvent::HealthCheckInstanceEvent(
398 : HealthCheckInstanceBase *inst, HealthCheckService *service,
399 0 : EventType type, const std::string &message) :
400 0 : instance_(inst), service_(service), type_(type), message_(message) {
401 0 : }
402 :
403 0 : HealthCheckInstanceEvent::~HealthCheckInstanceEvent() {
404 0 : }
405 :
406 : ////////////////////////////////////////////////////////////////////////////////
407 :
408 0 : HealthCheckService::HealthCheckService(const HealthCheckTable *table,
409 0 : const boost::uuids::uuid &id) :
410 0 : AgentOperDBEntry(), table_(table), uuid_(id) {
411 0 : health_check_type_ = GetHealthCheckType();
412 0 : }
413 :
414 0 : HealthCheckService::~HealthCheckService() {
415 : // Call DeleteInstances() so that the instances can be gracefully removed
416 : // after all events queued are flushed.
417 : // TODO pdsouza: This code doesn't seem to be required since the instances
418 : // should already have been deleted via the same call on processing DB entry
419 : // delete on the Health Check Table ( i.e., the list should be empty so it
420 : // is benign.
421 0 : DeleteInstances();
422 0 : }
423 :
424 0 : bool HealthCheckService::IsLess(const DBEntry &rhs) const {
425 0 : const HealthCheckService &a =
426 : static_cast<const HealthCheckService &>(rhs);
427 0 : return (uuid_ < a.uuid_);
428 : }
429 :
430 0 : std::string HealthCheckService::ToString() const {
431 0 : return UuidToString(uuid_);
432 : }
433 :
434 0 : DBEntryBase::KeyPtr HealthCheckService::GetDBRequestKey() const {
435 0 : HealthCheckServiceKey *key = new HealthCheckServiceKey(uuid_);
436 0 : return DBEntryBase::KeyPtr(key);
437 : }
438 :
439 0 : void HealthCheckService::SetKey(const DBRequestKey *key) {
440 0 : const HealthCheckServiceKey *k =
441 : static_cast<const HealthCheckServiceKey *>(key);
442 0 : uuid_ = k->uuid_;
443 0 : }
444 :
445 0 : bool HealthCheckService::DBEntrySandesh(Sandesh *sresp,
446 : std::string &name) const {
447 0 : HealthCheckSandeshResp *resp = static_cast<HealthCheckSandeshResp *>(sresp);
448 :
449 0 : HealthCheckSandeshData data;
450 0 : data.set_uuid(UuidToString(uuid()));
451 0 : data.set_name(name_);
452 0 : data.set_service_type(service_type_);
453 0 : data.set_monitor_type(monitor_type_);
454 0 : data.set_http_method(http_method_);
455 0 : data.set_url_path(url_path_);
456 0 : data.set_expected_codes(expected_codes_);
457 0 : data.set_delay(delay_);
458 0 : data.set_delay_usecs(delay_usecs_);
459 0 : data.set_timeout(timeout_);
460 0 : data.set_timeout_usecs(timeout_usecs_);
461 0 : data.set_max_retries(max_retries_);
462 :
463 0 : std::vector<HealthCheckInstanceSandeshData> inst_list;
464 0 : InstanceList::const_iterator it = intf_list_.begin();
465 0 : while (it != intf_list_.end()) {
466 0 : HealthCheckInstanceSandeshData inst_data;
467 0 : inst_data.set_vm_interface(UuidToString(it->first));
468 : inst_data.set_metadata_ip
469 0 : (it->second->ip()->GetLinkLocalIp().to_string());
470 0 : inst_data.set_service_ip(it->second->source_ip().to_string());
471 : inst_data.set_health_check_ip
472 0 : (it->second->destination_ip().to_string());
473 0 : inst_data.set_active(it->second->active());
474 0 : inst_data.set_running(it->second->IsRunning());
475 0 : inst_data.set_last_update_time(it->second->last_update_time());
476 0 : inst_list.push_back(inst_data);
477 0 : it++;
478 0 : }
479 0 : data.set_inst_list(inst_list);
480 :
481 : std::vector<HealthCheckSandeshData> &list =
482 0 : const_cast<std::vector<HealthCheckSandeshData>&>(resp->get_hc_list());
483 0 : list.push_back(data);
484 0 : return true;
485 0 : }
486 :
487 0 : void HealthCheckService::PostAdd() {
488 0 : UpdateInstanceServiceReference();
489 0 : }
490 :
491 0 : bool HealthCheckService::IsSegmentHealthCheckService() const {
492 0 : return (service_type_.find("segment") != std::string::npos);
493 : }
494 :
495 0 : bool HealthCheckService::IsInstanceTaskBased() const {
496 0 : return ((monitor_type_.find("BFD") == std::string::npos) &&
497 0 : !IsSegmentHealthCheckService());
498 : }
499 :
500 0 : bool HealthCheckService::IsVnIpListHealthCheckService() const {
501 0 : return (service_type_.find("vn-ip-list") != std::string::npos);
502 : }
503 :
504 : HealthCheckInstanceBase *
505 0 : HealthCheckService::StartHealthCheckService(VmInterface *intrface,
506 : VmInterface *paired_vmi,
507 : const IpAddress &source_ip,
508 : const IpAddress &destination_ip,
509 : const MacAddress &destination_mac,
510 : bool ignore_status_event,
511 : bool multi_hop
512 : ) {
513 0 : HealthCheckInstanceBase *instance = NULL;
514 0 : if (IsInstanceTaskBased()) {
515 0 : instance = new HealthCheckInstanceTask(
516 0 : this, table_->agent()->metadata_ip_allocator(),
517 0 : intrface, ignore_status_event);
518 0 : } else if (IsVnIpListHealthCheckService()) {
519 0 : instance = new HealthCheckMacIpInstanceService(
520 0 : this, table_->agent()->metadata_ip_allocator(),
521 0 : intrface, paired_vmi, ignore_status_event, multi_hop);
522 0 : HealthCheckMacIpInstanceService *mac_ip_inst =
523 : static_cast<HealthCheckMacIpInstanceService *>(instance);
524 0 : mac_ip_inst->set_destination_mac(destination_mac);
525 : } else {
526 0 : instance = new HealthCheckInstanceService(
527 0 : this, table_->agent()->metadata_ip_allocator(),
528 0 : intrface, paired_vmi, ignore_status_event, multi_hop);
529 : }
530 :
531 0 : instance->set_source_ip(source_ip);
532 0 : instance->set_destination_ip(destination_ip);
533 0 : return instance;
534 : }
535 :
536 : void
537 0 : HealthCheckService::StopHealthCheckService(HealthCheckInstanceBase *instance) {
538 0 : if (!instance->DestroyInstanceTask()) {
539 : // Delete instance in Agent::HealthCheck task bacause there may be
540 : // events queued for this instance that need to be processed without
541 : // crashing while trying to access the instance.
542 : // Note that db::DBTable Task and Agent::HealthCheck task are mutually
543 : // exclusive, so there are no concurrency issues to be addressed.
544 0 : instance->StopTask(instance->service());
545 : }
546 0 : }
547 :
548 : HealthCheckService::HealthCheckType
549 0 : HealthCheckService::GetHealthCheckType() const {
550 0 : if (IsSegmentHealthCheckService())
551 0 : return HealthCheckService::SEGMENT;
552 0 : if (monitor_type_.find("BFD") != std::string::npos)
553 0 : return HealthCheckService::BFD;
554 0 : if (monitor_type_.find("HTTP") != std::string::npos)
555 0 : return HealthCheckService::HTTP;
556 0 : return HealthCheckService::PING;
557 : }
558 :
559 0 : bool HealthCheckService::Copy(HealthCheckTable *table,
560 : const HealthCheckServiceData *data) {
561 0 : bool ret = false;
562 0 : bool dest_ip_changed = false;
563 0 : bool service_type_changed = false;
564 0 : bool monitor_type_changed = false;
565 0 : bool is_prev_hc_segment = IsSegmentHealthCheckService();
566 :
567 : HealthCheckService::HealthCheckType old_health_check_type =
568 0 : GetHealthCheckType();
569 0 : if (monitor_type_ != data->monitor_type_) {
570 0 : monitor_type_ = data->monitor_type_;
571 0 : monitor_type_changed = true;
572 0 : ret = true;
573 : }
574 :
575 0 : if (service_type_ != data->service_type_) {
576 0 : service_type_ = data->service_type_;
577 0 : service_type_changed = true;
578 0 : ret = true;
579 : }
580 :
581 0 : if (http_method_ != data->http_method_) {
582 0 : http_method_ = data->http_method_;
583 0 : ret = true;
584 : }
585 :
586 0 : if (ip_proto_ != data->ip_proto_) {
587 0 : ip_proto_ = data->ip_proto_;
588 0 : ret = true;
589 : }
590 :
591 0 : if (url_path_ != data->url_path_) {
592 0 : url_path_ = data->url_path_;
593 0 : ret = true;
594 : }
595 :
596 0 : if (url_port_ != data->url_port_) {
597 0 : url_port_ = data->url_port_;
598 0 : ret = true;
599 : }
600 :
601 0 : if (expected_codes_ != data->expected_codes_) {
602 0 : expected_codes_ = data->expected_codes_;
603 0 : ret = true;
604 : }
605 :
606 0 : if (delay_ != data->delay_) {
607 0 : delay_ = data->delay_;
608 0 : ret = true;
609 : }
610 :
611 0 : if (delay_usecs_ != data->delay_usecs_) {
612 0 : delay_usecs_ = data->delay_usecs_;
613 0 : ret = true;
614 : }
615 :
616 0 : if (timeout_ != data->timeout_) {
617 0 : timeout_ = data->timeout_;
618 0 : ret = true;
619 : }
620 :
621 0 : if (timeout_usecs_ != data->timeout_usecs_) {
622 0 : timeout_usecs_ = data->timeout_usecs_;
623 0 : ret = true;
624 : }
625 :
626 0 : if (max_retries_ != data->max_retries_) {
627 0 : max_retries_ = data->max_retries_;
628 0 : ret = true;
629 : }
630 :
631 0 : if (target_ip_list_ != data->new_target_ip_list_) {
632 0 : target_ip_list_ = data->new_target_ip_list_;
633 0 : ret = true;
634 : }
635 :
636 0 : if (is_hc_enable_all_ip_ != data->is_all_ip_) {
637 0 : is_hc_enable_all_ip_ = data->is_all_ip_;
638 0 : ret = true;
639 : }
640 :
641 0 : if (vn_uuid_list_ != data->vn_uuid_list_) {
642 0 : vn_uuid_list_ = data->vn_uuid_list_;
643 0 : ret = true;
644 : }
645 :
646 0 : if (dest_ip_ != data->dest_ip_) {
647 0 : dest_ip_ = data->dest_ip_;
648 0 : dest_ip_changed = true;
649 0 : ret = true;
650 : }
651 :
652 0 : if (ret) {
653 : /* If service-type of health-check changes from segment to non-segment
654 : * or vice-versa, remove all the health-check instance objects.
655 : * Addition of new health-check instances with updated config happens
656 : * later in this function */
657 0 : if ((service_type_changed &&
658 0 : is_prev_hc_segment != IsSegmentHealthCheckService()) ||
659 0 : (monitor_type_changed &&
660 0 : (GetHealthCheckType() == HealthCheckService::BFD ||
661 : old_health_check_type == HealthCheckService::BFD))) {
662 0 : DeleteInstances();
663 : } else {
664 : // stop previously allocated health check instances
665 : // to force them restart with updated values.
666 0 : InstanceList::iterator it = intf_list_.begin();
667 0 : while (it != intf_list_.end()) {
668 0 : it->second->StopInstanceTask();
669 0 : it++;
670 : }
671 : }
672 : // update type after deleting the previous instance
673 0 : health_check_type_ = GetHealthCheckType();
674 : }
675 :
676 0 : if (name_ != data->name_) {
677 0 : name_ = data->name_;
678 0 : ret = true;
679 : }
680 :
681 : std::set<boost::uuids::uuid>::iterator it_cfg =
682 0 : data->intf_uuid_list_.begin();
683 0 : InstanceList::iterator it = intf_list_.begin();
684 0 : while (it_cfg != data->intf_uuid_list_.end() ||
685 0 : it != intf_list_.end()) {
686 0 : if (it_cfg == data->intf_uuid_list_.end() ||
687 0 : ((it != intf_list_.end()) && ((*it_cfg) > it->first))) {
688 0 : InstanceList::iterator it_prev = it;
689 0 : it++;
690 0 : StopHealthCheckService(it_prev->second);
691 0 : intf_list_.erase(it_prev);
692 0 : ret = true;
693 : } else {
694 0 : if ((it == intf_list_.end()) || ((*it_cfg) < it->first)) {
695 0 : VmInterfaceKey key(AgentKey::ADD_DEL_CHANGE, (*it_cfg), "");
696 0 : VmInterface *intf = static_cast<VmInterface *>
697 0 : (table_->agent()->interface_table()->Find(&key, false));
698 : // interface might be unavailable if config is received
699 : // before nova message for interface creation, in such case
700 : // skip adding instancee for this interface
701 : // config dependency manager will then ensure re-notification
702 : // of dependent config Health-Check-Service in this case to
703 : // handle creation of interface later
704 0 : if (intf != NULL) {
705 0 : IpAddress source_ip;
706 0 : IpAddress destination_ip = dest_ip_;
707 0 : VmInterface *paired_vmi = NULL;
708 0 : if (IsSegmentHealthCheckService()) {
709 0 : paired_vmi = intf->PortTuplePairedInterface();
710 0 : if (paired_vmi == NULL) {
711 0 : it_cfg++;
712 0 : continue;
713 : }
714 : destination_ip = paired_vmi->GetServiceIp
715 0 : (paired_vmi->primary_ip_addr());
716 0 : if (destination_ip.is_unspecified()) {
717 0 : it_cfg++;
718 0 : continue;
719 : }
720 : }
721 0 : if (health_check_type_ == HealthCheckService::BFD)
722 0 : source_ip = intf->GetGatewayIp(intf->primary_ip_addr());
723 : HealthCheckInstanceBase *inst =
724 : // Note that a new instance is alway used when starting
725 : // hence the same instance will not be re-used for a
726 : // different service. From this we can be sure that once
727 : // an instance is deleted it will not be re-used.
728 0 : StartHealthCheckService(intf, paired_vmi, source_ip,
729 0 : destination_ip, MacAddress(),
730 0 : false, false);
731 0 : intf_list_.insert(std::pair<boost::uuids::uuid,
732 0 : HealthCheckInstanceBase *>(*(it_cfg), inst));
733 0 : ret = true;
734 : }
735 0 : } else {
736 0 : if (dest_ip_changed || IsInstanceTaskBased()) {
737 : // change in destination IP needs to be propagated
738 : // explicitly to metadata-IP object
739 0 : it->second->set_destination_ip(dest_ip_);
740 : }
741 0 : it++;
742 : }
743 0 : it_cfg++;
744 : }
745 : }
746 :
747 0 : return ret;
748 : }
749 :
750 0 : void HealthCheckService::UpdateInstanceServiceReference() {
751 0 : InstanceList::iterator it = intf_list_.begin();
752 0 : while (it != intf_list_.end()) {
753 0 : it->second->set_service(this);
754 0 : it++;
755 : }
756 0 : }
757 :
758 0 : void HealthCheckInstanceBase::EnqueueHealthCheckResync(
759 : const HealthCheckService *service,
760 : const VmInterface *itf) const {
761 0 : DBRequest req;
762 0 : req.oper = DBRequest::DB_ENTRY_ADD_CHANGE;
763 0 : req.key.reset(new HealthCheckServiceKey(service->uuid(), AgentKey::RESYNC));
764 0 : req.data.reset(new HealthCheckResyncInterfaceData(NULL, NULL, itf));
765 0 : service->table()->agent()->health_check_table()->Enqueue(&req);
766 0 : }
767 :
768 0 : void HealthCheckService::ResyncHealthCheckInterface(
769 : const HealthCheckService *service,
770 : const VmInterface *intf) {
771 0 : InstanceList::iterator it;
772 :
773 0 : it = intf_list_.find(intf->vmi_cfg_uuid());
774 0 : if (it != intf_list_.end()) {
775 0 : HEALTH_CHECK_TRACE(Trace, "Enqueue instance " + intf->name());
776 0 : it->second->EnqueueHealthCheckResync(service, intf);
777 : } else {
778 0 : HEALTH_CHECK_TRACE(Trace, "Enqueue instance not found " + intf->name());
779 : }
780 0 : }
781 :
782 0 : void HealthCheckService::UpdateInterfaceInstanceServiceReference(
783 : const VmInterface *intf) {
784 0 : InstanceList::iterator it;
785 :
786 0 : it = intf_list_.find(intf->vmi_cfg_uuid());
787 0 : if (it != intf_list_.end()) {
788 0 : it->second->set_service(this);
789 : } else {
790 0 : HEALTH_CHECK_TRACE(Trace, "Service not found :" + intf->name());
791 : }
792 0 : }
793 :
794 0 : void HealthCheckService::DeleteInstances() {
795 0 : InstanceList::iterator it = intf_list_.begin();
796 0 : while (it != intf_list_.end()) {
797 0 : StopHealthCheckService(it->second);
798 0 : intf_list_.erase(it);
799 0 : it = intf_list_.begin();
800 : }
801 0 : }
802 :
803 : ////////////////////////////////////////////////////////////////////////////////
804 :
805 2 : HealthCheckTable::HealthCheckTable(Agent *agent, DB *db,
806 2 : const std::string &name) :
807 10 : AgentOperDBTable(db, name) {
808 2 : set_agent(agent);
809 4 : inst_event_queue_ = new WorkQueue<HealthCheckInstanceEvent *>(
810 4 : agent->task_scheduler()->GetTaskId(kTaskHealthCheck), 0,
811 2 : boost::bind(&HealthCheckTable::InstanceEventProcess, this, _1));
812 2 : inst_event_queue_->set_name("HealthCheck instance event queue");
813 2 : }
814 :
815 12 : HealthCheckTable::~HealthCheckTable() {
816 2 : inst_event_queue_->Shutdown();
817 2 : delete inst_event_queue_;
818 16 : }
819 :
820 2 : DBTableBase *HealthCheckTable::CreateTable(Agent *agent, DB *db,
821 : const std::string &name) {
822 : HealthCheckTable *health_check_table =
823 2 : new HealthCheckTable(agent, db, name);
824 2 : (static_cast<DBTable *>(health_check_table))->Init();
825 2 : return health_check_table;
826 : };
827 :
828 : std::unique_ptr<DBEntry>
829 0 : HealthCheckTable::AllocEntry(const DBRequestKey *k) const {
830 0 : const HealthCheckServiceKey *key =
831 : static_cast<const HealthCheckServiceKey *>(k);
832 0 : HealthCheckService *service = new HealthCheckService(this, key->uuid_);
833 0 : return std::unique_ptr<DBEntry>(static_cast<DBEntry *>(service));
834 : }
835 :
836 0 : DBEntry *HealthCheckTable::OperDBAdd(const DBRequest *req) {
837 : HealthCheckServiceKey *key =
838 0 : static_cast<HealthCheckServiceKey *>(req->key.get());
839 : HealthCheckServiceData *data =
840 0 : static_cast<HealthCheckServiceData *>(req->data.get());
841 0 : HealthCheckService *service = new HealthCheckService(this, key->uuid_);
842 0 : service->Copy(this, data);
843 0 : return service;
844 : }
845 :
846 0 : bool HealthCheckTable::OperDBOnChange(DBEntry *entry, const DBRequest *req) {
847 : /*
848 : * Ideally db-infra should have removed the delete mark for the db-entry
849 : * when Add/Update happens for the db-entry. It has to be root-caused.
850 : * For now it is handled here and doing here wont give any side-effects.
851 : */
852 0 : bool ret = false;
853 0 : if (entry->IsDeleted()) {
854 0 : entry->ClearDelete();
855 0 : ret = true;
856 : }
857 0 : HealthCheckService *service = static_cast<HealthCheckService *>(entry);
858 : HealthCheckServiceData *data =
859 0 : dynamic_cast<HealthCheckServiceData *>(req->data.get());
860 0 : assert(data);
861 0 : ret |= service->Copy(this, data);
862 0 : service->UpdateInstanceServiceReference();
863 0 : return ret;
864 : }
865 :
866 0 : bool HealthCheckTable::OperDBResync(DBEntry *entry, const DBRequest *req) {
867 : HealthCheckResyncInterfaceData *resync_data =
868 0 : dynamic_cast<HealthCheckResyncInterfaceData *>(req->data.get());
869 0 : if (resync_data) {
870 : // resync triggered from Vmi for which there is no source ip specified
871 : // in the interface health check service instance.
872 0 : HEALTH_CHECK_TRACE(Trace, "Resync interface " + resync_data->intf_->name());
873 0 : HealthCheckService *service = static_cast<HealthCheckService *>(entry);
874 0 : service->UpdateInterfaceInstanceServiceReference(resync_data->intf_);
875 0 : return false;
876 : }
877 :
878 0 : return OperDBOnChange(entry, req);
879 : }
880 :
881 0 : bool HealthCheckTable::OperDBDelete(DBEntry *entry, const DBRequest *req) {
882 0 : HealthCheckService *service = static_cast<HealthCheckService *>(entry);
883 0 : service->DeleteInstances();
884 0 : return true;
885 : }
886 :
887 :
888 0 : static HealthCheckServiceKey *BuildKey(const boost::uuids::uuid &u) {
889 0 : return new HealthCheckServiceKey(u);
890 : }
891 :
892 0 : static HealthCheckServiceData *BuildData(Agent *agent, IFMapNode *node,
893 : const autogen::ServiceHealthCheck *s) {
894 0 : boost::system::error_code ec;
895 0 : const autogen::ServiceHealthCheckType &p = s->properties();
896 0 : Ip4Address dest_ip;
897 0 : std::string url_path;
898 0 : uint8_t ip_proto = 0;
899 0 : uint16_t url_port = 0;
900 0 : if (p.monitor_type.find("BFD") != std::string::npos) {
901 0 : boost::system::error_code ec;
902 0 : dest_ip = Ip4Address::from_string(p.url_path, ec);
903 0 : url_path = p.url_path;
904 0 : ip_proto = IPPROTO_UDP;
905 0 : } else if (p.monitor_type.find("HTTP") == std::string::npos) {
906 0 : boost::system::error_code ec;
907 0 : dest_ip = Ip4Address::from_string(p.url_path, ec);
908 0 : url_path = p.url_path;
909 0 : ip_proto = IPPROTO_ICMP;
910 0 : } else if (!p.url_path.empty()) {
911 0 : ip_proto = IPPROTO_TCP;
912 : // parse url if available
913 : struct http_parser_url urldata;
914 0 : int ret = http_parser_parse_url(p.url_path.c_str(), p.url_path.size(),
915 : false, &urldata);
916 0 : if (ret == 0) {
917 0 : if (urldata.field_set & (1 << UF_HOST)) {
918 : std::string dest_ip_str =
919 0 : p.url_path.substr(urldata.field_data[UF_HOST].off,
920 0 : urldata.field_data[UF_HOST].len);
921 : // Parse dest-ip from the url to translate to metadata IP
922 0 : dest_ip = Ip4Address::from_string(dest_ip_str, ec);
923 : // keep rest of the url string as is
924 0 : url_path = p.url_path.substr(urldata.field_data[UF_HOST].off +\
925 0 : urldata.field_data[UF_HOST].len);
926 0 : }
927 0 : url_port = urldata.port;
928 0 : if ((urldata.field_set & (1 << UF_PORT)) == 0) {
929 0 : url_port = 80;
930 : }
931 : }
932 : }
933 :
934 0 : bool is_all_ip = false;
935 0 : if (p.target_ip_all) {
936 0 : is_all_ip = true;
937 : }
938 0 : std::set<IpAddress> ip_address_list;
939 0 : for (unsigned int i = 0; i < p.target_ip_list.ip_address.size(); ++i) {
940 0 : boost::system::error_code ec;
941 0 : IpAddress ip = Ip4Address::from_string(p.target_ip_list.ip_address[i], ec);
942 0 : if (ec.value() != 0) {
943 0 : ip = Ip6Address::from_string(p.target_ip_list.ip_address[i], ec);
944 : }
945 0 : if (ec.value() != 0) {
946 0 : continue;
947 : }
948 :
949 0 : ip_address_list.insert(ip);
950 : }
951 :
952 : HealthCheckServiceData *data =
953 0 : new HealthCheckServiceData(agent, dest_ip, node->name(),
954 0 : p.monitor_type, p.health_check_type,
955 0 : ip_proto, p.http_method,
956 0 : url_path, url_port, p.expected_codes,
957 0 : p.delay, p.delayUsecs, p.timeout,
958 0 : p.timeoutUsecs, p.max_retries,
959 0 : is_all_ip, ip_address_list, node);
960 :
961 0 : IFMapAgentTable *table = static_cast<IFMapAgentTable *>(node->table());
962 0 : for (DBGraphVertex::adjacency_iterator iter =
963 0 : node->begin(table->GetGraph());
964 0 : iter != node->end(table->GetGraph()); ++iter) {
965 0 : IFMapNode *adj_node = static_cast<IFMapNode *>(iter.operator->());
966 0 : if (agent->config_manager()->SkipNode(adj_node)) {
967 0 : continue;
968 : }
969 :
970 0 : if (adj_node->table() == agent->cfg()->cfg_vm_interface_table()) {
971 : boost::uuids::uuid intf_uuid;
972 : autogen::VirtualMachineInterface *intf =
973 0 : dynamic_cast<autogen::VirtualMachineInterface *>(adj_node->GetObject());
974 0 : assert(intf);
975 0 : const autogen::IdPermsType &id_perms = intf->id_perms();
976 0 : CfgUuidSet(id_perms.uuid.uuid_mslong,
977 0 : id_perms.uuid.uuid_lslong, intf_uuid);
978 :
979 0 : data->intf_uuid_list_.insert(intf_uuid);
980 : }
981 0 : if (adj_node->table() == agent->cfg()->cfg_vn_table()) {
982 : boost::uuids::uuid vn_uuid;
983 : autogen::VirtualNetwork *vn =
984 0 : dynamic_cast<autogen::VirtualNetwork *>(adj_node->GetObject());
985 0 : assert(vn);
986 0 : const autogen::IdPermsType &id_perms = vn->id_perms();
987 0 : CfgUuidSet(id_perms.uuid.uuid_mslong,
988 0 : id_perms.uuid.uuid_lslong, vn_uuid);
989 :
990 0 : data->vn_uuid_list_.insert(vn_uuid);
991 : }
992 : }
993 0 : return data;
994 0 : }
995 :
996 0 : bool HealthCheckTable::IFNodeToReq(IFMapNode *node, DBRequest &req,
997 : const boost::uuids::uuid &u) {
998 : autogen::ServiceHealthCheck *service =
999 0 : static_cast<autogen::ServiceHealthCheck *>(node->GetObject());
1000 0 : assert(service);
1001 :
1002 0 : assert(!u.is_nil());
1003 :
1004 0 : req.key.reset(BuildKey(u));
1005 0 : if ((req.oper == DBRequest::DB_ENTRY_DELETE) || node->IsDeleted()) {
1006 0 : req.oper = DBRequest::DB_ENTRY_DELETE;
1007 0 : return true;
1008 : }
1009 :
1010 0 : agent()->config_manager()->AddHealthCheckServiceNode(node);
1011 0 : return false;
1012 : }
1013 :
1014 0 : bool HealthCheckTable::ProcessConfig(IFMapNode *node, DBRequest &req,
1015 : const boost::uuids::uuid &u) {
1016 : autogen::ServiceHealthCheck *service =
1017 0 : static_cast <autogen::ServiceHealthCheck *>(node->GetObject());
1018 0 : assert(service);
1019 :
1020 0 : req.key.reset(BuildKey(u));
1021 0 : if (node->IsDeleted()) {
1022 0 : req.oper = DBRequest::DB_ENTRY_DELETE;
1023 0 : return true;
1024 : }
1025 :
1026 0 : req.oper = DBRequest::DB_ENTRY_ADD_CHANGE;
1027 0 : req.data.reset(BuildData(agent(), node, service));
1028 0 : Enqueue(&req);
1029 :
1030 0 : return false;
1031 : }
1032 :
1033 0 : bool HealthCheckTable::IFNodeToUuid(IFMapNode *node, boost::uuids::uuid &u) {
1034 : autogen::ServiceHealthCheck *service =
1035 0 : static_cast<autogen::ServiceHealthCheck *>(node->GetObject());
1036 0 : autogen::IdPermsType id_perms = service->id_perms();
1037 0 : CfgUuidSet(id_perms.uuid.uuid_mslong, id_perms.uuid.uuid_lslong, u);
1038 0 : return true;
1039 0 : }
1040 :
1041 :
1042 0 : HealthCheckService *HealthCheckTable::Find(const boost::uuids::uuid &u) {
1043 0 : HealthCheckServiceKey key(u);
1044 0 : return static_cast<HealthCheckService *>(FindActiveEntry(&key));
1045 0 : }
1046 :
1047 : void
1048 0 : HealthCheckTable::InstanceEventEnqueue(HealthCheckInstanceEvent *event) const {
1049 0 : inst_event_queue_->Enqueue(event);
1050 0 : }
1051 :
1052 0 : bool HealthCheckTable::InstanceEventProcess(HealthCheckInstanceEvent *event) {
1053 0 : HealthCheckInstanceBase *inst = event->instance_;
1054 0 : switch (event->type_) {
1055 0 : case HealthCheckInstanceEvent::MESSAGE_READ:
1056 : {
1057 : // We dont want to process status messages as the instance delete
1058 : // has ben queued.
1059 0 : if (inst->deleted_) {
1060 0 : HEALTH_CHECK_TRACE(Trace,
1061 : "Read Event while deleted! " + inst->to_string());
1062 0 : break;
1063 : }
1064 0 : if (inst->IsStatusEventIgnored())
1065 0 : break;
1066 0 : inst->last_update_time_ = UTCUsecToString(UTCTimestampUsec());
1067 0 : std::string msg = event->message_;
1068 0 : boost::algorithm::to_lower(msg);
1069 0 : if (msg.find("success") != std::string::npos) {
1070 0 : if (!inst->active_) {
1071 0 : inst->active_ = true;
1072 0 : inst->ResyncTarget(inst->service_.get());
1073 : }
1074 : }
1075 0 : if (msg.find("failure") != std::string::npos) {
1076 0 : if (inst->active_) {
1077 0 : inst->active_ = false;
1078 0 : inst->ResyncTarget(inst->service_.get());
1079 : }
1080 : }
1081 0 : HEALTH_CHECK_TRACE(Trace, inst->to_string() +
1082 : " Received msg = " + event->message_);
1083 0 : }
1084 0 : break;
1085 :
1086 0 : case HealthCheckInstanceEvent::TASK_EXIT:
1087 0 : if (inst->IsStatusEventIgnored())
1088 0 : break;
1089 0 : if (!inst->deleted_) {
1090 0 : HEALTH_CHECK_TRACE(Trace, "Restarting " + inst->to_string());
1091 0 : inst->RunInstanceTask();
1092 : } else {
1093 0 : HEALTH_CHECK_TRACE(Trace, "Stopped " + inst->to_string());
1094 0 : delete inst;
1095 : }
1096 0 : break;
1097 :
1098 0 : case HealthCheckInstanceEvent::SET_SERVICE:
1099 0 : inst->set_service(event->service_);
1100 0 : break;
1101 :
1102 0 : case HealthCheckInstanceEvent::STOP_TASK:
1103 0 : inst->DestroyInstanceTask();
1104 : // Freeing instance is handled here and not in any other task context.
1105 : // Unconditionally delete here since DestroyInstanceTask() may have
1106 : // alrady been called in the db::DBTable task context before queueing
1107 : // this event.
1108 0 : delete inst;
1109 0 : break;
1110 :
1111 0 : default:
1112 : // unhandled event
1113 0 : assert(0);
1114 : }
1115 :
1116 0 : delete event;
1117 0 : return true;
1118 : }
1119 :
1120 : AgentSandeshPtr
1121 0 : HealthCheckTable::GetAgentSandesh(const AgentSandeshArguments *args,
1122 : const std::string &context) {
1123 : return AgentSandeshPtr(new AgentHealthCheckSandesh(context,
1124 0 : args->GetString("uuid")));
1125 : }
1126 :
1127 0 : void HealthCheckSandeshReq::HandleRequest() const {
1128 0 : AgentSandeshPtr sand(new AgentHealthCheckSandesh(context(), get_uuid()));
1129 0 : sand->DoSandesh(sand);
1130 0 : }
1131 :
1132 : ////////////////////////////////////////////////////////////////////////////////
|