├── cabot ├── cabotapp │ ├── tests │ │ ├── __init__.py │ │ ├── fixtures │ │ │ ├── __init__.py │ │ │ ├── cabot_check_skeleton │ │ │ │ ├── __init__.py │ │ │ │ └── plugin.py │ │ │ ├── graphite_avg_response.json │ │ │ ├── gcal_response.ics │ │ │ ├── graphite_response.json │ │ │ ├── recurring_response_notz.ics │ │ │ ├── graphite_null_response.json │ │ │ ├── recurring_response.ics │ │ │ └── recurring_response_complex.ics │ │ ├── test_plugin_settings.py │ │ ├── tests_icmp_check.py │ │ ├── test_setup.py │ │ ├── test_urlprefix.py │ │ └── tests_jenkins.py │ ├── migrations │ │ ├── __init__.py │ │ ├── 0004_auto_20170802_1327.py │ │ ├── 0007_statuscheckresult_consecutive_failures.py │ │ ├── 0006_auto_20170821_1000.py │ │ ├── 0002_auto_20170131_1537.py │ │ ├── 0003_auto_20170201_1045.py │ │ └── 0005_auto_20170818_1202.py │ ├── templatetags │ │ ├── __init__.py │ │ └── extra.py │ ├── __init__.py │ ├── models │ │ ├── __init__.py │ │ └── jenkins_check_plugin.py │ ├── utils.py │ ├── apps.py │ ├── admin.py │ ├── jenkins.py │ ├── alert.py │ ├── calendar.py │ ├── graphite.py │ └── tasks.py ├── static │ ├── 500.html │ ├── 502.html │ ├── 503.html │ ├── 504.html │ ├── 404.html │ ├── robots.txt │ ├── favicon.ico │ ├── theme │ │ ├── img │ │ │ ├── bg.jpg │ │ │ ├── crop.gif │ │ │ ├── dbg.jpg │ │ │ ├── logo.png │ │ │ ├── buttons.gif │ │ │ ├── dialogs.png │ │ │ ├── favicon.ico │ │ │ ├── logo20.png │ │ │ ├── resize.png │ │ │ ├── sprite.png │ │ │ ├── toolbar.gif │ │ │ ├── toolbar.png │ │ │ ├── bg-input.png │ │ │ ├── bg-login.jpg │ │ │ ├── calendar.gif │ │ │ ├── chat-left.png │ │ │ ├── icons-big.png │ │ │ ├── progress.gif │ │ │ ├── chat-right.png │ │ │ ├── close-button.png │ │ │ ├── i_16_radio.png │ │ │ ├── icons-small.png │ │ │ ├── quicklook-bg.png │ │ │ ├── spinner-mini.gif │ │ │ ├── arrows-active.png │ │ │ ├── arrows-normal.png │ │ │ ├── bg-input-focus.png │ │ │ ├── chosen-sprite.png │ │ │ ├── animated-overlay.gif │ │ │ ├── quicklook-icons.png │ │ │ ├── close-button-white.png │ │ │ ├── glyphicons-halflings.png │ │ │ ├── glyphicons-halflings-red.png │ │ │ ├── ui-icons_222222_256x240.png │ │ │ ├── ui-icons_2e83ff_256x240.png │ │ │ ├── ui-icons_454545_256x240.png │ │ │ ├── ui-icons_888888_256x240.png │ │ │ ├── ui-icons_cd0a0a_256x240.png │ │ │ ├── glyphicons-halflings-white.png │ │ │ ├── ui-bg_flat_0_aaaaaa_40x100.png │ │ │ ├── ui-bg_flat_75_ffffff_40x100.png │ │ │ ├── ui-bg_glass_55_fbf9ee_1x400.png │ │ │ ├── ui-bg_glass_65_ffffff_1x400.png │ │ │ ├── ui-bg_glass_75_dadada_1x400.png │ │ │ ├── ui-bg_glass_75_e6e6e6_1x400.png │ │ │ ├── ui-bg_glass_95_fef1ec_1x400.png │ │ │ └── ui-bg_highlight-soft_75_cccccc_1x100.png │ │ ├── css │ │ │ ├── chosen-sprite.png │ │ │ ├── chosen-sprite@2x.png │ │ │ └── bootstrap-datatables.min.css │ │ ├── fonts │ │ │ ├── glyphicons-halflings-regular.eot │ │ │ ├── glyphicons-halflings-regular.ttf │ │ │ ├── glyphicons-halflings-regular.woff │ │ │ └── glyphicons-halflings-regular.woff2 │ │ └── js │ │ │ ├── jquery.dataTables.bootstrap.min.js │ │ │ └── custom.js │ ├── arachnys │ │ ├── img │ │ │ ├── favicon.ico │ │ │ ├── icon_48x48.png │ │ │ └── icon_96x96.png │ │ └── css │ │ │ ├── morris.css │ │ │ └── base.less │ └── bootstrap │ │ ├── fonts │ │ ├── glyphicons-halflings-regular.eot │ │ ├── glyphicons-halflings-regular.ttf │ │ ├── glyphicons-halflings-regular.woff │ │ └── glyphicons-halflings-regular.woff2 │ │ └── css │ │ └── dashboard.css ├── templates │ ├── 500.html │ ├── registration │ │ ├── logout.html │ │ ├── login.html │ │ └── social_auth.html │ ├── 404.html │ ├── cabotapp │ │ ├── about.html │ │ ├── instance_confirm_delete.html │ │ ├── service_confirm_delete.html │ │ ├── statuscheck_confirm_delete.html │ │ ├── service_public_list.html │ │ ├── statuscheck_list.html │ │ ├── setup.html │ │ ├── instance_list.html │ │ ├── _base_form.html │ │ ├── service_form.html │ │ ├── instance_form.html │ │ ├── service_list.html │ │ ├── _service_public_list.html │ │ ├── shift_list.html │ │ ├── alertpluginuserdata_form.html │ │ ├── _instance_list.html │ │ ├── statuscheck_report.html │ │ ├── statuscheckresult_detail.html │ │ ├── subscriptions.html │ │ ├── _service_list.html │ │ ├── plugin_settings_form.html │ │ ├── statuscheck_form.html │ │ ├── statuscheck_detail.html │ │ ├── instance_detail.html │ │ └── _statuscheck_list.html │ ├── base.html │ └── base_public.html ├── version.py ├── wsgi.py ├── __init__.py ├── context_processors.py ├── entrypoint.py ├── settings_utils.py ├── celeryconfig.py ├── celery.py ├── settings_ldap.py ├── cabot_config.py └── rest_urls.py ├── .foreman ├── .coveragerc ├── setup_dev.sh ├── makemigrations ├── conf ├── test.env ├── default.env ├── development.env.example └── production.env.example ├── requirements-dev.txt ├── bin ├── build-app ├── activate ├── activate.fish └── test_with_coverage ├── gunicorn.conf ├── requirements-plugins.txt ├── Procfile.dev ├── Procfile ├── MANIFEST.in ├── docker-compose-base.yml ├── setup.cfg ├── manage.py ├── docker-compose-test.yml ├── .gitignore ├── upstart └── process.conf.erb ├── example_local_config.yml ├── Pipfile ├── .travis.yml ├── Dockerfile ├── LICENSE ├── docker-compose.yml ├── setup.py ├── docker-entrypoint.sh ├── requirements.txt ├── Vagrantfile ├── tox.ini ├── README.md └── CHANGES /cabot/cabotapp/tests/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /cabot/static/500.html: -------------------------------------------------------------------------------- 1 | 500 error -------------------------------------------------------------------------------- /cabot/static/502.html: -------------------------------------------------------------------------------- 1 | 502 error -------------------------------------------------------------------------------- /cabot/static/503.html: -------------------------------------------------------------------------------- 1 | 503 error -------------------------------------------------------------------------------- /cabot/static/504.html: -------------------------------------------------------------------------------- 1 | 504 error -------------------------------------------------------------------------------- /cabot/cabotapp/migrations/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /cabot/cabotapp/templatetags/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /cabot/cabotapp/tests/fixtures/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /cabot/static/404.html: -------------------------------------------------------------------------------- 1 | 404 Error: Page not found -------------------------------------------------------------------------------- /cabot/static/robots.txt: -------------------------------------------------------------------------------- 1 | User-agent: * 2 | Disallow: / -------------------------------------------------------------------------------- /cabot/cabotapp/tests/fixtures/cabot_check_skeleton/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /cabot/cabotapp/__init__.py: -------------------------------------------------------------------------------- 1 | default_app_config = 'cabot.cabotapp.apps.CabotappConfig' 2 | -------------------------------------------------------------------------------- /.foreman: -------------------------------------------------------------------------------- 1 | # vi: set ft=yaml : 2 | 3 | procfile: Procfile.dev 4 | env: conf/development.env 5 | -------------------------------------------------------------------------------- /cabot/cabotapp/models/__init__.py: -------------------------------------------------------------------------------- 1 | from .base import * 2 | from .jenkins_check_plugin import * 3 | -------------------------------------------------------------------------------- /cabot/static/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/arachnys/cabot/HEAD/cabot/static/favicon.ico -------------------------------------------------------------------------------- /cabot/static/theme/img/bg.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/arachnys/cabot/HEAD/cabot/static/theme/img/bg.jpg -------------------------------------------------------------------------------- /.coveragerc: -------------------------------------------------------------------------------- 1 | [run] 2 | branch = True 3 | plugins = 4 | django_coverage_plugin 5 | 6 | omit = *migrations* 7 | -------------------------------------------------------------------------------- /cabot/static/theme/img/crop.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/arachnys/cabot/HEAD/cabot/static/theme/img/crop.gif -------------------------------------------------------------------------------- /cabot/static/theme/img/dbg.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/arachnys/cabot/HEAD/cabot/static/theme/img/dbg.jpg -------------------------------------------------------------------------------- /cabot/static/theme/img/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/arachnys/cabot/HEAD/cabot/static/theme/img/logo.png -------------------------------------------------------------------------------- /setup_dev.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | foreman run python manage.py syncdb 3 | foreman run python manage.py migrate 4 | -------------------------------------------------------------------------------- /cabot/static/theme/img/buttons.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/arachnys/cabot/HEAD/cabot/static/theme/img/buttons.gif -------------------------------------------------------------------------------- /cabot/static/theme/img/dialogs.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/arachnys/cabot/HEAD/cabot/static/theme/img/dialogs.png -------------------------------------------------------------------------------- /cabot/static/theme/img/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/arachnys/cabot/HEAD/cabot/static/theme/img/favicon.ico -------------------------------------------------------------------------------- /cabot/static/theme/img/logo20.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/arachnys/cabot/HEAD/cabot/static/theme/img/logo20.png -------------------------------------------------------------------------------- /cabot/static/theme/img/resize.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/arachnys/cabot/HEAD/cabot/static/theme/img/resize.png -------------------------------------------------------------------------------- /cabot/static/theme/img/sprite.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/arachnys/cabot/HEAD/cabot/static/theme/img/sprite.png -------------------------------------------------------------------------------- /cabot/static/theme/img/toolbar.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/arachnys/cabot/HEAD/cabot/static/theme/img/toolbar.gif -------------------------------------------------------------------------------- /cabot/static/theme/img/toolbar.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/arachnys/cabot/HEAD/cabot/static/theme/img/toolbar.png -------------------------------------------------------------------------------- /cabot/static/theme/img/bg-input.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/arachnys/cabot/HEAD/cabot/static/theme/img/bg-input.png -------------------------------------------------------------------------------- /cabot/static/theme/img/bg-login.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/arachnys/cabot/HEAD/cabot/static/theme/img/bg-login.jpg -------------------------------------------------------------------------------- /cabot/static/theme/img/calendar.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/arachnys/cabot/HEAD/cabot/static/theme/img/calendar.gif -------------------------------------------------------------------------------- /cabot/static/theme/img/chat-left.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/arachnys/cabot/HEAD/cabot/static/theme/img/chat-left.png -------------------------------------------------------------------------------- /cabot/static/theme/img/icons-big.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/arachnys/cabot/HEAD/cabot/static/theme/img/icons-big.png -------------------------------------------------------------------------------- /cabot/static/theme/img/progress.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/arachnys/cabot/HEAD/cabot/static/theme/img/progress.gif -------------------------------------------------------------------------------- /cabot/static/arachnys/img/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/arachnys/cabot/HEAD/cabot/static/arachnys/img/favicon.ico -------------------------------------------------------------------------------- /cabot/static/theme/img/chat-right.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/arachnys/cabot/HEAD/cabot/static/theme/img/chat-right.png -------------------------------------------------------------------------------- /cabot/static/theme/img/close-button.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/arachnys/cabot/HEAD/cabot/static/theme/img/close-button.png -------------------------------------------------------------------------------- /cabot/static/theme/img/i_16_radio.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/arachnys/cabot/HEAD/cabot/static/theme/img/i_16_radio.png -------------------------------------------------------------------------------- /cabot/static/theme/img/icons-small.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/arachnys/cabot/HEAD/cabot/static/theme/img/icons-small.png -------------------------------------------------------------------------------- /cabot/static/theme/img/quicklook-bg.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/arachnys/cabot/HEAD/cabot/static/theme/img/quicklook-bg.png -------------------------------------------------------------------------------- /cabot/static/theme/img/spinner-mini.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/arachnys/cabot/HEAD/cabot/static/theme/img/spinner-mini.gif -------------------------------------------------------------------------------- /makemigrations: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | rm dev.db 3 | foreman run python manage.py syncdb 4 | foreman run python manage.py migrate 5 | -------------------------------------------------------------------------------- /cabot/static/arachnys/img/icon_48x48.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/arachnys/cabot/HEAD/cabot/static/arachnys/img/icon_48x48.png -------------------------------------------------------------------------------- /cabot/static/arachnys/img/icon_96x96.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/arachnys/cabot/HEAD/cabot/static/arachnys/img/icon_96x96.png -------------------------------------------------------------------------------- /cabot/static/theme/css/chosen-sprite.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/arachnys/cabot/HEAD/cabot/static/theme/css/chosen-sprite.png -------------------------------------------------------------------------------- /cabot/static/theme/img/arrows-active.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/arachnys/cabot/HEAD/cabot/static/theme/img/arrows-active.png -------------------------------------------------------------------------------- /cabot/static/theme/img/arrows-normal.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/arachnys/cabot/HEAD/cabot/static/theme/img/arrows-normal.png -------------------------------------------------------------------------------- /cabot/static/theme/img/bg-input-focus.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/arachnys/cabot/HEAD/cabot/static/theme/img/bg-input-focus.png -------------------------------------------------------------------------------- /cabot/static/theme/img/chosen-sprite.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/arachnys/cabot/HEAD/cabot/static/theme/img/chosen-sprite.png -------------------------------------------------------------------------------- /cabot/static/theme/css/chosen-sprite@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/arachnys/cabot/HEAD/cabot/static/theme/css/chosen-sprite@2x.png -------------------------------------------------------------------------------- /cabot/static/theme/img/animated-overlay.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/arachnys/cabot/HEAD/cabot/static/theme/img/animated-overlay.gif -------------------------------------------------------------------------------- /cabot/static/theme/img/quicklook-icons.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/arachnys/cabot/HEAD/cabot/static/theme/img/quicklook-icons.png -------------------------------------------------------------------------------- /conf/test.env: -------------------------------------------------------------------------------- 1 | DATABASE_URL=sqlite://:memory: 2 | 3 | CELERY_BROKER_URL=memory:// 4 | CELERY_ALWAYS_EAGER=True 5 | 6 | SKIP_INIT=True 7 | -------------------------------------------------------------------------------- /requirements-dev.txt: -------------------------------------------------------------------------------- 1 | coverage==4.3.4 2 | django_coverage_plugin==1.5.0 3 | freezegun==0.3.9 4 | mock==2.0.0 5 | ipdb 6 | isort==4.2.15 7 | -------------------------------------------------------------------------------- /cabot/static/theme/img/close-button-white.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/arachnys/cabot/HEAD/cabot/static/theme/img/close-button-white.png -------------------------------------------------------------------------------- /bin/build-app: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | set -e 3 | python manage.py migrate 4 | python manage.py collectstatic --noinput 5 | # python manage.py compress 6 | -------------------------------------------------------------------------------- /cabot/static/theme/img/glyphicons-halflings.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/arachnys/cabot/HEAD/cabot/static/theme/img/glyphicons-halflings.png -------------------------------------------------------------------------------- /cabot/templates/500.html: -------------------------------------------------------------------------------- 1 | 2 |
3 | Unhandled error. Please look at logs. 4 |
5 | -------------------------------------------------------------------------------- /cabot/static/theme/img/glyphicons-halflings-red.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/arachnys/cabot/HEAD/cabot/static/theme/img/glyphicons-halflings-red.png -------------------------------------------------------------------------------- /cabot/static/theme/img/ui-icons_222222_256x240.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/arachnys/cabot/HEAD/cabot/static/theme/img/ui-icons_222222_256x240.png -------------------------------------------------------------------------------- /cabot/static/theme/img/ui-icons_2e83ff_256x240.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/arachnys/cabot/HEAD/cabot/static/theme/img/ui-icons_2e83ff_256x240.png -------------------------------------------------------------------------------- /cabot/static/theme/img/ui-icons_454545_256x240.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/arachnys/cabot/HEAD/cabot/static/theme/img/ui-icons_454545_256x240.png -------------------------------------------------------------------------------- /cabot/static/theme/img/ui-icons_888888_256x240.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/arachnys/cabot/HEAD/cabot/static/theme/img/ui-icons_888888_256x240.png -------------------------------------------------------------------------------- /cabot/static/theme/img/ui-icons_cd0a0a_256x240.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/arachnys/cabot/HEAD/cabot/static/theme/img/ui-icons_cd0a0a_256x240.png -------------------------------------------------------------------------------- /gunicorn.conf: -------------------------------------------------------------------------------- 1 | # -*- mode: python -*- 2 | # vi: set ft=python : 3 | 4 | import os 5 | 6 | bind = '127.0.0.1:%s' % os.environ['PORT'] 7 | workers = 3 8 | -------------------------------------------------------------------------------- /cabot/static/theme/img/glyphicons-halflings-white.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/arachnys/cabot/HEAD/cabot/static/theme/img/glyphicons-halflings-white.png -------------------------------------------------------------------------------- /cabot/static/theme/img/ui-bg_flat_0_aaaaaa_40x100.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/arachnys/cabot/HEAD/cabot/static/theme/img/ui-bg_flat_0_aaaaaa_40x100.png -------------------------------------------------------------------------------- /cabot/static/theme/img/ui-bg_flat_75_ffffff_40x100.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/arachnys/cabot/HEAD/cabot/static/theme/img/ui-bg_flat_75_ffffff_40x100.png -------------------------------------------------------------------------------- /cabot/static/theme/img/ui-bg_glass_55_fbf9ee_1x400.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/arachnys/cabot/HEAD/cabot/static/theme/img/ui-bg_glass_55_fbf9ee_1x400.png -------------------------------------------------------------------------------- /cabot/static/theme/img/ui-bg_glass_65_ffffff_1x400.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/arachnys/cabot/HEAD/cabot/static/theme/img/ui-bg_glass_65_ffffff_1x400.png -------------------------------------------------------------------------------- /cabot/static/theme/img/ui-bg_glass_75_dadada_1x400.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/arachnys/cabot/HEAD/cabot/static/theme/img/ui-bg_glass_75_dadada_1x400.png -------------------------------------------------------------------------------- /cabot/static/theme/img/ui-bg_glass_75_e6e6e6_1x400.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/arachnys/cabot/HEAD/cabot/static/theme/img/ui-bg_glass_75_e6e6e6_1x400.png -------------------------------------------------------------------------------- /cabot/static/theme/img/ui-bg_glass_95_fef1ec_1x400.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/arachnys/cabot/HEAD/cabot/static/theme/img/ui-bg_glass_95_fef1ec_1x400.png -------------------------------------------------------------------------------- /cabot/templates/registration/logout.html: -------------------------------------------------------------------------------- 1 | {% extends 'base_public.html' %} 2 | 3 | {% block content %} 4 | 5 | Thanks for visiting! 6 | 7 | {% endblock %} -------------------------------------------------------------------------------- /bin/activate: -------------------------------------------------------------------------------- 1 | DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" 2 | export PATH=$PATH:$DIR/../bin:$DIR/../app 3 | export PYTHONPATH=$PYTHONPATH:$DIR/../app 4 | -------------------------------------------------------------------------------- /cabot/static/theme/fonts/glyphicons-halflings-regular.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/arachnys/cabot/HEAD/cabot/static/theme/fonts/glyphicons-halflings-regular.eot -------------------------------------------------------------------------------- /cabot/static/theme/fonts/glyphicons-halflings-regular.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/arachnys/cabot/HEAD/cabot/static/theme/fonts/glyphicons-halflings-regular.ttf -------------------------------------------------------------------------------- /cabot/static/theme/fonts/glyphicons-halflings-regular.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/arachnys/cabot/HEAD/cabot/static/theme/fonts/glyphicons-halflings-regular.woff -------------------------------------------------------------------------------- /cabot/static/theme/fonts/glyphicons-halflings-regular.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/arachnys/cabot/HEAD/cabot/static/theme/fonts/glyphicons-halflings-regular.woff2 -------------------------------------------------------------------------------- /cabot/cabotapp/utils.py: -------------------------------------------------------------------------------- 1 | from django.contrib.auth import get_user_model 2 | 3 | 4 | def cabot_needs_setup(): 5 | return not get_user_model().objects.all().exists() 6 | -------------------------------------------------------------------------------- /cabot/static/bootstrap/fonts/glyphicons-halflings-regular.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/arachnys/cabot/HEAD/cabot/static/bootstrap/fonts/glyphicons-halflings-regular.eot -------------------------------------------------------------------------------- /cabot/static/bootstrap/fonts/glyphicons-halflings-regular.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/arachnys/cabot/HEAD/cabot/static/bootstrap/fonts/glyphicons-halflings-regular.ttf -------------------------------------------------------------------------------- /cabot/static/bootstrap/fonts/glyphicons-halflings-regular.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/arachnys/cabot/HEAD/cabot/static/bootstrap/fonts/glyphicons-halflings-regular.woff -------------------------------------------------------------------------------- /cabot/static/bootstrap/fonts/glyphicons-halflings-regular.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/arachnys/cabot/HEAD/cabot/static/bootstrap/fonts/glyphicons-halflings-regular.woff2 -------------------------------------------------------------------------------- /cabot/static/theme/img/ui-bg_highlight-soft_75_cccccc_1x100.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/arachnys/cabot/HEAD/cabot/static/theme/img/ui-bg_highlight-soft_75_cccccc_1x100.png -------------------------------------------------------------------------------- /cabot/version.py: -------------------------------------------------------------------------------- 1 | try: 2 | import pkg_resources 3 | version = pkg_resources.require("cabot")[0].version 4 | except Exception, ImportError: 5 | version = 'unknown' 6 | -------------------------------------------------------------------------------- /requirements-plugins.txt: -------------------------------------------------------------------------------- 1 | cabot_alert_email==1.4.3 2 | cabot_alert_hipchat==2.0.3 3 | cabot_alert_twilio==1.3.3 4 | cabot_alert_slack==0.8.3 5 | cabot_check_cloudwatch==0.1.2 6 | -------------------------------------------------------------------------------- /Procfile.dev: -------------------------------------------------------------------------------- 1 | web: python manage.py runserver 0.0.0.0:$PORT 2 | celery: celery -A cabot worker --loglevel=DEBUG -c 8 -Ofair 3 | beat: celery -A cabot beat --loglevel=DEBUG 4 | -------------------------------------------------------------------------------- /cabot/templates/404.html: -------------------------------------------------------------------------------- 1 | {% extends 'base.html' %} 2 | 3 | {% block content %} 4 | 5 |
6 | Page not found. 7 |
8 | {% endblock content %} 9 | -------------------------------------------------------------------------------- /cabot/wsgi.py: -------------------------------------------------------------------------------- 1 | import os 2 | 3 | os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'cabot.settings') 4 | 5 | from django.core.wsgi import get_wsgi_application 6 | application = get_wsgi_application() 7 | -------------------------------------------------------------------------------- /Procfile: -------------------------------------------------------------------------------- 1 | web: gunicorn cabot.wsgi:application --config gunicorn.conf 2 | celery: celery worker -A cabot --loglevel=INFO --concurrency=16 -Ofair 3 | beat: celery beat -A cabot --loglevel=INFO 4 | -------------------------------------------------------------------------------- /cabot/cabotapp/tests/fixtures/graphite_avg_response.json: -------------------------------------------------------------------------------- 1 | [{"target": "PROD", "datapoints": [[20.3, 1442834740], [12.5, 1442834750], [0.1, 1442834760], [0.8, 1442834770], [134.9, 1442834780], [151.0, 1442834790]]}] -------------------------------------------------------------------------------- /MANIFEST.in: -------------------------------------------------------------------------------- 1 | recursive-include cabot/.collectstatic * 2 | recursive-include cabot/templates * 3 | include requirements.txt 4 | include requirements-plugins.txt 5 | include requirements-dev.txt 6 | include README.md 7 | -------------------------------------------------------------------------------- /docker-compose-base.yml: -------------------------------------------------------------------------------- 1 | version: '2' 2 | services: 3 | base: 4 | build: . 5 | image: cabot:web 6 | command: "false" 7 | volumes: 8 | - .:/code 9 | env_file: 10 | - conf/default.env 11 | -------------------------------------------------------------------------------- /cabot/__init__.py: -------------------------------------------------------------------------------- 1 | from __future__ import absolute_import 2 | 3 | # This will make sure the app is always imported when 4 | # Django starts so that shared_task will use this app. 5 | from .celery import app as celery_app 6 | 7 | from .version import version 8 | -------------------------------------------------------------------------------- /cabot/context_processors.py: -------------------------------------------------------------------------------- 1 | from django.conf import settings 2 | 3 | 4 | def global_settings(request): 5 | return { 6 | 'ENABLE_SUBSCRIPTION': settings.ENABLE_SUBSCRIPTION, 7 | 'ENABLE_DUTY_ROTA': settings.ENABLE_DUTY_ROTA, 8 | } 9 | -------------------------------------------------------------------------------- /setup.cfg: -------------------------------------------------------------------------------- 1 | [isort] 2 | known_first_party = cabot 3 | multi_line_output = 0 4 | known_django = django,polymorphic,rest_framework 5 | sections = FUTURE,STDLIB,THIRDPARTY,DJANGO,FIRSTPARTY,LOCALFOLDER 6 | default_section = THIRDPARTY 7 | skip_glob = **/migrations/* 8 | -------------------------------------------------------------------------------- /cabot/entrypoint.py: -------------------------------------------------------------------------------- 1 | import os 2 | import sys 3 | 4 | 5 | def main(): 6 | os.environ.setdefault("DJANGO_SETTINGS_MODULE", "cabot.settings") 7 | 8 | from django.core.management import execute_from_command_line 9 | 10 | execute_from_command_line(sys.argv) 11 | -------------------------------------------------------------------------------- /manage.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | import os 3 | import sys 4 | 5 | 6 | if __name__ == "__main__": 7 | os.environ.setdefault("DJANGO_SETTINGS_MODULE", "cabot.settings") 8 | 9 | from django.core.management import execute_from_command_line 10 | 11 | execute_from_command_line(sys.argv) 12 | -------------------------------------------------------------------------------- /docker-compose-test.yml: -------------------------------------------------------------------------------- 1 | version: '2' 2 | services: 3 | test: 4 | extends: 5 | file: docker-compose-base.yml 6 | service: base 7 | entrypoint: /usr/bin/python 8 | command: manage.py test -v2 9 | env_file: 10 | - conf/test.env 11 | sut: 12 | image: ubuntu 13 | command: "true" 14 | -------------------------------------------------------------------------------- /cabot/settings_utils.py: -------------------------------------------------------------------------------- 1 | import os 2 | from distutils.util import strtobool 3 | 4 | 5 | def force_bool(val): 6 | return strtobool(str(val)) 7 | 8 | 9 | def environ_get_list(names, default=None): 10 | for name in names: 11 | if name in os.environ: 12 | return os.environ[name] 13 | return default 14 | -------------------------------------------------------------------------------- /bin/activate.fish: -------------------------------------------------------------------------------- 1 | # fix broken locale 2 | if not python -c 'import locale; locale.getdefaultlocale();' >/dev/null ^&1 3 | set -gx LANG en_US.UTF-8 4 | set -gx LC_ALL en_US.UTF-8 5 | end 6 | 7 | # set paths 8 | set DIR (dirname (status -f)) 9 | set -gx PATH $PATH $DIR/../bin $DIR/../app 10 | set -gx PYTHONPATH $PYTHONPATH $DIR/../app 11 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .dotcloud/* 2 | dev.db 3 | venv/* 4 | backups/* 5 | static/ 6 | cabot/.collectstatic/ 7 | node_modules/* 8 | .python-eggs/* 9 | cabot.egg-info 10 | cabot/static/ 11 | .env 12 | .DS_Store 13 | celerybeat-schedule 14 | *.pyc 15 | *.swp 16 | *.orig 17 | .vagrant 18 | conf/*.env 19 | dist/ 20 | local_config.yml 21 | build/ 22 | 23 | .idea 24 | Pipfile.lock 25 | .tox/ -------------------------------------------------------------------------------- /cabot/templates/cabotapp/about.html: -------------------------------------------------------------------------------- 1 | {% extends 'base_public.html' %} 2 | {% load static from staticfiles %} 3 | {% block title %}{{ block.super }} - About{% endblock title %} 4 | 5 | {% block content %} 6 | 7 |
8 |

