From 35c34c690ae9616d791c39fa218fe1621fa8d8d2 Mon Sep 17 00:00:00 2001 From: maxbr Date: Fri, 29 Jul 2016 12:43:43 +0200 Subject: import pharos dashboard code JIRA: RELENG-12 The last commit was missing some JS/CSS dependencies of the site. This happened because they are in folders that are named 'build' or 'dist'. This commit adds a bower.json file, that specifies dependencies. Dependencies can now be installed by running 'bower install' in the dashboard/static folder. Change-Id: I054f319c66771f767e97711cb678d79d3bd6bee4 Signed-off-by: maxbr --- tools/pharos-dashboard/.gitignore | 36 ++++++ tools/pharos-dashboard/README.md | 1 + tools/pharos-dashboard/TODO | 3 + tools/pharos-dashboard/dashboard/__init__.py | 0 tools/pharos-dashboard/dashboard/admin.py | 9 ++ tools/pharos-dashboard/dashboard/apps.py | 5 + .../dashboard/fixtures/DBdata_resources.json | 1 + .../dashboard/fixtures/DBdata_test_bookings.json | 1 + .../dashboard/fixtures/DBdata_users.json | 1 + .../dashboard/forms/booking_form.py | 37 ++++++ .../dashboard/jenkins/jenkins_adapter.py | 84 ++++++++++++++ .../dashboard/jenkins/jenkins_util.py | 70 ++++++++++++ .../dashboard/migrations/0001_initial.py | 107 ++++++++++++++++++ .../dashboard/migrations/__init__.py | 0 tools/pharos-dashboard/dashboard/models.py | 89 +++++++++++++++ tools/pharos-dashboard/dashboard/static/bower.json | 24 ++++ .../dashboard/static/css/theme.css | 7 ++ .../dashboard/static/js/booking-calendar.js | 68 +++++++++++ tools/pharos-dashboard/dashboard/static/js/csrf.js | 34 ++++++ .../dashboard/static/js/dataTables-sort.js | 26 +++++ .../dashboard/static/js/datetimepicker-options.js | 7 ++ .../dashboard/static/js/fullcalendar-options.js | 65 +++++++++++ tools/pharos-dashboard/dashboard/tests.py | 41 +++++++ tools/pharos-dashboard/dashboard/urls.py | 35 ++++++ tools/pharos-dashboard/dashboard/views/booking.py | 69 ++++++++++++ .../dashboard/views/registration.py | 16 +++ .../dashboard/views/table_views.py | 62 +++++++++++ tools/pharos-dashboard/deploy.org | 45 ++++++++ tools/pharos-dashboard/issues.org | 6 + tools/pharos-dashboard/manage.py | 10 ++ .../pharos-dashboard/pharos_dashboard/__init__.py | 0 .../pharos-dashboard/pharos_dashboard/settings.py | 124 +++++++++++++++++++++ tools/pharos-dashboard/pharos_dashboard/urls.py | 23 ++++ tools/pharos-dashboard/pharos_dashboard/wsgi.py | 16 +++ tools/pharos-dashboard/requirements.txt | 5 + .../pharos-dashboard/templates/dashboard/base.html | 93 ++++++++++++++++ .../templates/dashboard/booking_calendar.html | 79 +++++++++++++ .../templates/dashboard/table.html | 50 +++++++++ tools/pharos-dashboard/templates/layout/base.html | 70 ++++++++++++ .../templates/registration/login.html | 61 ++++++++++ .../pharos-dashboard/templates/tables/ci_pods.html | 59 ++++++++++ .../templates/tables/dev_pods.html | 58 ++++++++++ .../templates/tables/jenkins_slaves.html | 42 +++++++ tools/pharos_dashboard | 1 - 44 files changed, 1639 insertions(+), 1 deletion(-) create mode 100644 tools/pharos-dashboard/.gitignore create mode 100644 tools/pharos-dashboard/README.md create mode 100644 tools/pharos-dashboard/TODO create mode 100644 tools/pharos-dashboard/dashboard/__init__.py create mode 100644 tools/pharos-dashboard/dashboard/admin.py create mode 100644 tools/pharos-dashboard/dashboard/apps.py create mode 100644 tools/pharos-dashboard/dashboard/fixtures/DBdata_resources.json create mode 100644 tools/pharos-dashboard/dashboard/fixtures/DBdata_test_bookings.json create mode 100644 tools/pharos-dashboard/dashboard/fixtures/DBdata_users.json create mode 100644 tools/pharos-dashboard/dashboard/forms/booking_form.py create mode 100644 tools/pharos-dashboard/dashboard/jenkins/jenkins_adapter.py create mode 100644 tools/pharos-dashboard/dashboard/jenkins/jenkins_util.py create mode 100644 tools/pharos-dashboard/dashboard/migrations/0001_initial.py create mode 100644 tools/pharos-dashboard/dashboard/migrations/__init__.py create mode 100644 tools/pharos-dashboard/dashboard/models.py create mode 100644 tools/pharos-dashboard/dashboard/static/bower.json create mode 100644 tools/pharos-dashboard/dashboard/static/css/theme.css create mode 100644 tools/pharos-dashboard/dashboard/static/js/booking-calendar.js create mode 100644 tools/pharos-dashboard/dashboard/static/js/csrf.js create mode 100644 tools/pharos-dashboard/dashboard/static/js/dataTables-sort.js create mode 100644 tools/pharos-dashboard/dashboard/static/js/datetimepicker-options.js create mode 100644 tools/pharos-dashboard/dashboard/static/js/fullcalendar-options.js create mode 100644 tools/pharos-dashboard/dashboard/tests.py create mode 100644 tools/pharos-dashboard/dashboard/urls.py create mode 100644 tools/pharos-dashboard/dashboard/views/booking.py create mode 100644 tools/pharos-dashboard/dashboard/views/registration.py create mode 100644 tools/pharos-dashboard/dashboard/views/table_views.py create mode 100644 tools/pharos-dashboard/deploy.org create mode 100644 tools/pharos-dashboard/issues.org create mode 100755 tools/pharos-dashboard/manage.py create mode 100644 tools/pharos-dashboard/pharos_dashboard/__init__.py create mode 100644 tools/pharos-dashboard/pharos_dashboard/settings.py create mode 100644 tools/pharos-dashboard/pharos_dashboard/urls.py create mode 100644 tools/pharos-dashboard/pharos_dashboard/wsgi.py create mode 100644 tools/pharos-dashboard/requirements.txt create mode 100644 tools/pharos-dashboard/templates/dashboard/base.html create mode 100644 tools/pharos-dashboard/templates/dashboard/booking_calendar.html create mode 100644 tools/pharos-dashboard/templates/dashboard/table.html create mode 100644 tools/pharos-dashboard/templates/layout/base.html create mode 100644 tools/pharos-dashboard/templates/registration/login.html create mode 100644 tools/pharos-dashboard/templates/tables/ci_pods.html create mode 100644 tools/pharos-dashboard/templates/tables/dev_pods.html create mode 100644 tools/pharos-dashboard/templates/tables/jenkins_slaves.html delete mode 160000 tools/pharos_dashboard diff --git a/tools/pharos-dashboard/.gitignore b/tools/pharos-dashboard/.gitignore new file mode 100644 index 00000000..b5e9284a --- /dev/null +++ b/tools/pharos-dashboard/.gitignore @@ -0,0 +1,36 @@ +# Byte-compiled / optimized / DLL files +__pycache__/ +*.py[cod] + +# C extensions +*.so + +# Installer logs +pip-log.txt +pip-delete-this-directory.txt + +# Unit test / coverage reports +.tox/ +.coverage +.cache +nosetests.xml +coverage.xml + +# Django: +*.log +*.pot + +# KDE: +.directory + +# Pycharm: +.idea/ + +# Virtualenv: +venv/ + +# Vim: +*.swp + +# Bower Components: +bower_components/ diff --git a/tools/pharos-dashboard/README.md b/tools/pharos-dashboard/README.md new file mode 100644 index 00000000..be27bf51 --- /dev/null +++ b/tools/pharos-dashboard/README.md @@ -0,0 +1 @@ +# Pharos Dashboard diff --git a/tools/pharos-dashboard/TODO b/tools/pharos-dashboard/TODO new file mode 100644 index 00000000..1c539d6f --- /dev/null +++ b/tools/pharos-dashboard/TODO @@ -0,0 +1,3 @@ +- implement ajax booking form to call from fullcalendar, then implement editing of multiple events +- if the user is behind a VPN, his timezone settings might be wrong, there should be an option in the +user settings to override the browser timezone. diff --git a/tools/pharos-dashboard/dashboard/__init__.py b/tools/pharos-dashboard/dashboard/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/tools/pharos-dashboard/dashboard/admin.py b/tools/pharos-dashboard/dashboard/admin.py new file mode 100644 index 00000000..532173f0 --- /dev/null +++ b/tools/pharos-dashboard/dashboard/admin.py @@ -0,0 +1,9 @@ +from django.contrib import admin +from .models import * + +# Register your models here. +admin.site.register(Booking) +admin.site.register(Pod) +admin.site.register(Resource) +admin.site.register(Server) +admin.site.register(UserResource) \ No newline at end of file diff --git a/tools/pharos-dashboard/dashboard/apps.py b/tools/pharos-dashboard/dashboard/apps.py new file mode 100644 index 00000000..50878e76 --- /dev/null +++ b/tools/pharos-dashboard/dashboard/apps.py @@ -0,0 +1,5 @@ +from django.apps import AppConfig + + +class DashboardConfig(AppConfig): + name = 'dashboard' diff --git a/tools/pharos-dashboard/dashboard/fixtures/DBdata_resources.json b/tools/pharos-dashboard/dashboard/fixtures/DBdata_resources.json new file mode 100644 index 00000000..5aaccf7a --- /dev/null +++ b/tools/pharos-dashboard/dashboard/fixtures/DBdata_resources.json @@ -0,0 +1 @@ +[{"model": "dashboard.pod", "pk": 91, "fields": {"resource": 95, "chassis": null}}, {"model": "dashboard.pod", "pk": 92, "fields": {"resource": 96, "chassis": null}}, {"model": "dashboard.pod", "pk": 93, "fields": {"resource": 97, "chassis": null}}, {"model": "dashboard.pod", "pk": 94, "fields": {"resource": 98, "chassis": null}}, {"model": "dashboard.pod", "pk": 95, "fields": {"resource": 99, "chassis": null}}, {"model": "dashboard.pod", "pk": 96, "fields": {"resource": 100, "chassis": null}}, {"model": "dashboard.pod", "pk": 97, "fields": {"resource": 101, "chassis": null}}, {"model": "dashboard.pod", "pk": 98, "fields": {"resource": 102, "chassis": null}}, {"model": "dashboard.pod", "pk": 99, "fields": {"resource": 103, "chassis": null}}, {"model": "dashboard.pod", "pk": 100, "fields": {"resource": 104, "chassis": null}}, {"model": "dashboard.pod", "pk": 101, "fields": {"resource": 105, "chassis": null}}, {"model": "dashboard.pod", "pk": 102, "fields": {"resource": 106, "chassis": null}}, {"model": "dashboard.pod", "pk": 103, "fields": {"resource": 107, "chassis": null}}, {"model": "dashboard.pod", "pk": 104, "fields": {"resource": 108, "chassis": null}}, {"model": "dashboard.pod", "pk": 105, "fields": {"resource": 109, "chassis": null}}, {"model": "dashboard.pod", "pk": 106, "fields": {"resource": 110, "chassis": null}}, {"model": "dashboard.pod", "pk": 107, "fields": {"resource": 111, "chassis": null}}, {"model": "dashboard.pod", "pk": 108, "fields": {"resource": 112, "chassis": null}}, {"model": "dashboard.resource", "pk": 95, "fields": {"name": "Linux Foundation POD 1", "slavename": "lf-pod1", "description": "Some description", "url": "https://wiki.opnfv.org/display/pharos/Lf+Lab", "bookable": false, "active": true}}, {"model": "dashboard.resource", "pk": 96, "fields": {"name": "Linux Foundation POD 2", "slavename": "lf-pod2", "description": "Some description", "url": "https://wiki.opnfv.org/display/pharos/Lf+Lab", "bookable": false, "active": true}}, {"model": "dashboard.resource", "pk": 97, "fields": {"name": "Ericsson POD 2", "slavename": "ericsson-pod2", "description": "Some description", "url": "https://wiki.opnfv.org/display/pharos/Ericsson+Hosting+and+Request+Process", "bookable": false, "active": true}}, {"model": "dashboard.resource", "pk": 98, "fields": {"name": "Intel POD 2", "slavename": "intel-pod2", "description": "Some description", "url": "https://wiki.opnfv.org/display/pharos/Intel+Pod2", "bookable": false, "active": true}}, {"model": "dashboard.resource", "pk": 99, "fields": {"name": "Intel POD 5", "slavename": "intel-pod5", "description": "Some description", "url": "https://wiki.opnfv.org/display/pharos/Intel+Pod5", "bookable": false, "active": true}}, {"model": "dashboard.resource", "pk": 100, "fields": {"name": "Intel POD 6", "slavename": "intel-pod6", "description": "Some description", "url": "https://wiki.opnfv.org/display/pharos/Intel+Pod6", "bookable": false, "active": true}}, {"model": "dashboard.resource", "pk": 101, "fields": {"name": "Intel POD 8", "slavename": "intel-pod8", "description": "Some description", "url": "https://wiki.opnfv.org/display/pharos/Intel+Pod8", "bookable": false, "active": true}}, {"model": "dashboard.resource", "pk": 102, "fields": {"name": "Huawei POD 1", "slavename": "huawei-pod1", "description": "Some description", "url": "https://wiki.opnfv.org/display/pharos/Huawei+Hosting", "bookable": false, "active": true}}, {"model": "dashboard.resource", "pk": 103, "fields": {"name": "Intel POD 3", "slavename": "intel-pod3", "description": "Some description", "url": "https://wiki.opnfv.org/display/pharos/Intel+Pod3", "bookable": false, "active": true}}, {"model": "dashboard.resource", "pk": 104, "fields": {"name": "Dell POD 1", "slavename": "dell-pod1", "description": "Some description", "url": "https://wiki.opnfv.org/display/pharos/Dell+Hosting", "bookable": false, "active": true}}, {"model": "dashboard.resource", "pk": 105, "fields": {"name": "Dell POD 2", "slavename": "dell-pod2", "description": "Some description", "url": "https://wiki.opnfv.org/display/pharos/Dell+Hosting", "bookable": false, "active": true}}, {"model": "dashboard.resource", "pk": 106, "fields": {"name": "Orange POD 2", "slavename": "orange-pod2", "description": "Some description", "url": "https://wiki.opnfv.org/display/pharos/Opnfv-orange-pod2", "bookable": false, "active": true}}, {"model": "dashboard.resource", "pk": 107, "fields": {"name": "Arm POD 1", "slavename": "arm-build1", "description": "Some description", "url": "https://wiki.opnfv.org/display/pharos/Enea-pharos-lab", "bookable": false, "active": true}}, {"model": "dashboard.resource", "pk": 108, "fields": {"name": "Ericsson POD 1", "slavename": "ericsson-pod1", "description": "Some description", "url": "https://wiki.opnfv.org/display/pharos/Ericsson+Hosting+and+Request+Process", "bookable": false, "active": true}}, {"model": "dashboard.resource", "pk": 109, "fields": {"name": "Huawei POD 2", "slavename": "huawei-pod2", "description": "Some description", "url": "https://wiki.opnfv.org/display/pharos/Huawei+Hosting", "bookable": false, "active": true}}, {"model": "dashboard.resource", "pk": 110, "fields": {"name": "Huawei POD 3", "slavename": "huawei-pod3", "description": "Some description", "url": "https://wiki.opnfv.org/display/pharos/Huawei+Hosting", "bookable": false, "active": true}}, {"model": "dashboard.resource", "pk": 111, "fields": {"name": "Huawei POD 4", "slavename": "huawei-pod4", "description": "Some description", "url": "https://wiki.opnfv.org/display/pharos/Huawei+Hosting", "bookable": false, "active": true}}, {"model": "dashboard.resource", "pk": 112, "fields": {"name": "Intel POD 9", "slavename": "intel-pod8", "description": "Some description", "url": "https://wiki.opnfv.org/display/pharos/Intel+Pod9", "bookable": false, "active": true}}] diff --git a/tools/pharos-dashboard/dashboard/fixtures/DBdata_test_bookings.json b/tools/pharos-dashboard/dashboard/fixtures/DBdata_test_bookings.json new file mode 100644 index 00000000..19132810 --- /dev/null +++ b/tools/pharos-dashboard/dashboard/fixtures/DBdata_test_bookings.json @@ -0,0 +1 @@ +[{"model": "dashboard.pod", "pk": 91, "fields": {"resource": 95, "chassis": null}}, {"model": "dashboard.pod", "pk": 92, "fields": {"resource": 96, "chassis": null}}, {"model": "dashboard.pod", "pk": 93, "fields": {"resource": 97, "chassis": null}}, {"model": "dashboard.pod", "pk": 94, "fields": {"resource": 98, "chassis": null}}, {"model": "dashboard.pod", "pk": 95, "fields": {"resource": 99, "chassis": null}}, {"model": "dashboard.pod", "pk": 96, "fields": {"resource": 100, "chassis": null}}, {"model": "dashboard.pod", "pk": 97, "fields": {"resource": 101, "chassis": null}}, {"model": "dashboard.pod", "pk": 98, "fields": {"resource": 102, "chassis": null}}, {"model": "dashboard.pod", "pk": 99, "fields": {"resource": 103, "chassis": null}}, {"model": "dashboard.pod", "pk": 100, "fields": {"resource": 104, "chassis": null}}, {"model": "dashboard.pod", "pk": 101, "fields": {"resource": 105, "chassis": null}}, {"model": "dashboard.pod", "pk": 102, "fields": {"resource": 106, "chassis": null}}, {"model": "dashboard.pod", "pk": 103, "fields": {"resource": 107, "chassis": null}}, {"model": "dashboard.pod", "pk": 104, "fields": {"resource": 108, "chassis": null}}, {"model": "dashboard.pod", "pk": 105, "fields": {"resource": 109, "chassis": null}}, {"model": "dashboard.pod", "pk": 106, "fields": {"resource": 110, "chassis": null}}, {"model": "dashboard.pod", "pk": 107, "fields": {"resource": 111, "chassis": null}}, {"model": "dashboard.pod", "pk": 108, "fields": {"resource": 112, "chassis": null}}, {"model": "dashboard.resource", "pk": 95, "fields": {"name": "Linux Foundation POD 1", "slavename": "lf-pod1", "description": "Some description", "url": "https://wiki.opnfv.org/display/pharos/Lf+Lab", "bookable": false, "active": true}}, {"model": "dashboard.resource", "pk": 96, "fields": {"name": "Linux Foundation POD 2", "slavename": "lf-pod2", "description": "Some description", "url": "https://wiki.opnfv.org/display/pharos/Lf+Lab", "bookable": false, "active": true}}, {"model": "dashboard.resource", "pk": 97, "fields": {"name": "Ericsson POD 2", "slavename": "ericsson-pod2", "description": "Some description", "url": "https://wiki.opnfv.org/display/pharos/Ericsson+Hosting+and+Request+Process", "bookable": false, "active": true}}, {"model": "dashboard.resource", "pk": 98, "fields": {"name": "Intel POD 2", "slavename": "intel-pod2", "description": "Some description", "url": "https://wiki.opnfv.org/display/pharos/Intel+Pod2", "bookable": false, "active": true}}, {"model": "dashboard.resource", "pk": 99, "fields": {"name": "Intel POD 5", "slavename": "intel-pod5", "description": "Some description", "url": "https://wiki.opnfv.org/display/pharos/Intel+Pod5", "bookable": false, "active": true}}, {"model": "dashboard.resource", "pk": 100, "fields": {"name": "Intel POD 6", "slavename": "intel-pod6", "description": "Some description", "url": "https://wiki.opnfv.org/display/pharos/Intel+Pod6", "bookable": false, "active": true}}, {"model": "dashboard.resource", "pk": 101, "fields": {"name": "Intel POD 8", "slavename": "intel-pod8", "description": "Some description", "url": "https://wiki.opnfv.org/display/pharos/Intel+Pod8", "bookable": false, "active": true}}, {"model": "dashboard.resource", "pk": 102, "fields": {"name": "Huawei POD 1", "slavename": "huawei-pod1", "description": "Some description", "url": "https://wiki.opnfv.org/display/pharos/Huawei+Hosting", "bookable": false, "active": true}}, {"model": "dashboard.resource", "pk": 103, "fields": {"name": "Intel POD 3", "slavename": "intel-pod3", "description": "Some description", "url": "https://wiki.opnfv.org/display/pharos/Intel+Pod3", "bookable": false, "active": true}}, {"model": "dashboard.resource", "pk": 104, "fields": {"name": "Dell POD 1", "slavename": "dell-pod1", "description": "Some description", "url": "https://wiki.opnfv.org/display/pharos/Dell+Hosting", "bookable": false, "active": true}}, {"model": "dashboard.resource", "pk": 105, "fields": {"name": "Dell POD 2", "slavename": "dell-pod2", "description": "Some description", "url": "https://wiki.opnfv.org/display/pharos/Dell+Hosting", "bookable": false, "active": true}}, {"model": "dashboard.resource", "pk": 106, "fields": {"name": "Orange POD 2", "slavename": "orange-pod2", "description": "Some description", "url": "https://wiki.opnfv.org/display/pharos/Opnfv-orange-pod2", "bookable": false, "active": true}}, {"model": "dashboard.resource", "pk": 107, "fields": {"name": "Arm POD 1", "slavename": "arm-build1", "description": "Some description", "url": "https://wiki.opnfv.org/display/pharos/Enea-pharos-lab", "bookable": false, "active": true}}, {"model": "dashboard.resource", "pk": 108, "fields": {"name": "Ericsson POD 1", "slavename": "ericsson-pod1", "description": "Some description", "url": "https://wiki.opnfv.org/display/pharos/Ericsson+Hosting+and+Request+Process", "bookable": false, "active": true}}, {"model": "dashboard.resource", "pk": 109, "fields": {"name": "Huawei POD 2", "slavename": "huawei-pod2", "description": "Some description", "url": "https://wiki.opnfv.org/display/pharos/Huawei+Hosting", "bookable": false, "active": true}}, {"model": "dashboard.resource", "pk": 110, "fields": {"name": "Huawei POD 3", "slavename": "huawei-pod3", "description": "Some description", "url": "https://wiki.opnfv.org/display/pharos/Huawei+Hosting", "bookable": false, "active": true}}, {"model": "dashboard.resource", "pk": 111, "fields": {"name": "Huawei POD 4", "slavename": "huawei-pod4", "description": "Some description", "url": "https://wiki.opnfv.org/display/pharos/Huawei+Hosting", "bookable": false, "active": true}}, {"model": "dashboard.resource", "pk": 112, "fields": {"name": "Intel POD 9", "slavename": "intel-pod8", "description": "Some description", "url": "https://wiki.opnfv.org/display/pharos/Intel+Pod9", "bookable": false, "active": true}}, {"model": "contenttypes.contenttype", "pk": 1, "fields": {"app_label": "dashboard", "model": "booking"}}, {"model": "contenttypes.contenttype", "pk": 2, "fields": {"app_label": "dashboard", "model": "pod"}}, {"model": "contenttypes.contenttype", "pk": 3, "fields": {"app_label": "dashboard", "model": "resource"}}, {"model": "contenttypes.contenttype", "pk": 4, "fields": {"app_label": "dashboard", "model": "server"}}, {"model": "contenttypes.contenttype", "pk": 5, "fields": {"app_label": "dashboard", "model": "userresource"}}, {"model": "contenttypes.contenttype", "pk": 6, "fields": {"app_label": "admin", "model": "logentry"}}, {"model": "contenttypes.contenttype", "pk": 7, "fields": {"app_label": "auth", "model": "permission"}}, {"model": "contenttypes.contenttype", "pk": 8, "fields": {"app_label": "auth", "model": "group"}}, {"model": "contenttypes.contenttype", "pk": 9, "fields": {"app_label": "auth", "model": "user"}}, {"model": "contenttypes.contenttype", "pk": 10, "fields": {"app_label": "contenttypes", "model": "contenttype"}}, {"model": "contenttypes.contenttype", "pk": 11, "fields": {"app_label": "sessions", "model": "session"}}, {"model": "auth.permission", "pk": 1, "fields": {"name": "Can add booking", "content_type": 1, "codename": "add_booking"}}, {"model": "auth.permission", "pk": 2, "fields": {"name": "Can change booking", "content_type": 1, "codename": "change_booking"}}, {"model": "auth.permission", "pk": 3, "fields": {"name": "Can delete booking", "content_type": 1, "codename": "delete_booking"}}, {"model": "auth.permission", "pk": 4, "fields": {"name": "Can add pod", "content_type": 2, "codename": "add_pod"}}, {"model": "auth.permission", "pk": 5, "fields": {"name": "Can change pod", "content_type": 2, "codename": "change_pod"}}, {"model": "auth.permission", "pk": 6, "fields": {"name": "Can delete pod", "content_type": 2, "codename": "delete_pod"}}, {"model": "auth.permission", "pk": 7, "fields": {"name": "Can add resource", "content_type": 3, "codename": "add_resource"}}, {"model": "auth.permission", "pk": 8, "fields": {"name": "Can change resource", "content_type": 3, "codename": "change_resource"}}, {"model": "auth.permission", "pk": 9, "fields": {"name": "Can delete resource", "content_type": 3, "codename": "delete_resource"}}, {"model": "auth.permission", "pk": 10, "fields": {"name": "Can add server", "content_type": 4, "codename": "add_server"}}, {"model": "auth.permission", "pk": 11, "fields": {"name": "Can change server", "content_type": 4, "codename": "change_server"}}, {"model": "auth.permission", "pk": 12, "fields": {"name": "Can delete server", "content_type": 4, "codename": "delete_server"}}, {"model": "auth.permission", "pk": 13, "fields": {"name": "Can add user resource", "content_type": 5, "codename": "add_userresource"}}, {"model": "auth.permission", "pk": 14, "fields": {"name": "Can change user resource", "content_type": 5, "codename": "change_userresource"}}, {"model": "auth.permission", "pk": 15, "fields": {"name": "Can delete user resource", "content_type": 5, "codename": "delete_userresource"}}, {"model": "auth.permission", "pk": 16, "fields": {"name": "Can add log entry", "content_type": 6, "codename": "add_logentry"}}, {"model": "auth.permission", "pk": 17, "fields": {"name": "Can change log entry", "content_type": 6, "codename": "change_logentry"}}, {"model": "auth.permission", "pk": 18, "fields": {"name": "Can delete log entry", "content_type": 6, "codename": "delete_logentry"}}, {"model": "auth.permission", "pk": 19, "fields": {"name": "Can add permission", "content_type": 7, "codename": "add_permission"}}, {"model": "auth.permission", "pk": 20, "fields": {"name": "Can change permission", "content_type": 7, "codename": "change_permission"}}, {"model": "auth.permission", "pk": 21, "fields": {"name": "Can delete permission", "content_type": 7, "codename": "delete_permission"}}, {"model": "auth.permission", "pk": 22, "fields": {"name": "Can add group", "content_type": 8, "codename": "add_group"}}, {"model": "auth.permission", "pk": 23, "fields": {"name": "Can change group", "content_type": 8, "codename": "change_group"}}, {"model": "auth.permission", "pk": 24, "fields": {"name": "Can delete group", "content_type": 8, "codename": "delete_group"}}, {"model": "auth.permission", "pk": 25, "fields": {"name": "Can add user", "content_type": 9, "codename": "add_user"}}, {"model": "auth.permission", "pk": 26, "fields": {"name": "Can change user", "content_type": 9, "codename": "change_user"}}, {"model": "auth.permission", "pk": 27, "fields": {"name": "Can delete user", "content_type": 9, "codename": "delete_user"}}, {"model": "auth.permission", "pk": 28, "fields": {"name": "Can add content type", "content_type": 10, "codename": "add_contenttype"}}, {"model": "auth.permission", "pk": 29, "fields": {"name": "Can change content type", "content_type": 10, "codename": "change_contenttype"}}, {"model": "auth.permission", "pk": 30, "fields": {"name": "Can delete content type", "content_type": 10, "codename": "delete_contenttype"}}, {"model": "auth.permission", "pk": 31, "fields": {"name": "Can add session", "content_type": 11, "codename": "add_session"}}, {"model": "auth.permission", "pk": 32, "fields": {"name": "Can change session", "content_type": 11, "codename": "change_session"}}, {"model": "auth.permission", "pk": 33, "fields": {"name": "Can delete session", "content_type": 11, "codename": "delete_session"}}, {"model": "auth.user", "pk": 1, "fields": {"password": "pbkdf2_sha256$24000$kcQ5X8WQg1tH$KHynJPYTsoBq7vAipLsP0EZyo3qAu1VGJwaoEQeUz3E=", "last_login": "2016-07-24T13:01:31.199Z", "is_superuser": true, "username": "max", "first_name": "", "last_name": "", "email": "max.breitenfeldt@gmail.com", "is_staff": true, "is_active": true, "date_joined": "2016-07-24T12:49:54.488Z", "groups": [], "user_permissions": []}}, {"model": "auth.user", "pk": 2, "fields": {"password": "pbkdf2_sha256$24000$6z3xKCpZp0xl$76IaKRzJocbGmGG9JUtPz3is2AjlMEIfb9omiTEn2N8=", "last_login": null, "is_superuser": true, "username": "jose", "first_name": "Jose", "last_name": "Lausuch", "email": "jose.lausuch@ericsson.com", "is_staff": true, "is_active": true, "date_joined": "2016-07-24T12:51:30Z", "groups": [], "user_permissions": []}}, {"model": "auth.user", "pk": 3, "fields": {"password": "pbkdf2_sha256$24000$466rSOMbdzZk$3TXRz9CRoephCrjNbEa7+nLJ4xwz4W0jvTfs4A69D1o=", "last_login": null, "is_superuser": true, "username": "daniel", "first_name": "Daniel", "last_name": "Smith", "email": "daniel.smith@ericsson.com", "is_staff": true, "is_active": true, "date_joined": "2016-07-24T12:52:23Z", "groups": [], "user_permissions": []}}, {"model": "auth.user", "pk": 4, "fields": {"password": "pbkdf2_sha256$24000$wjeFIjdiblRl$6ugfj1neDEsaxMQbQ0xV+zg/FKw8ImIDyuPqYWkUxE4=", "last_login": null, "is_superuser": true, "username": "jack", "first_name": "Jack", "last_name": "Morgan", "email": "jack.morgan@intel.com", "is_staff": true, "is_active": true, "date_joined": "2016-07-24T12:53:02Z", "groups": [], "user_permissions": []}}, {"model": "auth.user", "pk": 5, "fields": {"password": "pbkdf2_sha256$24000$qYMcbKTahNGK$fdPgcQKTKhjSHDZx+C2bymU46HpIl/0n5vbXMpplXWE=", "last_login": null, "is_superuser": true, "username": "fatih", "first_name": "Fatih", "last_name": "Degirmenci", "email": "fatih.degirmenci@ericsson.com", "is_staff": true, "is_active": true, "date_joined": "2016-07-24T12:53:38Z", "groups": [], "user_permissions": []}}, {"model": "auth.user", "pk": 6, "fields": {"password": "pbkdf2_sha256$24000$Ndxyrota7u3U$KWmjRYo3GW25pcgf9NUuz5PSXvH8hU8E2dUARoKY7EI=", "last_login": null, "is_superuser": true, "username": "trevor", "first_name": "Trevor", "last_name": "Cooper", "email": "trevor.cooper@intel.com", "is_staff": true, "is_active": true, "date_joined": "2016-07-24T12:54:32Z", "groups": [], "user_permissions": []}}, {"model": "admin.logentry", "pk": 1, "fields": {"action_time": "2016-07-24T12:51:30.147Z", "user": 1, "content_type": 9, "object_id": "2", "object_repr": "jose", "action_flag": 1, "change_message": "Added."}}, {"model": "admin.logentry", "pk": 2, "fields": {"action_time": "2016-07-24T12:51:58.928Z", "user": 1, "content_type": 9, "object_id": "2", "object_repr": "jose", "action_flag": 2, "change_message": "Changed first_name, last_name, email, is_staff and is_superuser."}}, {"model": "admin.logentry", "pk": 3, "fields": {"action_time": "2016-07-24T12:52:23.821Z", "user": 1, "content_type": 9, "object_id": "3", "object_repr": "daniel", "action_flag": 1, "change_message": "Added."}}, {"model": "admin.logentry", "pk": 4, "fields": {"action_time": "2016-07-24T12:52:40.446Z", "user": 1, "content_type": 9, "object_id": "3", "object_repr": "daniel", "action_flag": 2, "change_message": "Changed first_name, last_name, email, is_staff and is_superuser."}}, {"model": "admin.logentry", "pk": 5, "fields": {"action_time": "2016-07-24T12:52:45.866Z", "user": 1, "content_type": 9, "object_id": "3", "object_repr": "daniel", "action_flag": 2, "change_message": "No fields changed."}}, {"model": "admin.logentry", "pk": 6, "fields": {"action_time": "2016-07-24T12:53:02.303Z", "user": 1, "content_type": 9, "object_id": "4", "object_repr": "jack", "action_flag": 1, "change_message": "Added."}}, {"model": "admin.logentry", "pk": 7, "fields": {"action_time": "2016-07-24T12:53:22.399Z", "user": 1, "content_type": 9, "object_id": "4", "object_repr": "jack", "action_flag": 2, "change_message": "Changed first_name, last_name, email, is_staff and is_superuser."}}, {"model": "admin.logentry", "pk": 8, "fields": {"action_time": "2016-07-24T12:53:24.541Z", "user": 1, "content_type": 9, "object_id": "4", "object_repr": "jack", "action_flag": 2, "change_message": "No fields changed."}}, {"model": "admin.logentry", "pk": 9, "fields": {"action_time": "2016-07-24T12:53:38.910Z", "user": 1, "content_type": 9, "object_id": "5", "object_repr": "fatih", "action_flag": 1, "change_message": "Added."}}, {"model": "admin.logentry", "pk": 10, "fields": {"action_time": "2016-07-24T12:53:54.585Z", "user": 1, "content_type": 9, "object_id": "5", "object_repr": "fatih", "action_flag": 2, "change_message": "Changed first_name, last_name, email, is_staff and is_superuser."}}, {"model": "admin.logentry", "pk": 11, "fields": {"action_time": "2016-07-24T12:53:56.777Z", "user": 1, "content_type": 9, "object_id": "5", "object_repr": "fatih", "action_flag": 2, "change_message": "No fields changed."}}, {"model": "admin.logentry", "pk": 12, "fields": {"action_time": "2016-07-24T12:54:32.683Z", "user": 1, "content_type": 9, "object_id": "6", "object_repr": "trevor", "action_flag": 1, "change_message": "Added."}}, {"model": "admin.logentry", "pk": 13, "fields": {"action_time": "2016-07-24T12:54:53.188Z", "user": 1, "content_type": 9, "object_id": "6", "object_repr": "trevor", "action_flag": 2, "change_message": "Changed first_name, last_name, email, is_staff and is_superuser."}}, {"model": "admin.logentry", "pk": 14, "fields": {"action_time": "2016-07-24T12:55:45.789Z", "user": 1, "content_type": 5, "object_id": "1", "object_repr": "UserResource object", "action_flag": 1, "change_message": "Added."}}, {"model": "admin.logentry", "pk": 15, "fields": {"action_time": "2016-07-24T12:55:51.347Z", "user": 1, "content_type": 5, "object_id": "2", "object_repr": "UserResource object", "action_flag": 1, "change_message": "Added."}}, {"model": "admin.logentry", "pk": 16, "fields": {"action_time": "2016-07-24T12:55:56.704Z", "user": 1, "content_type": 5, "object_id": "3", "object_repr": "UserResource object", "action_flag": 1, "change_message": "Added."}}, {"model": "admin.logentry", "pk": 17, "fields": {"action_time": "2016-07-24T12:56:08.238Z", "user": 1, "content_type": 5, "object_id": "4", "object_repr": "UserResource object", "action_flag": 1, "change_message": "Added."}}, {"model": "admin.logentry", "pk": 18, "fields": {"action_time": "2016-07-24T12:56:17.849Z", "user": 1, "content_type": 5, "object_id": "5", "object_repr": "UserResource object", "action_flag": 1, "change_message": "Added."}}, {"model": "admin.logentry", "pk": 19, "fields": {"action_time": "2016-07-24T12:56:24.215Z", "user": 1, "content_type": 5, "object_id": "6", "object_repr": "UserResource object", "action_flag": 1, "change_message": "Added."}}, {"model": "admin.logentry", "pk": 20, "fields": {"action_time": "2016-07-24T12:56:33.608Z", "user": 1, "content_type": 5, "object_id": "7", "object_repr": "UserResource object", "action_flag": 1, "change_message": "Added."}}, {"model": "admin.logentry", "pk": 21, "fields": {"action_time": "2016-07-24T12:56:37.554Z", "user": 1, "content_type": 5, "object_id": "8", "object_repr": "UserResource object", "action_flag": 1, "change_message": "Added."}}, {"model": "dashboard.userresource", "pk": 1, "fields": {"user": 3, "resource": 108}}, {"model": "dashboard.userresource", "pk": 2, "fields": {"user": 3, "resource": 97}}, {"model": "dashboard.userresource", "pk": 3, "fields": {"user": 4, "resource": 98}}, {"model": "dashboard.userresource", "pk": 4, "fields": {"user": 4, "resource": 103}}, {"model": "dashboard.userresource", "pk": 5, "fields": {"user": 4, "resource": 99}}, {"model": "dashboard.userresource", "pk": 6, "fields": {"user": 4, "resource": 100}}, {"model": "dashboard.userresource", "pk": 7, "fields": {"user": 4, "resource": 101}}, {"model": "dashboard.userresource", "pk": 8, "fields": {"user": 4, "resource": 112}}, {"model": "dashboard.booking", "pk": 1, "fields": {"resource": 103, "user": 1, "start_date_time": "2016-07-23T00:00:00Z", "end_date_time": "2016-08-08T00:00:00Z", "creation": "2016-07-24T13:01:47.945Z", "purpose": "Test1"}}, {"model": "dashboard.booking", "pk": 2, "fields": {"resource": 103, "user": 1, "start_date_time": "2016-07-18T03:00:00Z", "end_date_time": "2016-07-20T10:00:00Z", "creation": "2016-07-24T13:02:00.033Z", "purpose": "test2"}}, {"model": "dashboard.booking", "pk": 3, "fields": {"resource": 109, "user": 1, "start_date_time": "2016-07-23T06:00:00Z", "end_date_time": "2016-07-26T17:00:00Z", "creation": "2016-07-24T13:02:23.024Z", "purpose": "test3"}}, {"model": "dashboard.booking", "pk": 4, "fields": {"resource": 110, "user": 1, "start_date_time": "2016-07-09T00:00:00Z", "end_date_time": "2016-08-01T00:00:00Z", "creation": "2016-07-24T13:02:35.138Z", "purpose": "test4"}}, {"model": "dashboard.booking", "pk": 5, "fields": {"resource": 111, "user": 1, "start_date_time": "2016-07-20T07:00:00Z", "end_date_time": "2016-07-20T10:00:00Z", "creation": "2016-07-24T13:02:45.153Z", "purpose": "test5"}}, {"model": "dashboard.booking", "pk": 6, "fields": {"resource": 111, "user": 1, "start_date_time": "2016-07-22T03:00:00Z", "end_date_time": "2016-07-24T10:00:00Z", "creation": "2016-07-24T13:02:50.050Z", "purpose": "test6"}}, {"model": "dashboard.booking", "pk": 7, "fields": {"resource": 111, "user": 1, "start_date_time": "2016-07-24T11:00:00Z", "end_date_time": "2016-07-24T19:00:00Z", "creation": "2016-07-24T13:02:57.207Z", "purpose": "test7"}}] \ No newline at end of file diff --git a/tools/pharos-dashboard/dashboard/fixtures/DBdata_users.json b/tools/pharos-dashboard/dashboard/fixtures/DBdata_users.json new file mode 100644 index 00000000..d21be1fa --- /dev/null +++ b/tools/pharos-dashboard/dashboard/fixtures/DBdata_users.json @@ -0,0 +1 @@ +[{"model": "dashboard.pod", "pk": 91, "fields": {"resource": 95, "chassis": null}}, {"model": "dashboard.pod", "pk": 92, "fields": {"resource": 96, "chassis": null}}, {"model": "dashboard.pod", "pk": 93, "fields": {"resource": 97, "chassis": null}}, {"model": "dashboard.pod", "pk": 94, "fields": {"resource": 98, "chassis": null}}, {"model": "dashboard.pod", "pk": 95, "fields": {"resource": 99, "chassis": null}}, {"model": "dashboard.pod", "pk": 96, "fields": {"resource": 100, "chassis": null}}, {"model": "dashboard.pod", "pk": 97, "fields": {"resource": 101, "chassis": null}}, {"model": "dashboard.pod", "pk": 98, "fields": {"resource": 102, "chassis": null}}, {"model": "dashboard.pod", "pk": 99, "fields": {"resource": 103, "chassis": null}}, {"model": "dashboard.pod", "pk": 100, "fields": {"resource": 104, "chassis": null}}, {"model": "dashboard.pod", "pk": 101, "fields": {"resource": 105, "chassis": null}}, {"model": "dashboard.pod", "pk": 102, "fields": {"resource": 106, "chassis": null}}, {"model": "dashboard.pod", "pk": 103, "fields": {"resource": 107, "chassis": null}}, {"model": "dashboard.pod", "pk": 104, "fields": {"resource": 108, "chassis": null}}, {"model": "dashboard.pod", "pk": 105, "fields": {"resource": 109, "chassis": null}}, {"model": "dashboard.pod", "pk": 106, "fields": {"resource": 110, "chassis": null}}, {"model": "dashboard.pod", "pk": 107, "fields": {"resource": 111, "chassis": null}}, {"model": "dashboard.pod", "pk": 108, "fields": {"resource": 112, "chassis": null}}, {"model": "dashboard.resource", "pk": 95, "fields": {"name": "Linux Foundation POD 1", "slavename": "lf-pod1", "description": "Some description", "url": "https://wiki.opnfv.org/display/pharos/Lf+Lab", "bookable": false, "active": true}}, {"model": "dashboard.resource", "pk": 96, "fields": {"name": "Linux Foundation POD 2", "slavename": "lf-pod2", "description": "Some description", "url": "https://wiki.opnfv.org/display/pharos/Lf+Lab", "bookable": false, "active": true}}, {"model": "dashboard.resource", "pk": 97, "fields": {"name": "Ericsson POD 2", "slavename": "ericsson-pod2", "description": "Some description", "url": "https://wiki.opnfv.org/display/pharos/Ericsson+Hosting+and+Request+Process", "bookable": false, "active": true}}, {"model": "dashboard.resource", "pk": 98, "fields": {"name": "Intel POD 2", "slavename": "intel-pod2", "description": "Some description", "url": "https://wiki.opnfv.org/display/pharos/Intel+Pod2", "bookable": false, "active": true}}, {"model": "dashboard.resource", "pk": 99, "fields": {"name": "Intel POD 5", "slavename": "intel-pod5", "description": "Some description", "url": "https://wiki.opnfv.org/display/pharos/Intel+Pod5", "bookable": false, "active": true}}, {"model": "dashboard.resource", "pk": 100, "fields": {"name": "Intel POD 6", "slavename": "intel-pod6", "description": "Some description", "url": "https://wiki.opnfv.org/display/pharos/Intel+Pod6", "bookable": false, "active": true}}, {"model": "dashboard.resource", "pk": 101, "fields": {"name": "Intel POD 8", "slavename": "intel-pod8", "description": "Some description", "url": "https://wiki.opnfv.org/display/pharos/Intel+Pod8", "bookable": false, "active": true}}, {"model": "dashboard.resource", "pk": 102, "fields": {"name": "Huawei POD 1", "slavename": "huawei-pod1", "description": "Some description", "url": "https://wiki.opnfv.org/display/pharos/Huawei+Hosting", "bookable": false, "active": true}}, {"model": "dashboard.resource", "pk": 103, "fields": {"name": "Intel POD 3", "slavename": "intel-pod3", "description": "Some description", "url": "https://wiki.opnfv.org/display/pharos/Intel+Pod3", "bookable": false, "active": true}}, {"model": "dashboard.resource", "pk": 104, "fields": {"name": "Dell POD 1", "slavename": "dell-pod1", "description": "Some description", "url": "https://wiki.opnfv.org/display/pharos/Dell+Hosting", "bookable": false, "active": true}}, {"model": "dashboard.resource", "pk": 105, "fields": {"name": "Dell POD 2", "slavename": "dell-pod2", "description": "Some description", "url": "https://wiki.opnfv.org/display/pharos/Dell+Hosting", "bookable": false, "active": true}}, {"model": "dashboard.resource", "pk": 106, "fields": {"name": "Orange POD 2", "slavename": "orange-pod2", "description": "Some description", "url": "https://wiki.opnfv.org/display/pharos/Opnfv-orange-pod2", "bookable": false, "active": true}}, {"model": "dashboard.resource", "pk": 107, "fields": {"name": "Arm POD 1", "slavename": "arm-build1", "description": "Some description", "url": "https://wiki.opnfv.org/display/pharos/Enea-pharos-lab", "bookable": false, "active": true}}, {"model": "dashboard.resource", "pk": 108, "fields": {"name": "Ericsson POD 1", "slavename": "ericsson-pod1", "description": "Some description", "url": "https://wiki.opnfv.org/display/pharos/Ericsson+Hosting+and+Request+Process", "bookable": false, "active": true}}, {"model": "dashboard.resource", "pk": 109, "fields": {"name": "Huawei POD 2", "slavename": "huawei-pod2", "description": "Some description", "url": "https://wiki.opnfv.org/display/pharos/Huawei+Hosting", "bookable": false, "active": true}}, {"model": "dashboard.resource", "pk": 110, "fields": {"name": "Huawei POD 3", "slavename": "huawei-pod3", "description": "Some description", "url": "https://wiki.opnfv.org/display/pharos/Huawei+Hosting", "bookable": false, "active": true}}, {"model": "dashboard.resource", "pk": 111, "fields": {"name": "Huawei POD 4", "slavename": "huawei-pod4", "description": "Some description", "url": "https://wiki.opnfv.org/display/pharos/Huawei+Hosting", "bookable": false, "active": true}}, {"model": "dashboard.resource", "pk": 112, "fields": {"name": "Intel POD 9", "slavename": "intel-pod8", "description": "Some description", "url": "https://wiki.opnfv.org/display/pharos/Intel+Pod9", "bookable": false, "active": true}}, {"model": "contenttypes.contenttype", "pk": 1, "fields": {"app_label": "dashboard", "model": "booking"}}, {"model": "contenttypes.contenttype", "pk": 2, "fields": {"app_label": "dashboard", "model": "pod"}}, {"model": "contenttypes.contenttype", "pk": 3, "fields": {"app_label": "dashboard", "model": "resource"}}, {"model": "contenttypes.contenttype", "pk": 4, "fields": {"app_label": "dashboard", "model": "server"}}, {"model": "contenttypes.contenttype", "pk": 5, "fields": {"app_label": "dashboard", "model": "userresource"}}, {"model": "contenttypes.contenttype", "pk": 6, "fields": {"app_label": "admin", "model": "logentry"}}, {"model": "contenttypes.contenttype", "pk": 7, "fields": {"app_label": "auth", "model": "permission"}}, {"model": "contenttypes.contenttype", "pk": 8, "fields": {"app_label": "auth", "model": "group"}}, {"model": "contenttypes.contenttype", "pk": 9, "fields": {"app_label": "auth", "model": "user"}}, {"model": "contenttypes.contenttype", "pk": 10, "fields": {"app_label": "contenttypes", "model": "contenttype"}}, {"model": "contenttypes.contenttype", "pk": 11, "fields": {"app_label": "sessions", "model": "session"}}, {"model": "auth.permission", "pk": 1, "fields": {"name": "Can add booking", "content_type": 1, "codename": "add_booking"}}, {"model": "auth.permission", "pk": 2, "fields": {"name": "Can change booking", "content_type": 1, "codename": "change_booking"}}, {"model": "auth.permission", "pk": 3, "fields": {"name": "Can delete booking", "content_type": 1, "codename": "delete_booking"}}, {"model": "auth.permission", "pk": 4, "fields": {"name": "Can add pod", "content_type": 2, "codename": "add_pod"}}, {"model": "auth.permission", "pk": 5, "fields": {"name": "Can change pod", "content_type": 2, "codename": "change_pod"}}, {"model": "auth.permission", "pk": 6, "fields": {"name": "Can delete pod", "content_type": 2, "codename": "delete_pod"}}, {"model": "auth.permission", "pk": 7, "fields": {"name": "Can add resource", "content_type": 3, "codename": "add_resource"}}, {"model": "auth.permission", "pk": 8, "fields": {"name": "Can change resource", "content_type": 3, "codename": "change_resource"}}, {"model": "auth.permission", "pk": 9, "fields": {"name": "Can delete resource", "content_type": 3, "codename": "delete_resource"}}, {"model": "auth.permission", "pk": 10, "fields": {"name": "Can add server", "content_type": 4, "codename": "add_server"}}, {"model": "auth.permission", "pk": 11, "fields": {"name": "Can change server", "content_type": 4, "codename": "change_server"}}, {"model": "auth.permission", "pk": 12, "fields": {"name": "Can delete server", "content_type": 4, "codename": "delete_server"}}, {"model": "auth.permission", "pk": 13, "fields": {"name": "Can add user resource", "content_type": 5, "codename": "add_userresource"}}, {"model": "auth.permission", "pk": 14, "fields": {"name": "Can change user resource", "content_type": 5, "codename": "change_userresource"}}, {"model": "auth.permission", "pk": 15, "fields": {"name": "Can delete user resource", "content_type": 5, "codename": "delete_userresource"}}, {"model": "auth.permission", "pk": 16, "fields": {"name": "Can add log entry", "content_type": 6, "codename": "add_logentry"}}, {"model": "auth.permission", "pk": 17, "fields": {"name": "Can change log entry", "content_type": 6, "codename": "change_logentry"}}, {"model": "auth.permission", "pk": 18, "fields": {"name": "Can delete log entry", "content_type": 6, "codename": "delete_logentry"}}, {"model": "auth.permission", "pk": 19, "fields": {"name": "Can add permission", "content_type": 7, "codename": "add_permission"}}, {"model": "auth.permission", "pk": 20, "fields": {"name": "Can change permission", "content_type": 7, "codename": "change_permission"}}, {"model": "auth.permission", "pk": 21, "fields": {"name": "Can delete permission", "content_type": 7, "codename": "delete_permission"}}, {"model": "auth.permission", "pk": 22, "fields": {"name": "Can add group", "content_type": 8, "codename": "add_group"}}, {"model": "auth.permission", "pk": 23, "fields": {"name": "Can change group", "content_type": 8, "codename": "change_group"}}, {"model": "auth.permission", "pk": 24, "fields": {"name": "Can delete group", "content_type": 8, "codename": "delete_group"}}, {"model": "auth.permission", "pk": 25, "fields": {"name": "Can add user", "content_type": 9, "codename": "add_user"}}, {"model": "auth.permission", "pk": 26, "fields": {"name": "Can change user", "content_type": 9, "codename": "change_user"}}, {"model": "auth.permission", "pk": 27, "fields": {"name": "Can delete user", "content_type": 9, "codename": "delete_user"}}, {"model": "auth.permission", "pk": 28, "fields": {"name": "Can add content type", "content_type": 10, "codename": "add_contenttype"}}, {"model": "auth.permission", "pk": 29, "fields": {"name": "Can change content type", "content_type": 10, "codename": "change_contenttype"}}, {"model": "auth.permission", "pk": 30, "fields": {"name": "Can delete content type", "content_type": 10, "codename": "delete_contenttype"}}, {"model": "auth.permission", "pk": 31, "fields": {"name": "Can add session", "content_type": 11, "codename": "add_session"}}, {"model": "auth.permission", "pk": 32, "fields": {"name": "Can change session", "content_type": 11, "codename": "change_session"}}, {"model": "auth.permission", "pk": 33, "fields": {"name": "Can delete session", "content_type": 11, "codename": "delete_session"}}, {"model": "auth.user", "pk": 1, "fields": {"password": "pbkdf2_sha256$24000$kcQ5X8WQg1tH$KHynJPYTsoBq7vAipLsP0EZyo3qAu1VGJwaoEQeUz3E=", "last_login": "2016-07-24T12:50:11.954Z", "is_superuser": true, "username": "max", "first_name": "", "last_name": "", "email": "max.breitenfeldt@gmail.com", "is_staff": true, "is_active": true, "date_joined": "2016-07-24T12:49:54.488Z", "groups": [], "user_permissions": []}}, {"model": "auth.user", "pk": 2, "fields": {"password": "pbkdf2_sha256$24000$6z3xKCpZp0xl$76IaKRzJocbGmGG9JUtPz3is2AjlMEIfb9omiTEn2N8=", "last_login": null, "is_superuser": true, "username": "jose", "first_name": "Jose", "last_name": "Lausuch", "email": "jose.lausuch@ericsson.com", "is_staff": true, "is_active": true, "date_joined": "2016-07-24T12:51:30Z", "groups": [], "user_permissions": []}}, {"model": "auth.user", "pk": 3, "fields": {"password": "pbkdf2_sha256$24000$466rSOMbdzZk$3TXRz9CRoephCrjNbEa7+nLJ4xwz4W0jvTfs4A69D1o=", "last_login": null, "is_superuser": true, "username": "daniel", "first_name": "Daniel", "last_name": "Smith", "email": "daniel.smith@ericsson.com", "is_staff": true, "is_active": true, "date_joined": "2016-07-24T12:52:23Z", "groups": [], "user_permissions": []}}, {"model": "auth.user", "pk": 4, "fields": {"password": "pbkdf2_sha256$24000$wjeFIjdiblRl$6ugfj1neDEsaxMQbQ0xV+zg/FKw8ImIDyuPqYWkUxE4=", "last_login": null, "is_superuser": true, "username": "jack", "first_name": "Jack", "last_name": "Morgan", "email": "jack.morgan@intel.com", "is_staff": true, "is_active": true, "date_joined": "2016-07-24T12:53:02Z", "groups": [], "user_permissions": []}}, {"model": "auth.user", "pk": 5, "fields": {"password": "pbkdf2_sha256$24000$qYMcbKTahNGK$fdPgcQKTKhjSHDZx+C2bymU46HpIl/0n5vbXMpplXWE=", "last_login": null, "is_superuser": true, "username": "fatih", "first_name": "Fatih", "last_name": "Degirmenci", "email": "fatih.degirmenci@ericsson.com", "is_staff": true, "is_active": true, "date_joined": "2016-07-24T12:53:38Z", "groups": [], "user_permissions": []}}, {"model": "auth.user", "pk": 6, "fields": {"password": "pbkdf2_sha256$24000$Ndxyrota7u3U$KWmjRYo3GW25pcgf9NUuz5PSXvH8hU8E2dUARoKY7EI=", "last_login": null, "is_superuser": true, "username": "trevor", "first_name": "Trevor", "last_name": "Cooper", "email": "trevor.cooper@intel.com", "is_staff": true, "is_active": true, "date_joined": "2016-07-24T12:54:32Z", "groups": [], "user_permissions": []}}, {"model": "admin.logentry", "pk": 1, "fields": {"action_time": "2016-07-24T12:51:30.147Z", "user": 1, "content_type": 9, "object_id": "2", "object_repr": "jose", "action_flag": 1, "change_message": "Added."}}, {"model": "admin.logentry", "pk": 2, "fields": {"action_time": "2016-07-24T12:51:58.928Z", "user": 1, "content_type": 9, "object_id": "2", "object_repr": "jose", "action_flag": 2, "change_message": "Changed first_name, last_name, email, is_staff and is_superuser."}}, {"model": "admin.logentry", "pk": 3, "fields": {"action_time": "2016-07-24T12:52:23.821Z", "user": 1, "content_type": 9, "object_id": "3", "object_repr": "daniel", "action_flag": 1, "change_message": "Added."}}, {"model": "admin.logentry", "pk": 4, "fields": {"action_time": "2016-07-24T12:52:40.446Z", "user": 1, "content_type": 9, "object_id": "3", "object_repr": "daniel", "action_flag": 2, "change_message": "Changed first_name, last_name, email, is_staff and is_superuser."}}, {"model": "admin.logentry", "pk": 5, "fields": {"action_time": "2016-07-24T12:52:45.866Z", "user": 1, "content_type": 9, "object_id": "3", "object_repr": "daniel", "action_flag": 2, "change_message": "No fields changed."}}, {"model": "admin.logentry", "pk": 6, "fields": {"action_time": "2016-07-24T12:53:02.303Z", "user": 1, "content_type": 9, "object_id": "4", "object_repr": "jack", "action_flag": 1, "change_message": "Added."}}, {"model": "admin.logentry", "pk": 7, "fields": {"action_time": "2016-07-24T12:53:22.399Z", "user": 1, "content_type": 9, "object_id": "4", "object_repr": "jack", "action_flag": 2, "change_message": "Changed first_name, last_name, email, is_staff and is_superuser."}}, {"model": "admin.logentry", "pk": 8, "fields": {"action_time": "2016-07-24T12:53:24.541Z", "user": 1, "content_type": 9, "object_id": "4", "object_repr": "jack", "action_flag": 2, "change_message": "No fields changed."}}, {"model": "admin.logentry", "pk": 9, "fields": {"action_time": "2016-07-24T12:53:38.910Z", "user": 1, "content_type": 9, "object_id": "5", "object_repr": "fatih", "action_flag": 1, "change_message": "Added."}}, {"model": "admin.logentry", "pk": 10, "fields": {"action_time": "2016-07-24T12:53:54.585Z", "user": 1, "content_type": 9, "object_id": "5", "object_repr": "fatih", "action_flag": 2, "change_message": "Changed first_name, last_name, email, is_staff and is_superuser."}}, {"model": "admin.logentry", "pk": 11, "fields": {"action_time": "2016-07-24T12:53:56.777Z", "user": 1, "content_type": 9, "object_id": "5", "object_repr": "fatih", "action_flag": 2, "change_message": "No fields changed."}}, {"model": "admin.logentry", "pk": 12, "fields": {"action_time": "2016-07-24T12:54:32.683Z", "user": 1, "content_type": 9, "object_id": "6", "object_repr": "trevor", "action_flag": 1, "change_message": "Added."}}, {"model": "admin.logentry", "pk": 13, "fields": {"action_time": "2016-07-24T12:54:53.188Z", "user": 1, "content_type": 9, "object_id": "6", "object_repr": "trevor", "action_flag": 2, "change_message": "Changed first_name, last_name, email, is_staff and is_superuser."}}, {"model": "admin.logentry", "pk": 14, "fields": {"action_time": "2016-07-24T12:55:45.789Z", "user": 1, "content_type": 5, "object_id": "1", "object_repr": "UserResource object", "action_flag": 1, "change_message": "Added."}}, {"model": "admin.logentry", "pk": 15, "fields": {"action_time": "2016-07-24T12:55:51.347Z", "user": 1, "content_type": 5, "object_id": "2", "object_repr": "UserResource object", "action_flag": 1, "change_message": "Added."}}, {"model": "admin.logentry", "pk": 16, "fields": {"action_time": "2016-07-24T12:55:56.704Z", "user": 1, "content_type": 5, "object_id": "3", "object_repr": "UserResource object", "action_flag": 1, "change_message": "Added."}}, {"model": "admin.logentry", "pk": 17, "fields": {"action_time": "2016-07-24T12:56:08.238Z", "user": 1, "content_type": 5, "object_id": "4", "object_repr": "UserResource object", "action_flag": 1, "change_message": "Added."}}, {"model": "admin.logentry", "pk": 18, "fields": {"action_time": "2016-07-24T12:56:17.849Z", "user": 1, "content_type": 5, "object_id": "5", "object_repr": "UserResource object", "action_flag": 1, "change_message": "Added."}}, {"model": "admin.logentry", "pk": 19, "fields": {"action_time": "2016-07-24T12:56:24.215Z", "user": 1, "content_type": 5, "object_id": "6", "object_repr": "UserResource object", "action_flag": 1, "change_message": "Added."}}, {"model": "admin.logentry", "pk": 20, "fields": {"action_time": "2016-07-24T12:56:33.608Z", "user": 1, "content_type": 5, "object_id": "7", "object_repr": "UserResource object", "action_flag": 1, "change_message": "Added."}}, {"model": "admin.logentry", "pk": 21, "fields": {"action_time": "2016-07-24T12:56:37.554Z", "user": 1, "content_type": 5, "object_id": "8", "object_repr": "UserResource object", "action_flag": 1, "change_message": "Added."}}, {"model": "dashboard.userresource", "pk": 1, "fields": {"user": 3, "resource": 108}}, {"model": "dashboard.userresource", "pk": 2, "fields": {"user": 3, "resource": 97}}, {"model": "dashboard.userresource", "pk": 3, "fields": {"user": 4, "resource": 98}}, {"model": "dashboard.userresource", "pk": 4, "fields": {"user": 4, "resource": 103}}, {"model": "dashboard.userresource", "pk": 5, "fields": {"user": 4, "resource": 99}}, {"model": "dashboard.userresource", "pk": 6, "fields": {"user": 4, "resource": 100}}, {"model": "dashboard.userresource", "pk": 7, "fields": {"user": 4, "resource": 101}}, {"model": "dashboard.userresource", "pk": 8, "fields": {"user": 4, "resource": 112}}] \ No newline at end of file diff --git a/tools/pharos-dashboard/dashboard/forms/booking_form.py b/tools/pharos-dashboard/dashboard/forms/booking_form.py new file mode 100644 index 00000000..9cf8048f --- /dev/null +++ b/tools/pharos-dashboard/dashboard/forms/booking_form.py @@ -0,0 +1,37 @@ +from dashboard.models import Booking +import django.forms as forms +from django.utils.translation import ugettext_lazy as _ + + +class BookingForm(forms.ModelForm): + class Meta: + model = Booking + fields = ['start_date_time', 'end_date_time', 'purpose', 'booking_id'] + + PURPOSE = { + 'id': 'purposefield', + 'type': 'text', + 'placeholder': 'Booking purpose', + } + + widgets = { + 'purpose': forms.TextInput(attrs=PURPOSE), + } + + # DATETIMEFORMAT should be equivalent to the moment.js format string that datetimepicker is + # using ('YYYY-MM-DD HH:00 ZZ'). The string is used to create a timezone aware datetime object + DATETIMEFORMAT = '%Y-%m-%d %H:%M %z' + start_date_time = forms.DateTimeField(input_formats=[DATETIMEFORMAT, ], label='Start') + end_date_time = forms.DateTimeField(input_formats=[DATETIMEFORMAT, ], label='End') + + # we need this to determine if we create a new booking or change an existing booking + booking_id = forms.IntegerField(widget=forms.HiddenInput, required=False) + + def clean(self): + cleaned_data = super(BookingForm, self).clean() + if 'start_date_time' not in cleaned_data or 'end_date_time' not in cleaned_data: + raise forms.ValidationError('Date Missing', code='missing_date') + if cleaned_data['start_date_time'] >= cleaned_data['end_date_time']: + raise forms.ValidationError( + 'Start date is after end date', code='invalid_dates') + return cleaned_data diff --git a/tools/pharos-dashboard/dashboard/jenkins/jenkins_adapter.py b/tools/pharos-dashboard/dashboard/jenkins/jenkins_adapter.py new file mode 100644 index 00000000..cd848ebb --- /dev/null +++ b/tools/pharos-dashboard/dashboard/jenkins/jenkins_adapter.py @@ -0,0 +1,84 @@ +import requests +import logging +from django.core.cache import cache + +logger = logging.getLogger(__name__) + + +# TODO: implement caching decorator, cache get_* functions +def get_json(url): + if cache.get(url) is None: + try: + response = requests.get(url) + json = response.json() + cache.set(url, json, 180) # cache result for 180 seconds + return response.json() + except requests.exceptions.RequestException as e: + logger.exception(e) + except ValueError as e: + logger.exception(e) + else: + return cache.get(url) + + +def get_all_slaves(): + url = "https://build.opnfv.org/ci/computer/api/json?tree=computer[displayName,offline,idle]" + json = get_json(url) + if json is not None: + return json['computer'] # return list of dictionaries + return [] + + +def get_slave(slavename): + slaves = get_all_slaves() + for slave in slaves: + if slave['displayName'] == slavename: + return slave + return {} + + +def get_ci_slaves(): + url = "https://build.opnfv.org/ci/label/ci-pod/api/json?tree=nodes[nodeName,offline,idle]" + json = get_json(url) + if json is not None: + return json['nodes'] + return [] + + +def get_all_jobs(): + url = "https://build.opnfv.org/ci/api/json?tree=jobs[displayName,url,lastBuild[fullDisplayName,building,builtOn,timestamp,result]]" + json = get_json(url) + if json is not None: + return json['jobs'] # return list of dictionaries + return [] + + +def get_jenkins_job(slavename): + jobs = get_all_jobs() + max_time = 0 + last_job = None + for job in jobs: + if job['lastBuild'] is not None: + if job['lastBuild']['builtOn'] == slavename: + if job['lastBuild']['building'] is True: + return job # return active build + if job['lastBuild']['timestamp'] > max_time: + last_job = job + max_time = job['lastBuild']['timestamp'] + return last_job + + +def is_ci_slave(slavename): + ci_slaves = get_ci_slaves() + for ci_slave in ci_slaves: + if ci_slave['nodeName'] == slavename: + return True + return False + + +def is_dev_pod(slavename): + if is_ci_slave(slavename): + return False + if slavename.find('pod') != -1: + return True + return False diff --git a/tools/pharos-dashboard/dashboard/jenkins/jenkins_util.py b/tools/pharos-dashboard/dashboard/jenkins/jenkins_util.py new file mode 100644 index 00000000..ba945639 --- /dev/null +++ b/tools/pharos-dashboard/dashboard/jenkins/jenkins_util.py @@ -0,0 +1,70 @@ +import dashboard.jenkins.jenkins_adapter as jenkins +import re + + +def parse_slave_data(slave_dict, slave): + slave_dict['status'] = get_slave_status(slave) + slave_dict['status_color'] = get_status_color(slave) + slave_dict['slaveurl'] = get_slave_url(slave) + job = jenkins.get_jenkins_job(slave['displayName']) + if job is not None: + slave_dict['last_job'] = parse_job(job) + + +def parse_job(job): + result = parse_job_string(job['lastBuild']['fullDisplayName']) + result['url'] = job['url'] + result['color'] = get_job_color(job) + if job['lastBuild']['building']: + result['blink'] = 'class=blink_me' + return result + + +def parse_job_string(full_displayname): + job = {} + tokens = re.split(r'[ -]', full_displayname) + for i in range(len(tokens)): + if tokens[i] == 'os': + job['scenario'] = '-'.join(tokens[i: i + 4]) + elif tokens[i] in ['fuel', 'joid', 'apex', 'compass']: + job['installer'] = tokens[i] + elif tokens[i] in ['master', 'arno', 'brahmaputra', 'colorado']: + job['branch'] = tokens[i] + + tokens = full_displayname.split(' ') + job['name'] = tokens[0] + return job + + +# TODO: use css +def get_job_color(job): + if job['lastBuild']['building'] is True: + return '#646F73' + result = job['lastBuild']['result'] + if result == 'SUCCESS': + return '#33cc00' + if result == 'FAILURE': + return '#FF5555' + if result == 'UNSTABLE': + return '#EDD62B' + + +# TODO: use css +def get_status_color(slave): + if not slave['offline'] and slave['idle']: + return '#C8D6C3' + if not slave['offline']: + return '#BEFAAA' + return '#FAAAAB' + + +def get_slave_url(slave): + return 'https://build.opnfv.org/ci/computer/' + slave['displayName'] + + +def get_slave_status(slave): + if not slave['offline'] and slave['idle']: + return 'online / idle' + if not slave['offline']: + return 'online' + return 'offline' diff --git a/tools/pharos-dashboard/dashboard/migrations/0001_initial.py b/tools/pharos-dashboard/dashboard/migrations/0001_initial.py new file mode 100644 index 00000000..12de299e --- /dev/null +++ b/tools/pharos-dashboard/dashboard/migrations/0001_initial.py @@ -0,0 +1,107 @@ +# -*- coding: utf-8 -*- +# Generated by Django 1.9.8 on 2016-07-24 13:06 +from __future__ import unicode_literals + +from django.conf import settings +from django.db import migrations, models +import django.db.models.deletion + + +class Migration(migrations.Migration): + + initial = True + + dependencies = [ + migrations.swappable_dependency(settings.AUTH_USER_MODEL), + ] + + operations = [ + migrations.CreateModel( + name='Booking', + fields=[ + ('booking_id', models.AutoField(primary_key=True, serialize=False)), + ('start_date_time', models.DateTimeField()), + ('end_date_time', models.DateTimeField()), + ('creation', models.DateTimeField(auto_now=True)), + ('purpose', models.CharField(max_length=300)), + ], + options={ + 'db_table': 'booking', + }, + ), + migrations.CreateModel( + name='Pod', + fields=[ + ('pod_id', models.AutoField(primary_key=True, serialize=False)), + ('chassis', models.CharField(blank=True, max_length=500, null=True)), + ], + options={ + 'db_table': 'pod', + }, + ), + migrations.CreateModel( + name='Resource', + fields=[ + ('resource_id', models.AutoField( + primary_key=True, serialize=False)), + ('name', models.CharField(max_length=100, unique=True)), + ('slavename', models.CharField(blank=True, max_length=50, null=True)), + ('description', models.CharField( + blank=True, max_length=300, null=True)), + ('url', models.CharField(blank=True, max_length=100, null=True)), + ('bookable', models.BooleanField(default=False)), + ('active', models.BooleanField(default=True)), + ], + options={ + 'db_table': 'resource', + }, + ), + migrations.CreateModel( + name='Server', + fields=[ + ('server_id', models.AutoField(primary_key=True, serialize=False)), + ('model', models.CharField(blank=True, max_length=200, null=True)), + ('cpu', models.CharField(blank=True, max_length=200, null=True)), + ('ram', models.CharField(blank=True, max_length=200, null=True)), + ('storage', models.CharField(blank=True, max_length=200, null=True)), + ('count', models.IntegerField(default=1)), + ('resource', models.ForeignKey( + on_delete=django.db.models.deletion.DO_NOTHING, to='dashboard.Resource')), + ], + options={ + 'db_table': 'server', + }, + ), + migrations.CreateModel( + name='UserResource', + fields=[ + ('user_resource_id', models.AutoField( + primary_key=True, serialize=False)), + ('resource', models.ForeignKey( + on_delete=django.db.models.deletion.CASCADE, to='dashboard.Resource')), + ('user', models.ForeignKey( + on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL)), + ], + options={ + 'db_table': 'user_resource', + }, + ), + migrations.AddField( + model_name='pod', + name='resource', + field=models.ForeignKey( + on_delete=django.db.models.deletion.CASCADE, to='dashboard.Resource'), + ), + migrations.AddField( + model_name='booking', + name='resource', + field=models.ForeignKey( + on_delete=django.db.models.deletion.PROTECT, to='dashboard.Resource'), + ), + migrations.AddField( + model_name='booking', + name='user', + field=models.ForeignKey( + on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL), + ), + ] diff --git a/tools/pharos-dashboard/dashboard/migrations/__init__.py b/tools/pharos-dashboard/dashboard/migrations/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/tools/pharos-dashboard/dashboard/models.py b/tools/pharos-dashboard/dashboard/models.py new file mode 100644 index 00000000..132ba7c6 --- /dev/null +++ b/tools/pharos-dashboard/dashboard/models.py @@ -0,0 +1,89 @@ +from __future__ import unicode_literals + +from django.db import models +from django.contrib.auth.models import User + + +class Booking(models.Model): + booking_id = models.AutoField(primary_key=True) + # Bookings should be deleted before resources + resource = models.ForeignKey('Resource', models.PROTECT) + # delete Booking when user is deleted + user = models.ForeignKey(User, models.CASCADE) + start_date_time = models.DateTimeField() + end_date_time = models.DateTimeField() + creation = models.DateTimeField(auto_now=True) + purpose = models.CharField(max_length=300) + + class Meta: + db_table = 'booking' + + # check for conflicting bookings before saving + def save(self, *args, **kwargs): + # conflicts end after booking starts, and start before booking ends + conflicting_bookings = Booking.objects.filter(resource_id=self.resource_id) + conflicting_bookings = conflicting_bookings.filter(end_date_time__gt=self.start_date_time) + conflicting_bookings = conflicting_bookings.filter(start_date_time__lt=self.end_date_time) + # we may change a booking, so it is not a conflict + conflicting_bookings = conflicting_bookings.exclude(booking_id=self.booking_id) + if conflicting_bookings.count() > 0: + raise ValueError('This booking overlaps with another booking') + super(Booking, self).save(*args, **kwargs) + + +def __str__(self): + return 'Booking: ' + str(self.resource) + + +class Pod(models.Model): + pod_id = models.AutoField(primary_key=True) + # Delete Pod with resource + resource = models.ForeignKey('Resource', models.CASCADE) + chassis = models.CharField(max_length=500, blank=True, null=True) + + class Meta: + db_table = 'pod' + + def __str__(self): + if self.chassis is None: + return str(self.pod_id) + ' ' + str(self.resource) + return str(self.pod_id) + ' ' + self.chassis + + +class Resource(models.Model): + resource_id = models.AutoField(primary_key=True) + name = models.CharField(max_length=100, unique=True) + slavename = models.CharField(max_length=50, blank=True, null=True) + description = models.CharField(max_length=300, blank=True, null=True) + url = models.CharField(max_length=100, blank=True, null=True) + bookable = models.BooleanField(default=False) + active = models.BooleanField(default=True) + + class Meta: + db_table = 'resource' + + def __str__(self): + return self.name + + +class Server(models.Model): + server_id = models.AutoField(primary_key=True) + resource = models.ForeignKey(Resource, models.DO_NOTHING) + model = models.CharField(max_length=200, blank=True, null=True) + cpu = models.CharField(max_length=200, blank=True, null=True) + ram = models.CharField(max_length=200, blank=True, null=True) + storage = models.CharField(max_length=200, blank=True, null=True) + count = models.IntegerField(default=1) + + class Meta: + db_table = 'server' + + +class UserResource(models.Model): + user_resource_id = models.AutoField(primary_key=True) + user = models.ForeignKey(User, models.CASCADE) + # Delete if Resource is deleted + resource = models.ForeignKey(Resource, models.CASCADE) + + class Meta: + db_table = 'user_resource' diff --git a/tools/pharos-dashboard/dashboard/static/bower.json b/tools/pharos-dashboard/dashboard/static/bower.json new file mode 100644 index 00000000..78406217 --- /dev/null +++ b/tools/pharos-dashboard/dashboard/static/bower.json @@ -0,0 +1,24 @@ +{ + "name": "pharos-dashboard-dependencies", + "authors": [ + "maxbr " + ], + "description": "This package contains all the Js/CSS dependencies needed to run the Pharos Dashboard.", + "main": "", + "license": "Apache2", + "homepage": "", + "private": true, + "ignore": [ + "**/.*", + "node_modules", + "bower_components", + "test", + "tests" + ], + "dependencies": { + "eonasdan-bootstrap-datetimepicker": "^4.17.37", + "fullcalendar": "^2.9.0", + "jquery-migrate": "^3.0.0", + "startbootstrap-sb-admin-2": "^1.0.9" + } +} diff --git a/tools/pharos-dashboard/dashboard/static/css/theme.css b/tools/pharos-dashboard/dashboard/static/css/theme.css new file mode 100644 index 00000000..4cec341d --- /dev/null +++ b/tools/pharos-dashboard/dashboard/static/css/theme.css @@ -0,0 +1,7 @@ +.blink_me { + animation: blinker 1.5s linear infinite; +} + +@keyframes blinker { + 20% { opacity: 0.4; } +} \ No newline at end of file diff --git a/tools/pharos-dashboard/dashboard/static/js/booking-calendar.js b/tools/pharos-dashboard/dashboard/static/js/booking-calendar.js new file mode 100644 index 00000000..edc20551 --- /dev/null +++ b/tools/pharos-dashboard/dashboard/static/js/booking-calendar.js @@ -0,0 +1,68 @@ +function parseDisabledTimeIntervals(bookings) { + var timeIntervals = []; + + for (var i = 0; i < bookings.length; i++) { + var interval = [ + moment(bookings[i]['start_date_time']), + moment(bookings[i]['end_date_time']) + ]; + timeIntervals.push(interval); + } + return timeIntervals; +} + +function parseCalendarEvents(bookings) { + var events = []; + for (var i = 0; i < bookings.length; i++) { + event = { + id: bookings[i]['booking_id'], + title: bookings[i]['purpose'], + start: bookings[i]['start_date_time'], + end: bookings[i]['end_date_time'], + editable: true + }; + events.push(event); + } + return events; +} + +function loadEvents(bookings_url) { + $.ajax({ + url: bookings_url, + type: 'get', + success: function (data) { + $('#calendar').fullCalendar('addEventSource', parseCalendarEvents(data['bookings'])); + var intervals = parseDisabledTimeIntervals(data['bookings']); + $('#starttimepicker').data("DateTimePicker").disabledTimeIntervals(intervals); + $('#endtimepicker').data("DateTimePicker").disabledTimeIntervals(intervals); + }, + failure: function (data) { + alert('Error loading booking data'); + } + }); +} + +$(document).ready(function () { + $('#calendar').fullCalendar(calendarOptions); + $('#starttimepicker').datetimepicker(timepickerOptions); + $('#endtimepicker').datetimepicker(timepickerOptions); + + loadEvents(bookings_url); + + // send Post request to delete url if button is clicked + $("#deletebutton").click(function () { + var booking_id = $('#id_booking_id').val(); + $.ajax({ + type: 'post', + url: '/booking/' + booking_id + '/delete', + success: function () { + $('#calendar').fullCalendar('removeEvents'); + loadEvents(bookings_url); + $('#calendar').fullCalendar('rerenderEvents'); + }, + failure: function () { + alert('Deleting failed') + } + }) + }) +}); \ No newline at end of file diff --git a/tools/pharos-dashboard/dashboard/static/js/csrf.js b/tools/pharos-dashboard/dashboard/static/js/csrf.js new file mode 100644 index 00000000..12429b38 --- /dev/null +++ b/tools/pharos-dashboard/dashboard/static/js/csrf.js @@ -0,0 +1,34 @@ +/** + * use django csrf token in ajax requests + * source: https://docs.djangoproject.com/en/1.8/ref/csrf/#ajax + */ +// using jQuery +function getCookie(name) { + var cookieValue = null; + if (document.cookie && document.cookie != '') { + var cookies = document.cookie.split(';'); + for (var i = 0; i < cookies.length; i++) { + var cookie = jQuery.trim(cookies[i]); + // Does this cookie string begin with the name we want? + if (cookie.substring(0, name.length + 1) == (name + '=')) { + cookieValue = decodeURIComponent(cookie.substring(name.length + 1)); + break; + } + } + } + return cookieValue; +} +var csrftoken = getCookie('csrftoken'); + +function csrfSafeMethod(method) { + // these HTTP methods do not require CSRF protection + return (/^(GET|HEAD|OPTIONS|TRACE)$/.test(method)); +} + +$.ajaxSetup({ + beforeSend: function (xhr, settings) { + if (!csrfSafeMethod(settings.type) && !this.crossDomain) { + xhr.setRequestHeader("X-CSRFToken", csrftoken); + } + } +}); \ No newline at end of file diff --git a/tools/pharos-dashboard/dashboard/static/js/dataTables-sort.js b/tools/pharos-dashboard/dashboard/static/js/dataTables-sort.js new file mode 100644 index 00000000..7189ca15 --- /dev/null +++ b/tools/pharos-dashboard/dashboard/static/js/dataTables-sort.js @@ -0,0 +1,26 @@ +/** + * This is a sort function for dataTables to sort tables by the status column. + * The order should be: online < online/idle < offline + */ +jQuery.extend(jQuery.fn.dataTableExt.oSort, { + "status-pre": function (a) { + switch (a) { + case 'online': + return 1; + case 'online / idle': + return 2; + case 'offline': + return 3; + default: + return a; + } + }, + + "status-asc": function (a, b) { + return ((a < b) ? -1 : ((a > b) ? 1 : 0)); + }, + + "status-desc": function (a, b) { + return ((a < b) ? 1 : ((a > b) ? -1 : 0)); + } +}); \ No newline at end of file diff --git a/tools/pharos-dashboard/dashboard/static/js/datetimepicker-options.js b/tools/pharos-dashboard/dashboard/static/js/datetimepicker-options.js new file mode 100644 index 00000000..1deb8191 --- /dev/null +++ b/tools/pharos-dashboard/dashboard/static/js/datetimepicker-options.js @@ -0,0 +1,7 @@ +/** + * Created by max on 7/25/16. + */ +var timepickerOptions = { + format: 'YYYY-MM-DD HH:00 ZZ', + sideBySide: true +}; \ No newline at end of file diff --git a/tools/pharos-dashboard/dashboard/static/js/fullcalendar-options.js b/tools/pharos-dashboard/dashboard/static/js/fullcalendar-options.js new file mode 100644 index 00000000..c0417eb1 --- /dev/null +++ b/tools/pharos-dashboard/dashboard/static/js/fullcalendar-options.js @@ -0,0 +1,65 @@ +// converts a moment to a readable fomat for the backend +function convertInputTime(time) { + return moment(time).format('YYYY-MM-DD HH:00 ZZ'); +} + +function sendEventToForm(event) { + var start = convertInputTime(event.start); + var end = convertInputTime(event.end); + $('#starttimepicker').data("DateTimePicker").date(start); + $('#endtimepicker').data("DateTimePicker").date(end); + $('#submitform').html("Change Booking"); + $('#purposefield').val(event.title); + $('#id_booking_id').val(event.id); // create a new booking + $("#deletebutton").removeClass('hidden'); // show delete button +} + +var calendarOptions = { + height: 600, + header: { + left: 'prev,next today', + center: 'title', + right: 'agendaWeek,month' + }, + timezone: 'local', + defaultView: 'agendaWeek', + slotDuration: '00:60:00', + slotLabelFormat: "HH:mm", + firstDay: 1, + allDaySlot: false, + selectOverlap: false, + eventOverlap: false, + selectable: true, + selectHelper: true, + editable: false, + eventLimit: true, // allow "more" link when too many events + timeFormat: 'H(:mm)', // uppercase H for 24-hour clock + unselectAuto: false, + + select: function (start, end) { + var start = convertInputTime(start); + var end = convertInputTime(end); + + $('#starttimepicker').data("DateTimePicker").date(start); + $('#endtimepicker').data("DateTimePicker").date(end); + $('#submitform').html("Book Pod"); + $('#purposefield').val(''); + $('#id_booking_id').val(''); // create a new booking + $("#deletebutton").addClass('hidden'); // hide delete button + }, + + eventClick: function (event, jsEvent, view) { + $('#calendar').fullCalendar('unselect'); + sendEventToForm(event); + }, + + eventDrop: function (event) { + $('#calendar').fullCalendar('unselect'); + sendEventToForm(event); + }, + + eventResize: function (event) { + $('#calendar').fullCalendar('unselect'); + sendEventToForm(event); + } +}; \ No newline at end of file diff --git a/tools/pharos-dashboard/dashboard/tests.py b/tools/pharos-dashboard/dashboard/tests.py new file mode 100644 index 00000000..75095782 --- /dev/null +++ b/tools/pharos-dashboard/dashboard/tests.py @@ -0,0 +1,41 @@ +import dashboard.jenkins.jenkins_adapter as jenkins +from django.test import SimpleTestCase + + +# Tests that the data we get with the jenkinsadapter contains all the +# data we need. These test will fail if; +# - there is no internet connection +# - the opnfv jenkins url has changed +# - the jenkins api has changed +# - jenkins is not set up / there is no data +class JenkinsAdapterTestCase(SimpleTestCase): + def test_get_all_slaves(self): + slaves = jenkins.get_all_slaves() + self.assertTrue(len(slaves) > 0) + for slave in slaves: + self.assertTrue('displayName' in slave) + self.assertTrue('idle' in slave) + self.assertTrue('offline' in slave) + + def test_get_ci_slaves(self): + slaves = jenkins.get_ci_slaves() + self.assertTrue(len(slaves) > 0) + for slave in slaves: + self.assertTrue('nodeName' in slave) + + def test_get_all_jobs(self): + jobs = jenkins.get_all_jobs() + lastBuild = False + self.assertTrue(len(jobs) > 0) + for job in jobs: + self.assertTrue('displayName' in job) + self.assertTrue('url' in job) + self.assertTrue('lastBuild' in job) + if job['lastBuild'] is not None: + lastBuild = True + self.assertTrue('building' in job['lastBuild']) + self.assertTrue('fullDisplayName' in job['lastBuild']) + self.assertTrue('result' in job['lastBuild']) + self.assertTrue('timestamp' in job['lastBuild']) + self.assertTrue('builtOn' in job['lastBuild']) + self.assertTrue(lastBuild) diff --git a/tools/pharos-dashboard/dashboard/urls.py b/tools/pharos-dashboard/dashboard/urls.py new file mode 100644 index 00000000..8050eb88 --- /dev/null +++ b/tools/pharos-dashboard/dashboard/urls.py @@ -0,0 +1,35 @@ +from dashboard.views.booking import BookingCalendarView, ResourceBookingsView, DeleteBookingView +from dashboard.views.table_views import CIPodsView, DevelopmentPodsView, JenkinsSlavesView +from django.conf.urls import url, include +from django.contrib.auth import views as auth_views + + +urlpatterns = [ + # registration + url(r'^accounts/login/$', auth_views.login, name='login'), + url(r'^accounts/logout/$', auth_views.logout, name='logout'), + + # Index + url(r'^index/$', CIPodsView.as_view(), name='index'), + url(r'^index/$', CIPodsView.as_view(), name='index'), + url(r'^$', CIPodsView.as_view(), name=""), + + # Tables + url(r'^ci_pods/$', CIPodsView.as_view(), name='ci_pods'), + url(r'^dev_pods/$', DevelopmentPodsView.as_view(), name='dev_pods'), + url(r'^jenkins_slaves/$', JenkinsSlavesView.as_view(), name='jenkins_slaves'), + + # Booking Calendar + url(r'^booking_calendar/$', DevelopmentPodsView.as_view(), + name='booking_calendar'), + url(r'^booking_calendar/(?P[0-9]+)/$', + BookingCalendarView.as_view(), name='booking_calendar'), + url(r'^booking_calendar/(?P[0-9]+)/(?P[0-9]+)/$', + BookingCalendarView.as_view(), name='booking_calendar'), + + # AJAX urls + url(r'^resource/(?P[0-9]+)/bookings/$', + ResourceBookingsView.as_view(), name='resource_bookings'), + url(r'^booking/(?P[0-9]+)/delete$', + DeleteBookingView.as_view(), name='delete_booking'), +] diff --git a/tools/pharos-dashboard/dashboard/views/booking.py b/tools/pharos-dashboard/dashboard/views/booking.py new file mode 100644 index 00000000..1499edb7 --- /dev/null +++ b/tools/pharos-dashboard/dashboard/views/booking.py @@ -0,0 +1,69 @@ +from dashboard.forms.booking_form import BookingForm +from dashboard.models import Resource, Booking +from dashboard.views.registration import BookingUserTestMixin +from django.contrib import messages +from django.contrib.auth.decorators import login_required +from django.http import Http404, JsonResponse +from django.shortcuts import get_object_or_404 +from django.utils.decorators import method_decorator +from django.views.generic import FormView, View + + +@method_decorator(login_required, name='dispatch') +class BookingCalendarView(BookingUserTestMixin, FormView): + template_name = "dashboard/booking_calendar.html" + form_class = BookingForm + + # set instance variables + def dispatch(self, request, *args, **kwargs): + self.foo = request.GET.get('foo', False) + self.resource = get_object_or_404(Resource, resource_id=self.kwargs['resource_id']) + return super(BookingCalendarView, self).dispatch(request, *args, **kwargs) + + def form_valid(self, form): + self.success_url = self.request.path + booking = None + # change existing booking + if form.cleaned_data['booking_id'] is not None: + booking = get_object_or_404(Booking, booking_id=form.cleaned_data['booking_id']) + # create new booking + else: + booking = Booking() + booking.resource = self.resource + booking.user = self.request.user + booking.start_date_time = form.cleaned_data['start_date_time'] + booking.end_date_time = form.cleaned_data['end_date_time'] + booking.purpose = form.cleaned_data['purpose'] + try: + booking.save() + except ValueError: + messages.add_message(self.request, messages.ERROR, + 'This booking overlaps with another booking') + return super(BookingCalendarView, self).form_invalid(form) + messages.add_message(self.request, messages.SUCCESS, + 'Booking saved') + return super(BookingCalendarView, self).form_valid(form) + + def get_context_data(self, **kwargs): + title = 'Booking: ' + self.resource.name + context = super(BookingCalendarView, self).get_context_data(**kwargs) + context.update({'title': title, 'resource': self.resource}) + return context + + +@method_decorator(login_required, name='dispatch') +class ResourceBookingsView(BookingUserTestMixin, View): + def get(self, request, *args, **kwargs): + resource = Resource.objects.get(resource_id=self.kwargs['resource_id']) + bookings = resource.booking_set.get_queryset().values( + 'booking_id', 'user__username', 'user__email', + 'start_date_time', 'end_date_time', 'purpose') + return JsonResponse({'bookings': list(bookings)}) + + +@method_decorator(login_required, name='dispatch') +class DeleteBookingView(BookingUserTestMixin, View): + def post(self, request, *args, **kwargs): + booking = get_object_or_404(Booking, booking_id=self.kwargs['booking_id']) + booking.delete() + return JsonResponse({True: self.kwargs['booking_id']}) diff --git a/tools/pharos-dashboard/dashboard/views/registration.py b/tools/pharos-dashboard/dashboard/views/registration.py new file mode 100644 index 00000000..516fb298 --- /dev/null +++ b/tools/pharos-dashboard/dashboard/views/registration.py @@ -0,0 +1,16 @@ +from django.contrib.auth.mixins import UserPassesTestMixin + + +class BookingUserTestMixin(UserPassesTestMixin): + # Test if a user has permission to book this Pod + def test_func(self): + user = self.request.user + # Check if User is troubleshooter / admin + if user.has_perm(('dashboard.add_booking')): + return True + # Check if User owns this resource + user_resources = user.userresource_set.get_queryset() + for user_resource in user_resources: + if user_resource.resource_id == self.resource.resource_id: + return True + return False diff --git a/tools/pharos-dashboard/dashboard/views/table_views.py b/tools/pharos-dashboard/dashboard/views/table_views.py new file mode 100644 index 00000000..4404234e --- /dev/null +++ b/tools/pharos-dashboard/dashboard/views/table_views.py @@ -0,0 +1,62 @@ +import dashboard.jenkins.jenkins_util as jenkins_util + +import dashboard.jenkins.jenkins_adapter as jenkins +from dashboard.models import Resource, Booking +from django.utils import timezone +from django.views.generic import TemplateView + + +class JenkinsSlavesView(TemplateView): + template_name = "tables/jenkins_slaves.html" + + def get_context_data(self, **kwargs): + slaves = jenkins.get_all_slaves() + for slave in slaves: + jenkins_util.parse_slave_data(slave, slave) + + context = super(JenkinsSlavesView, self).get_context_data(**kwargs) + context.update({'title': "Jenkins Slaves", 'slaves': slaves}) + return context + + +class CIPodsView(TemplateView): + template_name = "tables/ci_pods.html" + + def get_context_data(self, **kwargs): + resources = Resource.objects.filter().values() # get resources as a set of dicts + ci_pods = [] + for resource in resources: + if not jenkins.is_ci_slave(resource['slavename']): + continue + ci_slave = jenkins.get_slave(resource['slavename']) + jenkins_util.parse_slave_data(resource, ci_slave) + ci_pods.append(resource) + + context = super(CIPodsView, self).get_context_data(**kwargs) + context.update({'title': "CI Pods", 'ci_pods': ci_pods}) + return context + + +class DevelopmentPodsView(TemplateView): + template_name = "tables/dev_pods.html" + + def get_context_data(self, **kwargs): + resources = Resource.objects.filter().values() # get resources as a set of dicts + dev_pods = [] + + current_bookings = Booking.objects.filter(start_date_time__lte=timezone.now()) + current_bookings = current_bookings.filter(end_date_time__gt=timezone.now()) + + for resource in resources: + if not jenkins.is_dev_pod(resource['slavename']): + continue + dev_pod = jenkins.get_slave(resource['slavename']) + jenkins_util.parse_slave_data(resource, dev_pod) + for booking in current_bookings: + if booking.resource.slavename == resource['slavename']: + resource['current_booking'] = booking + dev_pods.append(resource) + + context = super(DevelopmentPodsView, self).get_context_data(**kwargs) + context.update({'title': "Development Pods", 'dev_pods': dev_pods}) + return context diff --git a/tools/pharos-dashboard/deploy.org b/tools/pharos-dashboard/deploy.org new file mode 100644 index 00000000..b8399215 --- /dev/null +++ b/tools/pharos-dashboard/deploy.org @@ -0,0 +1,45 @@ +* Database + +** Setup +- sudo -u postgres psql +- postgres=# CREATE DATABASE pharos_dashboard +- postgres=# CREATE USER opnfv WITH PASSWORD 'opnfvopnfv' +- postgres# createuser --interactive +- postgres# ALTER ROLE opnfv SET client_encoding TO 'utf8'; +- postgres# ALTER ROLE opnfv SET default_transaction_isolation TO 'read committed'; +- postgres# ALTER ROLE opnfv SET timezone TO 'UTC'; +- postgres# GRANT ALL PRIVILEGES ON DATABASE pharos_dashboard TO opnfv; + +** Dump data + +- log out all users, stop server +- (venv) # python manage.py dumpdata > dashboard/fixtures/.json + +** Load dump + +- setup clean database, run migrate +- (venv) # python manage.py loaddata + +* Django + +** Virtualenv setup + +- # virtualenv venv +- # source venv/bin/activate +- (venv) # pip install -r requirements.txt + +** initializing or after change in models.py + +- (venv) # python manage.py makemigrations +- (venv) # python manage.py migrate + +** Development + +- (venv) # python manage.py runserver +- (venv) # python manage.py shell + +* Dependencies + +Javascript / CSS dependencies are managed with bower. To install them, you have to install bower, switch directory to the dashboard/static folder and run +# bower install +Bower will download and install the right versions of all the static files. diff --git a/tools/pharos-dashboard/issues.org b/tools/pharos-dashboard/issues.org new file mode 100644 index 00000000..0a7d3cae --- /dev/null +++ b/tools/pharos-dashboard/issues.org @@ -0,0 +1,6 @@ +* Fullcalendar +- no selectHelper for month select +- if an event is selected in month select, browser timezone is ignored + +* layout +- datatable does not stay in its panel if zoom is to high / browser window is not maximized diff --git a/tools/pharos-dashboard/manage.py b/tools/pharos-dashboard/manage.py new file mode 100755 index 00000000..65e6fc6a --- /dev/null +++ b/tools/pharos-dashboard/manage.py @@ -0,0 +1,10 @@ +#!/usr/bin/env python +import os +import sys + +if __name__ == "__main__": + os.environ.setdefault("DJANGO_SETTINGS_MODULE", "pharos_dashboard.settings") + + from django.core.management import execute_from_command_line + + execute_from_command_line(sys.argv) diff --git a/tools/pharos-dashboard/pharos_dashboard/__init__.py b/tools/pharos-dashboard/pharos_dashboard/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/tools/pharos-dashboard/pharos_dashboard/settings.py b/tools/pharos-dashboard/pharos_dashboard/settings.py new file mode 100644 index 00000000..2bc94965 --- /dev/null +++ b/tools/pharos-dashboard/pharos_dashboard/settings.py @@ -0,0 +1,124 @@ +""" +Django settings for opnfvdashboard project. + +Generated by 'django-admin startproject' using Django 1.9.7. + +For more information on this file, see +https://docs.djangoproject.com/en/1.9/topics/settings/ + +For the full list of settings and their values, see +https://docs.djangoproject.com/en/1.9/ref/settings/ +""" + +import os + +# Build paths inside the project like this: os.path.join(BASE_DIR, ...) +BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) + +# Quick-start development settings - unsuitable for production +# See https://docs.djangoproject.com/en/1.9/howto/deployment/checklist/ + +# SECURITY WARNING: keep the secret key used in production secret! +SECRET_KEY = '=awtgkzaq@ytwbsp$$n=7=m&9*cm7gci7o-dy07)!x1um=g(gf' + +# SECURITY WARNING: don't run with debug turned on in production! +DEBUG = True + +ALLOWED_HOSTS = [] + +# Application definition + +INSTALLED_APPS = [ + 'dashboard', + 'django.contrib.admin', + 'django.contrib.auth', + 'django.contrib.contenttypes', + 'django.contrib.sessions', + 'django.contrib.messages', + 'django.contrib.staticfiles', + 'bootstrap3' +] + +MIDDLEWARE_CLASSES = [ + 'django.middleware.security.SecurityMiddleware', + 'django.contrib.sessions.middleware.SessionMiddleware', + 'django.middleware.common.CommonMiddleware', + 'django.middleware.csrf.CsrfViewMiddleware', + 'django.contrib.auth.middleware.AuthenticationMiddleware', + 'django.contrib.auth.middleware.SessionAuthenticationMiddleware', + 'django.contrib.messages.middleware.MessageMiddleware', + 'django.middleware.clickjacking.XFrameOptionsMiddleware', +] + +ROOT_URLCONF = 'pharos_dashboard.urls' + +TEMPLATES = [ + { + 'BACKEND': 'django.template.backends.django.DjangoTemplates', + 'DIRS': [os.path.join(BASE_DIR, 'templates')] + , + 'APP_DIRS': True, + 'OPTIONS': { + 'context_processors': [ + 'django.template.context_processors.debug', + 'django.template.context_processors.request', + 'django.contrib.auth.context_processors.auth', + 'django.contrib.messages.context_processors.messages', + ], + }, + }, +] + +WSGI_APPLICATION = 'pharos_dashboard.wsgi.application' + +# Database +# https://docs.djangoproject.com/en/1.9/ref/settings/#databases + +DATABASES = { + 'default': { + 'ENGINE': 'django.db.backends.postgresql', + 'NAME': 'pharos_dashboard', + 'USER': 'opnfv', + 'PASSWORD': 'opnfvopnfv', + 'HOST': 'localhost', + 'PORT': '', + } +} + +# Password validation +# https://docs.djangoproject.com/en/1.9/ref/settings/#auth-password-validators + +AUTH_PASSWORD_VALIDATORS = [ + { + 'NAME': 'django.contrib.auth.password_validation.UserAttributeSimilarityValidator', + }, + { + 'NAME': 'django.contrib.auth.password_validation.MinimumLengthValidator', + }, + { + 'NAME': 'django.contrib.auth.password_validation.CommonPasswordValidator', + }, + { + 'NAME': 'django.contrib.auth.password_validation.NumericPasswordValidator', + }, +] + +LOGIN_REDIRECT_URL = '/' + +# Internationalization +# https://docs.djangoproject.com/en/1.9/topics/i18n/ + +LANGUAGE_CODE = 'en-us' + +TIME_ZONE = 'UTC' + +USE_I18N = True + +USE_L10N = True + +USE_TZ = True + +# Static files (CSS, JavaScript, Images) +# https://docs.djangoproject.com/en/1.9/howto/static-files/ + +STATIC_URL = '/static/' diff --git a/tools/pharos-dashboard/pharos_dashboard/urls.py b/tools/pharos-dashboard/pharos_dashboard/urls.py new file mode 100644 index 00000000..03b9c256 --- /dev/null +++ b/tools/pharos-dashboard/pharos_dashboard/urls.py @@ -0,0 +1,23 @@ +"""opnfvdashboard URL Configuration + +The `urlpatterns` list routes URLs to views. For more information please see: + https://docs.djangoproject.com/en/1.9/topics/http/urls/ +Examples: +Function views + 1. Add an import: from my_app import views + 2. Add a URL to urlpatterns: url(r'^$', views.home, name='home') +Class-based views + 1. Add an import: from other_app.views import Home + 2. Add a URL to urlpatterns: url(r'^$', Home.as_view(), name='home') +Including another URLconf + 1. Import the include() function: from django.conf.urls import url, include + 2. Add a URL to urlpatterns: url(r'^blog/', include('blog.urls')) +""" + +from django.conf.urls import include, url +from django.contrib import admin + +urlpatterns = [ + url(r'^', include('dashboard.urls', namespace='dashboard')), + url(r'^admin/', include(admin.site.urls)), +] \ No newline at end of file diff --git a/tools/pharos-dashboard/pharos_dashboard/wsgi.py b/tools/pharos-dashboard/pharos_dashboard/wsgi.py new file mode 100644 index 00000000..54f57355 --- /dev/null +++ b/tools/pharos-dashboard/pharos_dashboard/wsgi.py @@ -0,0 +1,16 @@ +""" +WSGI config for pharos_dashboard project. + +It exposes the WSGI callable as a module-level variable named ``application``. + +For more information on this file, see +https://docs.djangoproject.com/en/1.9/howto/deployment/wsgi/ +""" + +import os + +from django.core.wsgi import get_wsgi_application + +os.environ.setdefault("DJANGO_SETTINGS_MODULE", "pharos_dashboard.settings") + +application = get_wsgi_application() diff --git a/tools/pharos-dashboard/requirements.txt b/tools/pharos-dashboard/requirements.txt new file mode 100644 index 00000000..6306c0b5 --- /dev/null +++ b/tools/pharos-dashboard/requirements.txt @@ -0,0 +1,5 @@ +Django==1.9.8 +django-bootstrap3==7.0.1 +psycopg2==2.6.2 +pytz==2016.6.1 +requests==2.10.0 diff --git a/tools/pharos-dashboard/templates/dashboard/base.html b/tools/pharos-dashboard/templates/dashboard/base.html new file mode 100644 index 00000000..544bf0b3 --- /dev/null +++ b/tools/pharos-dashboard/templates/dashboard/base.html @@ -0,0 +1,93 @@ +{% extends "layout/base.html" %} +{% load bootstrap3 %} + +{% block basecontent %} +
+ + + + +
+
+
+

