Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix(subnet.jinja): multiple ranges allowed in pool as per docs #59

Open
wants to merge 1 commit into
base: master
Choose a base branch
from

Conversation

waynegemmell
Copy link

@waynegemmell waynegemmell commented Feb 24, 2022

PR progress checklist (to be filled in by reviewers)

  • Changes to documentation are appropriate (or tick if not required)
  • Changes to tests are appropriate (or tick if not required)
  • Reviews completed

What type of PR is this?

Primary type

  • [build] Changes related to the build system
  • [chore] Changes to the build process or auxiliary tools and libraries such as documentation generation
  • [ci] Changes to the continuous integration configuration
  • [feat] A new feature
  • [fix] A bug fix
  • [perf] A code change that improves performance
  • [refactor] A code change that neither fixes a bug nor adds a feature
  • [revert] A change used to revert a previous commit
  • [style] Changes that do not affect the meaning of the code (white-space, formatting, missing semi-colons, etc.)

Secondary type

  • [docs] Documentation changes
  • [test] Adding missing or correcting existing tests

Does this PR introduce a BREAKING CHANGE?

No.

Related issues and/or pull requests

None

Describe the changes you're proposing

Pools should allow multiple ranges. It's in the docs but doesn't work in practice. I've just added a for loop to fix that.

Pillar / config required to test the proposed changes

From pillar.example

  pools:
    # And no, those quotation marks won't get stripped:
    - allow: members of "foo"
      range:
        - 10.17.224.10
        - 10.17.224.250
    - deny: members of "foo"
      range:
        - 10.0.29.10
        - 10.0.29.230

Debug log showing how the proposed changes work

Documentation checklist

  • Updated the README (e.g. Available states).
  • Updated pillar.example.

Testing checklist

  • Included in Kitchen (i.e. under state_top).
  • Covered by new/existing tests (e.g. InSpec, Serverspec, etc.).
  • Updated the relevant test pillar.

Additional context

Copy link
Member

@myii myii left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@waynegemmell Thanks for the PR. Still some work needed here.

  1. salt-lint violation needs to be resolved.
[201] Trailing whitespace
dhcpd/files/subnet.jinja:82
    {%- for range_def in pool.range %}
  1. Test(s) will need to be updated, since all the instances are failing.

  2. It would be helpful if you could link to the upstream docs, showing how this is supposed to be defined.

Pools should allow multiple ranges. It's in the docs but doesn't work in practice.

@waynegemmell
Copy link
Author

  1. Will sort trivial whitespace issue asap.
  2. I'm not sure about the tests. I'll have a look.
  3. The documentation in question is the pillar.example. It's already there.

@myii
Copy link
Member

myii commented Feb 25, 2022

  1. Will sort trivial whitespace issue asap.

  2. I'm not sure about the tests. I'll have a look.

@waynegemmell Might not be necessary, see below.

  1. The documentation in question is the pillar.example. It's already there.

I meant the upstream documentation. I've searched for it myself:

https://kb.isc.org/v1/docs/isc-dhcp-44-manual-pages-dhcpdconf#examples

...

subnet 204.254.239.0 netmask 255.255.255.224 {
  subnet-specific parameters...
  range 204.254.239.10 204.254.239.30;
}

subnet 204.254.239.32 netmask 255.255.255.224 {
  subnet-specific parameters...
  range 204.254.239.42 204.254.239.62;
}

subnet 204.254.239.64 netmask 255.255.255.224 {
  subnet-specific parameters...
  range 204.254.239.74 204.254.239.94;
}

...

So it looks like the formula is already working as expected:

pool {
allow members of "foo";
range 10.17.224.10 10.17.224.250;
}
pool {
deny members of "foo";
range 10.0.29.10 10.0.29.230;
}

Unless you see something I've missed.


Other references:

@waynegemmell
Copy link
Author

For one thing, I have that in production.
The definition is as follows.

The range statement

range [ dynamic-bootp ] low-address [ high-address];

For any subnet on which addresses will be assigned dynamically, there must be at least one range statement. The range statement gives the lowest and highest IP addresses in a range. All IP addresses in the range should be in the subnet in which the range statement is declared. The dynamic-bootp flag may be specified if addresses in the specified range may be dynamically assigned to BOOTP clients as well as DHCP clients. When specifying a single address, high-address can be omitted. 

So at least one, which to me implies more than one . I do this in practice and it works fine.

@myii
Copy link
Member

myii commented Feb 25, 2022