About Cabot

9 |
10 | 11 |
12 |
13 | Version: {{ cabot_version }} 14 |
15 | 16 | {% endblock content %} 17 | -------------------------------------------------------------------------------- /cabot/static/arachnys/css/morris.css: -------------------------------------------------------------------------------- 1 | .morris-hover{position:absolute;z-index:1000}.morris-hover.morris-default-style{border-radius:10px;padding:6px;color:#666;background:rgba(255,255,255,0.8);border:solid 2px rgba(230,230,230,0.8);font-family:sans-serif;font-size:12px;text-align:center}.morris-hover.morris-default-style .morris-hover-row-label{font-weight:bold;margin:0.25em 0} 2 | .morris-hover.morris-default-style .morris-hover-point{white-space:nowrap;margin:0.1em 0} 3 | -------------------------------------------------------------------------------- /bin/test_with_coverage: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | set -x 3 | 4 | output_dir='test-results' 5 | mkdir -p $output_dir 6 | 7 | ./manage.py collectstatic --no-input 8 | TEMPLATE_DEBUG=True coverage run --source="./cabot/" manage.py test $@ 9 | status=$? 10 | 11 | coverage report --omit="cabot/cabotapp/tests*" 12 | coverage xml --omit="cabot/cabotapp/tests*" -o $output_dir/coverage.xml 13 | coverage html --omit="cabot/cabotapp/tests*" -d $output_dir/htmlcov/ 14 | 15 | exit $status 16 | -------------------------------------------------------------------------------- /cabot/templates/cabotapp/instance_confirm_delete.html: -------------------------------------------------------------------------------- 1 | {% extends 'base.html' %} 2 | 3 | {% block content %} 4 |

Delete instance

5 |
{% csrf_token %} 6 | 7 |
8 | {% endblock %} 9 | 10 | {% load compress %} 11 | {% block js %} 12 | {{ block.super }} 13 | {% compress js %} 14 | 17 | {% endcompress %} 18 | {% endblock %} 19 | -------------------------------------------------------------------------------- /cabot/templates/cabotapp/service_confirm_delete.html: -------------------------------------------------------------------------------- 1 | {% extends 'base.html' %} 2 | 3 | {% block content %} 4 |

Delete service

5 |
{% csrf_token %} 6 | 7 |
8 | {% endblock %} 9 | 10 | {% load compress %} 11 | {% block js %} 12 | {{ block.super }} 13 | {% compress js %} 14 | 17 | {% endcompress %} 18 | {% endblock %} 19 | -------------------------------------------------------------------------------- /cabot/templates/cabotapp/statuscheck_confirm_delete.html: -------------------------------------------------------------------------------- 1 | {% extends 'base.html' %} 2 | 3 | {% block content %} 4 |

Delete check

5 |
{% csrf_token %} 6 | 7 |
8 | {% endblock %} 9 | 10 | {% load compress %} 11 | {% block js %} 12 | {{ block.super }} 13 | {% compress js %} 14 | 17 | {% endcompress %} 18 | {% endblock %} 19 | -------------------------------------------------------------------------------- /upstart/process.conf.erb: -------------------------------------------------------------------------------- 1 | start on starting <%= app %>-<%= name %> 2 | stop on stopping <%= app %>-<%= name %> 3 | respawn 4 | respawn limit 50 5 5 | 6 | exec su - <%= user %> -c 'cd <%= engine.root %>; export PORT=<%= port %>;<% engine.env.each_pair do |var,env| %> export <%= var.upcase %>='\''<%= env %>'\''; <% end %> export TMPDIR=$TMPDIR/<%= app %>/<%= name %>/<%= num %>; rm -rf $TMPDIR; mkdir -p $TMPDIR; source $VENV/bin/activate; <%= process.command %> >> <%= log %>/<%=name%>-<%=num%>.log 2>&1' 7 | -------------------------------------------------------------------------------- /cabot/celeryconfig.py: -------------------------------------------------------------------------------- 1 | import os 2 | from cabot.settings_utils import environ_get_list 3 | 4 | broker_url = environ_get_list(['CELERY_BROKER_URL', 'CACHE_URL']) 5 | # Set environment variable if you want to run tests without a redis instance 6 | 7 | task_always_eager = environ_get_list(['CELERY_ALWAYS_EAGER', 'CELERY_TASK_ALWAYS_EAGER'], False) 8 | backend = os.environ.get('CELERY_RESULT_BACKEND', None) 9 | task_default_queue = os.environ.get('CELERY_DEFAULT_QUEUE', 'celery') 10 | 11 | timezone = 'UTC' 12 | -------------------------------------------------------------------------------- /cabot/templates/cabotapp/service_public_list.html: -------------------------------------------------------------------------------- 1 | {% extends 'base_public.html' %} 2 | {% block header_navbar_menu %} 3 | 8 | {{block.super}} 9 | {% endblock header_navbar_menu %} 10 | {% block content %} 11 |
12 | 13 |
14 | {% include 'cabotapp/_service_public_list.html' %} 15 |
16 |
17 | {% endblock content %} 18 | -------------------------------------------------------------------------------- /example_local_config.yml: -------------------------------------------------------------------------------- 1 | --- 2 | # Example Local Vagrant config 3 | # With this file, you can override the amount of RAM and CPUs allocated 4 | # to the VM, as well as change the base box it uses. Just copy it to 5 | # local_config.yml and edit with whatever values you want. 6 | # 7 | # Note that it doesn't take effect until you vagrant destroy && vagrant up 8 | 9 | # double your pleasure double your CPU and RAM 10 | ram: 2048 11 | cpu: 2 12 | # hashicorp ubuntu doesn't support VMWare, so use box-cutter 13 | box: box-cutter/ubuntu1404 14 | -------------------------------------------------------------------------------- /cabot/cabotapp/apps.py: -------------------------------------------------------------------------------- 1 | from django.apps import AppConfig 2 | from django.db.models.signals import post_migrate 3 | 4 | 5 | def post_migrate_callback(**kwargs): 6 | from cabot.cabotapp.alert import update_alert_plugins 7 | from cabot.cabotapp.models import create_default_jenkins_config 8 | update_alert_plugins() 9 | create_default_jenkins_config() 10 | 11 | class CabotappConfig(AppConfig): 12 | name = 'cabot.cabotapp' 13 | 14 | def ready(self): 15 | post_migrate.connect(post_migrate_callback, sender=self) 16 | -------------------------------------------------------------------------------- /cabot/cabotapp/tests/fixtures/gcal_response.ics: -------------------------------------------------------------------------------- 1 | BEGIN:VCALENDAR 2 | PRODID:-//Google Inc//Google Calendar 70.9054//EN 3 | VERSION:2.0 4 | CALSCALE:GREGORIAN 5 | METHOD:PUBLISH 6 | X-WR-CALNAME:Cabottest 7 | X-WR-TIMEZONE:Europe/Copenhagen 8 | X-WR-CALDESC: 9 | BEGIN:VEVENT 10 | DTSTART:20160719T130000Z 11 | DTEND:20160719T160000Z 12 | DTSTAMP:20160719T102406Z 13 | UID:kf6gd4uc1hue70m7gkb7fdtsrc@google.com 14 | CREATED:20160719T091318Z 15 | DESCRIPTION: 16 | LAST-MODIFIED:20160719T091320Z 17 | LOCATION: 18 | SEQUENCE:0 19 | STATUS:CONFIRMED 20 | SUMMARY:troels 21 | TRANSP:OPAQUE 22 | END:VEVENT 23 | END:VCALENDAR -------------------------------------------------------------------------------- /cabot/cabotapp/migrations/0004_auto_20170802_1327.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | from __future__ import unicode_literals 3 | 4 | from django.db import migrations, models 5 | 6 | 7 | class Migration(migrations.Migration): 8 | 9 | dependencies = [ 10 | ('cabotapp', '0003_auto_20170201_1045'), 11 | ] 12 | 13 | operations = [ 14 | migrations.AddField( 15 | model_name='Service', 16 | name='is_public', 17 | field=models.BooleanField(default=False, help_text=b'The service will be shown in the public home', verbose_name=b'Is Public'), 18 | ), 19 | ] 20 | -------------------------------------------------------------------------------- /cabot/cabotapp/migrations/0007_statuscheckresult_consecutive_failures.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # Generated by Django 1.11 on 2017-08-24 11:19 3 | from __future__ import unicode_literals 4 | 5 | from django.db import migrations, models 6 | 7 | 8 | class Migration(migrations.Migration): 9 | 10 | dependencies = [ 11 | ('cabotapp', '0006_auto_20170821_1000'), 12 | ] 13 | 14 | operations = [ 15 | migrations.AddField( 16 | model_name='statuscheckresult', 17 | name='consecutive_failures', 18 | field=models.PositiveIntegerField(null=True), 19 | ), 20 | ] 21 | -------------------------------------------------------------------------------- /cabot/cabotapp/migrations/0006_auto_20170821_1000.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # Generated by Django 1.11 on 2017-08-21 10:00 3 | from __future__ import unicode_literals 4 | 5 | from django.db import migrations 6 | 7 | 8 | class Migration(migrations.Migration): 9 | 10 | dependencies = [ 11 | ('cabotapp', '0005_auto_20170818_1202'), 12 | ] 13 | 14 | operations = [ 15 | migrations.DeleteModel( 16 | name='JenkinsStatusCheck', 17 | ), 18 | migrations.RenameModel( 19 | old_name='JenkinsCheck', 20 | new_name='JenkinsStatusCheck', 21 | ) 22 | ] 23 | -------------------------------------------------------------------------------- /Pipfile: -------------------------------------------------------------------------------- 1 | [[source]] 2 | url = "https://pypi.python.org/simple" 3 | verify_ssl = true 4 | 5 | [packages] 6 | amqp = ">=2.1.4" 7 | celery = ">=4,<5" 8 | djangorestframework = "*" 9 | django-jsonify = "*" 10 | django-filter = "*" 11 | django-auth-ldap = "*" 12 | anyjson = "*" 13 | dj-database-url = "*" 14 | freezegun = "*" 15 | gevent = "*" 16 | gunicorn = "*" 17 | icalendar = "*" 18 | psycopg2 = "*" 19 | python-dateutil = "*" 20 | pytz = "*" 21 | redis = "*" 22 | requests = "*" 23 | twilio = ">=5,<6" 24 | whitenoise = "*" 25 | coreapi = "*" 26 | Django = ">=1.11,<2" 27 | django_polymorphic = "*" 28 | django_compressor = "*" 29 | Markdown = "*" 30 | Pygments = "*" 31 | -------------------------------------------------------------------------------- /cabot/cabotapp/migrations/0002_auto_20170131_1537.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | from __future__ import unicode_literals 3 | 4 | from django.db import models, migrations 5 | 6 | 7 | class Migration(migrations.Migration): 8 | 9 | dependencies = [ 10 | ('cabotapp', '0001_initial'), 11 | ] 12 | 13 | operations = [ 14 | migrations.RenameField( 15 | model_name='statuscheckresult', 16 | old_name='check', 17 | new_name='status_check', 18 | ), 19 | migrations.AlterIndexTogether( 20 | name='statuscheckresult', 21 | index_together=set([('status_check', 'time_complete'), ('status_check', 'id')]), 22 | ), 23 | ] 24 | -------------------------------------------------------------------------------- /cabot/cabotapp/templatetags/extra.py: -------------------------------------------------------------------------------- 1 | from django import template 2 | from django.conf import settings 3 | from datetime import timedelta 4 | 5 | register = template.Library() 6 | 7 | 8 | @register.simple_tag 9 | def jenkins_human_url(jobname): 10 | return '{}job/{}/'.format(settings.JENKINS_API, jobname) 11 | 12 | 13 | @register.simple_tag 14 | def echo_setting(setting): 15 | return getattr(settings, setting, '') 16 | 17 | 18 | @register.filter(name='format_timedelta') 19 | def format_timedelta(delta): 20 | # Getting rid of microseconds. 21 | return str(timedelta(days=delta.days, seconds=delta.seconds)) 22 | 23 | @register.filter 24 | def for_service(objects, service): 25 | return objects.filter(service=service) 26 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | sudo: required 2 | services: 3 | - docker 4 | 5 | before_install: 6 | - sudo pip install tox 7 | 8 | # setup databases 9 | before_script: 10 | - cp conf/development.env.example conf/development.env 11 | - docker-compose build 12 | 13 | script: 14 | - tox 15 | - docker-compose -f docker-compose-test.yml run --rm --entrypoint bin/test_with_coverage test -v2 16 | - git checkout $(git describe --abbrev=0 --tags `git describe --tags`^) && docker-compose build web 17 | - docker-compose run --rm web true 18 | - git checkout - && docker-compose build web 19 | - docker-compose run --rm web true 20 | 21 | after_success: 22 | - sudo pip install codecov 23 | - sudo pip install django_coverage_plugin==1.4.2 24 | - codecov 25 | -------------------------------------------------------------------------------- /cabot/templates/cabotapp/statuscheck_list.html: -------------------------------------------------------------------------------- 1 | {% extends 'base.html' %} 2 | 3 | {% block content %} 4 | 5 | {% include 'cabotapp/_statuscheck_list.html' with checks_type="All" %} 6 | 7 | {% endblock content %} 8 | 9 | {% block js %} 10 | {% load compress %} 11 | {{ block.super }} 12 | {% compress js %} 13 | 23 | {% endcompress %} 24 | {% endblock js %} 25 | -------------------------------------------------------------------------------- /cabot/templates/registration/login.html: -------------------------------------------------------------------------------- 1 | {% extends 'base_public.html' %} 2 | 3 | {% block content %} 4 |
5 |
6 |
7 |

Log in

8 |
9 |
10 |
11 |
12 | {% include "cabotapp/_base_form.html" %} 13 |
14 |
15 |
16 | 17 | Reset password 18 |
19 |
20 |
21 |
22 | {% include "./social_auth.html" %} 23 | {% endblock %} 24 | -------------------------------------------------------------------------------- /cabot/cabotapp/tests/fixtures/graphite_response.json: -------------------------------------------------------------------------------- 1 | [{"target": "PROD", "datapoints": [[8.14908, 1387817760], [8.14908, 1387817820], [8.14908, 1387817880], [8.14908, 1387817940], [8.14908, 1387818000], [8.14908, 1387818060], [8.14908, 1387818120], [8.14908, 1387818180], [8.14908, 1387818240], [8.14908, 1387818300], [9.16092, 1387818360], [9.16092, 1387818420], [9.16092, 1387818480], [9.16092, 1387818540], [9.16092, 1387818600]]}, {"target": "stage", "datapoints": [[8.17349, 1387817760], [8.17349, 1387817820], [8.17349, 1387817880], [8.17349, 1387817940], [8.17349, 1387818000], [8.16242, 1387818060], [8.16242, 1387818120], [8.16242, 1387818180], [8.16242, 1387818240], [8.16242, 1387818300], [8.16242, 1387818360], [8.16242, 1387818420], [8.16242, 1387818480], [8.16242, 1387818540], [8.16242, 1387818600]]}] -------------------------------------------------------------------------------- /cabot/templates/cabotapp/setup.html: -------------------------------------------------------------------------------- 1 | {% extends 'base_public.html' %} 2 | 3 | {% block content %} 4 |

Welcome to Cabot!

5 |

It looks like this is your first time launching Cabot. Please create a superuser account. 6 |

7 |
8 |
9 |

Create Super User

10 |
11 |
12 |
13 |
14 | {% include "cabotapp/_base_form.html" %} 15 |
16 |
17 |
18 | 19 |
20 |
21 |
22 |
23 | 24 | {% endblock content %} 25 | -------------------------------------------------------------------------------- /cabot/celery.py: -------------------------------------------------------------------------------- 1 | from __future__ import absolute_import 2 | 3 | import os 4 | from datetime import timedelta 5 | 6 | from django.conf import settings 7 | from celery import Celery 8 | 9 | os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'cabot.settings') 10 | 11 | app = Celery('cabot') 12 | app.config_from_object('cabot.celeryconfig') 13 | app.autodiscover_tasks() 14 | 15 | app.conf.beat_schedule = { 16 | 'run-all-checks': { 17 | 'task': 'cabot.cabotapp.tasks.run_all_checks', 18 | 'schedule': timedelta(seconds=60), 19 | }, 20 | 'update-shifts': { 21 | 'task': 'cabot.cabotapp.tasks.update_shifts', 22 | 'schedule': timedelta(seconds=1800), 23 | }, 24 | 'clean-db': { 25 | 'task': 'cabot.cabotapp.tasks.clean_db', 26 | 'schedule': timedelta(seconds=60 * 60 * 24), 27 | }, 28 | } 29 | -------------------------------------------------------------------------------- /cabot/settings_ldap.py: -------------------------------------------------------------------------------- 1 | import os 2 | import ldap 3 | from django_auth_ldap.config import LDAPSearch, GroupOfNamesType 4 | 5 | 6 | # Baseline configuration. 7 | AUTH_LDAP_SERVER_URI = os.environ.get('AUTH_LDAP_SERVER_URI', 'ldap://ldap.example.com') 8 | 9 | AUTH_LDAP_BIND_DN = os.environ.get('AUTH_LDAP_BIND_DN', 'cn=Manager,dc=example,dc=com') 10 | AUTH_LDAP_BIND_PASSWORD = os.environ.get('AUTH_LDAP_BIND_PASSWORD', '') 11 | AUTH_LDAP_USER_FILTER = os.environ.get('AUTH_LDAP_USER_FILTER', '(uid=%(user)s)') 12 | AUTH_LDAP_USER_SEARCH = LDAPSearch(os.environ.get('AUTH_LDAP_USER_SEARCH', 'ou=user,dc=example,dc=com'), 13 | ldap.SCOPE_SUBTREE, AUTH_LDAP_USER_FILTER) 14 | 15 | # Populate the Django user from the LDAP directory. 16 | AUTH_LDAP_USER_ATTR_MAP = { 17 | 'first_name': 'givenName', 18 | 'last_name': 'sn', 19 | 'email': 'mail', 20 | } 21 | -------------------------------------------------------------------------------- /cabot/templates/cabotapp/instance_list.html: -------------------------------------------------------------------------------- 1 | {% extends 'base.html' %} 2 | 3 | {% block content %} 4 |
5 |
6 |

