openwisp / netjsonconfig

Network configuration management library based on NetJSON DeviceConfiguration
https://netjsonconfig.openwisp.org/
Other
359 stars 72 forks source link

Overriding json-schema from backend does not override definitions #93

Open edoput opened 7 years ago

edoput commented 7 years ago

Let's say that we have a custom schema like in airos and that we print the schema, like it happens on a validation error report.

The interesting part is that redefined definitions do not override the default one as it can be read from the log

Against schema {'$ref': '#/definitions/ap_wireless_settings'}
{'proto': 'wpa2_personal', 'key': 'and-pizza-too'} is valid under each of
{'$ref': '#/definitions/encryption_wpa_personal'},
{'$ref': '#/definitions/encryption_wps'},
{'$ref': '#/definitions/encryption_wep'},
OrderedDict([('$ref', '#/definitions/encryption_none')]),
OrderedDict([('$ref', '#/definitions/encryption_wpa_personal')]),
OrderedDict([('$ref', '#/definitions/encryption_wpa_enterprise_sta')]),
{'$ref': '#/definitions/encryption_none'}

It seems that merge_config can merge a json-schema and OrderedDict but it treat the same definition as different elements.

nemesifier commented 7 years ago

@EdoPut could you add an example of how to replicate the error please?

edoput commented 7 years ago

Using the schema and tests from https://github.com/openwisp/netjsonconfig/commit/7c8e27bb34476e1b4cc70b398345cb8df33d3728 you can reproduce this with the command

nosetest tests/airos/test_aaa.py

Paying attention to the logs from the failing tests we can see that it raises a validation error because of two different schema that validate the same instance under a OneOf constraint

As an example

======================================================================
ERROR: test_sta_psk_authentication (tests.airos.test_aaa.TestResolvConverter)
----------------------------------------------------------------------
Traceback (most recent call last):
  File "/home/edoput/repo/netjsonconfig/netjsonconfig/backends/base/backend.py", line 113, in validate
    validate(self.config, self.schema, format_checker=FormatChecker())
  File "/home/edoput/.virtualenvs/netjsonconfig3/local/lib/python3.5/site-packages/jsonschema/validators.py", line 541, in validate
    cls(schema, *args, **kwargs).validate(instance)
  File "/home/edoput/.virtualenvs/netjsonconfig3/local/lib/python3.5/site-packages/jsonschema/validators.py", line 130, in validate
    raise error
jsonschema.exceptions.ValidationError: {'type': 'wireless', 'name': 'wlan0', 'wireless': {'bssid': '00:11:22:33:44:55', 'mode': 'station', 'radio': 'ath0', 'encryption': {'protocol': 'wpa2_personal', 'key': 'and-pizza-too'}, 'ssid': 'i-like-pasta'}} is not valid under any of the given schemas

Failed validating 'oneOf' in schema['properties']['interfaces']['items']:
    {'oneOf': [{'$ref': '#/definitions/network_interface'},
               {'$ref': '#/definitions/wireless_interface'},
               {'$ref': '#/definitions/bridge_interface'}],
     'title': 'Interface'}

On instance['interfaces'][0]:
    {'name': 'wlan0',
     'type': 'wireless',
     'wireless': {'bssid': '00:11:22:33:44:55',
                  'encryption': {'key': 'and-pizza-too',
                                 'protocol': 'wpa2_personal'},
                  'mode': 'station',
                  'radio': 'ath0',
                  'ssid': 'i-like-pasta'}}

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
  File "/home/edoput/repo/netjsonconfig/tests/airos/test_aaa.py", line 142, in test_sta_psk_authentication
    o.to_intermediate()
  File "/home/edoput/repo/netjsonconfig/netjsonconfig/backends/airos/airos.py", line 54, in to_intermediate
    super(AirOs, self).to_intermediate()
  File "/home/edoput/repo/netjsonconfig/netjsonconfig/backends/base/backend.py", line 252, in to_intermediate
    self.validate()
  File "/home/edoput/repo/netjsonconfig/netjsonconfig/backends/base/backend.py", line 115, in validate
    raise ValidationError(e)
netjsonconfig.exceptions.ValidationError: ValidationError {'type': 'wireless', 'name': 'wlan0', 'wireless': {'bssid': '00:11:22:33:44:55', 'mode': 'station', 'radio': 'ath0', 'encryption': {'protocol': 'wpa2_personal', 'key': 'and-pizza-too'}, 'ssid': 'i-like-pasta'}} is not valid under any of the given schemas

Failed validating 'oneOf' in schema['properties']['interfaces']['items']:
    {'oneOf': [{'$ref': '#/definitions/network_interface'},
               {'$ref': '#/definitions/wireless_interface'},
               {'$ref': '#/definitions/bridge_interface'}],
     'title': 'Interface'}

On instance['interfaces'][0]:
    {'name': 'wlan0',
     'type': 'wireless',
     'wireless': {'bssid': '00:11:22:33:44:55',
                  'encryption': {'key': 'and-pizza-too',
                                 'protocol': 'wpa2_personal'},
                  'mode': 'station',
                  'radio': 'ath0',
                  'ssid': 'i-like-pasta'}}

Against schema {'$ref': '#/definitions/network_interface'}
'wireless' is not one of ['ethernet', 'virtual', 'loopback', 'other']

Against schema {'$ref': '#/definitions/wireless_interface'}
{'bssid': '00:11:22:33:44:55', 'mode': 'station', 'radio': 'ath0', 'encryption': {'protocol': 'wpa2_personal', 'key': 'and-pizza-too'}, 'ssid': 'i-like-pasta'} is not valid under any of the given schemas

Against schema {'$ref': '#/definitions/ap_wireless_settings'}
'station' is not one of ['access_point']

Against schema {'$ref': '#/definitions/sta_wireless_settings'}
{'protocol': 'wpa2_personal', 'key': 'and-pizza-too'} is valid under each of OrderedDict([('$ref', '#/definitions/encryption_wpa_personal')]), {'$ref': '#/definitions/encryption_wpa_personal'}

Against schema {'$ref': '#/definitions/adhoc_wireless_settings'}
{'protocol': 'wpa2_personal', 'key': 'and-pizza-too'} is valid under each of OrderedDict([('$ref', '#/definitions/encryption_wpa_personal')]), {'$ref': '#/definitions/encryption_wpa_personal'}

Against schema {'$ref': '#/definitions/monitor_wireless_settings'}
'station' is not one of ['adhoc']

Against schema {'$ref': '#/definitions/mesh_wireless_settings'}
'station' is not one of ['monitor']

Against schema {'$ref': '#/definitions/bridge_interface'}
'wireless' is not one of ['bridge']

where the interesting part is

Against schema {'$ref': '#/definitions/sta_wireless_settings'}
{'protocol': 'wpa2_personal', 'key': 'and-pizza-too'} is valid under each of OrderedDict([('$ref', '#/definitions/encryption_wpa_personal')]), {'$ref': '#/definitions/encryption_wpa_personal'}

Obviously there are two because the airos branch has an override for the available authentication encryption here

nemesifier commented 7 years ago

Spent some time to analyze the issue, but unfortunately it is not a bug but the intended mechanism.

Read more about this here: http://netjsonconfig.openwisp.org/en/stable/general/basics.html#implementation-details

Pay attention to these two passages:

  1. values of type list in both config and template will be merged using to the merge_list function
  2. The remaining elements will be summed in order to create a list which contains elements of both lists.

I will suggest a workaround for your issue on PR #91