Generating the DHCP Configuration File

We have all the information we require, but it's not much use in its current form. All data is in the database tables, and although it spells out how the DHCP server should be configured, it cannot be used in this form. We need to write a view that will generate a configuration file, which the DHCP server will be able to understand.

Let's go back and revisit what the DHCP configuration file should look like. Since we're using the ISC DHCP server, the configuration file (including only those elements that we're interested in) has the following structure:

<dhcpd configuration items or generic DHCP options>

<class definitions>

<network definition> <subnet definition> <subnet options> <pool definitions>

Let's make this configuration file available as a web resource. So we need to approach it very similarly to the way we generated the user interface pages—we need to define a view that supplies data and the template that lays out this data on a page, in this instance a plain text document. We start with the view, shown in Listing 4-17.

Listing 4-17. The view that collects data for the DHCP configuration file def dhcpd_conf_generate(request):

class_rules = ClassRule.objects.all() networks = []

for net in DHCPNetwork.objects.all(): networks.append( { 'dhcp_net': net,

'pools' : DHCPAddressPool.objects.filter(dhcp_network=net),

return render_to_response('dhcpd.conf.txt',

{'class_rules': class_rules,

'networks': networks, }, mimetype='text/plain')

We don't keep the DHCP server configuration items in the database; therefore, we'll put them straight into the template. Class rules are simply listed outside any other structure, so we generate a list of all class rules on the system and pass it as a list.

Each DHCP subnet may have several distinct DHCP pools defined within its range, so those pools need to appear only within the specific DHCP pool definition. We therefore loop through all available DHCP networks and generate a list that contains

• The DHCP address object

• A list of all DHCP pools that are related to given DHCP network

Finally we're telling Django to change the MIME type of the document to ' text/plain '. This doesn't matter much if we're only going to download it. If you tried to open this file in a web browser, you would get the whole document presented on one line, because the web browser would think that it is a valid HTML document. So to preserve the formatting when viewing in a browser we need to format the response to indicate that the document is a flat text file.

Finally, in Listing 4-18 we have a template that puts all the data in a structure that can be used by the DHCP server.

Listing 4-18. A template for the DHCP configuration file

2 ignore client-updates;

3 ddns-update-style interim;

10 11 12

16 17 net

subnet {{ net.dhcp_net.physical_net.address }} netmask {{^ .dhcp_net.physical_net.get_netmask }} {

20 2l 22

option routers {{ net.dhcp_net.router }};

option domain-name-servers {{ net.dhcp_net.dns_server.address }}; option domain-name {{ net.dhcp_net.domain_name.name }};

allow members of "class_rule_{{ pool.class_rule.id^

range {{ pool.range_start }} {{ pool.range_finish }};

Now let's look in more detail at some of the lines.

Line 1: The Django template engine has a built-in text escaping capability that changes all characters that are not HTML-compliant to their HTML code presentation. For example, the " character would be replaced by the &quot; string. Because we're serving a flat text document, we need to present all characters in their original notation and not HTML encoded. So we turn off autoescape functionality, which is on by default.

Lines 2-3: These are just standard DHCP server configuration items, which you may want to replace with those suitable for your environment.

Lines 5-12: A simple check to see if the class_rules list is not empty, followed by a loop that goes through all elements and displays them.

Lines 14-15: Again, a pre-check to see if the networks list is not empty followed by the loop statement.

Line 17: Here you can see how we refer to related objects. We're not passing any information about the physical network directly to the template, but we can still access it through the DHCP Network object, which has a foreign key to the related Physical Network object. As long as the relation is unambiguous (a DHCP network can only belong to one physical network) you can use this syntax to access relevant information.

Lines 19-20: Similarly we're accessing related Router and DNS objects.

Lines 22-23: Check to see if there are any pools available for the DHCP network and loop through them if there are any.

Lines25-26: Note that we're generating class and network names based on their object IDs. This is the easiest way to ensure that the names are unique and can also be used to make cross-references within the configuration file.

You might have noticed that we're using the get_netmask property of the Physical Network object. This field does not exist, so what is it? Well, the DHCP server expects subnets defined as pairs consisting of a base network address and a netmask. We do not have a netmask field in the model, but it is very simple to derive from the network size, which is expressed in number of bits; Listing 4-19 shows the code.

Listing 4-19. Calculating the netmask from the network size def get_netmask(self): bit_netmask = 0;

bit_netmask = pow(2, self.network_size) bit_netmask = bit_netmask << (32 - self nmask_array = [] for c in range(4):

dec = bit_netmask & 255 bit_netmask = bit_netmask >> 8 nmask_array.insert(0, str(dec)) return ".".join(nmask_array)

The logic of this function is very simple:

• Set the number of bits in an integer variable on (that is, set them to 1). This can be expressed as 2A<number of bits> -1

• Shift the result to the left, filling in the remaining number of bits with 0. The total number of bits in a netmask is always 32.

• For every 8 bits (4 sets in total), convert them to a decimal number string.

• Join all numbers, using the dot symbol to separate individual numbers. Finally we need to add an additional URL pattern that calls this view:

url(r'Adhcpd.conf/$', views.dhcpd_conf_generate, name='dhcp-conf-generate')

Following is an example of the DHCP configuration file that was generated from some sample data I entered into my database:

.network_size)

ignore client-updates; ddns-update-style interim;

# class rule 1

class "class_rule_1" {

match if substring (option host-name, 0, 6) = "server";;

# test rule (gen form) class "class_rule_2" {

test rule - gen form;

shared-network network_4 {

subnet 192.168.0.128 netmask 255.255.255.128 { option routers 192.168.0.130; option domain-name-servers 208.67.222.222; option domain-name domain1.example.com;

shared-network network_5 {

subnet 192.168.0.0 netmask 255.255.255.128 { option routers 192.168.0.113; option domain-name-servers 208.67.220.220; option domain-name domain2.example.com; pool {

allow members of "class_rule_1"; range 192.168.0.1 192.168.0.20;

Was this article helpful?

0 0

Post a comment