7 |

Instances

8 |
9 |

10 |
11 |
12 |
13 | {% include 'cabotapp/_instance_list.html' %} 14 |
15 |
16 | {% endblock content %} 17 | 18 | {% block js %} 19 | {% load compress %} 20 | {{ block.super }} 21 | {% compress js %} 22 | 25 | {% endcompress %} 26 | {% endblock js %} 27 | -------------------------------------------------------------------------------- /cabot/static/arachnys/css/base.less: -------------------------------------------------------------------------------- 1 | body{ 2 | padding-top:50px; 3 | } 4 | .form-group { 5 | ul { 6 | list-style-type: none; 7 | padding: 5px 0 0 0; 8 | } 9 | 10 | input[type="radio"] { 11 | margin-top: -3px; 12 | } 13 | } 14 | 15 | tr.warning a { 16 | text-decoration: line-through; 17 | } 18 | 19 | #graphite_data_container { 20 | max-height: 200px; 21 | overflow: scroll; 22 | } 23 | 24 | .ui-autocomplete { 25 | max-height: 400px; 26 | overflow-y: scroll; 27 | font-size: 10px; 28 | } 29 | 30 | .jqstooltip { 31 | display: none; 32 | } 33 | 34 | div.dataTables_paginate { 35 | float: right; 36 | } 37 | 38 | div.dataTables_info { 39 | float: left; 40 | margin: 20px 0; 41 | } 42 | 43 | div.dataTables_length { 44 | float: right; 45 | } 46 | 47 | .messages { 48 | margin: auto; 49 | width: 50%; 50 | margin-top: 10px; 51 | } 52 | 53 | .navbar-brand > img { 54 | display: inline-block; 55 | } -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | FROM node:4-alpine 2 | 3 | ENV PYTHONUNBUFFERED 1 4 | 5 | RUN mkdir /code 6 | 7 | WORKDIR /code 8 | 9 | RUN apk add --no-cache \ 10 | python-dev \ 11 | py-pip \ 12 | postgresql-dev \ 13 | gcc \ 14 | curl \ 15 | curl-dev \ 16 | libcurl \ 17 | musl-dev \ 18 | libffi-dev \ 19 | openldap-dev \ 20 | ca-certificates \ 21 | bash 22 | 23 | RUN npm config set unsafe-perm true 24 | RUN npm install -g \ 25 | --registry http://registry.npmjs.org/ \ 26 | coffee-script \ 27 | less@1.3 28 | 29 | RUN pip install --upgrade pip 30 | 31 | COPY requirements.txt ./ 32 | RUN pip install --no-cache-dir -r requirements.txt 33 | 34 | COPY requirements-dev.txt ./ 35 | RUN pip install --no-cache-dir -r requirements-dev.txt 36 | 37 | COPY requirements-plugins.txt ./ 38 | RUN pip install --no-cache-dir -r requirements-plugins.txt 39 | 40 | ADD . /code/ 41 | 42 | ENTRYPOINT ["./docker-entrypoint.sh"] 43 | -------------------------------------------------------------------------------- /cabot/templates/cabotapp/_base_form.html: -------------------------------------------------------------------------------- 1 | {% load bootstrap %} 2 | 3 |
4 |
5 | {{ form.non_field_errors }} 6 |
7 |
8 | {% csrf_token %} 9 | {% for field in form %} 10 |
11 |
12 | 13 | {% if field.errors %} 14 |
{{ field|bootstrap_inline }}
15 |
{{ field.errors }}
16 | {% else %} 17 |
{{ field|bootstrap_inline }}
18 | {% endif %} 19 |
20 | {% if field.name == 'metric' %} 21 |
22 |
23 |
24 |

