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