Kea 2.5.8
ha_impl.cc
Go to the documentation of this file.
1// Copyright (C) 2018-2024 Internet Systems Consortium, Inc. ("ISC")
2//
3// This Source Code Form is subject to the terms of the Mozilla Public
4// License, v. 2.0. If a copy of the MPL was not distributed with this
5// file, You can obtain one at http://mozilla.org/MPL/2.0/.
6
7#include <config.h>
8
9#include <ha_config_parser.h>
10#include <ha_impl.h>
11#include <ha_log.h>
12#include <asiolink/io_service.h>
13#include <cc/data.h>
15#include <dhcp/pkt4.h>
16#include <dhcp/pkt6.h>
17#include <dhcpsrv/lease.h>
19#include <dhcpsrv/subnet.h>
20#include <stats/stats_mgr.h>
21
22using namespace isc::asiolink;
23using namespace isc::config;
24using namespace isc::data;
25using namespace isc::dhcp;
26using namespace isc::hooks;
27using namespace isc::log;
28
29namespace isc {
30namespace ha {
31
33 : config_(), services_(new HAServiceMapper()) {
34}
35
36void
37HAImpl::configure(const ConstElementPtr& input_config) {
38 config_ = HAConfigParser::parse(input_config);
39}
40
41void
43 const NetworkStatePtr& network_state,
44 const HAServerType& server_type) {
45 auto configs = config_->getAll();
46 for (auto id = 0; id < configs.size(); ++id) {
47 // Create the HA service and crank up the state machine.
48 auto service = boost::make_shared<HAService>(id, io_service, network_state,
49 configs[id], server_type);
50 for (auto const& peer_config : configs[id]->getAllServersConfig()) {
51 services_->map(peer_config.first, service);
52 }
53 }
54 // Schedule a start of the services. This ensures we begin after
55 // the dust has settled and Kea MT mode has been firmly established.
56 io_service->post([&]() {
57 for (auto const& service : services_->getAll()) {
58 service->startClientAndListener();
59 }
60 });
61}
62
64 for (auto const& service : services_->getAll()) {
65 // Shut down the services explicitly, we need finer control
66 // than relying on destruction order.
67 service->stopClientAndListener();
68 }
69}
70
71void
73 // If there are multiple relationships, the HA-specific processing is
74 // in the subnet4_select hook point.
75 if (services_->hasMultiple()) {
76 return;
77 }
78
79 Pkt4Ptr query4;
80 callout_handle.getArgument("query4", query4);
81
84 try {
85 // We have to unpack the query to get access into HW address which is
86 // used to load balance the packet.
87 if (callout_handle.getStatus() != CalloutHandle::NEXT_STEP_SKIP) {
88 query4->unpack();
89 }
90
91 } catch (const SkipRemainingOptionsError& ex) {
92 // An option failed to unpack but we are to attempt to process it
93 // anyway. Log it and let's hope for the best.
96 .arg(ex.what());
97
98 } catch (const std::exception& ex) {
99 // Packet parsing failed. Drop the packet.
101 .arg(query4->getRemoteAddr().toText())
102 .arg(query4->getLocalAddr().toText())
103 .arg(query4->getIface())
104 .arg(ex.what());
105
106 // Increase the statistics of parse failures and dropped packets.
107 isc::stats::StatsMgr::instance().addValue("pkt4-parse-failed",
108 static_cast<int64_t>(1));
109 isc::stats::StatsMgr::instance().addValue("pkt4-receive-drop",
110 static_cast<int64_t>(1));
111
112
114 return;
115 }
116
117 // Check if we should process this query. If not, drop it.
118 if (!services_->get()->inScope(query4)) {
120 .arg(query4->getLabel());
122
123 } else {
124 // We have successfully parsed the query so we have to signal
125 // to the server that it must not parse it.
127 }
128}
129
130void
132 // This callout only applies in the case of multiple relationships.
133 // When there is only one relationship it has no effect because
134 // the decision if we should process the packet has been made
135 // in the buffer4_receive callout.
136 if (!services_->hasMultiple()) {
137 // Return silently. It is not an error.
138 return;
139 }
140
141 Pkt4Ptr query4;
142 callout_handle.getArgument("query4", query4);
143
144 Subnet4Ptr subnet4;
145 callout_handle.getArgument("subnet4", subnet4);
146
147 // If the server failed to select the subnet this pointer is null.
148 // There is nothing we can do with this packet because we don't know
149 // which relationship it belongs to. We're even unable to check if the
150 // server is responsible for this packet.
151 if (!subnet4) {
152 // Log at debug level because that's the level at which the server
153 // logs the subnet selection failure.
155 .arg(query4->getLabel());
157 return;
158 }
159
160 // The subnet configuration should contain a user context
161 // and this context should contain a mapping of the subnet to a
162 // relationship. If the context doesn't exist there is no way
163 // to determine which relationship the packet belongs to.
164 std::string server_name;
165 try {
166 server_name = HAConfig::getSubnetServerName(subnet4);
167 if (server_name.empty()) {
169 .arg(query4->getLabel())
170 .arg(subnet4->toText());
172 return;
173 }
174
175 } catch (...) {
177 .arg(query4->getLabel())
178 .arg(subnet4->toText());
180 return;
181 }
182
183 // Try to find a relationship matching this server name.
184 auto service = services_->get(server_name);
185 if (!service) {
187 .arg(query4->getLabel())
188 .arg(server_name);
190 return;
191 }
192
193 // We have found appropriate relationship. Let's see if we should
194 // process the packet. We'll drop the packet if our partner is
195 // operational and is responsible for this packet.
196 if (!service->inScope(query4)) {
198 .arg(query4->getLabel())
199 .arg(server_name);
201 return;
202 }
203
204 // Remember the server name we retrieved from the subnet. We will
205 // need it in a leases4_committed callout that doesn't have access
206 // to the subnet object.
207 callout_handle.setContext("ha-server-name", server_name);
208}
209
210void
212 Pkt4Ptr query4;
213 Lease4CollectionPtr leases4;
214 Lease4CollectionPtr deleted_leases4;
215
216 // Get all arguments available for the leases4_committed hook point.
217 // If any of these arguments is not available this is a programmatic
218 // error. An exception will be thrown which will be caught by the
219 // caller and logged.
220 callout_handle.getArgument("query4", query4);
221
222 callout_handle.getArgument("leases4", leases4);
223 callout_handle.getArgument("deleted_leases4", deleted_leases4);
224
225 // In some cases we may have no leases, e.g. DHCPNAK.
226 if (leases4->empty() && deleted_leases4->empty()) {
228 .arg(query4->getLabel());
229 return;
230 }
231
232 // Get default config and service instances.
233 HAConfigPtr config = config_->get();
234 HAServicePtr service = services_->get();
235
236 // If we have multiple relationships we need to find the one that
237 // matches our subnet.
238 if (services_->hasMultiple()) {
239 try {
240 // Retrieve the server name from the context and the respective
241 // config and service instances.
242 std::string server_name;
243 callout_handle.getContext("ha-server-name", server_name);
244 config = config_->get(server_name);
245 service = services_->get(server_name);
246
247 // This is rather impossible but let's be safe.
248 if (!config || !service) {
249 isc_throw(Unexpected, "relationship not configured for server '" << server_name << "'");
250 }
251
252 } catch (const std::exception& ex) {
254 .arg(query4->getLabel())
255 .arg(ex.what());
257 return;
258 }
259 }
260
261 // If the hook library is configured to not send lease updates to the
262 // partner, there is nothing to do because this whole callout is
263 // currently about sending lease updates.
264 if (!config->amSendingLeaseUpdates()) {
265 // No need to log it, because it was already logged when configuration
266 // was applied.
267 return;
268 }
269
270 // Get the parking lot for this hook point. We're going to remember this
271 // pointer until we unpark the packet.
272 ParkingLotHandlePtr parking_lot = callout_handle.getParkingLotHandlePtr();
273
274 // Create a reference to the parked packet. This signals that we have a
275 // stake in unparking it.
276 parking_lot->reference(query4);
277
278 // Asynchronously send lease updates. In some cases no updates will be sent,
279 // e.g. when this server is in the partner-down state and there are no backup
280 // servers. In those cases we simply return without parking the DHCP query.
281 // The response will be sent to the client immediately.
282 try {
283 if (service->asyncSendLeaseUpdates(query4, leases4, deleted_leases4, parking_lot) == 0) {
284 // Dereference the parked packet. This releases our stake in it.
285 parking_lot->dereference(query4);
286 return;
287 }
288 } catch (...) {
289 // Make sure we dereference.
290 parking_lot->dereference(query4);
291 throw;
292 }
293
294 // The callout returns this status code to indicate to the server that it
295 // should leave the packet parked. It will be parked until each hook
296 // library with a reference, unparks the packet.
298}
299
300void
302 // Always return CONTINUE.
304 size_t peers_to_update = 0;
305
306 // If the hook library is configured to not send lease updates to the
307 // partner, there is nothing to do because this whole callout is
308 // currently about sending lease updates.
309 if (!config_->get()->amSendingLeaseUpdates()) {
310 // No need to log it, because it was already logged when configuration
311 // was applied.
312 callout_handle.setArgument("peers_to_update", peers_to_update);
313 return;
314 }
315
316 // Get all arguments available for the lease4_server_decline hook point.
317 // If any of these arguments is not available this is a programmatic
318 // error. An exception will be thrown which will be caught by the
319 // caller and logged.
320 Pkt4Ptr query4;
321 callout_handle.getArgument("query4", query4);
322
323 Lease4Ptr lease4;
324 callout_handle.getArgument("lease4", lease4);
325
326 // Asynchronously send the lease update. In some cases no updates will be sent,
327 // e.g. when this server is in the partner-down state and there are no backup
328 // servers.
329 peers_to_update = services_->get()->asyncSendSingleLeaseUpdate(query4, lease4, 0);
330 callout_handle.setArgument("peers_to_update", peers_to_update);
331}
332
333void
335 // If there are multiple relationships, the HA-specific processing is
336 // in the subnet6_select hook point.
337 if (services_->hasMultiple()) {
338 return;
339 }
340
341 Pkt6Ptr query6;
342 callout_handle.getArgument("query6", query6);
343
346 try {
347 // We have to unpack the query to get access into DUID which is
348 // used to load balance the packet.
349 if (callout_handle.getStatus() != CalloutHandle::NEXT_STEP_SKIP) {
350 query6->unpack();
351 }
352
353 } catch (const SkipRemainingOptionsError& ex) {
354 // An option failed to unpack but we are to attempt to process it
355 // anyway. Log it and let's hope for the best.
358 .arg(ex.what());
359
360 } catch (const std::exception& ex) {
361 // Packet parsing failed. Drop the packet.
363 .arg(query6->getRemoteAddr().toText())
364 .arg(query6->getLocalAddr().toText())
365 .arg(query6->getIface())
366 .arg(ex.what());
367
368 // Increase the statistics of parse failures and dropped packets.
369 isc::stats::StatsMgr::instance().addValue("pkt6-parse-failed",
370 static_cast<int64_t>(1));
371 isc::stats::StatsMgr::instance().addValue("pkt6-receive-drop",
372 static_cast<int64_t>(1));
373
374
376 return;
377 }
378
379 // Check if we should process this query. If not, drop it.
380 if (!services_->get()->inScope(query6)) {
382 .arg(query6->getLabel());
384
385 } else {
386 // We have successfully parsed the query so we have to signal
387 // to the server that it must not parse it.
389 }
390}
391
392void
394 // This callout only applies in the case of multiple relationships.
395 // When there is only one relationship it has no effect because
396 // the decision if we should process the packet has been made
397 // in the buffer6_receive callout.
398 if (!services_->hasMultiple()) {
399 // Return silently. It is not an error.
400 return;
401 }
402
403 Pkt6Ptr query6;
404 callout_handle.getArgument("query6", query6);
405
406 Subnet6Ptr subnet6;
407 callout_handle.getArgument("subnet6", subnet6);
408
409 // If the server failed to select the subnet this pointer is null.
410 // There is nothing we can do with this packet because we don't know
411 // which relationship it belongs to. We're even unable to check if the
412 // server is responsible for this packet.
413 if (!subnet6) {
414 // Log at debug level because that's the level at which the server
415 // logs the subnet selection failure.
417 .arg(query6->getLabel());
419 return;
420 }
421
422 // The subnet configuration should contain a user context
423 // and this context should contain a mapping of the subnet to a
424 // relationship. If the context doesn't exist there is no way
425 // to determine which relationship the packet belongs to.
426 std::string server_name;
427 try {
428 server_name = HAConfig::getSubnetServerName(subnet6);
429 if (server_name.empty()) {
431 .arg(query6->getLabel())
432 .arg(subnet6->toText());
434 return;
435 }
436
437 } catch (...) {
439 .arg(query6->getLabel())
440 .arg(subnet6->toText());
442 return;
443 }
444
445 // Try to find a relationship matching this server name.
446 auto service = services_->get(server_name);
447 if (!service) {
449 .arg(query6->getLabel())
450 .arg(server_name);
452 return;
453 }
454
455 // We have found appropriate relationship. Let's see if we should
456 // process the packet. We'll drop the packet if our partner is
457 // operational and is responsible for this packet.
458 if (!service->inScope(query6)) {
460 .arg(query6->getLabel())
461 .arg(server_name);
463 return;
464 }
465
466 // Remember the server name we retrieved from the subnet. We will
467 // need it in a leases4_committed callout that doesn't have access
468 // to the subnet object.
469 callout_handle.setContext("ha-server-name", server_name);
470}
471
472void
474 Pkt6Ptr query6;
475 Lease6CollectionPtr leases6;
476 Lease6CollectionPtr deleted_leases6;
477
478 // Get all arguments available for the leases6_committed hook point.
479 // If any of these arguments is not available this is a programmatic
480 // error. An exception will be thrown which will be caught by the
481 // caller and logged.
482 callout_handle.getArgument("query6", query6);
483
484 callout_handle.getArgument("leases6", leases6);
485 callout_handle.getArgument("deleted_leases6", deleted_leases6);
486
487 // In some cases we may have no leases.
488 if (leases6->empty() && deleted_leases6->empty()) {
490 .arg(query6->getLabel());
491 return;
492 }
493
494 HAConfigPtr config = config_->get();
495 HAServicePtr service = services_->get();
496 if (services_->hasMultiple()) {
497 try {
498 std::string server_name;
499 callout_handle.getContext("ha-server-name", server_name);
500 config = config_->get(server_name);
501 service = services_->get(server_name);
502
503 if (!config || !service) {
504 isc_throw(Unexpected, "relationship not found for the ha-server-name='" << server_name << "'");
505 }
506
507 } catch (const std::exception& ex) {
509 .arg(query6->getLabel())
510 .arg(ex.what());
512 return;
513 }
514 }
515
516 // If the hook library is configured to not send lease updates to the
517 // partner, there is nothing to do because this whole callout is
518 // currently about sending lease updates.
519 if (!config->amSendingLeaseUpdates()) {
520 // No need to log it, because it was already logged when configuration
521 // was applied.
522 return;
523 }
524
525 // Get the parking lot for this hook point. We're going to remember this
526 // pointer until we unpark the packet.
527 ParkingLotHandlePtr parking_lot = callout_handle.getParkingLotHandlePtr();
528
529 // Create a reference to the parked packet. This signals that we have a
530 // stake in unparking it.
531 parking_lot->reference(query6);
532
533 // Asynchronously send lease updates. In some cases no updates will be sent,
534 // e.g. when this server is in the partner-down state and there are no backup
535 // servers. In those cases we simply return without parking the DHCP query.
536 // The response will be sent to the client immediately.
537 try {
538 if (service->asyncSendLeaseUpdates(query6, leases6, deleted_leases6, parking_lot) == 0) {
539 // Dereference the parked packet. This releases our stake in it.
540 parking_lot->dereference(query6);
541 return;
542 }
543 } catch (...) {
544 // Make sure we dereference.
545 parking_lot->dereference(query6);
546 throw;
547 }
548
549 // The callout returns this status code to indicate to the server that it
550 // should leave the packet parked. It will be unparked until each hook
551 // library with a reference, unparks the packet.
553}
554
555void
557 std::string command_name;
558 callout_handle.getArgument("name", command_name);
559 if (command_name == "status-get") {
560 // Get the response.
561 ConstElementPtr response;
562 callout_handle.getArgument("response", response);
563 if (!response || (response->getType() != Element::map)) {
564 return;
565 }
566 // Get the arguments item from the response.
567 ConstElementPtr resp_args = response->get("arguments");
568 if (!resp_args || (resp_args->getType() != Element::map)) {
569 return;
570 }
571 // Add the ha servers info to arguments.
572 ElementPtr mutable_resp_args =
573 boost::const_pointer_cast<Element>(resp_args);
574
575 // Process the status get command for each HA service.
576 auto ha_relationships = Element::createList();
577 for (auto const& service : services_->getAll()) {
578 auto ha_relationship = Element::createMap();
579 ConstElementPtr ha_servers = service->processStatusGet();
580 ha_relationship->set("ha-servers", ha_servers);
581 ha_relationship->set("ha-mode", Element::create(HAConfig::HAModeToString(config_->get()->getHAMode())));
582 ha_relationships->add(ha_relationship);
583 mutable_resp_args->set("high-availability", ha_relationships);
584 }
585 }
586}
587
588void
590 // Command must always be provided.
591 ConstElementPtr command;
592 callout_handle.getArgument("command", command);
593
594 // Retrieve arguments.
595 ConstElementPtr args;
596 static_cast<void>(parseCommand(args, command));
597
598 HAServicePtr service;
599 try {
600 service = getHAServiceByServerName("ha-heartbeat", args);
601
602 } catch (const std::exception& ex) {
603 // There was an error while parsing command arguments. Return an error status
604 // code to notify the user.
606 callout_handle.setArgument("response", response);
607 return;
608 }
609
610 // Command parsing was successful, so let's process the command.
611 ConstElementPtr response = service->processHeartbeat();
612 callout_handle.setArgument("response", response);
613}
614
615void
617 // Command must always be provided.
618 ConstElementPtr command;
619 callout_handle.getArgument("command", command);
620
621 // Retrieve arguments.
622 ConstElementPtr args;
623 static_cast<void>(parseCommand(args, command));
624
625 ConstElementPtr server_name;
626 unsigned int max_period_value = 0;
627
628 HAServicePtr service;
629 try {
630 // Arguments are required for the ha-sync command.
631 if (!args) {
632 isc_throw(BadValue, "arguments not found in the 'ha-sync' command");
633 }
634
635 // Arguments must be a map.
636 if (args->getType() != Element::map) {
637 isc_throw(BadValue, "arguments in the 'ha-sync' command are not a map");
638 }
639
640 // server-name is mandatory. Otherwise how can we know the server to
641 // communicate with.
642 server_name = args->get("server-name");
643 if (!server_name) {
644 isc_throw(BadValue, "'server-name' is mandatory for the 'ha-sync' command");
645 }
646
647 // server-name must obviously be a string.
648 if (server_name->getType() != Element::string) {
649 isc_throw(BadValue, "'server-name' must be a string in the 'ha-sync' command");
650 }
651
652 // max-period is optional. In fact it is optional for dhcp-disable command too.
653 ConstElementPtr max_period = args->get("max-period");
654 if (max_period) {
655 // If it is specified, it must be a positive integer.
656 if ((max_period->getType() != Element::integer) ||
657 (max_period->intValue() <= 0)) {
658 isc_throw(BadValue, "'max-period' must be a positive integer in the 'ha-sync' command");
659 }
660
661 max_period_value = static_cast<unsigned int>(max_period->intValue());
662 }
663
664 service = getHAServiceByServerName("ha-sync", args);
665
666 } catch (const std::exception& ex) {
667 // There was an error while parsing command arguments. Return an error status
668 // code to notify the user.
670 callout_handle.setArgument("response", response);
671 return;
672 }
673
674 // Command parsing was successful, so let's process the command.
675 ConstElementPtr response = service->processSynchronize(server_name->stringValue(),
676 max_period_value);
677 callout_handle.setArgument("response", response);
678}
679
680void
682 // Command must always be provided.
683 ConstElementPtr command;
684 callout_handle.getArgument("command", command);
685
686 // Retrieve arguments.
687 ConstElementPtr args;
688 static_cast<void>(parseCommand(args, command));
689
690 HAServicePtr service;
691 std::vector<std::string> scopes_vector;
692 try {
693 // Arguments must be present.
694 if (!args) {
695 isc_throw(BadValue, "arguments not found in the 'ha-scopes' command");
696 }
697
698 // Arguments must be a map.
699 if (args->getType() != Element::map) {
700 isc_throw(BadValue, "arguments in the 'ha-scopes' command are not a map");
701 }
702
703 // scopes argument is mandatory.
704 ConstElementPtr scopes = args->get("scopes");
705 if (!scopes) {
706 isc_throw(BadValue, "'scopes' is mandatory for the 'ha-scopes' command");
707 }
708
709 // It contains a list of scope names.
710 if (scopes->getType() != Element::list) {
711 isc_throw(BadValue, "'scopes' must be a list in the 'ha-scopes' command");
712 }
713
714 // Retrieve scope names from this list. The list may be empty to clear the
715 // scopes.
716 for (size_t i = 0; i < scopes->size(); ++i) {
717 ConstElementPtr scope = scopes->get(i);
718 if (!scope || scope->getType() != Element::string) {
719 isc_throw(BadValue, "scope name must be a string in the 'scopes' argument");
720 }
721 scopes_vector.push_back(scope->stringValue());
722 }
723
724 service = getHAServiceByServerName("ha-scopes", args);
725
726 } catch (const std::exception& ex) {
727 // There was an error while parsing command arguments. Return an error status
728 // code to notify the user.
730 callout_handle.setArgument("response", response);
731 return;
732 }
733
734 // Command parsing was successful, so let's process the command.
735 ConstElementPtr response = service->processScopes(scopes_vector);
736 callout_handle.setArgument("response", response);
737}
738
739void
741 // Command must always be provided.
742 ConstElementPtr command;
743 callout_handle.getArgument("command", command);
744
745 // Retrieve arguments.
746 ConstElementPtr args;
747 static_cast<void>(parseCommand(args, command));
748
749 HAServicePtr service;
750 try {
751 service = getHAServiceByServerName("ha-continue", args);
752
753 } catch (const std::exception& ex) {
754 // There was an error while parsing command arguments. Return an error status
755 // code to notify the user.
757 callout_handle.setArgument("response", response);
758 return;
759 }
760 ConstElementPtr response = service->processContinue();
761 callout_handle.setArgument("response", response);
762}
763
764void
766 // Command must always be provided.
767 ConstElementPtr command;
768 callout_handle.getArgument("command", command);
769
770 HAServicePtr service;
771 try {
772 // Retrieve arguments.
773 ConstElementPtr args;
774 static_cast<void>(parseCommandWithArgs(args, command));
775
776 ConstElementPtr cancel_op = args->get("cancel");
777 if (!cancel_op) {
778 isc_throw(BadValue, "'cancel' is mandatory for the 'ha-maintenance-notify' command");
779 }
780
781 if (cancel_op->getType() != Element::boolean) {
782 isc_throw(BadValue, "'cancel' must be a boolean in the 'ha-maintenance-notify' command");
783 }
784
785 service = getHAServiceByServerName("ha-maintenance-notify", args);
786
787 ConstElementPtr response = service->processMaintenanceNotify(cancel_op->boolValue());
788 callout_handle.setArgument("response", response);
789
790 } catch (const std::exception& ex) {
791 // There was an error while parsing command arguments. Return an error status
792 // code to notify the user.
794 callout_handle.setArgument("response", response);
795 }
796}
797
798void
800 ConstElementPtr response;
801 for (auto const& service : services_->getAll()) {
802 response = service->processMaintenanceStart();
803 int rcode = CONTROL_RESULT_SUCCESS;
804 static_cast<void>(parseAnswer(rcode, response));
805 if (rcode != CONTROL_RESULT_SUCCESS) {
806 break;
807 }
808 }
809 callout_handle.setArgument("response", response);
810}
811
812void
814 ConstElementPtr response;
815 for (auto const& service : services_->getAll()) {
816 response = service->processMaintenanceCancel();
817 }
818 callout_handle.setArgument("response", response);
819}
820
821void
823 // Command must always be provided.
824 ConstElementPtr command;
825 callout_handle.getArgument("command", command);
826
827 // Retrieve arguments.
828 ConstElementPtr args;
829 static_cast<void>(parseCommand(args, command));
830
831 HAServicePtr service;
832 try {
833 service = getHAServiceByServerName("ha-reset", args);
834
835 } catch (const std::exception& ex) {
836 // There was an error while parsing command arguments. Return an error status
837 // code to notify the user.
839 callout_handle.setArgument("response", response);
840 return;
841 }
842
843 ConstElementPtr response = service->processHAReset();
844 callout_handle.setArgument("response", response);
845}
846
847void
849 // Command must always be provided.
850 ConstElementPtr command;
851 callout_handle.getArgument("command", command);
852
853 // Retrieve arguments.
854 ConstElementPtr args;
855 static_cast<void>(parseCommand(args, command));
856
857 HAServicePtr service;
858 auto origin_value = NetworkState::HA_REMOTE_COMMAND+1;
859 try {
860 if (args) {
861 auto origin = args->get("origin");
862 if (origin) {
863 if (origin->getType() != Element::integer) {
864 isc_throw(BadValue, "'origin' must be an integer in the 'ha-sync-complete-notify' command");
865 }
866 origin_value = origin->intValue();
867 }
868 }
869
870 service = getHAServiceByServerName("ha-sync-complete-notify", args);
871
872 } catch (const std::exception& ex) {
873 // There was an error while parsing command arguments. Return an error status
874 // code to notify the user.
876 callout_handle.setArgument("response", response);
877 return;
878 }
879
880 ConstElementPtr response = service->processSyncCompleteNotify(origin_value);
881 callout_handle.setArgument("response", response);
882}
883
885HAImpl::getHAServiceByServerName(const std::string& command_name, ConstElementPtr args) const {
886 HAServicePtr service;
887 if (args) {
888 // Arguments must be a map.
889 if (args->getType() != Element::map) {
890 isc_throw(BadValue, "arguments in the '" << command_name << "' command are not a map");
891 }
892
893 auto server_name = args->get("server-name");
894
895 if (server_name) {
896 if (server_name->getType() != Element::string) {
897 isc_throw(BadValue, "'server-name' must be a string in the '" << command_name << "' command");
898 }
899 service = services_->get(server_name->stringValue());
900 if (!service) {
901 isc_throw(BadValue, server_name->stringValue() << " matches no configured"
902 << " 'server-name'");
903 }
904 }
905 }
906
907 if (!service) {
908 service = services_->get();
909 }
910
911 return (service);
912}
913
914} // end of namespace isc::ha
915} // end of namespace isc
A generic exception that is thrown if a parameter given to a method is considered invalid in that con...
virtual const char * what() const
Returns a C-style character string of the cause of the exception.
A generic exception that is thrown when an unexpected error condition occurs.
static ElementPtr create(const Position &pos=ZERO_POSITION())
Definition: data.cc:249
static ElementPtr createMap(const Position &pos=ZERO_POSITION())
Creates an empty MapElement type ElementPtr.
Definition: data.cc:304
static ElementPtr createList(const Position &pos=ZERO_POSITION())
Creates an empty ListElement type ElementPtr.
Definition: data.cc:299
static const unsigned int HA_REMOTE_COMMAND
The network state is being altered by a "dhcp-disable" or "dhcp-enable" command sent by a HA partner.
Exception thrown during option unpacking This exception is thrown when an error has occurred,...
Definition: option.h:52
static HAConfigMapperPtr parse(const data::ConstElementPtr &config)
Parses HA configuration.
static std::string getSubnetServerName(const dhcp::SubnetPtr &subnet)
Convenience function extracting a value of the ha-server-name parameter from a subnet context.
Definition: ha_config.cc:524
static std::string HAModeToString(const HAMode &ha_mode)
Returns HA mode name.
Definition: ha_config.cc:233
void scopesHandler(hooks::CalloutHandle &callout_handle)
Implements handler for the ha-scopes command.
Definition: ha_impl.cc:681
void continueHandler(hooks::CalloutHandle &callout_handle)
Implements handler for the ha-continue command.
Definition: ha_impl.cc:740
HAConfigMapperPtr config_
Holds parsed configuration.
Definition: ha_impl.h:229
void syncCompleteNotifyHandler(hooks::CalloutHandle &callout_handle)
Implements handler for the ha-sync-complete-notify command.
Definition: ha_impl.cc:848
HAServicePtr getHAServiceByServerName(const std::string &command_name, data::ConstElementPtr args) const
Attempts to get an HAService by server name.
Definition: ha_impl.cc:885
HAServiceMapperPtr services_
Pointer to the high availability services (state machines).
Definition: ha_impl.h:232
void subnet4Select(hooks::CalloutHandle &callout_handle)
Implementation of the "subnet4_select" callout.
Definition: ha_impl.cc:131
void configure(const data::ConstElementPtr &input_config)
Parses configuration.
Definition: ha_impl.cc:37
~HAImpl()
Destructor.
Definition: ha_impl.cc:63
void maintenanceCancelHandler(hooks::CalloutHandle &callout_handle)
Implements handler for the ha-maintenance-cancel command.
Definition: ha_impl.cc:813
void haResetHandler(hooks::CalloutHandle &callout_handle)
Implements handler for the ha-reset command.
Definition: ha_impl.cc:822
void maintenanceNotifyHandler(hooks::CalloutHandle &callout_handle)
Implements handler for the ha-maintenance-notify command.
Definition: ha_impl.cc:765
void synchronizeHandler(hooks::CalloutHandle &callout_handle)
Implements handler for the ha-sync command.
Definition: ha_impl.cc:616
void maintenanceStartHandler(hooks::CalloutHandle &callout_handle)
Implements handler for the ha-maintenance-start command.
Definition: ha_impl.cc:799
void leases4Committed(hooks::CalloutHandle &callout_handle)
Implementation of the "leases4_committed" callout.
Definition: ha_impl.cc:211
void startServices(const asiolink::IOServicePtr &io_service, const dhcp::NetworkStatePtr &network_state, const HAServerType &server_type)
Creates high availability services using current configuration.
Definition: ha_impl.cc:42
void buffer4Receive(hooks::CalloutHandle &callout_handle)
Implementation of the "buffer4_receive" callout.
Definition: ha_impl.cc:72
void buffer6Receive(hooks::CalloutHandle &callout_handle)
Implementation of the "buffer6_receive" callout.
Definition: ha_impl.cc:334
void commandProcessed(hooks::CalloutHandle &callout_handle)
Implementation of the "command_processed" callout.
Definition: ha_impl.cc:556
HAImpl()
Constructor.
Definition: ha_impl.cc:32
void leases6Committed(hooks::CalloutHandle &callout_handle)
Implementation of the "leases6_committed" callout.
Definition: ha_impl.cc:473
void subnet6Select(hooks::CalloutHandle &callout_handle)
Implementation of the "subnet6_select" callout.
Definition: ha_impl.cc:393
void lease4ServerDecline(hooks::CalloutHandle &callout_handle)
Implementation of the "lease4_server_decline" callout.
Definition: ha_impl.cc:301
void heartbeatHandler(hooks::CalloutHandle &callout_handle)
Implements handle for the heartbeat command.
Definition: ha_impl.cc:589
Holds associations between objects and HA relationships.
Per-packet callout handle.
void getContext(const std::string &name, T &value) const
Get context.
@ NEXT_STEP_PARK
park the packet
@ NEXT_STEP_CONTINUE
continue normally
@ NEXT_STEP_DROP
drop the packet
@ NEXT_STEP_SKIP
skip the next processing step
ParkingLotHandlePtr getParkingLotHandlePtr() const
Returns pointer to the parking lot handle for this hook point.
void setContext(const std::string &name, T value)
Set context.
CalloutNextStep getStatus() const
Returns the next processing step.
void setStatus(const CalloutNextStep next)
Sets the next processing step.
void getArgument(const std::string &name, T &value) const
Get argument.
void setArgument(const std::string &name, T value)
Set argument.
static StatsMgr & instance()
Statistics Manager accessor method.
This file contains several functions and constants that are used for handling commands and responses ...
#define isc_throw(type, stream)
A shortcut macro to insert known values into exception arguments.
void addValue(const std::string &name, const int64_t value)
Records incremental integer observation.
#define LOG_ERROR(LOGGER, MESSAGE)
Macro to conveniently test error output and log it.
Definition: macros.h:32
#define LOG_DEBUG(LOGGER, LEVEL, MESSAGE)
Macro to conveniently test debug output and log it.
Definition: macros.h:14
std::string parseCommandWithArgs(ConstElementPtr &arg, ConstElementPtr command)
const int CONTROL_RESULT_ERROR
Status code indicating a general failure.
std::string parseCommand(ConstElementPtr &arg, ConstElementPtr command)
ConstElementPtr createAnswer()
Creates a standard config/command level success answer message (i.e.
ConstElementPtr parseAnswer(int &rcode, const ConstElementPtr &msg)
const int CONTROL_RESULT_SUCCESS
Status code indicating a successful operation.
boost::shared_ptr< const Element > ConstElementPtr
Definition: data.h:29
boost::shared_ptr< Element > ElementPtr
Definition: data.h:28
boost::shared_ptr< Subnet4 > Subnet4Ptr
A pointer to a Subnet4 object.
Definition: subnet.h:498
boost::shared_ptr< Lease4Collection > Lease4CollectionPtr
A shared pointer to the collection of IPv4 leases.
Definition: lease.h:500
boost::shared_ptr< Pkt4 > Pkt4Ptr
A pointer to Pkt4 object.
Definition: pkt4.h:555
boost::shared_ptr< Subnet6 > Subnet6Ptr
A pointer to a Subnet6 object.
Definition: subnet.h:663
boost::shared_ptr< NetworkState > NetworkStatePtr
Pointer to the NetworkState object.
boost::shared_ptr< Lease6Collection > Lease6CollectionPtr
A shared pointer to the collection of IPv6 leases.
Definition: lease.h:673
boost::shared_ptr< Pkt6 > Pkt6Ptr
A pointer to Pkt6 packet.
Definition: pkt6.h:31
boost::shared_ptr< Lease4 > Lease4Ptr
Pointer to a Lease4 structure.
Definition: lease.h:292
const isc::log::MessageID HA_BUFFER4_RECEIVE_UNPACK_FAILED
Definition: ha_messages.h:14
const isc::log::MessageID HA_SUBNET4_SELECT_NO_SUBNET_SELECTED
Definition: ha_messages.h:112
const isc::log::MessageID HA_LEASES6_COMMITTED_NOTHING_TO_UPDATE
Definition: ha_messages.h:58
const isc::log::MessageID HA_LEASES6_COMMITTED_NO_RELATIONSHIP
Definition: ha_messages.h:59
const isc::log::MessageID HA_BUFFER4_RECEIVE_PACKET_OPTIONS_SKIPPED
Definition: ha_messages.h:13
const isc::log::MessageID HA_BUFFER6_RECEIVE_UNPACK_FAILED
Definition: ha_messages.h:18
const isc::log::MessageID HA_SUBNET6_SELECT_NO_SUBNET_SELECTED
Definition: ha_messages.h:118
const isc::log::MessageID HA_SUBNET6_SELECT_INVALID_HA_SERVER_NAME
Definition: ha_messages.h:114
const isc::log::MessageID HA_LEASES4_COMMITTED_NO_RELATIONSHIP
Definition: ha_messages.h:56
isc::log::Logger ha_logger("ha-hooks")
Definition: ha_log.h:17
const isc::log::MessageID HA_SUBNET6_SELECT_NOT_FOR_US
Definition: ha_messages.h:115
const isc::log::MessageID HA_BUFFER6_RECEIVE_PACKET_OPTIONS_SKIPPED
Definition: ha_messages.h:17
boost::shared_ptr< HAConfig > HAConfigPtr
Pointer to the High Availability configuration structure.
Definition: ha_config.h:37
const isc::log::MessageID HA_SUBNET4_SELECT_INVALID_HA_SERVER_NAME
Definition: ha_messages.h:108
HAServerType
Lists possible server types for which HA service is created.
const isc::log::MessageID HA_SUBNET4_SELECT_NO_RELATIONSHIP_FOR_SUBNET
Definition: ha_messages.h:110
const isc::log::MessageID HA_SUBNET4_SELECT_NOT_FOR_US
Definition: ha_messages.h:109
const isc::log::MessageID HA_SUBNET6_SELECT_NO_RELATIONSHIP_FOR_SUBNET
Definition: ha_messages.h:116
const isc::log::MessageID HA_LEASES4_COMMITTED_NOTHING_TO_UPDATE
Definition: ha_messages.h:55
const isc::log::MessageID HA_SUBNET4_SELECT_NO_RELATIONSHIP_SELECTOR_FOR_SUBNET
Definition: ha_messages.h:111
const isc::log::MessageID HA_SUBNET6_SELECT_NO_RELATIONSHIP_SELECTOR_FOR_SUBNET
Definition: ha_messages.h:117
const isc::log::MessageID HA_BUFFER4_RECEIVE_NOT_FOR_US
Definition: ha_messages.h:12
const isc::log::MessageID HA_BUFFER6_RECEIVE_NOT_FOR_US
Definition: ha_messages.h:16
boost::shared_ptr< HAService > HAServicePtr
Pointer to the HAService class.
Definition: ha_service.h:1379
boost::shared_ptr< ParkingLotHandle > ParkingLotHandlePtr
Pointer to the parking lot handle.
Definition: parking_lots.h:381
const int DBGLVL_TRACE_BASIC
Trace basic operations.
Definition: log_dbglevels.h:69
Defines the logger used by the top-level component of kea-lfc.