{{ title }}

+
+ +
+ + {% bootstrap_messages %} + + {% block content %} + + {% endblock content %} +
+ +
+ +{% endblock basecontent %} \ No newline at end of file diff --git a/tools/pharos-dashboard/templates/dashboard/booking_calendar.html b/tools/pharos-dashboard/templates/dashboard/booking_calendar.html new file mode 100644 index 00000000..0f6bece0 --- /dev/null +++ b/tools/pharos-dashboard/templates/dashboard/booking_calendar.html @@ -0,0 +1,79 @@ +{% extends "dashboard/base.html" %} +{% load staticfiles %} +{% load bootstrap3 %} + +{% block extrahead %} + + +{% endblock extrahead %} + +{% block content %} +
+
+
+
+
+ Calendar +
+
+
+
+
+ +
+ +
+
+ +
+
+
+ Booking +
+
+ {% bootstrap_form_errors form type='non_fields' %} + +
+ {% csrf_token %} +
+ {% bootstrap_field form.start_date_time addon_after='' %} +
+
+ {% bootstrap_field form.end_date_time addon_after='' %} +
+ {% bootstrap_field form.purpose %} + {{ form.booking_id }} + {% buttons %} + + + {% endbuttons %} +
+ +
+
+
+
+{% endblock content %} + +{% block extrajs %} + + + + + + + + + +{% endblock extrajs %} \ No newline at end of file diff --git a/tools/pharos-dashboard/templates/dashboard/table.html b/tools/pharos-dashboard/templates/dashboard/table.html new file mode 100644 index 00000000..2d0b82ee --- /dev/null +++ b/tools/pharos-dashboard/templates/dashboard/table.html @@ -0,0 +1,50 @@ +{% extends "dashboard/base.html" %} +{% load staticfiles %} + +{% block extrahead %} + + + + + +{% endblock extrahead %} + +{% block content %} +
+
+
+
+
+ + + {% block table %} + {% endblock table %} + +
+
+ +
+ +
+ +
+ +
+{% endblock content %} + +{% block extrajs %} + + + + + + + + + + {% block tablejs %} + {% endblock tablejs %} +{% endblock extrajs %} \ No newline at end of file diff --git a/tools/pharos-dashboard/templates/layout/base.html b/tools/pharos-dashboard/templates/layout/base.html new file mode 100644 index 00000000..e9e1cd1f --- /dev/null +++ b/tools/pharos-dashboard/templates/layout/base.html @@ -0,0 +1,70 @@ +{% load staticfiles %} + + + + + + + + + + + + OPNFV Pharos {{ title }} + + + + + + + + + + + + + + + + + + {% block extrahead %} + {% endblock extrahead %} + + + + + + + +{% block extrastyle %} +{% endblock extrastyle %} + + +{% block basecontent %} +{% endblock basecontent %} + + + + + +{##} + + + + + + + + + +{% block extrajs %} +{% endblock extrajs %} + + diff --git a/tools/pharos-dashboard/templates/registration/login.html b/tools/pharos-dashboard/templates/registration/login.html new file mode 100644 index 00000000..efdcd1fb --- /dev/null +++ b/tools/pharos-dashboard/templates/registration/login.html @@ -0,0 +1,61 @@ +{% extends "layout/base.html" %} + +{% block basecontent %} +
+
+
+ {% if next %} +
+ + {% if user.is_authenticated %} + Your account doesn't have access to this page. To proceed, + please login with an account that has access. + {% else %} + Please login to see this page. + {% endif %} +
+ {% endif %} + {% if form.errors %} +
+ + Your username and password didn't match. Please try again. +
+ {% endif %} +
+
+
+
+ +
+
+
+ {# Assumes you setup the password_reset view in your URLconf #} + {#

Lost password?

#} +{% endblock basecontent %} diff --git a/tools/pharos-dashboard/templates/tables/ci_pods.html b/tools/pharos-dashboard/templates/tables/ci_pods.html new file mode 100644 index 00000000..3889664b --- /dev/null +++ b/tools/pharos-dashboard/templates/tables/ci_pods.html @@ -0,0 +1,59 @@ +{% extends "dashboard/table.html" %} +{% load staticfiles %} + +{% block table %} + + + Name + Slave Name + Status + Installer + Scenario + Branch + Job + + + + {% for pod in ci_pods %} + + + {{ pod.name }} + + + {{ pod.slavename }} + + + {{ pod.status }} + + + {{ pod.last_job.installer }} + + + {{ pod.last_job.scenario }} + + + {{ pod.last_job.branch }} + + {{ pod.last_job.name }} + + + {% endfor %}` + + +{% endblock table %} + + +{% block tablejs %} + +{% endblock tablejs %} \ No newline at end of file diff --git a/tools/pharos-dashboard/templates/tables/dev_pods.html b/tools/pharos-dashboard/templates/tables/dev_pods.html new file mode 100644 index 00000000..730aa953 --- /dev/null +++ b/tools/pharos-dashboard/templates/tables/dev_pods.html @@ -0,0 +1,58 @@ +{% extends "dashboard/table.html" %} +{% load staticfiles %} + +{% block table %} + + + Name + Slave Name + Booked by + Booked until + Purpose + Status + + + + + {% for pod in dev_pods %} + + + {{ pod.name }} + + + {{ pod.slavename }} + + + {{ pod.current_booking.user }} + + + {{ pod.current_booking.end_date_time }} + + + {{ pod.current_booking.purpose }} + + + {{ pod.status }} + + + + Book + + + + {% endfor %} + +{% endblock table %} + +{% block tablejs %} + +{% endblock tablejs %} \ No newline at end of file diff --git a/tools/pharos-dashboard/templates/tables/jenkins_slaves.html b/tools/pharos-dashboard/templates/tables/jenkins_slaves.html new file mode 100644 index 00000000..2d011b46 --- /dev/null +++ b/tools/pharos-dashboard/templates/tables/jenkins_slaves.html @@ -0,0 +1,42 @@ +{% extends "dashboard/table.html" %} +{% load staticfiles %} + +{% block table %} + + + Slave name + Status + Job + + + + {% for slave in slaves %} + + {{ slave.displayName }} + + + {{ slave.status }} + + + {{ slave.last_job.name }} + + + {% endfor %} + +{% endblock table %} + + +{% block tablejs %} + +{% endblock tablejs %} \ No newline at end of file diff --git a/tools/pharos_dashboard b/tools/pharos_dashboard deleted file mode 160000 index 88df205b..00000000 --- a/tools/pharos_dashboard +++ /dev/null @@ -1 +0,0 @@ -Subproject commit 88df205bde4fe9bda20a59f2ab63a6e0408f3c35 -- cgit 1.2.3-korg