diff options
Diffstat (limited to 'tools/pharos-dashboard')
43 files changed, 1639 insertions, 0 deletions
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 --- /dev/null +++ b/tools/pharos-dashboard/dashboard/__init__.py 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 --- /dev/null +++ b/tools/pharos-dashboard/dashboard/migrations/__init__.py 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 <maxbr@mi.fu-berlin.de>" + ], + "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<resource_id>[0-9]+)/$', + BookingCalendarView.as_view(), name='booking_calendar'), + url(r'^booking_calendar/(?P<resource_id>[0-9]+)/(?P<booking_id>[0-9]+)/$', + BookingCalendarView.as_view(), name='booking_calendar'), + + # AJAX urls + url(r'^resource/(?P<resource_id>[0-9]+)/bookings/$', + ResourceBookingsView.as_view(), name='resource_bookings'), + url(r'^booking/(?P<booking_id>[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/<dump_name>.json + +** Load dump + +- setup clean database, run migrate +- (venv) # python manage.py loaddata <dump_name> + +* 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 --- /dev/null +++ b/tools/pharos-dashboard/pharos_dashboard/__init__.py 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 %} + <div id="wrapper"> + <!-- Navigation --> + <nav class="navbar navbar-default navbar-static-top" role="navigation" + style="margin-bottom: 0"> + <div class="navbar-header"> + <button type="button" class="navbar-toggle" data-toggle="collapse" + data-target=".navbar-collapse"> + <span class="sr-only">Toggle navigation</span> + <span class="icon-bar"></span> + <span class="icon-bar"></span> + <span class="icon-bar"></span> + </button> + <a href="https://www.opnfv.org/" class="navbar-left"><img + src="https://www.opnfv.org/sites/all/themes/opnfv/logo.png"></a> + <a class="navbar-brand" href={% url 'dashboard:' %}>Pharos Dashboard</a> + </div> + <!-- /.navbar-header --> + + <ul class="nav navbar-top-links navbar-right"> + <li class="dropdown"> + <a class="dropdown-toggle" data-toggle="dropdown" href="#"> + <i class="fa fa-user fa-fw"></i> <i class="fa fa-caret-down"></i> + </a> + <ul class="dropdown-menu dropdown-user"> + {% if user.is_authenticated %} + <li><a href="#"><i class="fa fa-user fa-fw"></i> User Profile</a> + </li> + <li><a href="#"><i class="fa fa-gear fa-fw"></i> Settings</a> + </li> + <li class="divider"></li> + <li><a href="{% url 'dashboard:logout' %}?next={{ request.path }}"><i + class="fa fa-sign-out fa-fw"></i> + Logout</a> + </li> + {% else %} + <li><a href="{% url 'dashboard:login' %}"><i + class="fa fa-sign-out fa-fw"></i> + Login</a> + {% endif %} + </ul> + <!-- /.dropdown-user --> + </li> + <!-- /.dropdown --> + </ul> + <!-- /.navbar-top-links --> + + <div class="navbar-default sidebar" role="navigation"> + <div class="sidebar-nav navbar-collapse"> + <ul class="nav" id="side-menu"> + <li> + <a href="{% url 'dashboard:ci_pods' %}"><i + class="fa fa-table fa-fw"></i>CI-Pods</a> + </li> + <li> + <a href="{% url 'dashboard:dev_pods' %}"><i + class="fa fa-table fa-fw"></i>Development + Pods</a> + </li> + <li> + <a href="{% url 'dashboard:jenkins_slaves' %}"><i + class="fa fa-table fa-fw"></i>Jenkins + Slaves</a> + </li> + </ul> + </div> + <!-- /.sidebar-collapse --> + </div> + <!-- /.navbar-static-side --> + </nav> + + <!-- Page Content --> + <div id="page-wrapper"> + <div class="row"> + <div class="col-lg-12"> + <h1 class="page-header">{{ title }}</h1> + </div> + <!-- /.col-lg-12 --> + </div> + + {% bootstrap_messages %} + + {% block content %} + + {% endblock content %} + </div> + <!-- /#page-wrapper --> + </div> + <!-- /#wrapper --> +{% 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 %} + <link href="{% static "bower_components/fullcalendar/dist/fullcalendar.css" %}" + rel='stylesheet'/> + <link href="{% static "bower_components/eonasdan-bootstrap-datetimepicker/build/css/bootstrap-datetimepicker.min.css" %}" + rel='stylesheet'/> +{% endblock extrahead %} + +{% block content %} + <div class="row"> + <div class="col-lg-8"> + <div class="container-fluid"> + <div class="panel panel-default"> + <div class="panel-heading"> + Calendar + </div> + <div class="panel-body"> + <div id='calendar'> + </div> + </div> + <!-- /.panel-body --> + </div> + <!-- /.panel --> + </div> + </div> + + <div class="col-lg-4"> + <div class="panel panel-default"> + <div class="panel-heading"> + Booking + </div> + <div class="panel-body"> + {% bootstrap_form_errors form type='non_fields' %} + + <form method="post" class="form" id="bookingform"> + {% csrf_token %} + <div class='input-group' id='starttimepicker'> + {% bootstrap_field form.start_date_time addon_after='<span class="glyphicon glyphicon-calendar"></span>' %} + </div> + <div class='input-group' id='endtimepicker'> + {% bootstrap_field form.end_date_time addon_after='<span class="glyphicon glyphicon-calendar"></span>' %} + </div> + {% bootstrap_field form.purpose %} + {{ form.booking_id }} + {% buttons %} + <button type="submit" class="btn btn btn-success" + id="submitform"> + Book Pod + </button> + <button type="button" class="btn btn btn-danger hidden" + id="deletebutton"> + Delete Booking + </button> + {% endbuttons %} + </form> + + </div> + </div> + </div> + </div> +{% endblock content %} + +{% block extrajs %} + <script type="text/javascript"> + var bookings_url = '/resource/' + {{ resource.resource_id }} +'/bookings/'; + </script> + + <script src={% static "bower_components/moment/moment.js" %}></script> + <script src={% static "bower_components/fullcalendar/dist/fullcalendar.js" %}></script> + <script type="text/javascript" + src={% static "bower_components/eonasdan-bootstrap-datetimepicker/build/js/bootstrap-datetimepicker.min.js" %}></script> + <script src={% static "js/fullcalendar-options.js" %}></script> + <script src={% static "js/datetimepicker-options.js" %}></script> + <script src={% static "js/csrf.js" %}></script> + <script src={% static "js/booking-calendar.js" %}></script> +{% 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 %} + <!-- DataTables CSS --> + <link href="{% static "bower_components/datatables-plugins/integration/bootstrap/3/dataTables.bootstrap.css" %}" + rel="stylesheet"> + + <!-- DataTables Responsive CSS --> + <link href="{% static "bower_components/datatables-responsive/css/dataTables.responsive.css" %}" rel="stylesheet"> +{% endblock extrahead %} + +{% block content %} + <div class="row"> + <div class="col-lg-12"> + <div class="panel panel-default"> + <div class="panel-body"> + <div class="dataTables_wrapper"> + <table class="table table-striped table-bordered table-hover" id="table" cellspacing="0" + width="100%"> + + {% block table %} + {% endblock table %} + + </table> + </div> + <!-- /.table-responsive --> + </div> + <!-- /.panel-body --> + </div> + <!-- /.panel --> + </div> + <!-- /.col-lg-12 --> + </div> +{% endblock content %} + +{% block extrajs %} + <!-- DataTables JavaScript --> + <link href="{% static "bower_components/datatables-plugins/integration/bootstrap/3/dataTables.bootstrap.css" %}" + rel="stylesheet"> + + + <script src={% static "bower_components/datatables/media/js/jquery.dataTables.min.js" %}></script> + <script src={% static "bower_components/datatables-plugins/integration/bootstrap/3/dataTables.bootstrap.min.js" %}></script> + + <script src={% static "js/dataTables-sort.js" %}></script> + + {% 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 %} +<!DOCTYPE html> +<html lang="en"> + +<head> + + <meta charset="utf-8"> + <meta http-equiv="X-UA-Compatible" content="IE=edge"> + <meta name="viewport" content="width=device-width, initial-scale=1"> + <meta name="description" content=""> + <meta name="author" content=""> + + <title>OPNFV Pharos {{ title }}</title> + + <!-- Bootstrap Core CSS --> + <link href="{% static "bower_components/bootstrap/dist/css/bootstrap.min.css" %}" + rel="stylesheet"> + + <!-- MetisMenu CSS --> + <link href="{% static "bower_components/metisMenu/dist/metisMenu.min.css" %}" rel="stylesheet"> + + <!-- Custom CSS --> + <link href="{% static "bower_components/startbootstrap-sb-admin-2/dist/css/sb-admin-2.css" %}" + rel="stylesheet"> + <link href="{% static "css/theme.css" %}" rel="stylesheet"> + + <!-- Custom Fonts --> + <link href="{% static "bower_components/font-awesome/css/font-awesome.min.css" %}" + rel="stylesheet" type="text/css"> + + <!-- Favicon --> + <link rel="shortcut icon" href="{% static 'favicon.ico' %}"> + + {% block extrahead %} + {% endblock extrahead %} + + <!-- HTML5 Shim and Respond.js IE8 support of HTML5 elements and media queries --> + <!-- WARNING: Respond.js doesn't work if you view the page via file:// --> + <!--[if lt IE 9]> + <script src="https://oss.maxcdn.com/libs/html5shiv/3.7.0/html5shiv.js"></script> + <script src="https://oss.maxcdn.com/libs/respond.js/1.4.2/respond.min.js"></script> + <![endif]--> + +</head> + +{% block extrastyle %} +{% endblock extrastyle %} + +<body> +{% block basecontent %} +{% endblock basecontent %} + +<!-- jQuery --> +<script src="{% static "bower_components/jquery/dist/jquery.min.js" %}"></script> +<script src="{% static "bower_components/jquery-migrate/jquery-migrate.min.js" %}"></script> + +{#<script src="https://code.jquery.com/jquery-2.2.0.min.js"></script>#} +<!-- Bootstrap Core JavaScript --> +<script src="{% static "bower_components/bootstrap/dist/js/bootstrap.min.js" %}"></script> + +<!-- Metis Menu Plugin JavaScript --> +<script src="{% static "bower_components/metisMenu/dist/metisMenu.min.js" %}"></script> + +<!-- Custom Theme JavaScript --> +<script src="{% static "bower_components/startbootstrap-sb-admin-2/dist/js/sb-admin-2.js" %}"></script> + +{% block extrajs %} +{% endblock extrajs %} +</body> +</html> 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 %} + <div class="container"> + <div class="row"> + <div class="col-md-4 col-md-offset-4"> + {% if next %} + <div class="alert alert-dismissable alert-info"> + <button type="button" class="close" data-dismiss="alert" aria-label="Close"> + <span aria-hidden="true">×</span> + </button> + {% 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 %} + </div> + {% endif %} + {% if form.errors %} + <div class="alert alert-danger alert-dismissable"> + <button type="button" class="close" data-dismiss="alert" aria-label="Close"> + <span aria-hidden="true">×</span> + </button> + Your username and password didn't match. Please try again. + </div> + {% endif %} + </div> + </div> + <div class="row"> + <div class="col-md-4 col-md-offset-4"> + <div class="login-panel panel panel-default"> + <div class="panel-heading"> + <h3 class="panel-title"> + Login + </h3> + </div> + <div class="panel-body"> + <form method="post" action="{% url 'dashboard:login' %}"> + {% csrf_token %} + <fieldset> + <div class="form-group"> + <input class="form-control" placeholder="Username" name="username" type="text" + autofocus> + </div> + <div class="form-group"> + <input class="form-control" placeholder="Password" name="password" + type="password" value=""> + </div> + <input type="submit" value="Login" class="btn btn-lg btn-success btn-block"/> + <input type="hidden" name="next" value="{{ next }}"/> + </fieldset> + </form> + </div> + </div> + </div> + </div> + </div> + {# Assumes you setup the password_reset view in your URLconf #} + {# <p><a href="{% url 'password_reset' %}">Lost password?</a></p>#} +{% 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 %} + <thead> + <tr> + <th>Name</th> + <th>Slave Name</th> + <th>Status</th> + <th>Installer</th> + <th>Scenario</th> + <th>Branch</th> + <th>Job</th> + </tr> + </thead> + <tbody> + {% for pod in ci_pods %} + <tr> + <th> + <a target='_blank' href={{ pod.url }}>{{ pod.name }}</a> + </th> + <th> + <a target='_blank' href={{ pod.slaveurl }}>{{ pod.slavename }}</a> + </th> + <th style="background-color:{{ pod.status_color }}"> + {{ pod.status }} + </th> + <th {{ pod.last_job.blink }}> + {{ pod.last_job.installer }} + </th> + <th {{ pod.last_job.blink }}> + {{ pod.last_job.scenario }} + </th> + <th {{ pod.last_job.blink }}> + {{ pod.last_job.branch }} + </th> + <th><a {{ pod.last_job.blink }} style="color:{{ pod.last_job.color }}" + target='_blank' + href={{ pod.last_job.url }}>{{ pod.last_job.name }}</a> + </th> + </tr> + {% endfor %}` + </tbody> + </table> +{% endblock table %} + + +{% block tablejs %} + <script type="text/javascript"> + $(document).ready(function () { + $('#table').DataTable({ + columnDefs: [ + {type: 'status', targets: 2} + ], + "order": [[2, "asc"]] + }); + }); + </script> +{% 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 %} + <thead> + <tr> + <th>Name</th> + <th>Slave Name</th> + <th>Booked by</th> + <th>Booked until</th> + <th>Purpose</th> + <th>Status</th> + <th></th> + </tr> + </thead> + <tbody> + {% for pod in dev_pods %} + <tr> + <th> + <a target='_blank' href={{ pod.url }}>{{ pod.name }}</a> + </th> + <th> + <a target='_blank' href={{ pod.slaveurl }}>{{ pod.slavename }}</a> + </th> + <th> + {{ pod.current_booking.user }} + </th> + <th> + {{ pod.current_booking.end_date_time }} + </th> + <th> + {{ pod.current_booking.purpose }} + </th> + <th style="background-color:{{ pod.status_color }}"> + {{ pod.status }} + </th> + <th> + <a href='{% url 'dashboard:booking_calendar' %}{{ pod.resource_id }}' class="btn btn-primary"> + Book + </a> + </th> + </tr> + {% endfor %} + </tbody> +{% endblock table %} + +{% block tablejs %} + <script type="text/javascript"> + $(document).ready(function () { + $('#table').DataTable({ + columnDefs: [ + {type: 'status', targets: 5} + ], + "order": [[5, "asc"]] + }); + }); + </script> +{% 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 %} + <thead> + <tr> + <th>Slave name</th> + <th>Status</th> + <th>Job</th> + </tr> + </thead> + <tbody> + {% for slave in slaves %} + <tr> + <th><a target='_blank' + href={{ slave.slaveurl }}>{{ slave.displayName }}</a> + </th> + <th style="background-color:{{ slave.status_color }}"> + {{ slave.status }} + </th> + <th><a {{ slave.last_job.blink }} style="color:{{ slave.last_job.color }}" + target="_blank" href={{ slave.last_job.url }}> + {{ slave.last_job.name }}</a> + </th> + </tr> + {% endfor %} + </tbody> +{% endblock table %} + + +{% block tablejs %} + <script type="text/javascript"> + $(document).ready(function () { + $('#table').DataTable({ + columnDefs: [ + {type: 'status', targets: 1} + ], + "order": [[1, "asc"]] + }); + }); + </script> +{% endblock tablejs %}
\ No newline at end of file |