@waynegemmell Perhaps this is easier to understand with a comparison. Taking the block from pillar.example, which is used in the CI here:

subnets:
10.152.187.0:
comment: |-
No service will be given on this subnet, but declaring it helps the
DHCP server to understand the network topology.
netmask: 255.255.255.0
pools:
- failover_peer: dhcp-failover
range:
- 10.152.187.1
- 10.152.187.254

Before this PR, this results in:

subnet 10.152.187.0 netmask 255.255.255.0 {
  pool {
    failover peer "dhcp-failover";
    range 10.152.187.1 10.152.187.254;
  }
}

After this PR, we get:

subnet 10.152.187.0 netmask 255.255.255.0 {
  pool {
    failover peer "dhcp-failover";
    range 10.152.187.1; 
    range 10.152.187.254;
  }
}

Taking what you've quoted above:

range [ dynamic-bootp ] low-address [ high-address];

Then we can see that the range isn't being set correctly, if this PR was merged.


However, there is something that still needs to be fixed. The current implementation does not allow for a range to be set using only a low-address. Say we wanted the following instead:

subnet 10.152.187.0 netmask 255.255.255.0 {
  pool {
    failover peer "dhcp-failover";
    range 10.152.187.1; 
  }
}

So we could adjust this PR in order to provide that functionality, depending on how the data is provided in the pillar/config. There are two obvious ways to deal with this.

Allow a single entry (low-address only) in the list, e.g.:

       pools: 
         - failover_peer: dhcp-failover 
           range: 
             - 10.152.187.1 

Or avoid using a list when there's only a low-address required:

       pools: 
         - failover_peer: dhcp-failover 
           range: 10.152.187.1 

I've seen the second method used in other formulas and it seems more explicit (string vs. list).

@waynegemmell
Copy link
Author

I definitely think the string is the way to go with this, mostly to cater for all the possible variation that you mentioned. TBH I implemented it as a string on my side and that's why I was quite certain that it worked fine. My only worry with changing this to a string is that it's a breaking change.

@waynegemmell
Copy link
Author

Having it in a list really seems like the hard way to do it.

@myii
Copy link
Member

myii commented Feb 28, 2022

Having it in a list really seems like the hard way to do it.

@waynegemmell This method was introduced ~8 years ago in #4.

In fact, looking at 9115b05, you can see that the intent was to replace the string-based method with the list-based method.

I definitely think the string is the way to go with this, mostly to cater for all the possible variation that you mentioned. TBH I implemented it as a string on my side and that's why I was quite certain that it worked fine. My only worry with changing this to a string is that it's a breaking change.

As I mentioned above, there's another way that's used in other formulas. Allow the end user to supply the data as a string or a list; process it accordingly using an if block. That has the additional benefit of avoiding a breaking change, so no one has to change their existing pillar/config files. Furthermore, by allowing a string-based method, we can supply a range with only a low-address. Basically, it should solve all of the problems that have been raised by this PR.

Try out this block:

    {%- if 'range' in pool %}
    {%-   if pool.range | is_list %}
    range {{ pool.range[0] }} {{ pool.range[1] }};
    {%-   else %}
    range {{ pool.range }};
    {%-   endif %}
    {%- endif %}

Then you can supply the data in any of the following forms.

Existing method using a list:

       pools: 
         - failover_peer: dhcp-failover 
           range: 
             - 10.152.187.1
             - 10.152.187.254

The same result by using a string:

       pools: 
         - failover_peer: dhcp-failover 
           range: 10.152.187.1 10.152.187.254

Using a string to provide only a low-address:

       pools: 
         - failover_peer: dhcp-failover 
           range: 10.152.187.1 

If this works out for you, we can finalise all the updates required to pillar.example and the InSpec tests -- we should test all three cases.

@myii
Copy link
Member

myii commented Feb 28, 2022

@waynegemmell If the method above works, for consistency, we should allow list/string configuration for all the places that the range can be set, such as:

range:
- 10.152.187.1
- 10.152.187.254

range:
- 10.254.239.10
- 10.254.239.20

range:
- 10.254.239.40
- 10.254.239.60

range:
- 10.5.5.26
- 10.5.5.30

pools:
# And no, those quotation marks won't get stripped:
- allow: members of "foo"
range:
- 10.17.224.10
- 10.17.224.250
- deny: members of "foo"
range:
- 10.0.29.10
- 10.0.29.230

@waynegemmell
Copy link
Author

Sounds good. Can we add a ranges variable as well? It would have a list of string ranges so I can still achieve what I set out to achieve.

