Commit bab2030f authored by Tomek Mrugalski's avatar Tomek Mrugalski 🛰
Browse files

[master] Merge branch 'trac3798' (per subnet stats in DHCPv4)

Conflicts:
	src/bin/dhcp4/tests/dhcp4_test_utils.cc
parents ff499a19 70f37c3b
......@@ -1841,11 +1841,11 @@ temporarily override a list of interface names and listen on all interfaces.
The rules for determining the FQDN option are as follows:
<orderedlist>
<listitem><para>
If configured to do, so ignore the REQUEST contents and generate a
If configured to do, so ignore the DHCPREQUEST contents and generate a
FQDN using a configurable prefix and suffix.
</para></listitem>
<listitem><para>
If the REQUEST contains the client FQDN option, the candidate
If the DHCPREQUEST contains the client FQDN option, the candidate
name is taken from there, otherwise it is taken from the Host Name option.
The candidate name may then be modified:
<orderedlist>
......@@ -2825,6 +2825,41 @@ temporarily override a list of interface names and listen on all interfaces.
</table>
</section>
<!-- proper section structure added in ticket 3794, will merge it
appropiately -->
<section id="dummy">
<title>MERGE ME</title>
<para>
<itemizedlist>
<listitem>
<simpara><emphasis>subnet[id].total-addresses</emphasis> (integer) -
this statistic shows the total number of addresses available for the
DHCPv4 management. In other words, this is the sum of all addresses in
all configured pools. This statistic changes only during configuration
changes. Note it does not take into account any addresses that may be
reserved due to host reservation. The <emphasis>id</emphasis> is the
subnet-id of a given subnet. This statistic is exposed for each subnet
separately. This statistic is reset during reconfiguration event.
</simpara>
</listitem>
<listitem>
<simpara><emphasis>subnet[id].assigned-addresses</emphasis> (integer) -
this statistic shows the number of assigned addresses in a given subnet.
This statistic increases every time a new lease is allocated (as a result
of receiving a DHCPREQUEST message) and is decreased every time a lease is
released (a DHCPRELEASE message is received). When lease expiration
is implemented (planned for Kea 1.0), it will also decrease when a lease
is expired. The <emphasis>id</emphasis> is the subnet-id of a given
subnet. This statistic is exposed for each subnet separately. This
statistic is reset during reconfiguration event.
</simpara>
</listitem>
</itemizedlist>
</para>
</section>
<section id="dhcp4-std">
<title>Supported DHCP Standards</title>
<para>The following standards are currently supported:</para>
......@@ -2832,8 +2867,9 @@ temporarily override a list of interface names and listen on all interfaces.
<listitem>
<simpara><emphasis>Dynamic Host Configuration Protocol</emphasis>,
<ulink url="http://tools.ietf.org/html/rfc2131">RFC 2131</ulink>:
Supported messages are DISCOVER (1), OFFER (2),
REQUEST (3), RELEASE (7), INFORM (8), ACK (5), and NAK(6).</simpara>
Supported messages are DHCPDISCOVER (1), DHCPOFFER (2),
DHCPREQUEST (3), DHCPRELEASE (7), DHCPINFORM (8), DHCPACK (5), and
DHCPNAK(6).</simpara>
</listitem>
<listitem>
<simpara><emphasis>DHCP Options and BOOTP Vendor Extensions</emphasis>,
......
......@@ -75,6 +75,7 @@ kea_dhcp4_LDADD += $(top_builddir)/src/lib/asiolink/libkea-asiolink.la
kea_dhcp4_LDADD += $(top_builddir)/src/lib/log/libkea-log.la
kea_dhcp4_LDADD += $(top_builddir)/src/lib/config/libkea-cfgclient.la
kea_dhcp4_LDADD += $(top_builddir)/src/lib/cc/libkea-cc.la
kea_dhcp4_LDADD += $(top_builddir)/src/lib/stats/libkea-stats.la
kea_dhcp4_LDADD += $(top_builddir)/src/lib/hooks/libkea-hooks.la
kea_dhcp4_LDADD += $(top_builddir)/src/lib/stats/libkea-stats.la
kea_dhcp4_LDADD += $(top_builddir)/src/lib/cryptolink/libkea-cryptolink.la
......
......@@ -40,6 +40,7 @@
#include <hooks/callout_handle.h>
#include <hooks/hooks_log.h>
#include <hooks/hooks_manager.h>
#include <stats/stats_mgr.h>
#include <util/strutil.h>
#include <stats/stats_mgr.h>
#include <log/logger.h>
......@@ -60,6 +61,7 @@ using namespace isc::dhcp;
using namespace isc::dhcp_ddns;
using namespace isc::hooks;
using namespace isc::log;
using namespace isc::stats;
using namespace std;
/// Structure that holds registered hook indexes
......@@ -1789,6 +1791,11 @@ Dhcpv4Srv::processRelease(Pkt4Ptr& release) {
.arg(release->getLabel())
.arg(lease->addr_.toText());
// Need to decrease statistic for assigned addresses.
StatsMgr::instance().addValue(
StatsMgr::generateName("subnet", lease->subnet_id_, "assigned-addresses"),
static_cast<int64_t>(-1));
if (CfgMgr::instance().ddnsEnabled()) {
// Remove existing DNS entries for the lease, if any.
queueNameChangeRequest(isc::dhcp_ddns::CHG_REMOVE, lease);
......
......@@ -42,6 +42,10 @@ namespace test {
Dhcpv4SrvTest::Dhcpv4SrvTest()
:rcode_(-1), srv_(0) {
// Wipe any existing statistics
isc::stats::StatsMgr::instance().removeAll();
subnet_ = Subnet4Ptr(new Subnet4(IOAddress("192.0.2.0"), 24, 1000,
2000, 3000));
pool_ = Pool4Ptr(new Pool4(IOAddress("192.0.2.100"), IOAddress("192.0.2.110")));
......
......@@ -21,13 +21,16 @@
#include <dhcpsrv/subnet_id.h>
#include <dhcp4/tests/dhcp4_test_utils.h>
#include <dhcp4/tests/dhcp4_client.h>
#include <stats/stats_mgr.h>
#include <boost/shared_ptr.hpp>
#include <sstream>
using namespace isc;
using namespace isc::asiolink;
using namespace isc::data;
using namespace isc::dhcp;
using namespace isc::dhcp::test;
using namespace isc::stats;
namespace {
......@@ -144,6 +147,18 @@ ReleaseTest::acquireAndRelease(const std::string& hw_address_1,
// Perform 4-way exchange to obtain a new lease.
acquireLease(client);
std::stringstream name;
// Let's get the subnet-id and generate statistics name out of it
const Subnet4Collection* subnets =
CfgMgr::instance().getCurrentCfg()->getCfgSubnets4()->getAll();
ASSERT_EQ(1, subnets->size());
name << "subnet[" << subnets->at(0)->getID() << "].assigned-addresses";
ObservationPtr assigned_cnt = StatsMgr::instance().getObservation(name.str());
ASSERT_TRUE(assigned_cnt);
uint64_t before = assigned_cnt->getInteger().first;
// Remember the acquired address.
IOAddress leased_address = client.config_.lease_.addr_;
......@@ -157,14 +172,25 @@ ReleaseTest::acquireAndRelease(const std::string& hw_address_1,
ASSERT_NO_THROW(client.doRelease());
Lease4Ptr lease = LeaseMgrFactory::instance().getLease4(leased_address);
assigned_cnt = StatsMgr::instance().getObservation(name.str());
ASSERT_TRUE(assigned_cnt);
uint64_t after = assigned_cnt->getInteger().first;
// We check if the release process was successful by checking if the
// lease is in the database. It is expected that it is not present,
// i.e. has been deleted with the release.
if (expected_result == SHOULD_PASS) {
EXPECT_FALSE(lease);
// The removal succeded, so the assigned-addresses statistic should
// be decreased by one
EXPECT_EQ(before, after + 1);
} else {
EXPECT_TRUE(lease);
// The removal failed, so the assigned-address should be the same
// as before
EXPECT_EQ(before, after);
}
}
......
......@@ -150,6 +150,7 @@ libkea_dhcpsrv_la_LIBADD += $(top_builddir)/src/lib/hooks/libkea-hooks.la
libkea_dhcpsrv_la_LIBADD += $(top_builddir)/src/lib/log/libkea-log.la
libkea_dhcpsrv_la_LIBADD += $(top_builddir)/src/lib/util/libkea-util.la
libkea_dhcpsrv_la_LIBADD += $(top_builddir)/src/lib/cc/libkea-cc.la
libkea_dhcpsrv_la_LIBADD += $(top_builddir)/src/lib/stats/libkea-stats.la
libkea_dhcpsrv_la_LIBADD += $(top_builddir)/src/lib/hooks/libkea-hooks.la
libkea_dhcpsrv_la_LIBADD += $(top_builddir)/src/lib/exceptions/libkea-exceptions.la
......
......@@ -21,11 +21,12 @@
#include <dhcpsrv/host.h>
#include <dhcpsrv/lease_mgr_factory.h>
#include <dhcp/dhcp6.h>
#include <stats/stats_mgr.h>
#include <hooks/server_hooks.h>
#include <hooks/hooks_manager.h>
#include <cstring>
#include <sstream>
#include <limits>
#include <vector>
#include <stdint.h>
......@@ -34,6 +35,7 @@
using namespace isc::asiolink;
using namespace isc::dhcp;
using namespace isc::hooks;
using namespace isc::stats;
namespace {
......@@ -1693,6 +1695,11 @@ AllocEngine::requestLease4(AllocEngine::ClientContext4& ctx) {
.arg(client_lease->addr_.toText());
lease_mgr.deleteLease(client_lease->addr_);
// Need to decrease statistic for assigned addresses.
StatsMgr::instance().addValue(
StatsMgr::generateName("subnet", ctx.subnet_->getID(), "assigned-addresses"),
static_cast<int64_t>(-1));
}
// Return the allocated lease or NULL pointer if allocation was
......@@ -1770,6 +1777,12 @@ AllocEngine::createLease4(const ClientContext4& ctx, const IOAddress& addr) {
// That is a real (REQUEST) allocation
bool status = LeaseMgrFactory::instance().addLease(lease);
if (status) {
// The lease insertion succeeded, let's bump up the statistic.
isc::stats::StatsMgr::instance().addValue(
StatsMgr::generateName("subnet", ctx.subnet_->getID(), "assigned-addresses"),
static_cast<int64_t>(1));
return (lease);
} else {
// One of many failures with LeaseMgr (e.g. lost connection to the
......
......@@ -17,6 +17,7 @@
#include <dhcpsrv/cfg_subnets4.h>
#include <dhcpsrv/dhcpsrv_log.h>
#include <dhcpsrv/subnet_id.h>
#include <stats/stats_mgr.h>
using namespace isc::asiolink;
......@@ -109,7 +110,7 @@ CfgSubnets4::selectSubnet(const SubnetSelector& selector) const {
}
// We have identified an address in the client's packet that can be
// used for subnet selection. Match this packet with the subnets.
// used for subnet selection. Match this packet with the subnets.
return (selectSubnet(address, selector.client_classes_));
}
......@@ -145,8 +146,40 @@ CfgSubnets4::isDuplicate(const Subnet4& subnet) const {
return (false);
}
void
CfgSubnets4::removeStatistics() {
using namespace isc::stats;
// For each v4 subnet currently configured, remove the statistic.
/// @todo: May move this to CfgSubnets4 class if there will be more
/// statistics here.
for (Subnet4Collection::const_iterator subnet4 = subnets_.begin();
subnet4 != subnets_.end(); ++subnet4) {
StatsMgr::instance().del(StatsMgr::generateName("subnet",
(*subnet4)->getID(),
"total-addresses"));
StatsMgr::instance().del(StatsMgr::generateName("subnet",
(*subnet4)->getID(),
"assigned-addresses"));
}
}
void
CfgSubnets4::updateStatistics() {
using namespace isc::stats;
/// @todo: May move this to CfgSubnets4 class if there will be more
/// statistics here.
for (Subnet4Collection::const_iterator subnet = subnets_.begin();
subnet != subnets_.end(); ++subnet) {
StatsMgr::instance().setValue(
StatsMgr::generateName("subnet", (*subnet)->getID(), "total-addresses"),
static_cast<int64_t>((*subnet)->getPoolCapacity(Lease::TYPE_V4)));
}
}
} // end of namespace isc::dhcp
} // end of namespace isc
// Copyright (C) 2014 Internet Systems Consortium, Inc. ("ISC")
// Copyright (C) 2014-2015 Internet Systems Consortium, Inc. ("ISC")
//
// Permission to use, copy, modify, and/or distribute this software for any
// purpose with or without fee is hereby granted, provided that the above
......@@ -125,6 +125,23 @@ public:
const ClientClasses& client_classes
= ClientClasses()) const;
/// @brief Updates statistics.
///
/// This method updates statistics that are affected by the newly committed
/// configuration. In particular, it updates the number of available addresses
/// in each subnet. Other statistics may be added in the future. In general,
/// these are statistics that are dependant only on configuration, so they are
/// not expected to change until the next reconfiguration event.
void updateStatistics();
/// @brief Removes statistics.
///
/// During commitment of a new configuration, we need to get rid of the old
/// statistics for the old configuration. In particular, we need to remove
/// anything related to subnets, as there may be fewer subnets in the new
/// configuration and also subnet-ids may change.
void removeStatistics();
private:
/// @brief Checks that the IPv4 subnet with the given id already exists.
......
......@@ -19,10 +19,13 @@
#include <dhcpsrv/cfgmgr.h>
#include <dhcpsrv/dhcpsrv_log.h>
#include <dhcpsrv/subnet_id.h>
#include <stats/stats_mgr.h>
#include <sstream>
#include <string>
using namespace isc::asiolink;
using namespace isc::util;
using namespace isc::stats;
namespace isc {
namespace dhcp {
......@@ -110,13 +113,24 @@ CfgMgr::ensureCurrentAllocated() {
void
CfgMgr::clear() {
if (configuration_) {
configuration_->removeStatistics();
}
configs_.clear();
ensureCurrentAllocated();
}
void
CfgMgr::commit() {
ensureCurrentAllocated();
// First we need to remove statistics. The new configuration can have fewer
// subnets. Also, it may change subnet-ids. So we need to remove them all
// and add it back.
configuration_->removeStatistics();
if (!configs_.back()->sequenceEquals(*configuration_)) {
configuration_ = configs_.back();
// Keep track of the maximum size of the configs history. Before adding
......@@ -127,6 +141,9 @@ CfgMgr::commit() {
configs_.erase(configs_.begin(), it);
}
}
// Now we need to set the statistics back.
configuration_->updateStatistics();
}
void
......
// Copyright (C) 2012-2014 Internet Systems Consortium, Inc. ("ISC")
// Copyright (C) 2012-2015 Internet Systems Consortium, Inc. ("ISC")
//
// Permission to use, copy, modify, and/or distribute this software for any
// purpose with or without fee is hereby granted, provided that the above
......
......@@ -145,5 +145,20 @@ SrvConfig::equals(const SrvConfig& other) const {
(*cfg_option_ == *other.cfg_option_));
}
void
SrvConfig::removeStatistics() {
// For now, this method only removes statistics for v4 subnets, but in the
// near future, we'll also get statistics for v6 subnets.
getCfgSubnets4()->removeStatistics();
}
void
SrvConfig::updateStatistics() {
// For now, this method only updates statistics for v4 subnets, but in the
// near future, we'll also get statistics for v6 subnets.
getCfgSubnets4()->updateStatistics();
}
}
}
......@@ -349,6 +349,17 @@ public:
//@}
/// @brief Updates statistics.
///
/// This method calls appropriate methods in child objects that update
/// related statistics. See @ref CfgSubnets4::updateStatistics for details.
void updateStatistics();
/// @brief Removes statistics.
///
/// This method calls appropriate methods in child objects that remove
/// related statistics. See @ref CfgSubnets4::removeStatistics for details.
void removeStatistics();
private:
......
......@@ -139,6 +139,7 @@ libdhcpsrv_unittests_LDADD += $(top_builddir)/src/lib/cc/libkea-cc.la
libdhcpsrv_unittests_LDADD += $(top_builddir)/src/lib/asiolink/libkea-asiolink.la
libdhcpsrv_unittests_LDADD += $(top_builddir)/src/lib/hooks/libkea-hooks.la
libdhcpsrv_unittests_LDADD += $(top_builddir)/src/lib/log/libkea-log.la
libdhcpsrv_unittests_LDADD += $(top_builddir)/src/lib/stats/libkea-stats.la
libdhcpsrv_unittests_LDADD += $(top_builddir)/src/lib/exceptions/libkea-exceptions.la
libdhcpsrv_unittests_LDADD += $(GTEST_LDADD)
endif
......
......@@ -16,10 +16,12 @@
#include <dhcp/pkt4.h>
#include <dhcpsrv/tests/alloc_engine_utils.h>
#include <dhcpsrv/tests/test_utils.h>
#include <stats/stats_mgr.h>
using namespace std;
using namespace isc::hooks;
using namespace isc::asiolink;
using namespace isc::stats;
namespace isc {
namespace dhcp {
......@@ -80,7 +82,7 @@ TEST_F(AllocEngine4Test, simpleAlloc4) {
detailCompareLease(lease, from_mgr);
}
// This test checks if the fake allocation (for DISCOVER) can succeed
// This test checks if the fake allocation (for DHCPDISCOVER) can succeed
TEST_F(AllocEngine4Test, fakeAlloc4) {
boost::scoped_ptr<AllocEngine> engine;
ASSERT_NO_THROW(engine.reset(new AllocEngine(AllocEngine::ALLOC_ITERATIVE,
......@@ -431,7 +433,8 @@ TEST_F(AllocEngine4Test, outOfAddresses4) {
EXPECT_FALSE(ctx.old_lease_);
}
// This test checks if an expired lease can be reused in DISCOVER (fake allocation)
// This test checks if an expired lease can be reused in DHCPDISCOVER (fake
// allocation)
TEST_F(AllocEngine4Test, discoverReuseExpiredLease4) {
boost::scoped_ptr<AllocEngine> engine;
ASSERT_NO_THROW(engine.reset(new AllocEngine(AllocEngine::ALLOC_ITERATIVE,
......@@ -1547,6 +1550,114 @@ TEST_F(AllocEngine4Test, findReservation) {
EXPECT_FALSE(ctx.host_);
}
// This test checks if the simple IPv4 allocation can succeed and that
// statistic for allocated addresses is increased appropriately.
TEST_F(AllocEngine4Test, simpleAlloc4Stats) {
boost::scoped_ptr<AllocEngine> engine;
ASSERT_NO_THROW(engine.reset(new AllocEngine(AllocEngine::ALLOC_ITERATIVE,
100, false)));
ASSERT_TRUE(engine);
AllocEngine::ClientContext4 ctx(subnet_, clientid_, hwaddr_, IOAddress("0.0.0.0"),
false, true, "somehost.example.com.", false);
// Let's pretend 100 addresses were allocated already
stringstream name;
name << "subnet[" << subnet_->getID() << "].assigned-addresses";
StatsMgr::instance().addValue(name.str(), static_cast<int64_t>(100));
Lease4Ptr lease = engine->allocateLease4(ctx);
// The new lease has been allocated, so the old lease should not exist.
EXPECT_FALSE(ctx.old_lease_);
// Check that we got a lease
ASSERT_TRUE(lease);
// The statistic should be there and it should be increased by 1 (to 101).
ObservationPtr stat = StatsMgr::instance().getObservation(name.str());
ASSERT_TRUE(stat);
EXPECT_EQ(101, stat->getInteger().first);
}
// This test checks if the fake allocation (for DHCPDISCOVER) can succeed
// and that it doesn't increase allocated-addresses statistic.
TEST_F(AllocEngine4Test, fakeAlloc4Stat) {
boost::scoped_ptr<AllocEngine> engine;
ASSERT_NO_THROW(engine.reset(new AllocEngine(AllocEngine::ALLOC_ITERATIVE,
100, false)));
ASSERT_TRUE(engine);
AllocEngine::ClientContext4 ctx(subnet_, clientid_, hwaddr_,
IOAddress("0.0.0.0"), false, true,
"host.example.com.", true);
// Let's pretend 100 addresses were allocated already
stringstream name;
name << "subnet[" << subnet_->getID() << "].assigned-addresses";
StatsMgr::instance().addValue(name.str(), static_cast<int64_t>(100));
Lease4Ptr lease = engine->allocateLease4(ctx);
// The new lease has been allocated, so the old lease should not exist.
EXPECT_FALSE(ctx.old_lease_);
// Check that we got a lease
ASSERT_TRUE(lease);
// The statistic should be there and it should not be increased
// (should be still equal to 100).
ObservationPtr stat = StatsMgr::instance().getObservation(name.str());
ASSERT_TRUE(stat);
EXPECT_EQ(100, stat->getInteger().first);
}
// This test checks that the allocated-addresses statistic is decreased when
// the client has a lease and a reservation for a different address is
// available.
TEST_F(AllocEngine4Test, reservedAddressExistingLeaseStat) {
// Create the reservation for the client.
HostPtr host(new Host(&hwaddr_->hwaddr_[0], hwaddr_->hwaddr_.size(),
Host::IDENT_HWADDR, subnet_->getID(),
SubnetID(0), IOAddress("192.0.2.123")));
CfgMgr::instance().getStagingCfg()->getCfgHosts()->add(host);
CfgMgr::instance().commit();
// Create a lease for the client with a different address than the reserved
// one.
Lease4Ptr lease(new Lease4(IOAddress("192.0.2.101"), hwaddr_,
&clientid_->getClientId()[0],
clientid_->getClientId().size(),
100, 30, 60, time(NULL), subnet_->getID(),
false, false, ""));
LeaseMgrFactory::instance().addLease(lease);
AllocEngine engine(AllocEngine::ALLOC_ITERATIVE, 100, false);
// Let's pretend 100 addresses were allocated already
stringstream name;
name << "subnet[" << subnet_->getID() << "].assigned-addresses";
StatsMgr::instance().addValue(name.str(), static_cast<int64_t>(100));
// Request allocation of the reserved address.
AllocEngine::ClientContext4 ctx(subnet_, clientid_, hwaddr_,
IOAddress("192.0.2.123"), false, false,
"", false);
AllocEngine::findReservation(ctx);
Lease4Ptr allocated_lease = engine.allocateLease4(ctx);
ASSERT_TRUE(allocated_lease);
// The statistic should be still at 100. Note that it was decreased
// (because old lease was removed), but also increased (because the
// new lease was immediately allocated).
ObservationPtr stat = StatsMgr::instance().getObservation(name.str());
ASSERT_TRUE(stat);
EXPECT_EQ(100, stat->getInteger().first);
// Lets' double check that the actual allocation took place.
EXPECT_FALSE(ctx.fake_allocation_);
}
}; // namespace test
}; // namespace dhcp
}; // namespace isc
// Copyright (C) 2012-2014 Internet Systems Consortium, Inc. ("ISC")
// Copyright (C) 2012-2015 Internet Systems Consortium, Inc. ("ISC")
//
// Permission to use, copy, modify, and/or distribute this software for any
// purpose with or without fee is hereby granted, provided that the above
......@@ -20,6 +20,7 @@
#include <dhcpsrv/cfgmgr.h>
#include <dhcpsrv/subnet_id.h>
#include <dhcpsrv/parsers/dhcp_parsers.h>
#include <stats/stats_mgr.h>
#include <gtest/gtest.h>
......@@ -34,6 +35,7 @@ using namespace isc::data;
using namespace isc::dhcp;
using namespace isc::dhcp::test;
using namespace isc::util;
using namespace isc::stats;
using namespace isc;
// don't import the entire boost namespace. It will unexpectedly hide uint8_t
......@@ -574,6 +576,72 @@ TEST_F(CfgMgrTest, verbosity) {
EXPECT_FALSE(CfgMgr::instance().isVerbose());
}
// This test verifies that once the configuration is committed, statistics
// are updated appropriately.
TEST_F(CfgMgrTest, commitStats) {
CfgMgr& cfg_mgr = CfgMgr::instance();
StatsMgr& stats_mgr = StatsMgr::instance();
// Let's prepare the "old" configuration: a subnet with id 123
// and pretent there ware addresses assigned, so statistics are non-zero.
Subnet4Ptr subnet1(new Subnet4(IOAddress("192.1.2.0"), 24, 1, 2, 3, 123));
CfgSubnets4Ptr subnets = cfg_mgr.getStagingCfg()->getCfgSubnets4();
subnets->add(subnet1);
cfg_mgr.commit();
stats_mgr.addValue("subnet[123].total-addresses", static_cast<int64_t>(256));
stats_mgr.setValue("subnet[123].assigned-addresses", static_cast<int64_t>(150));
// Now, let's change the configuration to something new.
// There's a subnet 192.1.2.0/24 with ID=42
Subnet4Ptr subnet2(new Subnet4(IOAddress("192.1.2.0"), 24, 1, 2, 3, 42));