25 |     
26 |
27 | {% else %} 28 | {% endif %} 29 |
30 | {% endfor %} 31 | -------------------------------------------------------------------------------- /cabot/cabotapp/tests/fixtures/cabot_check_skeleton/plugin.py: -------------------------------------------------------------------------------- 1 | from cabot.cabotapp.models import StatusCheck 2 | from cabot.cabotapp.views import CheckCreateView 3 | from cabot.cabotapp.views import CheckUpdateView 4 | from cabot.cabotapp.views import StatusCheckForm 5 | from django.http import HttpResponseRedirect 6 | from django.core.urlresolvers import reverse 7 | 8 | class SkeletonStatusCheck(StatusCheck): 9 | edit_url_name = 'update-skeleton-check' 10 | duplicate_url_name = 'duplicate-skeleton-check' 11 | 12 | check_name = 'skeleton' 13 | 14 | class SkeletonStatusCheckForm(StatusCheckForm): 15 | class Meta: 16 | model = SkeletonStatusCheck 17 | fields = ('name',) 18 | 19 | class SkeletonCheckCreateView(CheckCreateView): 20 | model = StatusCheck 21 | form_class = SkeletonStatusCheckForm 22 | 23 | class SkeletonCheckUpdateView(CheckUpdateView): 24 | model = StatusCheck 25 | form_class = SkeletonStatusCheckForm 26 | 27 | def duplicate_check(request, pk): 28 | return HttpResponseRedirect(reverse('update-skeleton-check', kwargs={'pk': 25})) 29 | -------------------------------------------------------------------------------- /cabot/templates/registration/social_auth.html: -------------------------------------------------------------------------------- 1 | {% load extra %} 2 | {% echo_setting 'AUTH_GOOGLE_OAUTH2' as google_enable %} 3 | {% echo_setting 'AUTH_GITHUB_ORG' as github_enable %} 4 | {% echo_setting 'AUTH_GITHUB_ENTERPRISE_ORG' as github_enterprise_enable %} 5 | 6 |
7 |
8 | {% if github_enable %} 9 | 10 | 11 | Sig in with Github 12 | 13 | {% endif %} 14 | 15 | {% if github_enterprise_enable %} 16 | 17 | 18 | Sig in with GitHub Enterprise 19 | 20 | {% endif %} 21 | 22 | {% if google_enable %} 23 | 24 | 25 | Sign in with Google 26 | 27 | {% endif %} 28 |
-------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2014 Arachnys Information Services Ltd and individual contributors. 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 6 | 7 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 8 | 9 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -------------------------------------------------------------------------------- /cabot/templates/cabotapp/service_form.html: -------------------------------------------------------------------------------- 1 | {% extends 'base.html' %} 2 | 3 | {% block content %} 4 |
5 |
6 |
7 |