@myii
Copy link
Member

myii commented Mar 1, 2022

Sounds good. Can we add a ranges variable as well? It would have a list of string ranges so I can still achieve what I set out to achieve.

@waynegemmell Would you mind providing an example of what you would like as the end (rendered) result? Based on the following snippet:

subnet 10.152.187.0 netmask 255.255.255.0 {
  pool {
    failover peer "dhcp-failover";
    range 10.152.187.1 10.152.187.254;
  }
}

Then we can figure out how the data should be provided and processed.

@waynegemmell
Copy link
Author

It goes something like this

subnet 10.152.0.0 netmask 255.255.0.0 {
pool {
failover peer "dhcp-failover";
range 10.152.187.1 10.152.187.254;
range 10.152.188.1 10.152.188.254;
range 10.152.189.1 10.152.189.254;
}
}

@myii
Copy link
Member

myii commented Mar 1, 2022

@waynegemmell Thanks for that example. I've also found this in the docs:

Multiple address ranges may be specified like this:

    subnet 239.252.197.0 netmask 255.255.255.0 {
      range 239.252.197.10 239.252.197.107;
      range 239.252.197.113 239.252.197.250;
    }

Reverse compatibility and multiple address ranges is now a step too far for any simple solution. Other than introducing TOFS, I reckon the cleanest solution is something like this:

  • Use a new ranges key, to deal with multiple address ranges.
    • This will be looped through in the template, to tackle each single range using the macro that follows.
  • Add a macro at the top of the template, to work with any single range line to be output.

I've got more to share but I'm short on time right now. Feel free to share any feedback that you have.

@myii
Copy link
Member

myii commented Mar 3, 2022

@waynegemmell I've put something together using macros for range and ranges, appears to be working fine, reverse-compatible as well. How would you prefer to test this out? I can push it to a branch in my fork or I could force-push it to this PR's branch. Which do you prefer?

@waynegemmell
Copy link
Author

Maybe just make a branch your side? I'll have a look asap. Slammed today. Hopefully tomorrow I can get back onto this.

@myii
Copy link
Member

myii commented Mar 3, 2022

@waynegemmell This is the branch (just one commit added):

This covers all the possible range-related configuration items, as mentioned in the comment above:

I haven't updated the pillar.example or tests just yet, simply to show that this method is still completely reverse compatible:


Firstly, the configuration examples from the comment above are all possible:

Existing method using a list:

       pools: 
         - failover_peer: dhcp-failover 
           range: 
             - 10.152.187.1
             - 10.152.187.254

The same result by using a string:

       pools: 
         - failover_peer: dhcp-failover 
           range: 10.152.187.1 10.152.187.254

Using a string to provide only a low-address:

       pools: 
         - failover_peer: dhcp-failover 
           range: 10.152.187.1 

The interesting addition is the ranges key, which can be used to provide multiple ranges. In fact, ranges can be used to do all of what range does, so one could use this for everything going forward, even when only a single range is required. If both ranges and range keys are present, the former will take precedence over the latter.

All the examples below are based on this block:

range:
- 10.254.239.10
- 10.254.239.20


Examples using lists

Where the basic range is:

      range:
        - 10.254.239.10
        - 10.254.239.20

This is identical using ranges:

      ranges:
        -
          - 10.254.239.10
          - 10.254.239.20

And for multiple ranges:

      ranges:
        -
          - 10.254.239.10
          - 10.254.239.14
        -
          - 10.254.239.15
          - 10.254.239.20

Examples using strings

Where the basic range is:

      range: 10.254.239.10 10.254.239.20

This is identical using ranges:

      ranges:
        - 10.254.239.10 10.254.239.20

And for multiple ranges:

      ranges:
        - 10.254.239.10 10.254.239.14
        - 10.254.239.15 10.254.239.20

@waynegemmell
Copy link
Author

Hi, it looks good. For some reason the config check is breaking. If I run the check manually it's fine and if I remove the check everything is fine. I'll have another look Monday. Thanks for the effort.

@myii
Copy link
Member

myii commented Mar 10, 2022

Hi, it looks good. For some reason the config check is breaking. If I run the check manually it's fine and if I remove the check everything is fine. I'll have another look Monday. Thanks for the effort.

@waynegemmell Did you manage to have another look? The only config check failures I faced were when I mistakenly tested ranges that were outside the relevant subnet/netmask. Perhaps something similar for you in your tests?

@waynegemmell
Copy link
Author

It works well. Thanks. My issue was a slight config issue which I have corrected. Apologies.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants