BIND 10 #3360: memfile: implement file support
BIND 10 Development
do-not-reply at isc.org
Wed Apr 2 13:55:30 UTC 2014
#3360: memfile: implement file support
-------------------------------------+-------------------------------------
Reporter: tomek | Owner:
Type: enhancement | stephen
Priority: medium | Status:
Component: dhcpdb | reviewing
Keywords: | Milestone: DHCP-
Sensitive: 0 | Kea0.9-alpha
Sub-Project: DHCP | Resolution:
Estimated Difficulty: 5 | CVSS Scoring:
Total Hours: 2 | Defect Severity: N/A
| Feature Depending on Ticket:
| Add Hours to Ticket: 2
| Internal?: 0
-------------------------------------+-------------------------------------
Changes (by marcin):
* owner: marcin => stephen
Comment:
Replying to [comment:6 stephen]:
> Reviewed commit 991fea36336a0303a1f61f1ef0bcfe82d3a682ce
>
> I've made some small changes to comments and to the BIND 10 guide and
pushed.
>
>
> '''General'''[[BR]]
> This is mentioned below, but I think it should be highlighted here.
Rather than introduce two new configuration parameters ("leasefile" and
"persist"), why not use the existing "name" parameter? If the "type"
parameter is "memfile", then "name" is the name of the file to which
leases are written; if "name" is null, leases are not persisted to disk.
This simplifies the parsing.
I agree that using !''name!'' parameter is a good idea.
I disagree with the suggestion that the null name should be used to
disable persistence:
- I want to stick to the concept of the default file location. The default
is taken when the file is not specified or is empty. This is not really
doable if we want to use null or not null to disable or enable
persistence. This can't have two meanings.
- I don't want to remove the file name from the configuration to
temporarily disable persistence because I will have hard time recreating
the name when I want to re-enable it. It is so much easier for the user to
flip boolean flag, rather than copy back and forth the absolute file path.
>
> A related issue is one of defaults. I would suggest that we remove the
default values for the lease database and explicitly require the user to
set it. (They have to do a fair amount of configuration to run a DHCP
server already, so setting the lease database information is not a big
deal. And they would not be able to run the server until it was set, as
LeaseMgrFactory::create logs an error and throws an exception if the
database type is not set or is unrecognised.) This would eliminate the
problem of deciding a default location for the lease file; it would also
remove the case where a parameter default value is hard-coded instead of
being in the .spec file.
I disagree. We should strive to make configuration user friendly. People
will install the server and will want it to work instantly. And less they
have to configure, the better. I don't think it is a good idea to provide
defaults for MySQL or Postgres because there is very little chance that
the defaults will be accurate. In fact it is impossible, given that
password has to be specified.
For Memfile there is no password and there is a known directory structure,
with the var folder in it, that can be used by default. I don't see a
compelling reason to not use this opportunity and create a lease file for
a user if we can. We do it once in the code, users would have to do it
over and over again.
Also, this doesn't impose any additional burden on the user, comparing
with the case when default is not supported. He'd have to change the
location if he wanted a non-default one anyway.
Finally, is .spec file really an issue? We are going to toss our
configuration mechanism quite soon anyway. I also remember that defaults
didn't really work properly through the spec file.
>
>
> '''doc/guide/bind10-guide.xml'''[[BR]]
> I've made some changes to the text in this file.
Thanks.
>
> '''src/lib/dhcp/duid.cc'''[[BR]]
> decode: see the comment below about the fromText method in hwaddr.cc
concerning the handling of two adjacent separators.
I did as suggested.
>
> '''src/lib/dhcp/hwaddr.{cc,h}'''[[BR]]
> fromText: at present, the hardware type set in the HWAddr object is
HTYPE_ETHER. For more flexibility, it could be passed as the second
argument which has a default value of HTYPE_ETHER.
Added.
>
> fromText: do we want token compression in the split operation? AIUI, it
treats adjacent separators as a single separator, so 5::7 appears as 5:7.
Given that an IPv6 address can look similar to a hardware address - and
that given the size of the IPv6 address range, most IPv6 addresses include
a "::" - it might be better to treat the apperance of two adjacent
separators as an error. That way we have some form of guard should an IPv6
address be passed to this class.
Ok. I did as suggested.
>
> '''src/lib/dhcp/tests/hwaddr_unittest.cc'''[[BR]]
> fromText test: should also check the case where the input string
contains two adjacent colons.
It checks now.
>
>
> '''src/lib/dhcpsrv/csv_lease_file4.cc'''[[BR]]
> CSVLeasefile4::next(): There seems no reason not to be the the first few
statements (apart from the declaration of "row") in the "try" block as
well. If this is done, the initial lease.reset() call can be moved into
the "if" block that tests for the empty row returned from the next() call.
It is a trivial saving, but it means that lease.reset() is only ever
called once regardless of the path through the method.
Not a big deal to change that. I did.
>
> CSVLeasefile4::next(): client_id_len can simply be set to
client_id_vec.size(). If the vector is empty, size() will return 0.
Ooops. Fixed.
>
> '''src/lib/dhcpsrv/csv_lease_file4.h'''
> I've made some small changes to the comments, but have not made large
ones. Unless there is a plan to change the documentation, I suggest
removing the reference to ticket #2405. A ticket is a transient thing and
the comment will not really be relevant in a year or so.
There is a plan to update documentation. Specifically, revisit @todos.
>
> '''src/lib/dhcpsrv/csv_lease_file6.cc'''[[BR]]
> See comment for CSVLeasefile4::next() (above).
>
> '''src/lib/dhcpsrv/csv_lease_file6.h'''[[BR]]
> See comments for src/lib/dhcpsrv/csv_lease_file4.h.
>
Addressed.
>
> '''src/lib/dhcpsrv/dbaccess_parser.cc'''[[BR]]
> !DbAccessParser constructor: ctx_ has already been set in the
initializer list, so the body of the method can be empty.
Ooops. Fixed.
>
>
> '''src/lib/dhcpsrv/memfile_lease_mgr.cc'''[[BR]]
> A future ticket should look at this file: there's a lot of replication,
I'm sure it could be shrunk with judicious use of templates.
There is a bunch of things that should be fixed in this file. I agree.
But, nothing in this ticket.
>
> '''src/lib/dhcpsrv/memfile_lease_mgr.h'''[[BR]]
> I would suggest an alteration to the lease manager parameters. Instead
of "leasefile" and "persist", use "name" as the name of the file: if it is
empty, persistence is disabled. ("name" is meant to be the name of the
database - in this case, the file is the database.).
See my answer above.
>
> I've made some small changes to the header, but some comments are wrong:
there is no "persist{4,6}" or "leasefile{4,6}" in the database access
string, they are "persist" and "leasefile".
Yes. I had a few turns of action with this ticket and these parameters
were a reminder of it.
>
> I do not think the idea of a default file is a good one: we don't supply
a default for the database name, why a default for the memfile backend?
I explained that above. The database name and the lease file location is
not an apple to apple.
>
> persistLeases: The universe could be stored in the Memfile_LeaseMgr
class when it is constructed, so it is not clear why persistLeases()
requires a "universe" parameter.
It could. But, nothing precludes memfile from being extended to support v4
and v6 leases simultaneously, so I thought it may be good to query the
persistence using universe.
>
> '''src/lib/dhcpsrv/tests/Makefile.am'''[[BR]]
> As all the .csv files are created by the tests, is there any really need
to put them in a special testdata directory? Why not put them in the build
directory (as is done elsewhere for other temporary files)? It eliminates
the need for the testdata directory.
Initially I thought that I will have some files there (instead of
dynamically creating them). Then I decided differently, but the folder
remained and I didn't see any harm. But, I took your point and removed it.
>
>
> '''src/lib/dhcpsrv/tests/generic_lease_mgr_unittest.cc'''[[BR]]
> testBasicLease4: one of the reopen() calls should be passed an explicit
"v4" parameter. A possible future edit could change the default value and
have that default value open a V4 lease file, but an explicit "v4"
parameter open something else. (Admittedly this is highly unlikely, but it
is a possibility.)
Ok.
>
> '''src/lib/dhcpsrv/tests/lease_file_io.h'''[[BR]]
> Should be a header describing the purpose of the class.
Added.
>
> '''src/lib/dhcpsrv/tests/memfile_lease_mgr_unittest.cc'''[[BR]]
> There needs to be two additional tests checking that the recording of a
lease deletion survives a re-opening of the lease file.
> * Add a lease and delete it, then reopen the lease file and check that
the lease is absent.
This test has been there already in testBasicLeaseX.
> * Add a lease, delete it, and add it again with different parameters.
Reopen the lease file and check that the lease is as expected.
Added new unit test.
>
> '''src/lib/dhcpsrv/tests/mysql_lease_mgr_unittest.cc'''
> Comments: Strictly speaking, there is no need for the v4 and v6 leases
to share the same database: the system could be configured so that they
use different databases. However the schema for the database defines both
V4 and V6 tables so whatever the configuration, the database has
appropriate tables to hold the data being stored.
Ok. But, what exactly I should do about it? I couldn't find a culprit
comment.
>
>
> '''src/lib/util/csv_file.{h,cc}'''[[BR]]
> CSVRow::parse: as this is C++, shouldn't the argument be a const
std::string&? Every time it is used in this file, the caller converts a
string to a char* using the c_str() method and the first thing the method
does is to convert it back to a string.
I made it a string.
>
> CSVRow::parse: wouldn't it be easier to use boost::split (as used in
hwaddr.cc) to split a CSV line into multiple fields?
Ok. I use split now.
>
> CSVRow::EMPTY_ROW: I can see why EMPTY_ROW is used, but as the use is
typically comparing a row just read from the file to EMPTY_ROW, I can't
help feeling that a CSVRow::empty() method would be better.
I assign empty row to the object, not only check that it is empty.
>
> General: Some of the one-line methods (e.g. operator==(), operator!=(),
writeAt with a "string" as a second argument) could be encoded in the
header file, allowing for optimisation by the compiler.
Moved to the header file.
>
> CSVFile: rather than open/recreate, why not open() with an argument
stating "always open a new file regardless"?
These are two different functionalities. I didn't want to enclose them in
one super-big and complex function. I think it makes them clearer.
>
> CSVFile::checkStreamStatus: in most cases where it is used,
checkStreamStatus() is followed by fs_->clear(). (The exception is in the
flush() method, where clear is not called: is this an oversight?) Suggest
adding the clear() call into the method and renaming the method something
like checkStreamStatusAndReset().
True. I put clear() into checkStreamStatus
>
> CSVFile::addColumn: it appears that columns can be added dynamically
while the file is open. If this is not desired, the method should throw an
exception if fs_ is not null.
Right. addColumn was also used internally in which case it was allowed for
the stream to be opened. But, I added another protected function that
serves this purpose. From the public interface it is now not possible to
add column while a stream is open.
>
> CSVFile::append: seekg is only used on input streams, seekp is for
output streams. As this is an output method, I think the call to seekg is
unnecessary. (I see that the unit test checks that if a file is being read
and is then written to, the input pointer is pushed to the end of the file
- I'm not clear why this behaviour is required.)
In theory. In practice the seekg and seekp happen to do the same thing on
iostream. I added some extra comment.
>
> CSVFile::getColumnName: the comparison should return an error if the
given col_index is ">=" the number of columns (indexes start at 0 and go
through to "size-1").
Good catch.
>
> CSVFile::open: a check is made whether the file is open and if not, an
error is thrown. The isc_throw call is preceded by a call to close: this
is unnecessary as the immediately preceding "if" test shows that the file
is not open. (But see the next comment.)
Not only does close() performs an actual close, but it also sets the fs_
pointer to NULL, which is essential.
>
> CSVFile::open: the "else" block comprises a series of tests; if a test
fails, the file is closed and an exception thrown. The final clause in
that block is a loop that calls addColumn on each iteration. Should
addColumn throw an exception (which it can if adding a duplicate column),
close() will not be called. To get round this, I suggest putting the
whole "else" clause in a "try" block with the close() call in the
corresponding "catch", i.e.
> {{{
> else {
> try {
> :
> if (!fs_->good()) {
> isc_throw(CSVFileError, "unable to set read pointer in
the file '"
> << filename_ << "'");
> }
> :
> etc.
> :
> } catch (...) {
> close();
> throw; // re-throw the exception
> }
> }
> }}}
I wanted to avoid too many "catches" but I agree it is less error prone
and more readable.
>
> '''!ChangeLog'''[[BR]]
> If my suggestions about parameter names is adopted, s/new
configuration/configuration/
>
> s/using configuration//
They aren't.
>
> Otherwise the !ChangeLog entry looks OK.
Ok. Thanks.
--
Ticket URL: <http://bind10.isc.org/ticket/3360#comment:7>
BIND 10 Development <http://bind10.isc.org>
BIND 10 Development
More information about the bind10-tickets
mailing list