From 6ed33af0636e48d4bd9e834b6c9ab79233807aa4 Mon Sep 17 00:00:00 2001 From: Robert Drake Date: Sat, 6 Aug 2016 02:35:42 -0400 Subject: [PATCH 01/10] Changes to Dockerfile to make the build faster To download a new version with docker, I've been running git pull docker-compose build --no-cache This is slow, but no-cache is needed so that "git clone" pulls the latest copy. Most of the slowness comes from pulling down apt files each time a rebuild needs to be done. If we move that into a docker image then only the local changes need to be rebuilt. Further refinements could be done. If the python dependencies that are brought in from requirements.txt could be moved to an image then nothing would change between updates as long as dependant versions hadn't changed. This would probably be more trouble than it's worth, unless you're recreating netbox containers 10-20 times a day. --- Dockerfile | 26 ++++++-------------------- requirements.txt | 1 + 2 files changed, 7 insertions(+), 20 deletions(-) diff --git a/Dockerfile b/Dockerfile index c2e2c38ab34..aac301f2bd9 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,24 +1,10 @@ -FROM ubuntu:14.04 +FROM python:2.7-wheezy -RUN apt-get update && apt-get install -y \ - python2.7 \ - python-dev \ - git \ - python-pip \ - libxml2-dev \ - libxslt1-dev \ - libffi-dev \ - graphviz \ - libpq-dev \ - build-essential \ - gunicorn \ - --no-install-recommends \ - && rm -rf /var/lib/apt/lists/* \ - && mkdir -p /opt/netbox \ - && cd /opt/netbox \ - && git clone --depth 1 https://github.com/digitalocean/netbox.git -b master . \ - && pip install -r requirements.txt \ - && apt-get purge -y --auto-remove git build-essential +WORKDIR /opt/netbox + +ADD . /opt/netbox +#RUN git clone --depth 1 https://github.com/digitalocean/netbox.git -b master . \ +RUN pip install -r requirements.txt ADD docker/docker-entrypoint.sh /docker-entrypoint.sh ADD netbox/netbox/configuration.docker.py /opt/netbox/netbox/netbox/configuration.py diff --git a/requirements.txt b/requirements.txt index 40b0b707fea..f1836ea7f71 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,3 +1,4 @@ +gunicorn==17.5 cryptography==1.4 Django==1.10 django-debug-toolbar==1.4 From de8fd550cba5f8a28db9fd1927732ff1d144fdae Mon Sep 17 00:00:00 2001 From: Jeremy Stretch Date: Thu, 18 Aug 2016 10:12:43 -0400 Subject: [PATCH 02/10] Fixes #484: Allow bulk deletion of >1K objects --- netbox/netbox/settings.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/netbox/netbox/settings.py b/netbox/netbox/settings.py index 6e2359ebe6f..289367d37c4 100644 --- a/netbox/netbox/settings.py +++ b/netbox/netbox/settings.py @@ -164,6 +164,9 @@ STATICFILES_DIRS = ( os.path.join(BASE_DIR, "project-static"), ) +# Disable default limit of 1000 fields per request. Needed for bulk deletion of objects. (Added in Django 1.10.) +DATA_UPLOAD_MAX_NUMBER_FIELDS = None + # Messages MESSAGE_TAGS = { messages.ERROR: 'danger', From 63ac4e2c429e7cdd20503e095da4679c602fed30 Mon Sep 17 00:00:00 2001 From: Jeremy Stretch Date: Thu, 18 Aug 2016 15:23:28 -0400 Subject: [PATCH 03/10] Fixes #476: Corrected rack import instructions --- netbox/templates/dcim/rack_import.html | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/netbox/templates/dcim/rack_import.html b/netbox/templates/dcim/rack_import.html index e088f080944..c5775cffdb9 100644 --- a/netbox/templates/dcim/rack_import.html +++ b/netbox/templates/dcim/rack_import.html @@ -53,6 +53,11 @@ Name of tenant (optional) Pied Piper + + Role + Functional role (optional) + Compute + Type Rack type (optional) @@ -71,7 +76,7 @@

Example

-
DC-4,Cage 1400,R101,J12.100,Pied Piper,4-post cabinet,19,42
+
DC-4,Cage 1400,R101,J12.100,Pied Piper,Compute,4-post cabinet,19,42
{% endblock %} From 041a166217f6720dcbfe1fc17d2d17d3918c5289 Mon Sep 17 00:00:00 2001 From: Jeremy Stretch Date: Thu, 18 Aug 2016 15:31:31 -0400 Subject: [PATCH 04/10] Revert "Changes to Dockerfile to make the build faster" --- Dockerfile | 26 ++++++++++++++++++++------ requirements.txt | 1 - 2 files changed, 20 insertions(+), 7 deletions(-) diff --git a/Dockerfile b/Dockerfile index aac301f2bd9..c2e2c38ab34 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,10 +1,24 @@ -FROM python:2.7-wheezy +FROM ubuntu:14.04 -WORKDIR /opt/netbox - -ADD . /opt/netbox -#RUN git clone --depth 1 https://github.com/digitalocean/netbox.git -b master . \ -RUN pip install -r requirements.txt +RUN apt-get update && apt-get install -y \ + python2.7 \ + python-dev \ + git \ + python-pip \ + libxml2-dev \ + libxslt1-dev \ + libffi-dev \ + graphviz \ + libpq-dev \ + build-essential \ + gunicorn \ + --no-install-recommends \ + && rm -rf /var/lib/apt/lists/* \ + && mkdir -p /opt/netbox \ + && cd /opt/netbox \ + && git clone --depth 1 https://github.com/digitalocean/netbox.git -b master . \ + && pip install -r requirements.txt \ + && apt-get purge -y --auto-remove git build-essential ADD docker/docker-entrypoint.sh /docker-entrypoint.sh ADD netbox/netbox/configuration.docker.py /opt/netbox/netbox/netbox/configuration.py diff --git a/requirements.txt b/requirements.txt index f1836ea7f71..40b0b707fea 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,4 +1,3 @@ -gunicorn==17.5 cryptography==1.4 Django==1.10 django-debug-toolbar==1.4 From ab90a06c54fb90e5f2af7ef361e2a61ccf57c806 Mon Sep 17 00:00:00 2001 From: Jeremy Stretch Date: Thu, 18 Aug 2016 16:43:41 -0400 Subject: [PATCH 05/10] Fixes #486: Prompt for secret key only if updating a secret's value --- netbox/project-static/js/secrets.js | 11 +++++++---- netbox/secrets/forms.py | 10 ++++++---- netbox/templates/secrets/secret_edit.html | 2 +- netbox/templates/secrets/secret_import.html | 2 +- netbox/utilities/forms.py | 2 +- 5 files changed, 16 insertions(+), 11 deletions(-) diff --git a/netbox/project-static/js/secrets.js b/netbox/project-static/js/secrets.js index 7f67cdcda55..fadcc0d39a8 100644 --- a/netbox/project-static/js/secrets.js +++ b/netbox/project-static/js/secrets.js @@ -25,17 +25,20 @@ $(document).ready(function() { }); // Adding/editing a secret - $('form.requires-private-key').submit(function(event) { + private_key_field = $('#id_private_key'); + private_key_field.parents('form').submit(function(event) { + console.log("form submitted"); var private_key = sessionStorage.getItem('private_key'); if (private_key) { - $('#id_private_key').val(private_key); - } else { + private_key_field.val(private_key); + } else if ($('form .requires-private-key:first').val()) { + console.log("we need a key!"); $('#privkey_modal').modal('show'); return false; } }); - // Prompt the user to enter a private RSA key for decryption + // Saving a private RSA key locally $('#submit_privkey').click(function() { var private_key = $('#user_privkey').val(); sessionStorage.setItem('private_key', private_key); diff --git a/netbox/secrets/forms.py b/netbox/secrets/forms.py index 06c963957cf..1e45fe1630d 100644 --- a/netbox/secrets/forms.py +++ b/netbox/secrets/forms.py @@ -47,8 +47,9 @@ class SecretRoleForm(forms.ModelForm, BootstrapMixin): # class SecretForm(forms.ModelForm, BootstrapMixin): - private_key = forms.CharField(widget=forms.HiddenInput()) - plaintext = forms.CharField(max_length=65535, required=False, label='Plaintext') + private_key = forms.CharField(required=False, widget=forms.HiddenInput()) + plaintext = forms.CharField(max_length=65535, required=False, label='Plaintext', + widget=forms.TextInput(attrs={'class': 'requires-private-key'})) plaintext2 = forms.CharField(max_length=65535, required=False, label='Plaintext (verify)') class Meta: @@ -56,7 +57,8 @@ class SecretForm(forms.ModelForm, BootstrapMixin): fields = ['role', 'name', 'plaintext', 'plaintext2'] def clean(self): - validate_rsa_key(self.cleaned_data['private_key']) + if self.cleaned_data['plaintext']: + validate_rsa_key(self.cleaned_data['private_key']) def clean_plaintext2(self): plaintext = self.cleaned_data['plaintext'] @@ -84,7 +86,7 @@ class SecretFromCSVForm(forms.ModelForm): class SecretImportForm(BulkImportForm, BootstrapMixin): private_key = forms.CharField(widget=forms.HiddenInput()) - csv = CSVDataField(csv_form=SecretFromCSVForm) + csv = CSVDataField(csv_form=SecretFromCSVForm, widget=forms.Textarea(attrs={'class': 'requires-private-key'})) class SecretBulkEditForm(forms.Form, BootstrapMixin): diff --git a/netbox/templates/secrets/secret_edit.html b/netbox/templates/secrets/secret_edit.html index 9a3df1a4550..c2426391f15 100644 --- a/netbox/templates/secrets/secret_edit.html +++ b/netbox/templates/secrets/secret_edit.html @@ -5,7 +5,7 @@ {% block title %}{% if secret.pk %}Editing {{ secret }}{% else %}Add a Secret{% endif %}{% endblock %} {% block content %} -
+ {% csrf_token %} {{ form.private_key }}
diff --git a/netbox/templates/secrets/secret_import.html b/netbox/templates/secrets/secret_import.html index 56e4194e493..9c7f6764049 100644 --- a/netbox/templates/secrets/secret_import.html +++ b/netbox/templates/secrets/secret_import.html @@ -17,7 +17,7 @@
{% endif %} - + {% csrf_token %} {% render_form form %}
diff --git a/netbox/utilities/forms.py b/netbox/utilities/forms.py index f1c942fe65b..4cbc1028bcd 100644 --- a/netbox/utilities/forms.py +++ b/netbox/utilities/forms.py @@ -130,11 +130,11 @@ class CSVDataField(forms.CharField): '"New York, NY",new-york-ny,Other stuff' => ['New York, NY', 'new-york-ny', 'Other stuff'] """ csv_form = None + widget = forms.Textarea def __init__(self, csv_form, *args, **kwargs): self.csv_form = csv_form self.columns = self.csv_form().fields.keys() - self.widget = forms.Textarea super(CSVDataField, self).__init__(*args, **kwargs) self.strip = False if not self.label: From 4b4602b7039ecfa290aaa87893776455d489c0f4 Mon Sep 17 00:00:00 2001 From: Robert Drake Date: Sat, 6 Aug 2016 02:35:42 -0400 Subject: [PATCH 06/10] Changes to Dockerfile to make the build faster To download a new version with docker, I've been running git pull docker-compose build --no-cache This is slow, but no-cache is needed so that "git clone" pulls the latest copy. Most of the slowness comes from pulling down apt files each time a rebuild needs to be done. If we move that into a docker image then only the local changes need to be rebuilt. Further refinements could be done. If the python dependencies that are brought in from requirements.txt could be moved to an image then nothing would change between updates as long as dependant versions hadn't changed. This would probably be more trouble than it's worth, unless you're recreating netbox containers 10-20 times a day. --- Dockerfile | 26 ++++++-------------------- 1 file changed, 6 insertions(+), 20 deletions(-) diff --git a/Dockerfile b/Dockerfile index c2e2c38ab34..fc05a45075c 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,24 +1,10 @@ -FROM ubuntu:14.04 +FROM python:2.7-wheezy -RUN apt-get update && apt-get install -y \ - python2.7 \ - python-dev \ - git \ - python-pip \ - libxml2-dev \ - libxslt1-dev \ - libffi-dev \ - graphviz \ - libpq-dev \ - build-essential \ - gunicorn \ - --no-install-recommends \ - && rm -rf /var/lib/apt/lists/* \ - && mkdir -p /opt/netbox \ - && cd /opt/netbox \ - && git clone --depth 1 https://github.com/digitalocean/netbox.git -b master . \ - && pip install -r requirements.txt \ - && apt-get purge -y --auto-remove git build-essential +WORKDIR /opt/netbox + +ADD . /opt/netbox +RUN git clone --depth 1 https://github.com/digitalocean/netbox.git -b master . \ +RUN pip install gunicorn==17.5 && pip install -r requirements.txt ADD docker/docker-entrypoint.sh /docker-entrypoint.sh ADD netbox/netbox/configuration.docker.py /opt/netbox/netbox/netbox/configuration.py From 8cf2ae7851e587666db09450dc1c87367a95b255 Mon Sep 17 00:00:00 2001 From: Jeremy Stretch Date: Fri, 19 Aug 2016 10:31:16 -0400 Subject: [PATCH 07/10] Fixes #495: Include tenant in prefix and IP CSV export --- netbox/ipam/models.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/netbox/ipam/models.py b/netbox/ipam/models.py index eebc43fad1d..1639daee684 100644 --- a/netbox/ipam/models.py +++ b/netbox/ipam/models.py @@ -275,6 +275,7 @@ class Prefix(CreatedUpdatedModel): return ','.join([ str(self.prefix), self.vrf.rd if self.vrf else '', + self.tenant.name if self.tenant else '', self.site.name if self.site else '', self.get_status_display(), self.role.name if self.role else '', @@ -378,6 +379,7 @@ class IPAddress(CreatedUpdatedModel): return ','.join([ str(self.address), self.vrf.rd if self.vrf else '', + self.tenant.name if self.tenant else '', self.device.identifier if self.device else '', self.interface.name if self.interface else '', 'True' if is_primary else '', From 7a558d8332e6ef74df84f4f1e7f6f4851811a579 Mon Sep 17 00:00:00 2001 From: Jeremy Stretch Date: Fri, 19 Aug 2016 10:41:37 -0400 Subject: [PATCH 08/10] Fixes #490: Corrected display of circuit commit rate --- netbox/templates/circuits/circuit.html | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/netbox/templates/circuits/circuit.html b/netbox/templates/circuits/circuit.html index 099832054b7..489dc42a658 100644 --- a/netbox/templates/circuits/circuit.html +++ b/netbox/templates/circuits/circuit.html @@ -95,8 +95,8 @@ Commit Rate - {% if circuit.commit_speed %} - {{ circuit.commit_speed_human }} + {% if circuit.commit_rate %} + {{ circuit.commit_rate_human }} {% else %} N/A {% endif %} From eab18a81c9088e118ce2a68e0a449967ce1935c9 Mon Sep 17 00:00:00 2001 From: Jeremy Stretch Date: Fri, 19 Aug 2016 12:19:02 -0400 Subject: [PATCH 09/10] Adjusted display of created and last_updated times for primary objects --- netbox/templates/circuits/circuit.html | 9 +-------- netbox/templates/circuits/provider.html | 9 +-------- netbox/templates/dcim/device.html | 9 +-------- netbox/templates/dcim/rack.html | 9 +-------- netbox/templates/dcim/site.html | 9 +-------- netbox/templates/inc/created_updated.html | 3 +++ netbox/templates/ipam/aggregate.html | 9 +-------- netbox/templates/ipam/ipaddress.html | 9 +-------- netbox/templates/ipam/prefix.html | 10 ++-------- netbox/templates/ipam/vlan.html | 9 +-------- netbox/templates/ipam/vrf.html | 9 +-------- netbox/templates/secrets/secret.html | 9 +-------- netbox/templates/tenancy/tenant.html | 9 +-------- netbox/templates/users/userkey.html | 1 + 14 files changed, 17 insertions(+), 96 deletions(-) create mode 100644 netbox/templates/inc/created_updated.html diff --git a/netbox/templates/circuits/circuit.html b/netbox/templates/circuits/circuit.html index 489dc42a658..8844dc43a91 100644 --- a/netbox/templates/circuits/circuit.html +++ b/netbox/templates/circuits/circuit.html @@ -102,16 +102,9 @@ {% endif %} - - Created - {{ circuit.created }} - - - Last Updated - {{ circuit.last_updated }} -
+ {% include 'inc/created_updated.html' with obj=circuit %}
diff --git a/netbox/templates/circuits/provider.html b/netbox/templates/circuits/provider.html index 1388a2c5d96..e5aa6c0c49a 100644 --- a/netbox/templates/circuits/provider.html +++ b/netbox/templates/circuits/provider.html @@ -103,14 +103,6 @@ {% endif %} - - Created - {{ provider.created }} - - - Last Updated - {{ provider.last_updated }} -
@@ -125,6 +117,7 @@ {% endif %}
+ {% include 'inc/created_updated.html' with obj=provider %}
diff --git a/netbox/templates/dcim/device.html b/netbox/templates/dcim/device.html index 04955557155..6bdd48c29cd 100644 --- a/netbox/templates/dcim/device.html +++ b/netbox/templates/dcim/device.html @@ -79,14 +79,6 @@ {% endif %} - - Created - {{ device.created }} - - - Last Updated - {{ device.last_updated }} -
@@ -306,6 +298,7 @@
None found
{% endif %}
+ {% include 'inc/created_updated.html' with obj=device %}
{% if device_bays or device.device_type.is_parent_device %} diff --git a/netbox/templates/dcim/rack.html b/netbox/templates/dcim/rack.html index 16a4731b614..927ec230584 100644 --- a/netbox/templates/dcim/rack.html +++ b/netbox/templates/dcim/rack.html @@ -130,14 +130,6 @@ {{ rack.devices.count }} - - Created - {{ rack.created }} - - - Last Updated - {{ rack.last_updated }} -
@@ -187,6 +179,7 @@ {% endif %}
+ {% include 'inc/created_updated.html' with obj=rack %}
diff --git a/netbox/templates/dcim/site.html b/netbox/templates/dcim/site.html index 2bd0ffce855..2509721b940 100644 --- a/netbox/templates/dcim/site.html +++ b/netbox/templates/dcim/site.html @@ -109,14 +109,6 @@ {% endif %} - - Created - {{ site.created }} - - - Last Updated - {{ site.last_updated }} -
@@ -131,6 +123,7 @@ {% endif %}
+ {% include 'inc/created_updated.html' with obj=site %}
diff --git a/netbox/templates/inc/created_updated.html b/netbox/templates/inc/created_updated.html new file mode 100644 index 00000000000..001bb6b85c5 --- /dev/null +++ b/netbox/templates/inc/created_updated.html @@ -0,0 +1,3 @@ +

+ Created {{ obj.created }} · Updated {{ obj.last_updated|timesince }} ago +

diff --git a/netbox/templates/ipam/aggregate.html b/netbox/templates/ipam/aggregate.html index 9a0a8db1f68..ec408d33bac 100644 --- a/netbox/templates/ipam/aggregate.html +++ b/netbox/templates/ipam/aggregate.html @@ -77,16 +77,9 @@ {% endif %} - - Created - {{ aggregate.created }} - - - Last Updated - {{ aggregate.last_updated }} -
+ {% include 'inc/created_updated.html' with obj=aggregate %}
diff --git a/netbox/templates/ipam/ipaddress.html b/netbox/templates/ipam/ipaddress.html index de5ed637b0a..56f95479b5a 100644 --- a/netbox/templates/ipam/ipaddress.html +++ b/netbox/templates/ipam/ipaddress.html @@ -119,16 +119,9 @@ {% endif %} - - Created - {{ ipaddress.created }} - - - Last Updated - {{ ipaddress.last_updated }} -
+ {% include 'inc/created_updated.html' with obj=ipaddress %}
{% with heading='Parent Prefixes' %} diff --git a/netbox/templates/ipam/prefix.html b/netbox/templates/ipam/prefix.html index 802e9b90fa9..24a40ed667f 100644 --- a/netbox/templates/ipam/prefix.html +++ b/netbox/templates/ipam/prefix.html @@ -99,16 +99,10 @@ IP Addresses {{ ipaddress_count }} - - Created - {{ prefix.created }} - - - Last Updated - {{ prefix.last_updated }} -
+ {% include 'inc/created_updated.html' with obj=prefix %} +
{% if duplicate_prefix_table.rows %} diff --git a/netbox/templates/ipam/vlan.html b/netbox/templates/ipam/vlan.html index d27184824c3..5b9d4cd3cf5 100644 --- a/netbox/templates/ipam/vlan.html +++ b/netbox/templates/ipam/vlan.html @@ -107,17 +107,10 @@ N/A {% endif %} - - - Created - {{ vlan.created }} - - - Last Updated - {{ vlan.last_updated }}
+ {% include 'inc/created_updated.html' with obj=vlan %}
diff --git a/netbox/templates/ipam/vrf.html b/netbox/templates/ipam/vrf.html index bd3cadc9ed3..899b14f54cf 100644 --- a/netbox/templates/ipam/vrf.html +++ b/netbox/templates/ipam/vrf.html @@ -79,17 +79,10 @@ N/A {% endif %} - - - Created - {{ vrf.created }} - - - Last Updated - {{ vrf.last_updated }}
+ {% include 'inc/created_updated.html' with obj=vrf %}
diff --git a/netbox/templates/secrets/secret.html b/netbox/templates/secrets/secret.html index 53cdb4d7ca5..98007c2b97b 100644 --- a/netbox/templates/secrets/secret.html +++ b/netbox/templates/secrets/secret.html @@ -56,16 +56,9 @@ {% endif %} - - Created - {{ secret.created }} - - - Last Updated - {{ secret.last_updated }} -
+ {% include 'inc/created_updated.html' with obj=secret %}
{% if secret|decryptable_by:request.user %} diff --git a/netbox/templates/tenancy/tenant.html b/netbox/templates/tenancy/tenant.html index 0ca38063941..c2334bda76a 100644 --- a/netbox/templates/tenancy/tenant.html +++ b/netbox/templates/tenancy/tenant.html @@ -63,14 +63,6 @@ {% endif %} - - Created - {{ tenant.created }} - - - Last Updated - {{ tenant.last_updated }} -
@@ -85,6 +77,7 @@ {% endif %}
+ {% include 'inc/created_updated.html' with obj=tenant %}
diff --git a/netbox/templates/users/userkey.html b/netbox/templates/users/userkey.html index cbc748d17b4..6318377c6a1 100644 --- a/netbox/templates/users/userkey.html +++ b/netbox/templates/users/userkey.html @@ -31,6 +31,7 @@ Edit user key
+ {% include 'inc/created_updated.html' with obj=userkey %} {% else %}

You don't have a user key on file.

From 8f34b6b0b963a502f8e5a00554b98fb20dda1adc Mon Sep 17 00:00:00 2001 From: Robert Drake Date: Sun, 21 Aug 2016 23:48:27 -0400 Subject: [PATCH 10/10] fix for Dockerfile It was hard to test with the old syntax. It was cloning the "master" branch, so trying to test a development change was difficult. I believe I've fixed it so that the "master" branch and "develop" branch can use the same Dockerfile options. You override which branch it pulls by setting a build-args variable, either via docker-compose or in the docker build options. --- Dockerfile | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/Dockerfile b/Dockerfile index fc05a45075c..2cf8b929464 100644 --- a/Dockerfile +++ b/Dockerfile @@ -2,9 +2,10 @@ FROM python:2.7-wheezy WORKDIR /opt/netbox -ADD . /opt/netbox -RUN git clone --depth 1 https://github.com/digitalocean/netbox.git -b master . \ -RUN pip install gunicorn==17.5 && pip install -r requirements.txt +ARG BRANCH=master +ARG URL=https://github.com/digitalocean/netbox.git +RUN git clone --depth 1 $URL -b $BRANCH . && \ + pip install gunicorn==17.5 && pip install -r requirements.txt ADD docker/docker-entrypoint.sh /docker-entrypoint.sh ADD netbox/netbox/configuration.docker.py /opt/netbox/netbox/netbox/configuration.py