{% if form.instance.id %}Edit service{% else %}New service{% endif %}

8 |
9 |
10 |
11 |
12 | {% include "cabotapp/_base_form.html" %} 13 |
14 |
15 |
16 | 17 | Back to dashboard 18 |
19 | {% if form.instance.id %} 20 |
21 | Delete service 22 |
23 | {% endif %} 24 |
25 |
26 |
27 | {% endblock %} 28 | 29 | {% load compress %} 30 | {% block js %} 31 | {{ block.super }} 32 | {% compress js %} 33 | 35 | {% endcompress %} 36 | {% endblock %} 37 | -------------------------------------------------------------------------------- /cabot/templates/cabotapp/instance_form.html: -------------------------------------------------------------------------------- 1 | {% extends 'base.html' %} 2 | 3 | {% block content %} 4 |
5 |
6 |
7 |

{% if form.instance.id %}Edit instance{% else %}New instance{% endif %}

8 |
9 |
10 |
11 |
12 | {% include "cabotapp/_base_form.html" %} 13 |
14 |
15 |
16 | 17 | Back to dashboard 18 |
19 | {% if form.instance.id %} 20 |
21 | Delete instance 22 |
23 | {% endif %} 24 |
25 |
26 |
27 | {% endblock %} 28 | 29 | {% load compress %} 30 | {% block js %} 31 | {{ block.super }} 32 | {% compress js %} 33 | 35 | {% endcompress %} 36 | {% endblock %} 37 | -------------------------------------------------------------------------------- /cabot/templates/cabotapp/service_list.html: -------------------------------------------------------------------------------- 1 | {% extends 'base.html' %} 2 | 3 | {% block content %} 4 |
5 |
6 |

