-
Notifications
You must be signed in to change notification settings - Fork 23.7k
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
adding an option for the filter ipsubnet and testing #40670
Conversation
lib/ansible/plugins/filter/ipaddr.py
Outdated
def ipsubnet(value, query='', index='x'): | ||
strquery = str(query) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Please don't add anything before a docstring.
lib/ansible/plugins/filter/ipaddr.py
Outdated
@@ -734,8 +738,7 @@ def ipsubnet(value, query='', index='x'): | |||
|
|||
if not query: |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
better add it above this line
CI failure in unit tests: https://app.shippable.com/github/ansible/ansible/runs/67119/3/tests
|
lib/ansible/plugins/filter/ipaddr.py
Outdated
@@ -735,10 +734,11 @@ def ipsubnet(value, query='', index='x'): | |||
value = netaddr.IPNetwork(v) | |||
except: | |||
return False | |||
|
|||
querystr = str(query) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Also, since in Python we use snake_case
for variables/functions, please change this to query_string
lib/ansible/plugins/filter/ipaddr.py
Outdated
v = ipaddr(query, 'subnet') | ||
|
||
query = netaddr.IPNetwork(v) | ||
except: |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It is a bad practice to catch all exceptions wholesale. You mask different errors, which you should've processed correctly. Also this makes it almost impossible to debug misbehavior.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I just make the same code as before line 727 to 736 but I am not sure how to cut this the good way :
try:
vtype = ipaddr(query, 'type')
except:
return False
if vtype == 'address':
try:
v = ipaddr(query, 'cidr')
except:
return False
elif vtype == 'network':
try:
v = ipaddr(query, 'subnet')
except:
return False
query = netaddr.IPNetwork(v)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, I see that, but it's not a good approach.
From https://github.com/drkjam/netaddr/blob/ebf714601e8cd6234575422f876d4d579b202b1a/netaddr/ip/__init__.py#L938 it looks like netaddr.IPNetwork(v)
may raise AddrFormatError
.
But if you want to catch all netaddr
library's thrown exceptions, you may do:
from netaddr.core import AddrConversionError, AddrFormatError, NotRegisteredError
NETADDR_ERRORS = AddrConversionError, AddrFormatError, NotRegisteredError
...
try:
...your exceptional code here
except NETADDR_ERRORS as err:
process_or_log(err)
# optionally raise/pass/return False (whatever fits)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I still insist on correct exception handling.
lib/ansible/plugins/filter/ipaddr.py
Outdated
if network_in_network(query, value): | ||
subnetlist = list(query.subnet(value.prefixlen)) | ||
for i in range(0, len(subnetlist)): | ||
if (subnetlist[i] == value): |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
In Python, we don't wrap clauses with parentheses. Please remove them.
lib/ansible/plugins/filter/ipaddr.py
Outdated
|
||
if network_in_network(query, value): | ||
subnetlist = list(query.subnet(value.prefixlen)) | ||
for i in range(0, len(subnetlist)): |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What you're trying to do is possible to achieve using "sliding window" technique with enumerate()
, it will be possible to even drop dancing around "index+1" problem:
for i, subnet in enumerate(query.subnet(value.prefixlen)):
if subnet == value:
return str(i+1)
lib/ansible/plugins/filter/ipaddr.py
Outdated
return False | ||
|
||
if network_in_network(query, value): | ||
subnetlist = list(query.subnet(value.prefixlen)) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
According to the doc, it's a generator: https://netaddr.readthedocs.io/en/latest/api.html#netaddr.IPNetwork.subnet
So you can iterate over it, no need to list()
it. Please remove it.
lib/ansible/plugins/filter/ipaddr.py
Outdated
except: | ||
return False | ||
|
||
if network_in_network(query, value): |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I believe, you don't even need the if-wrapper, if query.subnet
won't yield anything, the loop will be "skipped", just like your intention is.
@pierremahot This PR contains |
It's my choice i prefert to have the first subnet to have the value 1 and not 0. |
lib/ansible/plugins/filter/ipaddr.py
Outdated
@@ -782,7 +782,7 @@ def ipsubnet(value, query='', index='x'): | |||
except: | |||
return False | |||
|
|||
for i, subnet in enumerate(query.subnet(value.prefixlen)): | |||
for i, subnet in enumerate(query.subnet(value.prefixlen), 1): |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
😍 TIL
I've restarted failed CI jobs. |
@@ -473,3 +473,32 @@ def test_cidr_merge(self): | |||
subnets = ['1.12.1.1', '1.12.1.255'] | |||
self.assertEqual(cidr_merge(subnets), ['1.12.1.1/32', '1.12.1.255/32']) | |||
self.assertEqual(cidr_merge(subnets, 'span'), '1.12.1.0/24') | |||
|
|||
def test_ipsubnet(self): |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
where's negative tests?
self.assertEqual(ipsubnet(address, subnet), False) | ||
address = '192.168.144.5' | ||
subnet = '192.168.0.0/16' | ||
self.assertEqual(ipsubnet(address), '192.168.144.5/32') |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Please keep your code style consistent: first you assign vars for some time, then you pass lots of function args directly. There's a better way.
lib/ansible/plugins/filter/ipaddr.py
Outdated
v = ipaddr(query, 'subnet') | ||
query = netaddr.IPNetwork(v) | ||
except NETADDR_ERRORS as err: | ||
raise errors.AnsibleFilterError(err) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is minor, but I like to keep exception chain when re-raising new ones. This is how you do it in forward-compatible manner:
from ansible.module_utils import six
...
except NETADDR_ERRORS as err:
six.raise_from(errors.AnsibleFilterError(err), err)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
👍 🎉
I have a problème with the exception :( doesn't look to work it return 1 - hosts: localhost
tasks:
- debug:
msg: "{{ '192.168.1.1/24' | ipsubnet('-5') }}" result
|
@pierremahot can you add that to integration tests? |
Also, what do you mean by exception? |
self._test_ipsubnet(args, res) | ||
|
||
def _test_ipsubnet(self, ipsubnet_args, expected_result): | ||
self.assertEqual(ipsubnet(*ipsubnet_args), expected_result) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'd also add some test cases with assertRaises
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think
vtype = ipaddr(query, 'type')
if vtype == 'address':
v = ipaddr(query, 'cidr')
elif vtype == 'network':
v = ipaddr(query, 'subnet')
else:
return False
Can't raise error on :
query = netaddr.IPNetwork(v)
for i, subnet in enumerate(query.subnet(value.prefixlen), 1)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
i don't find the case
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You could mock.patch
it to do so :)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Also, look out for netaddr docs for the cases when it'll raise the exception.
I can't have assertion error because of the way ipaddr works |
expected = 'An Exception indicating a failure to convert between address types or notations.' | ||
with self.assertRaises(AddrConversionError) as exc: | ||
ipsubnet('192.168.144.5', '192.168.8.1.5') | ||
self.assertEqual(exc.exception.message, expected) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This needs to go outside of CM. If ipsubnet
will raise exception it will interrupt code block within with
.
self.assertEqual(ipsubnet(*ipsubnet_args), expected_result) | ||
|
||
expected = 'An Exception indicating a network address is not correctly formatted.' | ||
with self.assertRaises(AddrConversionError) as exc: |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think, you're looking for self.assertRaisesRegexp(AddrConversionError, expected)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't find case to raise an error
I think ipaddr is the function that can raise error but it filter already the input of netaddr i don't find case where it raise an error |
Yeah, you're right: |
@@ -541,3 +537,12 @@ def test_ipsubnet(self): | |||
|
|||
def _test_ipsubnet(self, ipsubnet_args, expected_result): | |||
self.assertEqual(ipsubnet(*ipsubnet_args), expected_result) | |||
expected = 'You must pass a valid subnet or IP address; invalid_subnet is invalid' | |||
with self.assertRaises(AnsibleFilterError) as exc: |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Please use assertRaisesRegexp
instead.
SUMMARY
Adding the option to request the index of a subnet in a bigger subnet
ISSUE TYPE
COMPONENT NAME
filter ipsubnet
ANSIBLE VERSION
ADDITIONAL INFORMATION
Need this feature to deduce easily a vlan for my switch