7 |

Services

8 |
9 |

10 |
11 |
12 |
13 | {% include 'cabotapp/_service_list.html' %} 14 |
15 |
16 | {% endblock content %} 17 | 18 | {% block js %} 19 | {% load compress %} 20 | {{ block.super }} 21 | {% compress js %} 22 | 32 | {% endcompress %} 33 | {% endblock js %} 34 | -------------------------------------------------------------------------------- /cabot/cabotapp/migrations/0003_auto_20170201_1045.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | from __future__ import unicode_literals 3 | 4 | from django.db import migrations, models 5 | 6 | 7 | class Migration(migrations.Migration): 8 | 9 | dependencies = [ 10 | ('cabotapp', '0002_auto_20170131_1537'), 11 | ] 12 | 13 | operations = [ 14 | migrations.AlterField( 15 | model_name='alertplugin', 16 | name='polymorphic_ctype', 17 | field=models.ForeignKey(related_name='polymorphic_cabotapp.alertplugin_set+', editable=False, to='contenttypes.ContentType', null=True), 18 | ), 19 | migrations.AlterField( 20 | model_name='alertpluginuserdata', 21 | name='polymorphic_ctype', 22 | field=models.ForeignKey(related_name='polymorphic_cabotapp.alertpluginuserdata_set+', editable=False, to='contenttypes.ContentType', null=True), 23 | ), 24 | migrations.AlterField( 25 | model_name='statuscheck', 26 | name='polymorphic_ctype', 27 | field=models.ForeignKey(related_name='polymorphic_cabotapp.statuscheck_set+', editable=False, to='contenttypes.ContentType', null=True), 28 | ), 29 | ] 30 | -------------------------------------------------------------------------------- /docker-compose.yml: -------------------------------------------------------------------------------- 1 | version: '2' 2 | services: 3 | web: 4 | extends: 5 | file: docker-compose-base.yml 6 | service: base 7 | env_file: 8 | - conf/development.env 9 | command: python manage.py runserver 0.0.0.0:5001 10 | ports: 11 | - "5001:5001" 12 | links: 13 | - redis 14 | - db 15 | 16 | worker: 17 | extends: 18 | file: docker-compose-base.yml 19 | service: base 20 | env_file: 21 | - conf/development.env 22 | command: celery worker -A cabot --loglevel=DEBUG --concurrency=16 -Ofair 23 | environment: 24 | - SKIP_INIT=1 25 | - WAIT_FOR_MIGRATIONS=1 26 | links: 27 | - redis 28 | - db 29 | 30 | beat: 31 | extends: 32 | file: docker-compose-base.yml 33 | service: base 34 | env_file: 35 | - conf/development.env 36 | command: celery beat -A cabot --loglevel=DEBUG 37 | environment: 38 | - SKIP_INIT=1 39 | - WAIT_FOR_MIGRATIONS=1 40 | links: 41 | - redis 42 | - db 43 | 44 | redis: 45 | image: redis:alpine 46 | 47 | db: 48 | image: postgres:alpine 49 | volumes: 50 | - datavolume:/var/lib/postgresql/data 51 | 52 | volumes: 53 | datavolume: 54 | -------------------------------------------------------------------------------- /setup.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | import os 3 | from setuptools import setup, find_packages 4 | from os import environ as env 5 | import subprocess 6 | 7 | from pip.req import parse_requirements 8 | 9 | requirements = [str(req.req) for req in parse_requirements('requirements.txt', session=False)] 10 | requirements_plugins = [str(req.req) for req in parse_requirements('requirements-plugins.txt', session=False)] 11 | 12 | try: 13 | VERSION = subprocess.check_output(['git', 'describe', '--tags']).strip() 14 | except subprocess.CalledProcessError: 15 | VERSION = '0.dev' 16 | 17 | setup( 18 | name='cabot', 19 | version=VERSION, 20 | description="Self-hosted, easily-deployable monitoring and alerts service" 21 | " - like a lightweight PagerDuty", 22 | long_description=open('README.md').read(), 23 | author="Arachnys", 24 | author_email='info@arachnys.com', 25 | url='http://cabotapp.com', 26 | license='MIT', 27 | install_requires=requirements + requirements_plugins, 28 | packages=find_packages(), 29 | include_package_data=True, 30 | entry_points={ 31 | 'console_scripts': [ 32 | 'cabot = cabot.entrypoint:main', 33 | ], 34 | }, 35 | zip_safe=False 36 | ) 37 | -------------------------------------------------------------------------------- /docker-entrypoint.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | set -e 3 | 4 | function wait_for_broker { 5 | set +e 6 | for try in {1..60} ; do 7 | python -c "from kombu import Connection; x=Connection('$CELERY_BROKER_URL', timeout=1); x.connect()" && break 8 | echo "Waiting for celery broker to respond..." 9 | sleep 1 10 | done 11 | } 12 | 13 | function wait_for_database { 14 | set +e 15 | for try in {1..60} ; do 16 | python -c "from django.db import connection; connection.connect()" && break 17 | echo "Waiting for database to respond..." 18 | sleep 1 19 | done 20 | } 21 | 22 | function wait_for_migrations { 23 | set +e 24 | for try in {1..60} ; do 25 | # Kind of ugly but not sure if there's another way to determine if migrations haven't run. 26 | # showmigrations -p returns a checkbox list of migrations, empty checkboxes mean they haven't been run 27 | python manage.py showmigrations -p | grep "\[ \]" &> /dev/null || break 28 | echo "Waiting for database migrations to be run..." 29 | sleep 1 30 | done 31 | } 32 | 33 | 34 | wait_for_broker 35 | wait_for_database 36 | 37 | if [ -z "$SKIP_INIT" ]; then 38 | /code/bin/build-app 39 | fi 40 | 41 | if [ -n "$WAIT_FOR_MIGRATIONS" ]; then 42 | wait_for_migrations 43 | fi 44 | 45 | exec "$@" 46 | -------------------------------------------------------------------------------- /cabot/cabotapp/admin.py: -------------------------------------------------------------------------------- 1 | from django.contrib import admin 2 | from polymorphic.admin import (PolymorphicChildModelAdmin, 3 | PolymorphicParentModelAdmin) 4 | 5 | from .alert import AlertPlugin, AlertPluginUserData 6 | from .models import (AlertAcknowledgement, Instance, JenkinsConfig, Service, 7 | ServiceStatusSnapshot, Shift, StatusCheck, 8 | StatusCheckResult, UserProfile) 9 | 10 | 11 | class StatusCheckAdmin(PolymorphicParentModelAdmin): 12 | base_model = StatusCheck 13 | child_models = StatusCheck.__subclasses__() 14 | 15 | 16 | class ChildStatusCheckAdmin(PolymorphicChildModelAdmin): 17 | base_model = StatusCheck 18 | 19 | 20 | for child_status_check in StatusCheck.__subclasses__(): 21 | admin.site.register(child_status_check, ChildStatusCheckAdmin) 22 | 23 | admin.site.register(UserProfile) 24 | admin.site.register(Shift) 25 | admin.site.register(Service) 26 | admin.site.register(ServiceStatusSnapshot) 27 | admin.site.register(StatusCheck, StatusCheckAdmin) 28 | admin.site.register(StatusCheckResult) 29 | admin.site.register(Instance) 30 | admin.site.register(AlertPlugin) 31 | admin.site.register(AlertPluginUserData) 32 | admin.site.register(AlertAcknowledgement) 33 | admin.site.register(JenkinsConfig) 34 | -------------------------------------------------------------------------------- /cabot/cabotapp/tests/fixtures/recurring_response_notz.ics: -------------------------------------------------------------------------------- 1 | BEGIN:VCALENDAR 2 | PRODID:-//Google Inc//Google Calendar 70.9054//EN 3 | VERSION:2.0 4 | CALSCALE:GREGORIAN 5 | METHOD:PUBLISH 6 | X-WR-CALNAME:Cabot Rota 7 | X-WR-CALDESC: 8 | BEGIN:VTIMEZONE 9 | X-LIC-LOCATION:America/New_York 10 | BEGIN:DAYLIGHT 11 | TZOFFSETFROM:-0500 12 | TZOFFSETTO:-0400 13 | TZNAME:EDT 14 | DTSTART:19700308T020000 15 | RRULE:FREQ=YEARLY;BYMONTH=3;BYDAY=2SU 16 | END:DAYLIGHT 17 | BEGIN:STANDARD 18 | TZOFFSETFROM:-0400 19 | TZOFFSETTO:-0500 20 | TZNAME:EST 21 | DTSTART:19701101T020000 22 | RRULE:FREQ=YEARLY;BYMONTH=11;BYDAY=1SU 23 | END:STANDARD 24 | END:VTIMEZONE 25 | BEGIN:VEVENT 26 | DTSTART:20161129T080000 27 | DTEND:20161130T080000 28 | RRULE:FREQ=DAILY;INTERVAL=2 29 | DTSTAMP:20161128T214445Z 30 | UID:mep8lmf2s1lhmmm366c6rhhers@google.com 31 | CREATED:20161128T204255Z 32 | DESCRIPTION: 33 | LAST-MODIFIED:20161128T204255Z 34 | LOCATION: 35 | SEQUENCE:0 36 | STATUS:CONFIRMED 37 | SUMMARY:foo 38 | TRANSP:OPAQUE 39 | END:VEVENT 40 | BEGIN:VEVENT 41 | DTSTART:20161128T080000 42 | DTEND:20161129T080000 43 | RRULE:FREQ=DAILY;INTERVAL=2 44 | DTSTAMP:20161128T214445Z 45 | UID:s4ftq5jd98cuubmbous3mgst5c@google.com 46 | CREATED:20161128T203907Z 47 | DESCRIPTION: 48 | LAST-MODIFIED:20161128T203947Z 49 | LOCATION: 50 | SEQUENCE:1 51 | STATUS:CONFIRMED 52 | SUMMARY:bar 53 | TRANSP:OPAQUE 54 | END:VEVENT 55 | END:VCALENDAR 56 | -------------------------------------------------------------------------------- /cabot/cabotapp/tests/fixtures/graphite_null_response.json: -------------------------------------------------------------------------------- 1 | [{"target": "minSeries(hosts.1.df.var.df_complex.free.value,hosts.2.df.var.df_complex.free.value,hosts.3.df.var.df_complex.free.value,hosts.4.df.var.df_complex.free.value)", "datapoints": [[null, 1441144030], [null, 1441144040], [null, 1441144050], [null, 1441144060], [null, 1441144070], [null, 1441144080], [null, 1441144090], [null, 1441144100], [null, 1441144110], [null, 1441144120], [null, 1441144130], [null, 1441144140], [null, 1441144150], [null, 1441144160], [null, 1441144170], [null, 1441144180], [null, 1441144190], [null, 1441144200], [null, 1441144210], [null, 1441144220], [null, 1441144230], [null, 1441144240], [null, 1441144250], [null, 1441144260], [null, 1441144270], [null, 1441144280], [null, 1441144290], [null, 1441144300], [null, 1441144310], [null, 1441144320], [null, 1441144330], [null, 1441144340], [null, 1441144350], [null, 1441144360], [null, 1441144370], [null, 1441144380], [null, 1441144390], [null, 1441144400], [null, 1441144410], [null, 1441144420], [null, 1441144430], [null, 1441144440], [null, 1441144450], [null, 1441144460], [null, 1441144470], [null, 1441144480], [null, 1441144490], [null, 1441144500], [null, 1441144510], [null, 1441144520], [null, 1441144530], [null, 1441144540], [null, 1441144550], [null, 1441144560], [null, 1441144570], [null, 1441144580], [null, 1441144590], [null, 1441144600], [null, 1441144610], [null, 1441144620]]}] -------------------------------------------------------------------------------- /cabot/templates/cabotapp/_service_public_list.html: -------------------------------------------------------------------------------- 1 | {% if not services %} 2 | 3 |
4 |
5 | No available services monitoring 6 |
7 | 8 | {% else %} 9 | 10 |
11 |
12 |

Name

13 |
14 |
15 |

Overall Checks

16 |
17 |
18 |

Acknowledgment

19 |
20 |
21 | {% for service in services %} 22 |
23 |
24 |
25 | {{service.name}} 26 |
27 |
28 | {% if service.alerts_enabled %}{{ service.overall_status|lower|capfirst }}{% else %}Disabled{% endif %} 29 |
30 |
31 | {% if service.overall_status != service.PASSING_STATUS %}{% if service.unexpired_acknowledgement %}Yes{% else %}No{% endif %}{% endif %} 32 |
33 |
34 | {% endfor %} 35 | 36 | {% endif %} -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | amqp==2.1.4 2 | anyjson==0.3.3 3 | appdirs==1.4.3 4 | billiard==3.5.0.2 5 | boto==2.49.0 6 | boto3==1.9.86 7 | botocore==1.12.86 8 | celery==4.2.1 9 | coreapi==2.3.0 10 | coreschema==0.0.4 11 | dj-database-url==0.4.2 12 | Django==1.11.29 13 | django-appconf==1.0.2 14 | django-auth-ldap==1.2.16 15 | django-autocomplete-light==3.2.10 16 | django-bootstrap-form==3.4 17 | django-compressor==2.2 18 | django-filter==1.0.4 19 | django-jsonify==0.3.0 20 | django-polymorphic==1.3 21 | djangorestframework==3.6.2 22 | docutils==0.14 23 | futures==3.2.0 24 | gevent==1.2.1 25 | greenlet==0.4.12 26 | gunicorn==19.7.1 27 | httplib2==0.10.3 28 | icalendar==3.11.3 29 | itypes==1.1.0 30 | Jinja2==2.9.6 31 | jmespath==0.9.3 32 | kombu==4.2.2.post1 33 | Markdown==2.6.8 34 | MarkupSafe==1.0 35 | multi-key-dict==2.0.3 36 | oauthlib==3.0.0 37 | packaging==16.8 38 | pbr==5.1.1 39 | psycopg2==2.7.1 40 | pyasn1==0.4.5 41 | pyasn1-modules==0.2.3 42 | pycurl==7.43.0.1 43 | Pygments==2.2.0 44 | PyJWT==1.7.1 45 | pyparsing==2.2.0 46 | PySocks==1.6.7 47 | python-dateutil==2.6.0 48 | python-jenkins==0.4.15 49 | python-ldap==3.1.0 50 | python-openid==2.2.5 51 | pytz==2017.2 52 | rcssmin==1.0.6 53 | redis==2.10.5 54 | requests==2.13.0 55 | requests-oauthlib==1.2.0 56 | rjsmin==1.0.12 57 | s3transfer==0.1.13 58 | six==1.10.0 59 | social-auth-app-django==1.1.0 60 | social-auth-core==3.0.0 61 | twilio==5.7.0 62 | uritemplate==3.0.0 63 | urllib3==1.24.1 64 | vine==1.1.3 65 | whitenoise==3.3.0 66 | -------------------------------------------------------------------------------- /cabot/cabot_config.py: -------------------------------------------------------------------------------- 1 | import os 2 | 3 | # Credentials for Graphite server to monitor 4 | GRAPHITE_API = os.environ.get('GRAPHITE_API') 5 | GRAPHITE_USER = os.environ.get('GRAPHITE_USER') 6 | GRAPHITE_PASS = os.environ.get('GRAPHITE_PASS') 7 | GRAPHITE_FROM = os.getenv('GRAPHITE_FROM', '-10minute') 8 | 9 | # Credentials for Jenkins server to monitor 10 | JENKINS_API = os.environ.get('JENKINS_API') 11 | JENKINS_USER = os.environ.get('JENKINS_USER') 12 | JENKINS_PASS = os.environ.get('JENKINS_PASS') 13 | 14 | # Point at a public calendar you want to use to schedule a duty rota 15 | CALENDAR_ICAL_URL = os.environ.get('CALENDAR_ICAL_URL') 16 | 17 | # So that links back to the Cabot instance display correctly 18 | WWW_HTTP_HOST = os.environ.get('WWW_HTTP_HOST') 19 | WWW_SCHEME = os.environ.get('WWW_SCHEME', "https") 20 | 21 | HTTP_USER_AGENT = os.environ.get('HTTP_USER_AGENT', 'Cabot') 22 | 23 | # How often should alerts be sent for important failures? 24 | ALERT_INTERVAL = int(os.environ.get('ALERT_INTERVAL', 10)) 25 | 26 | # How often should notifications be sent for less important issues? 27 | NOTIFICATION_INTERVAL = int(os.environ.get('NOTIFICATION_INTERVAL', 120)) 28 | 29 | # How long should an acknowledgement silence alerts for? 30 | ACKNOWLEDGEMENT_EXPIRY = int(os.environ.get('ACKNOWLEDGEMENT_EXPIRY', 20)) 31 | 32 | # Default plugins are used if the user has not specified. 33 | CABOT_PLUGINS_ENABLED = os.environ.get('CABOT_PLUGINS_ENABLED', 'cabot_alert_hipchat,cabot_alert_twilio,cabot_alert_email') 34 | -------------------------------------------------------------------------------- /cabot/cabotapp/tests/fixtures/recurring_response.ics: -------------------------------------------------------------------------------- 1 | BEGIN:VCALENDAR 2 | PRODID:-//Google Inc//Google Calendar 70.9054//EN 3 | VERSION:2.0 4 | CALSCALE:GREGORIAN 5 | METHOD:PUBLISH 6 | X-WR-CALNAME:Cabot Rota 7 | X-WR-TIMEZONE:America/New_York 8 | X-WR-CALDESC: 9 | BEGIN:VTIMEZONE 10 | TZID:America/New_York 11 | X-LIC-LOCATION:America/New_York 12 | BEGIN:DAYLIGHT 13 | TZOFFSETFROM:-0500 14 | TZOFFSETTO:-0400 15 | TZNAME:EDT 16 | DTSTART:19700308T020000 17 | RRULE:FREQ=YEARLY;BYMONTH=3;BYDAY=2SU 18 | END:DAYLIGHT 19 | BEGIN:STANDARD 20 | TZOFFSETFROM:-0400 21 | TZOFFSETTO:-0500 22 | TZNAME:EST 23 | DTSTART:19701101T020000 24 | RRULE:FREQ=YEARLY;BYMONTH=11;BYDAY=1SU 25 | END:STANDARD 26 | END:VTIMEZONE 27 | BEGIN:VEVENT 28 | DTSTART;TZID=America/New_York:20161129T080000 29 | DTEND;TZID=America/New_York:20161130T080000 30 | RRULE:FREQ=DAILY;INTERVAL=2 31 | DTSTAMP:20161128T214445Z 32 | UID:mep8lmf2s1lhmmm366c6rhhers@google.com 33 | CREATED:20161128T204255Z 34 | DESCRIPTION: 35 | LAST-MODIFIED:20161128T204255Z 36 | LOCATION: 37 | SEQUENCE:0 38 | STATUS:CONFIRMED 39 | SUMMARY:foo 40 | TRANSP:OPAQUE 41 | END:VEVENT 42 | BEGIN:VEVENT 43 | DTSTART;TZID=America/New_York:20161128T080000 44 | DTEND;TZID=America/New_York:20161129T080000 45 | RRULE:FREQ=DAILY;INTERVAL=2 46 | DTSTAMP:20161128T214445Z 47 | UID:s4ftq5jd98cuubmbous3mgst5c@google.com 48 | CREATED:20161128T203907Z 49 | DESCRIPTION: 50 | LAST-MODIFIED:20161128T203947Z 51 | LOCATION: 52 | SEQUENCE:1 53 | STATUS:CONFIRMED 54 | SUMMARY:bar 55 | TRANSP:OPAQUE 56 | END:VEVENT 57 | END:VCALENDAR 58 | -------------------------------------------------------------------------------- /cabot/templates/cabotapp/shift_list.html: -------------------------------------------------------------------------------- 1 | {% extends 'base.html' %} 2 | 3 | {% block content %} 4 |
5 |
6 |

7 |

Duty rota

8 |
9 |
10 | {% if not shifts %} 11 |
No shifts downloaded
12 | {% else %} 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | {% for shift in shifts %} 23 | 24 | 27 | 30 | 35 | 36 | {% endfor %} 37 | 38 |
UserShift time
25 | {{ shift.user.username }} 26 | 28 | {{ shift.start }} - {{ shift.end }} 29 | 31 | 32 | 33 | 34 |
39 | {% endif %} 40 |
41 |
42 | {% endblock content %} 43 | 44 | {% block js %} 45 | {% load compress %} 46 | {{ block.super }} 47 | {% compress js %} 48 | 51 | {% endcompress %} 52 | {% endblock js %} 53 | -------------------------------------------------------------------------------- /cabot/templates/cabotapp/alertpluginuserdata_form.html: -------------------------------------------------------------------------------- 1 | {% extends 'base.html' %} 2 | 3 | {% load bootstrap %} 4 | 5 | {% block content %} 6 |
7 |
8 | 16 |
17 |

User Profile

18 |
19 | {% csrf_token %} 20 | {% for field in form %} 21 |
22 | 23 |
{{ field|bootstrap_inline }}
24 |
25 | {% endfor %} 26 |
27 |
28 | 29 | Back to dashboard 30 |
31 |
32 | 33 |
34 |
35 |
36 |
37 | {% endblock %} 38 | 39 | {% load compress %} 40 | {% block js %} 41 | {{ block.super }} 42 | {% compress js %} 43 | 47 | {% endcompress %} 48 | {% endblock %} 49 | -------------------------------------------------------------------------------- /cabot/templates/cabotapp/_instance_list.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | {% if not instances %} 6 |
7 |
8 | No instances configured 9 |
10 | 11 | {% else %} 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | {% for instance in instances %} 21 | 22 | 25 | 28 | 31 | 34 | 39 | 40 | {% endfor %} 41 | 42 | {% endif %} 43 |
NameOverallActive checksDisabled checks
23 | {{instance.name}} 24 | 26 | {{ instance.overall_status|lower|capfirst }} 27 | 29 | {{ instance.active_status_checks.all.count }} 30 | 32 | {{ instance.inactive_status_checks.all.count }} 33 | 35 | 36 | 37 | 38 |
44 | -------------------------------------------------------------------------------- /cabot/templates/cabotapp/statuscheck_report.html: -------------------------------------------------------------------------------- 1 | {% extends 'base.html' %} 2 | 3 | {% load extra %} 4 | 5 | {% block content %} 6 |
7 |
8 |

9 |

{{ service.name }}

10 |
11 |
12 | {% if not checks %} 13 | No checks 14 | {% else %} 15 | {% for check in checks %} 16 |
17 |
18 |
19 |

20 | 21 |

22 |
23 |
24 |

25 | {{ check.name }} 26 |

27 |
28 |
29 |
30 | {% if check.success_rate != None %} 31 |

Success rate: {{ check.success_rate|floatformat:"2" }}%.

32 | {% endif %} 33 | {% if check.problems %} 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | {% for start_time, end_time, duration in check.problems %} 44 | 45 | 48 | 51 | 54 | 55 | {% endfor %} 56 | 57 |
Start timeEnd timeOutage duration
46 | {{ start_time }} 47 | 49 | {% if end_time %}{{ end_time }}{% else %}-{% endif %} 50 | 52 | {{ duration|format_timedelta }} 53 |
58 | {% endif %} 59 | {% endfor %} 60 | {% endif %} 61 | {% endblock content %} 62 | -------------------------------------------------------------------------------- /cabot/static/theme/js/jquery.dataTables.bootstrap.min.js: -------------------------------------------------------------------------------- 1 | /*! 2 | DataTables Bootstrap 3 integration 3 | ©2011-2015 SpryMedia Ltd - datatables.net/license 4 | */ 5 | (function(b){"function"===typeof define&&define.amd?define(["jquery","datatables.net"],function(a){return b(a,window,document)}):"object"===typeof exports?module.exports=function(a,d){a||(a=window);if(!d||!d.fn.dataTable)d=require("datatables.net")(a,d).$;return b(d,a,a.document)}:b(jQuery,window,document)})(function(b,a,d,m){var f=b.fn.dataTable;b.extend(!0,f.defaults,{dom:"<'row'<'col-sm-6'l><'col-sm-6'f>><'row'<'col-sm-12'tr>><'row'<'col-sm-5'i><'col-sm-7'p>>",renderer:"bootstrap"});b.extend(f.ext.classes, 6 | {sWrapper:"dataTables_wrapper form-inline dt-bootstrap",sFilterInput:"form-control input-sm",sLengthSelect:"form-control input-sm",sProcessing:"dataTables_processing panel panel-default"});f.ext.renderer.pageButton.bootstrap=function(a,h,r,s,j,n){var o=new f.Api(a),t=a.oClasses,k=a.oLanguage.oPaginate,u=a.oLanguage.oAria.paginate||{},e,g,p=0,q=function(d,f){var l,h,i,c,m=function(a){a.preventDefault();!b(a.currentTarget).hasClass("disabled")&&o.page()!=a.data.action&&o.page(a.data.action).draw("page")}; 7 | l=0;for(h=f.length;l",{"class":t.sPageButton+" "+g,id:0===r&&"string"===typeof c?a.sTableId+"_"+c:null}).append(b("",{href:"#", 8 | "aria-controls":a.sTableId,"aria-label":u[c],"data-dt-idx":p,tabindex:a.iTabIndex}).html(e)).appendTo(d),a.oApi._fnBindAction(i,{action:c},m),p++)}},i;try{i=b(h).find(d.activeElement).data("dt-idx")}catch(v){}q(b(h).empty().html('