├── salt_dashboard ├── __init__.py ├── api │ ├── __init__.py │ ├── salt_api.py │ └── common.py ├── views │ ├── __init__.py │ └── index.py ├── templates │ ├── css.html │ ├── js.html │ ├── auto_execute.html │ ├── auto_service_table.html │ ├── auto_detail.html │ ├── base.html │ ├── footer.html │ ├── auto_minions.html │ ├── auto_service.html │ ├── header.html │ ├── auto_minion.html │ ├── auto_overview.html │ └── auto_sidebar.html ├── models.py ├── urls.py ├── wsgi.py └── settings.py ├── screenshot ├── index.png ├── execute.png └── minions.png ├── static ├── extra │ ├── img │ │ ├── loading.gif │ │ ├── loadings.gif │ │ └── saltstack_logo.png │ └── js │ │ └── extra.js └── bootstrap │ ├── img │ ├── glyphicons-halflings.png │ └── glyphicons-halflings-white.png │ ├── css │ ├── bootstrap-responsive.min.css │ └── docs.css │ └── js │ ├── bootstrap.min.js │ └── bootstrap.js ├── manage.py ├── .gitignore ├── README.md └── salt.sql /salt_dashboard/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /salt_dashboard/api/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /salt_dashboard/views/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /screenshot/index.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cssug/salt-dashboard/HEAD/screenshot/index.png -------------------------------------------------------------------------------- /screenshot/execute.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cssug/salt-dashboard/HEAD/screenshot/execute.png -------------------------------------------------------------------------------- /screenshot/minions.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cssug/salt-dashboard/HEAD/screenshot/minions.png -------------------------------------------------------------------------------- /static/extra/img/loading.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cssug/salt-dashboard/HEAD/static/extra/img/loading.gif -------------------------------------------------------------------------------- /static/extra/img/loadings.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cssug/salt-dashboard/HEAD/static/extra/img/loadings.gif -------------------------------------------------------------------------------- /static/extra/img/saltstack_logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cssug/salt-dashboard/HEAD/static/extra/img/saltstack_logo.png -------------------------------------------------------------------------------- /static/bootstrap/img/glyphicons-halflings.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cssug/salt-dashboard/HEAD/static/bootstrap/img/glyphicons-halflings.png -------------------------------------------------------------------------------- /static/bootstrap/img/glyphicons-halflings-white.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cssug/salt-dashboard/HEAD/static/bootstrap/img/glyphicons-halflings-white.png -------------------------------------------------------------------------------- /manage.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | import os 3 | import sys 4 | 5 | if __name__ == "__main__": 6 | os.environ.setdefault("DJANGO_SETTINGS_MODULE", "salt_dashboard.settings") 7 | 8 | from django.core.management import execute_from_command_line 9 | 10 | execute_from_command_line(sys.argv) 11 | -------------------------------------------------------------------------------- /salt_dashboard/templates/css.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 7 | -------------------------------------------------------------------------------- /salt_dashboard/templates/js.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /salt_dashboard/templates/auto_execute.html: -------------------------------------------------------------------------------- 1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 | 13 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *.py[cod] 2 | 3 | # C extensions 4 | *.so 5 | 6 | # Packages 7 | *.egg 8 | *.egg-info 9 | dist 10 | build 11 | eggs 12 | parts 13 | bin 14 | var 15 | sdist 16 | develop-eggs 17 | .installed.cfg 18 | lib 19 | lib64 20 | 21 | # Installer logs 22 | pip-log.txt 23 | 24 | # Unit test / coverage reports 25 | .coverage 26 | .tox 27 | nosetests.xml 28 | 29 | # Translations 30 | *.mo 31 | 32 | # Mr Developer 33 | .mr.developer.cfg 34 | .project 35 | .pydevproject 36 | 37 | -------------------------------------------------------------------------------- /salt_dashboard/models.py: -------------------------------------------------------------------------------- 1 | from django.db import models 2 | 3 | class Service(models.Model): 4 | name = models.CharField(max_length=50) 5 | target = models.TextField() 6 | 7 | class Meta: 8 | db_table = 'service' 9 | 10 | class Script(models.Model): 11 | user_id = models.CharField(max_length=50) 12 | name = models.CharField(max_length=30) 13 | args = models.CharField(max_length=100) 14 | public = models.CharField(max_length=5) 15 | status = models.CharField(max_length=10) 16 | 17 | class Meta: 18 | db_table = 'script' 19 | -------------------------------------------------------------------------------- /salt_dashboard/templates/auto_service_table.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | {% for service in services %} 11 | 12 | 13 | 14 | 15 | 16 | {% endfor %} 17 | 18 |
Service Nametargetaction
{{service.name}}{{service.target}}删除
19 | -------------------------------------------------------------------------------- /salt_dashboard/api/salt_api.py: -------------------------------------------------------------------------------- 1 | #coding=utf8 2 | import salt.client 3 | client = salt.client.LocalClient() 4 | 5 | def overview(request): 6 | target = request.GET.get("target",'*') 7 | try: 8 | grains = client.cmd(target, 'grains.items') 9 | except: 10 | grains = {} 11 | return grains 12 | 13 | def execute(**kwargs): 14 | return client.cmd_async(**kwargs) 15 | 16 | def get_state(target): 17 | try: 18 | states = client.cmd(target,'state.show_top') 19 | except: 20 | states = {} 21 | return states 22 | 23 | 24 | 25 | if __name__ =="__main__": 26 | print get_state('60_12_201_27.nb_compute1.lightcloud.cn') 27 | 28 | -------------------------------------------------------------------------------- /salt_dashboard/templates/auto_detail.html: -------------------------------------------------------------------------------- 1 | 11 | 23 | -------------------------------------------------------------------------------- /salt_dashboard/urls.py: -------------------------------------------------------------------------------- 1 | from django.conf.urls import patterns, include, url 2 | from views.index import * 3 | 4 | # Uncomment the next two lines to enable the admin: 5 | # from django.contrib import admin 6 | # admin.autodiscover() 7 | 8 | urlpatterns = patterns('', 9 | # Examples: 10 | # url(r'^$', 'salt_dashboard.views.home', name='home'), 11 | # url(r'^salt_dashboard/', include('salt_dashboard.foo.urls')), 12 | 13 | # Uncomment the admin/doc line below to enable admin documentation: 14 | # url(r'^admin/doc/', include('django.contrib.admindocs.urls')), 15 | 16 | # Uncomment the next line to enable the admin: 17 | # url(r'^admin/', include(admin.site.urls)), 18 | url(r'^$',auto), 19 | url(r'^overview$',overview), 20 | url(r'^minions$',minions), 21 | url(r'^execute$',execute), 22 | url(r'^detail$',detail), 23 | url(r'^getjobinfo$',getjobinfo), 24 | url(r'^service$',service), 25 | url(r'^minion$',minion), 26 | ) 27 | -------------------------------------------------------------------------------- /salt_dashboard/api/common.py: -------------------------------------------------------------------------------- 1 | def my_page(request,paging_len): 2 | current_page = int(request.GET.get("page",0)) 3 | page_sum = 10 4 | page_extra = 3 5 | context = {} 6 | if current_page < 0: 7 | current_page = 0 8 | if ((current_page+1) * page_sum) >= paging_len: 9 | if paging_len%page_sum: 10 | current_page = paging_len/page_sum 11 | else: 12 | current_page = paging_len/page_sum-1 13 | if (page_extra*2+1) > paging_len/page_sum: 14 | context["page_num"] = range(paging_len/page_sum) 15 | elif (current_page-page_extra) < 0: 16 | context["page_num"] = range(page_extra*2+1) 17 | elif (current_page+page_extra)*page_sum >= paging_len: 18 | context["page_num"] = range((paging_len/page_sum-7),paging_len/page_sum) 19 | else: 20 | context["page_num"] = range(current_page-page_extra,current_page+page_extra) 21 | context["current_page"] = current_page 22 | context['page_tables'] = [] 23 | return context 24 | -------------------------------------------------------------------------------- /salt_dashboard/templates/base.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | salt dashboard 7 | 8 | {% include 'css.html' %} 9 | {% include 'js.html' %} 10 | 11 | 12 |
13 |
14 | {% include 'header.html' %} 15 |
16 | 17 |
18 | 19 |
20 | {% block sidebar %} 21 | {% endblock %} 22 |
23 | 24 |
25 | {% block body %}{% endblock %} 26 |
27 |
28 | 29 |
30 | {% include 'footer.html' %} 31 |
32 |
33 | {% block extrajs %}{% endblock %} 34 | {% block extracss %}{% endblock %} 35 | 36 | 37 | -------------------------------------------------------------------------------- /salt_dashboard/templates/footer.html: -------------------------------------------------------------------------------- 1 | 2 | 9 |
10 | {{test}} 11 | {% for message in messages %} 12 | {% if "info" in message.tags %} 13 |
14 | × 15 |

提示:{{ message }}

16 |
17 | {% endif %} 18 | {% if "warning" in message.tags %} 19 |
20 | × 21 |

警告:{{ message }}

22 |
23 | {% endif %} 24 | {% if "success" in message.tags %} 25 |
26 | × 27 |

成功了:{{ message }}

28 |
29 | {% endif %} 30 | {% if "error" in message.tags %} 31 |
32 | × 33 |

出错了:{{ message }}

34 |
35 | {% endif %} 36 | {% endfor %} 37 |
38 | -------------------------------------------------------------------------------- /salt_dashboard/templates/auto_minions.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | {% for grain in page_tables %} 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | {% endfor %} 23 | 24 |
HostModelflavorOSUptimeaction
{{grain.id}}{{grain.manufacturer}}/{{grain.productname}}{{grain.num_cpus}}U/{{grain.mem_total}}M RAM{{grain.os}}-{{grain.osrelease}}{{grain.time_diff}}detail
25 | 26 | 35 | -------------------------------------------------------------------------------- /salt_dashboard/templates/auto_service.html: -------------------------------------------------------------------------------- 1 | 26 | 46 | -------------------------------------------------------------------------------- /salt_dashboard/wsgi.py: -------------------------------------------------------------------------------- 1 | """ 2 | WSGI config for salt_dashboard project. 3 | 4 | This module contains the WSGI application used by Django's development server 5 | and any production WSGI deployments. It should expose a module-level variable 6 | named ``application``. Django's ``runserver`` and ``runfcgi`` commands discover 7 | this application via the ``WSGI_APPLICATION`` setting. 8 | 9 | Usually you will have the standard Django WSGI application here, but it also 10 | might make sense to replace the whole Django WSGI application with a custom one 11 | that later delegates to the Django one. For example, you could introduce WSGI 12 | middleware here, or combine a Django application with an application of another 13 | framework. 14 | 15 | """ 16 | import os 17 | 18 | # We defer to a DJANGO_SETTINGS_MODULE already in the environment. This breaks 19 | # if running multiple sites in the same mod_wsgi process. To fix this, use 20 | # mod_wsgi daemon mode with each site in its own daemon process, or use 21 | # os.environ["DJANGO_SETTINGS_MODULE"] = "salt_dashboard.settings" 22 | os.environ.setdefault("DJANGO_SETTINGS_MODULE", "salt_dashboard.settings") 23 | 24 | # This application object is used by any WSGI server configured to use this 25 | # file. This includes Django's development server, if the WSGI_APPLICATION 26 | # setting points here. 27 | from django.core.wsgi import get_wsgi_application 28 | application = get_wsgi_application() 29 | 30 | # Apply WSGI middleware here. 31 | # from helloworld.wsgi import HelloWorldApplication 32 | # application = HelloWorldApplication(application) 33 | -------------------------------------------------------------------------------- /salt_dashboard/templates/header.html: -------------------------------------------------------------------------------- 1 |
2 | 27 |
28 | 30 | 31 | 43 | -------------------------------------------------------------------------------- /salt_dashboard/templates/auto_minion.html: -------------------------------------------------------------------------------- 1 | 24 | 25 | 26 | 27 | 28 | 31 | 34 | 35 | 36 | 37 | 41 | 42 | 43 | 44 | 51 | 52 | 53 |
29 | ID 30 | 32 | {{id}} 33 |
信息 38 | grains 39 | pillar 40 |
系统管理 45 | crontab 46 | hosts 47 | iptables 48 | sysctl 49 | Highstate 50 |
54 |
55 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | #salt dashboard 2 | 3 | 4 | salt-dashboard is based on salt-client(so deploy this on salt master server),and use mysql returner as result backend 5 | 6 | 7 | --------------------------------------- 8 | 9 | 10 | 11 | #requirement 12 | 13 |
14 | pip install django mysql-python
15 | 
16 | 17 | 18 | 19 | #1:returner: 20 | 21 | 22 | ## salt [mysql returner ](http://docs.saltstack.com/ref/returners/all/salt.returners.mysql.html#module-salt.returners.mysql "Title") 23 | 24 | add time to salt_returns: 25 |
26 | alter table salt_returns add time timestamp;
27 | 
28 | 29 | the auth sql like this: 30 |
31 | grant all on salt.* to 'salt'@'localhost' identified by 'salt';
32 | 
33 | 34 | there is a bug when schedule use mysql return,fixed by this [schedule return by mysql ](https://github.com/halfss/salt/commit/3f5805f7b38fc867a3d12b8c36efd023b4957792) 35 | 36 | ##salt minion config: 37 |
38 | vim /etc/salt/minion
39 | mysql.host: 'localhost'
40 | mysql.user: 'salt'
41 | mysql.pass: 'salt'
42 | mysql.db: 'salt'
43 | mysql.port: 3306
44 | 
45 | 46 | ##salt-dashboard sync db 47 |
48 | cd salt-dashboard/
49 | python manage.py syncdb
50 | 
51 | 52 | #2:scheduler: 53 | [[
]]
54 | /srv/pillar/top.sls
55 | 
56 | base:
57 |   "*":
58 |     - schedule
59 | 
60 | /srv/pillar/schedule.sls 61 |
62 | schedule:
63 |   highstate:
64 |     function: state.highstate
65 |     minutes: 30
66 |     returner: mysql
67 | 
68 |
69 | 70 | then waiting minions update scheduler info by himself or run this command: 71 |
72 | salt '*' saltutil.refresh_pillar
73 | 
74 | 75 | #3:salt-dashboard 76 | this dashboard is based on django+bootstrap+amcharts 77 | 78 | demo screen like this: 79 | here just on minion, so is seems very simple 80 | 81 | 82 | ![index](https://raw.github.com/halfss/salt-dashboard/master/screenshot/index.png) 83 | ![minions](https://raw.github.com/halfss/salt-dashboard/master/screenshot/minions.png) 84 | ![execute](https://raw.github.com/halfss/salt-dashboard/master/screenshot/execute.png) 85 | 86 | 87 | 88 | 89 | -------------------------------------------------------------------------------- /static/extra/js/extra.js: -------------------------------------------------------------------------------- 1 | function my_ajax_replace(url,id){ 2 | $('#loading-img').show(); 3 | $.ajax({ 4 | url: url, 5 | cache: false, 6 | success: function(html){ 7 | var box = $( '#' + id ); 8 | if( box.length && /input/i.test( box[0].nodeName ) ){ 9 | box.val( html ); 10 | }else{ 11 | box.html( html ); 12 | } 13 | $('#loading-img').hide(); 14 | } 15 | }); 16 | } 17 | 18 | function ajaxCommand(url,option,id){ 19 | var _interVal, commonAjaxXhr,urlRex = /where=(\d+)/ , ajaxWhere = 0, count = 0, 20 | commonAjax = function(config){//发起ajax请求 21 | config = $.extend({ 22 | type:'post', 23 | dataType:'json' 24 | },config); 25 | commonAjaxXhr = $.ajax(config); 26 | }, 27 | _exportCommand = function(url){ 28 | var resultBox = document.getElementById('result'); 29 | if ( 'number' !== typeof ajaxWhere ) return; 30 | url = url.replace( urlRex,'where=' + ajaxWhere ); 31 | commonAjax({ 32 | url: url, 33 | type:'get', 34 | success: function(data){ 35 | ajaxWhere = data.where; 36 | if ( data.result.length ){ 37 | count = 0; 38 | var commandHtml = data.result.join(''); 39 | document.getElementById(id).innerHTML += commandHtml ; 40 | _exportCommand(url); 41 | }else{ 42 | count++; 43 | if(count >= 15) return; 44 | setTimeout(function(){ _exportCommand(url) }, 1000); 45 | } 46 | resultBox.scrollTop = resultBox.scrollHeight; 47 | } 48 | }); 49 | }; 50 | commonAjax({ 51 | url: url+'&option='+option, 52 | type:'get', 53 | dataType: 'text', 54 | success: function(data){ 55 | _exportCommand(data); 56 | } 57 | }) 58 | } 59 | -------------------------------------------------------------------------------- /salt_dashboard/templates/auto_overview.html: -------------------------------------------------------------------------------- 1 |
2 |
3 |
4 |
5 | 53 | 54 | state error host in the lastest 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | {% for host in error_host %} 66 | 67 | 68 | 69 | 70 | 71 | 72 | {% endfor %} 73 | 74 |
Hostmoduletimesuccess
{{host.id}}{{host.fun}}{{host.time}}{{host.success}}
75 | -------------------------------------------------------------------------------- /salt_dashboard/templates/auto_sidebar.html: -------------------------------------------------------------------------------- 1 | {% extends 'base.html' %} 2 | {# coding=utf8 #} 3 | {% load i18n %} 4 | {% block sidebar%} 5 |
6 | class manage 7 |
8 |
9 | 15 | {% endblock %} 16 | {% block body %} 17 | 18 | 51 | 67 |
68 |
69 | 74 | 75 |
{% csrf_token %} 76 | 77 | 78 | 79 | 80 |
81 |
82 |
83 |
84 |
85 |
86 |
87 |
88 | {% endblock %} 89 | -------------------------------------------------------------------------------- /salt_dashboard/settings.py: -------------------------------------------------------------------------------- 1 | # Django settings for salt_dashboard project. 2 | import os 3 | 4 | DEBUG = True 5 | TEMPLATE_DEBUG = DEBUG 6 | 7 | CURRENT_DIR=os.getcwd() 8 | 9 | ADMINS = ( 10 | # ('Your Name', 'your_email@example.com'), 11 | ) 12 | 13 | MANAGERS = ADMINS 14 | 15 | DATABASES = { 16 | 'default': { 17 | 'ENGINE': 'django.db.backends.mysql', # Add 'postgresql_psycopg2', 'mysql', 'sqlite3' or 'oracle'. 18 | 'NAME': 'salt', # Or path to database file if using sqlite3. 19 | # The following settings are not used with sqlite3: 20 | 'USER': 'salt', 21 | 'PASSWORD': 'salt', 22 | 'HOST': 'localhost', # Empty for localhost through domain sockets or '127.0.0.1' for localhost through TCP. 23 | 'PORT': '3306', # Set to empty string for default. 24 | } 25 | } 26 | 27 | # Hosts/domain names that are valid for this site; required if DEBUG is False 28 | # See https://docs.djangoproject.com/en/1.5/ref/settings/#allowed-hosts 29 | ALLOWED_HOSTS = [] 30 | 31 | # Local time zone for this installation. Choices can be found here: 32 | # http://en.wikipedia.org/wiki/List_of_tz_zones_by_name 33 | # although not all choices may be available on all operating systems. 34 | # In a Windows environment this must be set to your system time zone. 35 | TIME_ZONE = 'America/Chicago' 36 | 37 | # Language code for this installation. All choices can be found here: 38 | # http://www.i18nguy.com/unicode/language-identifiers.html 39 | LANGUAGE_CODE = 'en-us' 40 | 41 | SITE_ID = 1 42 | 43 | # If you set this to False, Django will make some optimizations so as not 44 | # to load the internationalization machinery. 45 | USE_I18N = True 46 | 47 | # If you set this to False, Django will not format dates, numbers and 48 | # calendars according to the current locale. 49 | USE_L10N = True 50 | 51 | # If you set this to False, Django will not use timezone-aware datetimes. 52 | USE_TZ = True 53 | 54 | # Absolute filesystem path to the directory that will hold user-uploaded files. 55 | # Example: "/var/www/example.com/media/" 56 | MEDIA_ROOT = '' 57 | 58 | # URL that handles the media served from MEDIA_ROOT. Make sure to use a 59 | # trailing slash. 60 | # Examples: "http://example.com/media/", "http://media.example.com/" 61 | MEDIA_URL = '' 62 | 63 | # Absolute path to the directory static files should be collected to. 64 | # Don't put anything in this directory yourself; store your static files 65 | # in apps' "static/" subdirectories and in STATICFILES_DIRS. 66 | # Example: "/var/www/example.com/static/" 67 | STATIC_ROOT = '' 68 | 69 | # URL prefix for static files. 70 | # Example: "http://example.com/static/", "http://static.example.com/" 71 | STATIC_URL = '/static/' 72 | 73 | # Additional locations of static files 74 | STATICFILES_DIRS = ( 75 | CURRENT_DIR+'/static', 76 | # Put strings here, like "/home/html/static" or "C:/www/django/static". 77 | # Always use forward slashes, even on Windows. 78 | # Don't forget to use absolute paths, not relative paths. 79 | ) 80 | 81 | # List of finder classes that know how to find static files in 82 | # various locations. 83 | STATICFILES_FINDERS = ( 84 | 'django.contrib.staticfiles.finders.FileSystemFinder', 85 | 'django.contrib.staticfiles.finders.AppDirectoriesFinder', 86 | # 'django.contrib.staticfiles.finders.DefaultStorageFinder', 87 | ) 88 | 89 | # Make this unique, and don't share it with anybody. 90 | SECRET_KEY = '1+k7k0f6#ri9b7#pay#sfii7u%n%e&3)y1ljmh5+us9*l*u4==' 91 | 92 | # List of callables that know how to import templates from various sources. 93 | TEMPLATE_LOADERS = ( 94 | 'django.template.loaders.filesystem.Loader', 95 | 'django.template.loaders.app_directories.Loader', 96 | # 'django.template.loaders.eggs.Loader', 97 | ) 98 | 99 | MIDDLEWARE_CLASSES = ( 100 | 'django.middleware.common.CommonMiddleware', 101 | 'django.contrib.sessions.middleware.SessionMiddleware', 102 | 'django.middleware.csrf.CsrfViewMiddleware', 103 | 'django.contrib.auth.middleware.AuthenticationMiddleware', 104 | 'django.contrib.messages.middleware.MessageMiddleware', 105 | # Uncomment the next line for simple clickjacking protection: 106 | # 'django.middleware.clickjacking.XFrameOptionsMiddleware', 107 | ) 108 | 109 | ROOT_URLCONF = 'salt_dashboard.urls' 110 | 111 | # Python dotted path to the WSGI application used by Django's runserver. 112 | WSGI_APPLICATION = 'salt_dashboard.wsgi.application' 113 | 114 | TEMPLATE_DIRS = ( 115 | CURRENT_DIR+'/salt_dashboard/templates', 116 | # Put strings here, like "/home/html/django_templates" or "C:/www/django/templates". 117 | # Always use forward slashes, even on Windows. 118 | # Don't forget to use absolute paths, not relative paths. 119 | ) 120 | 121 | INSTALLED_APPS = ( 122 | 'salt_dashboard', 123 | 'django.contrib.auth', 124 | 'django.contrib.contenttypes', 125 | 'django.contrib.sessions', 126 | 'django.contrib.sites', 127 | 'django.contrib.messages', 128 | 'django.contrib.staticfiles', 129 | # Uncomment the next line to enable the admin: 130 | # 'django.contrib.admin', 131 | # Uncomment the next line to enable admin documentation: 132 | # 'django.contrib.admindocs', 133 | ) 134 | 135 | # A sample logging configuration. The only tangible logging 136 | # performed by this configuration is to send an email to 137 | # the site admins on every HTTP 500 error when DEBUG=False. 138 | # See http://docs.djangoproject.com/en/dev/topics/logging for 139 | # more details on how to customize your logging configuration. 140 | LOGGING = { 141 | 'version': 1, 142 | 'disable_existing_loggers': False, 143 | 'filters': { 144 | 'require_debug_false': { 145 | '()': 'django.utils.log.RequireDebugFalse' 146 | } 147 | }, 148 | 'handlers': { 149 | 'mail_admins': { 150 | 'level': 'ERROR', 151 | 'filters': ['require_debug_false'], 152 | 'class': 'django.utils.log.AdminEmailHandler' 153 | } 154 | }, 155 | 'loggers': { 156 | 'django.request': { 157 | 'handlers': ['mail_admins'], 158 | 'level': 'ERROR', 159 | 'propagate': True, 160 | }, 161 | } 162 | } 163 | -------------------------------------------------------------------------------- /salt_dashboard/views/index.py: -------------------------------------------------------------------------------- 1 | #coding=UTF-8 2 | import datetime 3 | import os 4 | import re 5 | import md5 6 | import json 7 | 8 | 9 | 10 | from django.http import HttpResponse, Http404 11 | from django.shortcuts import render_to_response 12 | from django.core.context_processors import csrf 13 | from django.template import RequestContext 14 | from django.db import connection 15 | 16 | from salt_dashboard.api import salt_api,common 17 | from salt_dashboard.models import * 18 | 19 | 20 | def auto(request): 21 | context = {} 22 | context.update(csrf(request)) 23 | services = Service.objects.all() 24 | context['services'] = services 25 | return render_to_response('auto_sidebar.html', context) 26 | 27 | 28 | def overview(request): 29 | context = {} 30 | context.update(csrf(request)) 31 | grains = salt_api.overview(request).values() 32 | minions_os = {} 33 | minions_virtual = {} 34 | for grain in grains: 35 | try: 36 | minions_os[grain['osfullname']+grain['osrelease']] += 1 37 | minions_virtual[grain['virtual']] += 1 38 | except KeyError: 39 | minions_os[grain['osfullname']+grain['osrelease']] = 1 40 | minions_virtual[grain['virtual']] = 1 41 | try: 42 | cursor = connection.cursor() 43 | host_num = cursor.execute("select id,fun,time,success from \ 44 | (select id,fun,time,success from salt.salt_returns where \ 45 | success='0' order by id desc) as b group by id limit 10;") 46 | hosts = cursor.fetchall() 47 | error_host = [] 48 | for host in hosts: 49 | error_host.append({ 50 | 'id':host[0], 51 | 'fun':host[1], 52 | 'time':host[2], 53 | 'success':host[3] 54 | }) 55 | except: 56 | error_host = [] 57 | 58 | 59 | context['minions_os'] = minions_os 60 | context['minions_virtual'] = minions_virtual 61 | context['minions_num'] = len(grains) 62 | context['error_host'] = error_host 63 | return render_to_response('auto_overview.html', context) 64 | 65 | def minions(request): 66 | current_page = int(request.GET.get("page",0)) 67 | page_sum = 15 68 | page_extra = 3 69 | grains = salt_api.overview(request).values() 70 | try: 71 | cursor = connection.cursor() 72 | host_num = cursor.execute('select host_id,time from \ 73 | (select host_id,time from fluent.salt_result order \ 74 | by id desc ) as b group by host_id;') 75 | hosts_time = cursor.fetchall() 76 | now = datetime.datetime.now() 77 | times_diff = {} 78 | for host_time in hosts_time: 79 | time_dist = now - host_time[1] 80 | times_diff[host_time[0]] = str(time_dist).split('.')[0] 81 | except: 82 | times_diff = {} 83 | for grain in grains: 84 | grain['IP'] = grain['id'].split('.')[0].replace('_','.') 85 | grain['time_diff'] = times_diff.get(grain['id'],'无') 86 | context = common.my_page(request,len(grains)) 87 | context['page_tables'] = grains[current_page*page_sum:(current_page+1)*page_sum] 88 | return render_to_response('auto_minions.html', context) 89 | 90 | def execute(request): 91 | context = {'jid':''} 92 | tgt = request.POST.get('tgt','*') 93 | fun = request.POST.get('fun','cmd.run') 94 | arg = request.POST.get('arg','') 95 | if arg: 96 | kwargs = {'tgt': tgt, 97 | 'ret': 'mysql', 98 | 'expr_form': 'glob', 99 | 'timeout': 15, 100 | 'arg': [arg], 101 | 'fun': fun 102 | } 103 | jid = salt_api.execute(**kwargs) 104 | context['jid'] = jid 105 | return render_to_response('auto_execute.html', context) 106 | 107 | def detail(request): 108 | target = request.GET.get('target','') 109 | if target: 110 | grain = salt_api.overview(request) 111 | state = salt_api.get_state(target) 112 | state = repr(json.dumps(state,sort_keys=True, indent=4)) 113 | state = state.replace('\\n','
').replace(' ',' ') 114 | context = { 115 | 'grain':grain, 116 | 'state':state 117 | } 118 | return render_to_response('auto_detail.html', context) 119 | 120 | def getjobinfo(request): 121 | context = {} 122 | jid = request.GET.get('jid','') 123 | where = int(request.GET.get('where','12376894567235')) 124 | if where == 12376894567235: 125 | result = '/getjobinfo?jid=%s&where=%s' % (jid,0) 126 | return HttpResponse(result) 127 | else: 128 | cursor = connection.cursor() 129 | host_result = cursor.execute("select id,success,`return` from salt.salt_returns \ 130 | where jid='%s' limit %s,10000;" % (jid,where) ) 131 | hosts_result = cursor.fetchall() 132 | where = len(hosts_result) + where 133 | result = [] 134 | for host_result in hosts_result: 135 | result.append(u'host:%s   state:%s
return:%s
' % (host_result[0],host_result[1],host_result[2])) 136 | context = { 137 | "where":where, 138 | "result":result 139 | } 140 | return HttpResponse(json.dumps(context)) 141 | 142 | def service(request): 143 | context = {} 144 | context.update(csrf(request)) 145 | if request.method == 'GET': 146 | id = request.GET.get('id','') 147 | if id: 148 | Service.objects.get(id=id).delete() 149 | services = Service.objects.all() 150 | context['services'] = services 151 | return render_to_response('auto_service.html', context) 152 | else: 153 | service_name = u'%s' % request.POST.get('name','') 154 | service_tgt = request.POST.get('tgt','') 155 | if service_name and service_tgt: 156 | try: 157 | new_service = Service(name=service_name,target=service_tgt) 158 | new_service.save() 159 | except: 160 | raise 161 | return render_to_response('auto_service_table.html', context) 162 | 163 | def minion(request): 164 | os_dict = { 165 | "pillar":{'fun':'pillar.data'}, 166 | "grains":{'fun':'grains.items'}, 167 | "cron":{'fun':'cron.list_tab','arg':['root']}, 168 | "hosts":{'fun':'hosts.list_hosts'}, 169 | "iptables":{'fun':'iptables.get_rules'}, 170 | "sysctl":{'fun':'sysctl.show'}, 171 | "highstate":{'fun':'state.highstate'}, 172 | "sls":{'fun':'state.sls'}, 173 | "script":{'fun':'cmd.script'} 174 | } 175 | context = {} 176 | context.update(csrf(request)) 177 | tgt = request.GET.get('tgt','') 178 | cmd_type = request.GET.get('type') 179 | arg = request.GET.get('arg','').split(',') 180 | ext_arg = request.GET.get('ext_arg','').split(',') 181 | if cmd_type in os_dict.keys(): 182 | kwargs = {'tgt': tgt, 183 | 'expr_form': 'glob', 184 | 'ret': salt_returner, 185 | 'timeout': 60, 186 | 'arg':arg 187 | } 188 | kwargs.update(os_dict[cmd_type]) 189 | users.privilege(request,kwargs) 190 | if cmd_type in ['highstate','sls','script']: 191 | jid = salt_api.execute(kwargs) 192 | context['jid'] = jid 193 | return render_to_response('auto_execute.html', context) 194 | else: 195 | value = salt_api.execute_sync(kwargs) 196 | value = repr(json.dumps(value,sort_keys=True, indent=4)) 197 | value = value.replace('\\n','
').replace(' ',' ') 198 | context['key'] = cmd_type 199 | context['value'] = value 200 | return render_to_response('auto_detail.html', context) 201 | scripts_info = [] 202 | context['id'] = tgt 203 | return render_to_response('auto_minion.html', context) 204 | -------------------------------------------------------------------------------- /static/bootstrap/css/bootstrap-responsive.min.css: -------------------------------------------------------------------------------- 1 | .clearfix{*zoom:1;}.clearfix:before,.clearfix:after{display:table;content:"";} 2 | .clearfix:after{clear:both;} 3 | .hide-text{overflow:hidden;text-indent:100%;white-space:nowrap;} 4 | .input-block-level{display:block;width:100%;min-height:28px;-webkit-box-sizing:border-box;-moz-box-sizing:border-box;-ms-box-sizing:border-box;box-sizing:border-box;} 5 | .hidden{display:none;visibility:hidden;} 6 | .visible-phone{display:none;} 7 | .visible-tablet{display:none;} 8 | .visible-desktop{display:block;} 9 | .hidden-phone{display:block;} 10 | .hidden-tablet{display:block;} 11 | .hidden-desktop{display:none;} 12 | @media (max-width:767px){.visible-phone{display:block;} .hidden-phone{display:none;} .hidden-desktop{display:block;} .visible-desktop{display:none;}}@media (min-width:768px) and (max-width:979px){.visible-tablet{display:block;} .hidden-tablet{display:none;} .hidden-desktop{display:block;} .visible-desktop{display:none;}}@media (max-width:480px){.nav-collapse{-webkit-transform:translate3d(0, 0, 0);} .page-header h1 small{display:block;line-height:18px;} input[type="checkbox"],input[type="radio"]{border:1px solid #ccc;} .form-horizontal .control-group>label{float:none;width:auto;padding-top:0;text-align:left;} .form-horizontal .controls{margin-left:0;} .form-horizontal .control-list{padding-top:0;} .form-horizontal .form-actions{padding-left:10px;padding-right:10px;} .modal{position:absolute;top:10px;left:10px;right:10px;width:auto;margin:0;}.modal.fade.in{top:auto;} .modal-header .close{padding:10px;margin:-10px;} .carousel-caption{position:static;}}@media (max-width:767px){body{padding-left:20px;padding-right:20px;} .navbar-fixed-top{margin-left:-20px;margin-right:-20px;} .container{width:auto;} .row-fluid{width:100%;} .row{margin-left:0;} .row>[class*="span"],.row-fluid>[class*="span"]{float:none;display:block;width:auto;margin:0;} .thumbnails [class*="span"]{width:auto;} input[class*="span"],select[class*="span"],textarea[class*="span"],.uneditable-input{display:block;width:100%;min-height:28px;-webkit-box-sizing:border-box;-moz-box-sizing:border-box;-ms-box-sizing:border-box;box-sizing:border-box;} .input-prepend input[class*="span"],.input-append input[class*="span"]{width:auto;}}@media (min-width:768px) and (max-width:979px){.row{margin-left:-20px;*zoom:1;}.row:before,.row:after{display:table;content:"";} .row:after{clear:both;} [class*="span"]{float:left;margin-left:20px;} .container,.navbar-fixed-top .container,.navbar-fixed-bottom .container{width:724px;} .span12{width:724px;} .span11{width:662px;} .span10{width:600px;} .span9{width:538px;} .span8{width:476px;} .span7{width:414px;} .span6{width:352px;} .span5{width:290px;} .span4{width:228px;} .span3{width:166px;} .span2{width:104px;} .span1{width:42px;} .offset12{margin-left:764px;} .offset11{margin-left:702px;} .offset10{margin-left:640px;} .offset9{margin-left:578px;} .offset8{margin-left:516px;} .offset7{margin-left:454px;} .offset6{margin-left:392px;} .offset5{margin-left:330px;} .offset4{margin-left:268px;} .offset3{margin-left:206px;} .offset2{margin-left:144px;} .offset1{margin-left:82px;} .row-fluid{width:100%;*zoom:1;}.row-fluid:before,.row-fluid:after{display:table;content:"";} .row-fluid:after{clear:both;} .row-fluid>[class*="span"]{float:left;margin-left:2.762430939%;} .row-fluid>[class*="span"]:first-child{margin-left:0;} .row-fluid > .span12{width:99.999999993%;} .row-fluid > .span11{width:91.436464082%;} .row-fluid > .span10{width:82.87292817100001%;} .row-fluid > .span9{width:74.30939226%;} .row-fluid > .span8{width:65.74585634900001%;} .row-fluid > .span7{width:57.182320438000005%;} .row-fluid > .span6{width:48.618784527%;} .row-fluid > .span5{width:40.055248616%;} .row-fluid > .span4{width:31.491712705%;} .row-fluid > .span3{width:22.928176794%;} .row-fluid > .span2{width:14.364640883%;} .row-fluid > .span1{width:5.801104972%;} input,textarea,.uneditable-input{margin-left:0;} input.span12, textarea.span12, .uneditable-input.span12{width:714px;} input.span11, textarea.span11, .uneditable-input.span11{width:652px;} input.span10, textarea.span10, .uneditable-input.span10{width:590px;} input.span9, textarea.span9, .uneditable-input.span9{width:528px;} input.span8, textarea.span8, .uneditable-input.span8{width:466px;} input.span7, textarea.span7, .uneditable-input.span7{width:404px;} input.span6, textarea.span6, .uneditable-input.span6{width:342px;} input.span5, textarea.span5, .uneditable-input.span5{width:280px;} input.span4, textarea.span4, .uneditable-input.span4{width:218px;} input.span3, textarea.span3, .uneditable-input.span3{width:156px;} input.span2, textarea.span2, .uneditable-input.span2{width:94px;} input.span1, textarea.span1, .uneditable-input.span1{width:32px;}}@media (max-width:979px){body{padding-top:0;} .navbar-fixed-top{position:static;margin-bottom:18px;} .navbar-fixed-top .navbar-inner{padding:5px;} .navbar .container{width:auto;padding:0;} .navbar .brand{padding-left:10px;padding-right:10px;margin:0 0 0 -5px;} .navbar .nav-collapse{clear:left;} .navbar .nav{float:none;margin:0 0 9px;} .navbar .nav>li{float:none;} .navbar .nav>li>a{margin-bottom:2px;} .navbar .nav>.divider-vertical{display:none;} .navbar .nav .nav-header{color:#999999;text-shadow:none;} .navbar .nav>li>a,.navbar .dropdown-menu a{padding:6px 15px;font-weight:bold;color:#999999;-webkit-border-radius:3px;-moz-border-radius:3px;border-radius:3px;} .navbar .dropdown-menu li+li a{margin-bottom:2px;} .navbar .nav>li>a:hover,.navbar .dropdown-menu a:hover{background-color:#222222;} .navbar .dropdown-menu{position:static;top:auto;left:auto;float:none;display:block;max-width:none;margin:0 15px;padding:0;background-color:transparent;border:none;-webkit-border-radius:0;-moz-border-radius:0;border-radius:0;-webkit-box-shadow:none;-moz-box-shadow:none;box-shadow:none;} .navbar .dropdown-menu:before,.navbar .dropdown-menu:after{display:none;} .navbar .dropdown-menu .divider{display:none;} .navbar-form,.navbar-search{float:none;padding:9px 15px;margin:9px 0;border-top:1px solid #222222;border-bottom:1px solid #222222;-webkit-box-shadow:inset 0 1px 0 rgba(255, 255, 255, 0.1),0 1px 0 rgba(255, 255, 255, 0.1);-moz-box-shadow:inset 0 1px 0 rgba(255, 255, 255, 0.1),0 1px 0 rgba(255, 255, 255, 0.1);box-shadow:inset 0 1px 0 rgba(255, 255, 255, 0.1),0 1px 0 rgba(255, 255, 255, 0.1);} .navbar .nav.pull-right{float:none;margin-left:0;} .navbar-static .navbar-inner{padding-left:10px;padding-right:10px;} .btn-navbar{display:block;} .nav-collapse{overflow:hidden;height:0;}}@media (min-width:980px){.nav-collapse.collapse{height:auto !important;overflow:visible !important;}}@media (min-width:1200px){.row{margin-left:-30px;*zoom:1;}.row:before,.row:after{display:table;content:"";} .row:after{clear:both;} [class*="span"]{float:left;margin-left:30px;} .container,.navbar-fixed-top .container,.navbar-fixed-bottom .container{width:1170px;} .span12{width:1170px;} .span11{width:1070px;} .span10{width:970px;} .span9{width:870px;} .span8{width:770px;} .span7{width:670px;} .span6{width:570px;} .span5{width:470px;} .span4{width:370px;} .span3{width:270px;} .span2{width:170px;} .span1{width:70px;} .offset12{margin-left:1230px;} .offset11{margin-left:1130px;} .offset10{margin-left:1030px;} .offset9{margin-left:930px;} .offset8{margin-left:830px;} .offset7{margin-left:730px;} .offset6{margin-left:630px;} .offset5{margin-left:530px;} .offset4{margin-left:430px;} .offset3{margin-left:330px;} .offset2{margin-left:230px;} .offset1{margin-left:130px;} .row-fluid{width:100%;*zoom:1;}.row-fluid:before,.row-fluid:after{display:table;content:"";} .row-fluid:after{clear:both;} .row-fluid>[class*="span"]{float:left;margin-left:2.564102564%;} .row-fluid>[class*="span"]:first-child{margin-left:0;} .row-fluid > .span12{width:100%;} .row-fluid > .span11{width:91.45299145300001%;} .row-fluid > .span10{width:82.905982906%;} .row-fluid > .span9{width:74.358974359%;} .row-fluid > .span8{width:65.81196581200001%;} .row-fluid > .span7{width:57.264957265%;} .row-fluid > .span6{width:48.717948718%;} .row-fluid > .span5{width:40.170940171000005%;} .row-fluid > .span4{width:31.623931624%;} .row-fluid > .span3{width:23.076923077%;} .row-fluid > .span2{width:14.529914530000001%;} .row-fluid > .span1{width:5.982905983%;} input,textarea,.uneditable-input{margin-left:0;} input.span12, textarea.span12, .uneditable-input.span12{width:1160px;} input.span11, textarea.span11, .uneditable-input.span11{width:1060px;} input.span10, textarea.span10, .uneditable-input.span10{width:960px;} input.span9, textarea.span9, .uneditable-input.span9{width:860px;} input.span8, textarea.span8, .uneditable-input.span8{width:760px;} input.span7, textarea.span7, .uneditable-input.span7{width:660px;} input.span6, textarea.span6, .uneditable-input.span6{width:560px;} input.span5, textarea.span5, .uneditable-input.span5{width:460px;} input.span4, textarea.span4, .uneditable-input.span4{width:360px;} input.span3, textarea.span3, .uneditable-input.span3{width:260px;} input.span2, textarea.span2, .uneditable-input.span2{width:160px;} input.span1, textarea.span1, .uneditable-input.span1{width:60px;} .thumbnails{margin-left:-30px;} .thumbnails>li{margin-left:30px;}} 13 | -------------------------------------------------------------------------------- /salt.sql: -------------------------------------------------------------------------------- 1 | -- MySQL dump 10.13 Distrib 5.1.69, for redhat-linux-gnu (x86_64) 2 | -- 3 | -- Host: localhost Database: salt 4 | -- ------------------------------------------------------ 5 | -- Server version 5.1.69 6 | 7 | /*!40101 SET @OLD_CHARACTER_SET_CLIENT=@@CHARACTER_SET_CLIENT */; 8 | /*!40101 SET @OLD_CHARACTER_SET_RESULTS=@@CHARACTER_SET_RESULTS */; 9 | /*!40101 SET @OLD_COLLATION_CONNECTION=@@COLLATION_CONNECTION */; 10 | /*!40101 SET NAMES utf8 */; 11 | /*!40103 SET @OLD_TIME_ZONE=@@TIME_ZONE */; 12 | /*!40103 SET TIME_ZONE='+00:00' */; 13 | /*!40014 SET @OLD_UNIQUE_CHECKS=@@UNIQUE_CHECKS, UNIQUE_CHECKS=0 */; 14 | /*!40014 SET @OLD_FOREIGN_KEY_CHECKS=@@FOREIGN_KEY_CHECKS, FOREIGN_KEY_CHECKS=0 */; 15 | /*!40101 SET @OLD_SQL_MODE=@@SQL_MODE, SQL_MODE='NO_AUTO_VALUE_ON_ZERO' */; 16 | /*!40111 SET @OLD_SQL_NOTES=@@SQL_NOTES, SQL_NOTES=0 */; 17 | 18 | -- 19 | -- Table structure for table `auth_group` 20 | -- 21 | 22 | DROP TABLE IF EXISTS `auth_group`; 23 | /*!40101 SET @saved_cs_client = @@character_set_client */; 24 | /*!40101 SET character_set_client = utf8 */; 25 | CREATE TABLE `auth_group` ( 26 | `id` int(11) NOT NULL AUTO_INCREMENT, 27 | `name` varchar(80) NOT NULL, 28 | PRIMARY KEY (`id`), 29 | UNIQUE KEY `name` (`name`) 30 | ) ENGINE=MyISAM DEFAULT CHARSET=utf8; 31 | /*!40101 SET character_set_client = @saved_cs_client */; 32 | 33 | -- 34 | -- Table structure for table `auth_group_permissions` 35 | -- 36 | 37 | DROP TABLE IF EXISTS `auth_group_permissions`; 38 | /*!40101 SET @saved_cs_client = @@character_set_client */; 39 | /*!40101 SET character_set_client = utf8 */; 40 | CREATE TABLE `auth_group_permissions` ( 41 | `id` int(11) NOT NULL AUTO_INCREMENT, 42 | `group_id` int(11) NOT NULL, 43 | `permission_id` int(11) NOT NULL, 44 | PRIMARY KEY (`id`), 45 | UNIQUE KEY `group_id` (`group_id`,`permission_id`), 46 | KEY `auth_group_permissions_5f412f9a` (`group_id`), 47 | KEY `auth_group_permissions_83d7f98b` (`permission_id`) 48 | ) ENGINE=MyISAM DEFAULT CHARSET=utf8; 49 | /*!40101 SET character_set_client = @saved_cs_client */; 50 | 51 | -- 52 | -- Table structure for table `auth_permission` 53 | -- 54 | 55 | DROP TABLE IF EXISTS `auth_permission`; 56 | /*!40101 SET @saved_cs_client = @@character_set_client */; 57 | /*!40101 SET character_set_client = utf8 */; 58 | CREATE TABLE `auth_permission` ( 59 | `id` int(11) NOT NULL AUTO_INCREMENT, 60 | `name` varchar(50) NOT NULL, 61 | `content_type_id` int(11) NOT NULL, 62 | `codename` varchar(100) NOT NULL, 63 | PRIMARY KEY (`id`), 64 | UNIQUE KEY `content_type_id` (`content_type_id`,`codename`), 65 | KEY `auth_permission_37ef4eb4` (`content_type_id`) 66 | ) ENGINE=MyISAM AUTO_INCREMENT=25 DEFAULT CHARSET=utf8; 67 | /*!40101 SET character_set_client = @saved_cs_client */; 68 | 69 | -- 70 | -- Table structure for table `auth_user` 71 | -- 72 | 73 | DROP TABLE IF EXISTS `auth_user`; 74 | /*!40101 SET @saved_cs_client = @@character_set_client */; 75 | /*!40101 SET character_set_client = utf8 */; 76 | CREATE TABLE `auth_user` ( 77 | `id` int(11) NOT NULL AUTO_INCREMENT, 78 | `password` varchar(128) NOT NULL, 79 | `last_login` datetime NOT NULL, 80 | `is_superuser` tinyint(1) NOT NULL, 81 | `username` varchar(30) NOT NULL, 82 | `first_name` varchar(30) NOT NULL, 83 | `last_name` varchar(30) NOT NULL, 84 | `email` varchar(75) NOT NULL, 85 | `is_staff` tinyint(1) NOT NULL, 86 | `is_active` tinyint(1) NOT NULL, 87 | `date_joined` datetime NOT NULL, 88 | PRIMARY KEY (`id`), 89 | UNIQUE KEY `username` (`username`) 90 | ) ENGINE=MyISAM DEFAULT CHARSET=utf8; 91 | /*!40101 SET character_set_client = @saved_cs_client */; 92 | 93 | -- 94 | -- Table structure for table `auth_user_groups` 95 | -- 96 | 97 | DROP TABLE IF EXISTS `auth_user_groups`; 98 | /*!40101 SET @saved_cs_client = @@character_set_client */; 99 | /*!40101 SET character_set_client = utf8 */; 100 | CREATE TABLE `auth_user_groups` ( 101 | `id` int(11) NOT NULL AUTO_INCREMENT, 102 | `user_id` int(11) NOT NULL, 103 | `group_id` int(11) NOT NULL, 104 | PRIMARY KEY (`id`), 105 | UNIQUE KEY `user_id` (`user_id`,`group_id`), 106 | KEY `auth_user_groups_6340c63c` (`user_id`), 107 | KEY `auth_user_groups_5f412f9a` (`group_id`) 108 | ) ENGINE=MyISAM DEFAULT CHARSET=utf8; 109 | /*!40101 SET character_set_client = @saved_cs_client */; 110 | 111 | -- 112 | -- Table structure for table `auth_user_user_permissions` 113 | -- 114 | 115 | DROP TABLE IF EXISTS `auth_user_user_permissions`; 116 | /*!40101 SET @saved_cs_client = @@character_set_client */; 117 | /*!40101 SET character_set_client = utf8 */; 118 | CREATE TABLE `auth_user_user_permissions` ( 119 | `id` int(11) NOT NULL AUTO_INCREMENT, 120 | `user_id` int(11) NOT NULL, 121 | `permission_id` int(11) NOT NULL, 122 | PRIMARY KEY (`id`), 123 | UNIQUE KEY `user_id` (`user_id`,`permission_id`), 124 | KEY `auth_user_user_permissions_6340c63c` (`user_id`), 125 | KEY `auth_user_user_permissions_83d7f98b` (`permission_id`) 126 | ) ENGINE=MyISAM DEFAULT CHARSET=utf8; 127 | /*!40101 SET character_set_client = @saved_cs_client */; 128 | 129 | -- 130 | -- Table structure for table `django_content_type` 131 | -- 132 | 133 | DROP TABLE IF EXISTS `django_content_type`; 134 | /*!40101 SET @saved_cs_client = @@character_set_client */; 135 | /*!40101 SET character_set_client = utf8 */; 136 | CREATE TABLE `django_content_type` ( 137 | `id` int(11) NOT NULL AUTO_INCREMENT, 138 | `name` varchar(100) NOT NULL, 139 | `app_label` varchar(100) NOT NULL, 140 | `model` varchar(100) NOT NULL, 141 | PRIMARY KEY (`id`), 142 | UNIQUE KEY `app_label` (`app_label`,`model`) 143 | ) ENGINE=MyISAM AUTO_INCREMENT=9 DEFAULT CHARSET=utf8; 144 | /*!40101 SET character_set_client = @saved_cs_client */; 145 | 146 | -- 147 | -- Table structure for table `django_session` 148 | -- 149 | 150 | DROP TABLE IF EXISTS `django_session`; 151 | /*!40101 SET @saved_cs_client = @@character_set_client */; 152 | /*!40101 SET character_set_client = utf8 */; 153 | CREATE TABLE `django_session` ( 154 | `session_key` varchar(40) NOT NULL, 155 | `session_data` longtext NOT NULL, 156 | `expire_date` datetime NOT NULL, 157 | PRIMARY KEY (`session_key`), 158 | KEY `django_session_b7b81f0c` (`expire_date`) 159 | ) ENGINE=MyISAM DEFAULT CHARSET=utf8; 160 | /*!40101 SET character_set_client = @saved_cs_client */; 161 | 162 | -- 163 | -- Table structure for table `django_site` 164 | -- 165 | 166 | DROP TABLE IF EXISTS `django_site`; 167 | /*!40101 SET @saved_cs_client = @@character_set_client */; 168 | /*!40101 SET character_set_client = utf8 */; 169 | CREATE TABLE `django_site` ( 170 | `id` int(11) NOT NULL AUTO_INCREMENT, 171 | `domain` varchar(100) NOT NULL, 172 | `name` varchar(50) NOT NULL, 173 | PRIMARY KEY (`id`) 174 | ) ENGINE=MyISAM AUTO_INCREMENT=2 DEFAULT CHARSET=utf8; 175 | /*!40101 SET character_set_client = @saved_cs_client */; 176 | 177 | -- 178 | -- Table structure for table `jids` 179 | -- 180 | 181 | DROP TABLE IF EXISTS `jids`; 182 | /*!40101 SET @saved_cs_client = @@character_set_client */; 183 | /*!40101 SET character_set_client = utf8 */; 184 | CREATE TABLE `jids` ( 185 | `jid` varchar(255) NOT NULL, 186 | `load` mediumtext NOT NULL, 187 | UNIQUE KEY `jid` (`jid`) 188 | ) ENGINE=InnoDB DEFAULT CHARSET=utf8; 189 | /*!40101 SET character_set_client = @saved_cs_client */; 190 | 191 | -- 192 | -- Table structure for table `salt_returns` 193 | -- 194 | 195 | DROP TABLE IF EXISTS `salt_returns`; 196 | /*!40101 SET @saved_cs_client = @@character_set_client */; 197 | /*!40101 SET character_set_client = utf8 */; 198 | CREATE TABLE `salt_returns` ( 199 | `fun` varchar(50) NOT NULL, 200 | `jid` varchar(255) NOT NULL, 201 | `return` mediumtext NOT NULL, 202 | `id` varchar(255) NOT NULL, 203 | `success` varchar(10) NOT NULL, 204 | `full_ret` mediumtext NOT NULL, 205 | `time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, 206 | KEY `id` (`id`), 207 | KEY `jid` (`jid`), 208 | KEY `fun` (`fun`) 209 | ) ENGINE=InnoDB DEFAULT CHARSET=utf8; 210 | /*!40101 SET character_set_client = @saved_cs_client */; 211 | 212 | -- 213 | -- Table structure for table `script` 214 | -- 215 | 216 | DROP TABLE IF EXISTS `script`; 217 | /*!40101 SET @saved_cs_client = @@character_set_client */; 218 | /*!40101 SET character_set_client = utf8 */; 219 | CREATE TABLE `script` ( 220 | `id` int(11) NOT NULL AUTO_INCREMENT, 221 | `user_id` varchar(50) NOT NULL, 222 | `name` varchar(30) NOT NULL, 223 | `args` varchar(100) NOT NULL, 224 | `public` varchar(5) NOT NULL, 225 | `status` varchar(10) NOT NULL, 226 | PRIMARY KEY (`id`) 227 | ) ENGINE=MyISAM DEFAULT CHARSET=utf8; 228 | /*!40101 SET character_set_client = @saved_cs_client */; 229 | 230 | -- 231 | -- Table structure for table `service` 232 | -- 233 | 234 | DROP TABLE IF EXISTS `service`; 235 | /*!40101 SET @saved_cs_client = @@character_set_client */; 236 | /*!40101 SET character_set_client = utf8 */; 237 | CREATE TABLE `service` ( 238 | `id` int(11) NOT NULL AUTO_INCREMENT, 239 | `name` varchar(50) NOT NULL, 240 | `target` longtext NOT NULL, 241 | PRIMARY KEY (`id`) 242 | ) ENGINE=MyISAM AUTO_INCREMENT=2 DEFAULT CHARSET=utf8; 243 | /*!40101 SET character_set_client = @saved_cs_client */; 244 | /*!40103 SET TIME_ZONE=@OLD_TIME_ZONE */; 245 | 246 | /*!40101 SET SQL_MODE=@OLD_SQL_MODE */; 247 | /*!40014 SET FOREIGN_KEY_CHECKS=@OLD_FOREIGN_KEY_CHECKS */; 248 | /*!40014 SET UNIQUE_CHECKS=@OLD_UNIQUE_CHECKS */; 249 | /*!40101 SET CHARACTER_SET_CLIENT=@OLD_CHARACTER_SET_CLIENT */; 250 | /*!40101 SET CHARACTER_SET_RESULTS=@OLD_CHARACTER_SET_RESULTS */; 251 | /*!40101 SET COLLATION_CONNECTION=@OLD_COLLATION_CONNECTION */; 252 | /*!40111 SET SQL_NOTES=@OLD_SQL_NOTES */; 253 | 254 | -- Dump completed on 2013-09-03 21:52:14 255 | -------------------------------------------------------------------------------- /static/bootstrap/css/docs.css: -------------------------------------------------------------------------------- 1 | 2 | body { 3 | background-color: #FFFFFF; 4 | background-image: url("../img/grid-18px-masked.png"); 5 | background-position: 0 40px; 6 | background-repeat: repeat-x; 7 | padding-top: 90px; 8 | position: relative; 9 | } 10 | .navbar-fixed-top .brand { 11 | -moz-transition: all 0.2s linear 0s; 12 | color: #000000; 13 | float: right; 14 | font-weight: bold; 15 | margin-left: 20px; 16 | padding-left: 0; 17 | padding-right: 0; 18 | text-shadow: 0 1px 0 rgba(255, 255, 255, 0.1), 0 0 30px rgba(255, 255, 255, 0.125); 19 | } 20 | .navbar-fixed-top .brand:hover { 21 | text-decoration: none; 22 | } 23 | section { 24 | padding-top: 60px; 25 | } 26 | hr.soften { 27 | background-image: -moz-linear-gradient(left center , transparent, rgba(0, 0, 0, 0.1), transparent); 28 | border: 0 none; 29 | height: 1px; 30 | margin: 54px 0; 31 | } 32 | .jumbotron { 33 | position: relative; 34 | } 35 | .jumbotron h1 { 36 | font-size: 81px; 37 | font-weight: bold; 38 | letter-spacing: -1px; 39 | line-height: 1; 40 | margin-bottom: 9px; 41 | } 42 | .jumbotron p { 43 | font-weight: 300; 44 | margin-bottom: 18px; 45 | } 46 | .jumbotron .btn-large { 47 | border-radius: 6px 6px 6px 6px; 48 | font-size: 20px; 49 | font-weight: normal; 50 | margin-right: 10px; 51 | padding: 14px 24px; 52 | } 53 | .jumbotron .btn-large small { 54 | font-size: 14px; 55 | } 56 | .masthead { 57 | margin-bottom: 72px; 58 | padding-top: 36px; 59 | } 60 | .masthead h1, .masthead p { 61 | text-align: center; 62 | } 63 | .masthead h1 { 64 | margin-bottom: 18px; 65 | } 66 | .masthead p { 67 | font-size: 30px; 68 | line-height: 36px; 69 | margin-left: 5%; 70 | margin-right: 5%; 71 | } 72 | .subhead { 73 | margin-bottom: 9px; 74 | padding-bottom: 0; 75 | } 76 | .subhead h1 { 77 | font-size: 54px; 78 | } 79 | .subnav { 80 | background-color: #EEEEEE; 81 | background-image: -moz-linear-gradient(center top , #F5F5F5 0%, #EEEEEE 100%); 82 | background-repeat: repeat-x; 83 | border: 1px solid #E5E5E5; 84 | border-radius: 4px 4px 4px 4px; 85 | height: 36px; 86 | width: 100%; 87 | } 88 | .subnav .nav { 89 | margin-bottom: 0; 90 | } 91 | .subnav .nav > li > a { 92 | border-left: 1px solid #F5F5F5; 93 | border-radius: 0 0 0 0; 94 | border-right: 1px solid #E5E5E5; 95 | margin: 0; 96 | padding-bottom: 11px; 97 | padding-top: 11px; 98 | } 99 | .subnav .nav > .active > a, .subnav .nav > .active > a:hover { 100 | background-color: #E9E9E9; 101 | border-left: 0 none; 102 | border-right-color: #DDDDDD; 103 | box-shadow: 0 3px 5px rgba(0, 0, 0, 0.05) inset; 104 | color: #777777; 105 | padding-left: 13px; 106 | } 107 | .subnav .nav > .active > a .caret, .subnav .nav > .active > a:hover .caret { 108 | border-top-color: #777777; 109 | } 110 | .subnav .nav > li:first-child > a, .subnav .nav > li:first-child > a:hover { 111 | border-left: 0 none; 112 | border-radius: 4px 0 0 4px; 113 | padding-left: 12px; 114 | } 115 | .subnav .nav > li:last-child > a { 116 | border-right: 0 none; 117 | } 118 | .subnav .dropdown-menu { 119 | border-radius: 0 0 4px 4px; 120 | } 121 | .subnav-fixed { 122 | border-color: #D5D5D5; 123 | border-radius: 0 0 0 0; 124 | border-width: 0 0 1px; 125 | box-shadow: 0 1px 0 #FFFFFF inset, 0 1px 5px rgba(0, 0, 0, 0.1); 126 | left: 0; 127 | position: fixed; 128 | right: 0; 129 | top: 40px; 130 | z-index: 1020; 131 | } 132 | .subnav-fixed .nav { 133 | margin: 0 auto; 134 | padding: 0 1px; 135 | width: 938px; 136 | } 137 | .subnav .nav > li:first-child > a, .subnav .nav > li:first-child > a:hover { 138 | border-radius: 0 0 0 0; 139 | } 140 | .bs-links { 141 | margin: 36px 0; 142 | } 143 | .quick-links { 144 | list-style: none outside none; 145 | margin: 0; 146 | min-height: 30px; 147 | overflow: hidden; 148 | padding: 5px 20px; 149 | text-align: center; 150 | } 151 | .quick-links:first-child { 152 | min-height: 0; 153 | } 154 | .quick-links li { 155 | color: #999999; 156 | display: inline; 157 | margin: 0 5px; 158 | } 159 | .quick-links .github-btn, .quick-links .tweet-btn, .quick-links .follow-btn { 160 | position: relative; 161 | top: 5px; 162 | } 163 | .marketing .row { 164 | margin-bottom: 9px; 165 | } 166 | .marketing h1 { 167 | font-size: 40px; 168 | font-weight: 300; 169 | margin: 36px 0 27px; 170 | text-align: center; 171 | } 172 | .marketing h2, .marketing h3 { 173 | font-weight: 300; 174 | } 175 | .marketing h2 { 176 | font-size: 22px; 177 | } 178 | .marketing p { 179 | margin-right: 10px; 180 | } 181 | .marketing .bs-icon { 182 | float: left; 183 | margin: 7px 10px 0 0; 184 | opacity: 0.8; 185 | } 186 | .marketing .small-bs-icon { 187 | float: left; 188 | margin: 4px 5px 0 0; 189 | } 190 | .footer { 191 | border-top: 1px solid #E5E5E5; 192 | margin-top: 45px; 193 | padding: 35px 0 36px; 194 | } 195 | .footer p { 196 | color: #555555; 197 | margin-bottom: 0; 198 | } 199 | .show-grid { 200 | margin-bottom: 20px; 201 | margin-top: 10px; 202 | } 203 | .show-grid [class*="span"] { 204 | background-color: #EEEEEE; 205 | border-radius: 3px 3px 3px 3px; 206 | line-height: 30px; 207 | min-height: 30px; 208 | text-align: center; 209 | } 210 | .show-grid:hover [class*="span"] { 211 | background: none repeat scroll 0 0 #DDDDDD; 212 | } 213 | .show-grid .show-grid { 214 | margin-bottom: 0; 215 | margin-top: 0; 216 | } 217 | .show-grid .show-grid [class*="span"] { 218 | background-color: #CCCCCC; 219 | } 220 | .mini-layout { 221 | border: 1px solid #DDDDDD; 222 | border-radius: 6px 6px 6px 6px; 223 | box-shadow: 0 1px 2px rgba(0, 0, 0, 0.075); 224 | } 225 | .mini-layout { 226 | height: 240px; 227 | margin-bottom: 20px; 228 | padding: 9px; 229 | } 230 | .mini-layout div { 231 | border-radius: 3px 3px 3px 3px; 232 | } 233 | .mini-layout .mini-layout-body { 234 | background-color: #DCEAF4; 235 | height: 240px; 236 | margin: 0 auto; 237 | width: 70%; 238 | } 239 | .mini-layout.fluid .mini-layout-sidebar, .mini-layout.fluid .mini-layout-header, .mini-layout.fluid .mini-layout-body { 240 | float: left; 241 | } 242 | .mini-layout.fluid .mini-layout-sidebar { 243 | background-color: #BBD8E9; 244 | height: 240px; 245 | width: 20%; 246 | } 247 | .mini-layout.fluid .mini-layout-body { 248 | margin-left: 2.5%; 249 | width: 77.5%; 250 | } 251 | .popover-well { 252 | min-height: 160px; 253 | } 254 | .popover-well .popover { 255 | display: block; 256 | } 257 | .popover-well .popover-wrapper { 258 | float: left; 259 | height: 160px; 260 | margin-left: 55px; 261 | position: relative; 262 | width: 50%; 263 | } 264 | .popover-well .popover-menu-wrapper { 265 | height: 80px; 266 | } 267 | .large-bird { 268 | margin: 5px 0 0 310px; 269 | opacity: 0.1; 270 | } 271 | .download .page-header { 272 | margin-top: 36px; 273 | } 274 | .page-header .toggle-all { 275 | margin-top: 5px; 276 | } 277 | .download h3 { 278 | margin-bottom: 5px; 279 | } 280 | .download-builder input + h3, .download-builder .checkbox + h3 { 281 | margin-top: 9px; 282 | } 283 | .download-builder input[type="text"] { 284 | color: #DD1144; 285 | font-family: Menlo,Monaco,"Courier New",monospace; 286 | font-size: 12px; 287 | margin-bottom: 9px; 288 | } 289 | .download-builder input[type="text"]:focus { 290 | background-color: #FFFFFF; 291 | } 292 | .download .checkbox { 293 | background-color: #F9F9F9; 294 | border-radius: 3px 3px 3px 3px; 295 | color: #555555; 296 | cursor: pointer; 297 | padding: 6px 10px 6px 25px; 298 | } 299 | .download .checkbox:hover { 300 | background-color: #F5F5F5; 301 | color: #333333; 302 | } 303 | .download .checkbox small { 304 | color: #777777; 305 | font-size: 12px; 306 | } 307 | #variables label { 308 | margin-bottom: 0; 309 | } 310 | .download-btn { 311 | margin: 36px 0 108px; 312 | } 313 | #download p, #download h4 { 314 | color: #999999; 315 | margin: 0 auto; 316 | max-width: 50%; 317 | text-align: center; 318 | } 319 | #download h4 { 320 | margin-bottom: 0; 321 | } 322 | #download p { 323 | margin-bottom: 18px; 324 | } 325 | .download-btn .btn { 326 | border-radius: 6px 6px 6px 6px; 327 | display: block; 328 | font-size: 30px; 329 | line-height: 1; 330 | margin-bottom: 27px; 331 | padding: 19px 24px; 332 | text-align: center; 333 | width: auto; 334 | } 335 | .swatch-col { 336 | width: 30px; 337 | } 338 | .swatch { 339 | border-radius: 3px 3px 3px 3px; 340 | display: inline-block; 341 | height: 20px; 342 | margin: -6px 0; 343 | width: 30px; 344 | } 345 | .swatch-bordered { 346 | border: 1px solid #EEEEEE; 347 | height: 18px; 348 | width: 28px; 349 | } 350 | img { 351 | max-width: 100%; 352 | } 353 | h2 + table, h3 + table, h4 + table, h2 + .row { 354 | margin-top: 5px; 355 | } 356 | .example-sites img { 357 | margin: 0 auto; 358 | max-width: 100%; 359 | } 360 | .marketing-byline { 361 | color: #999999; 362 | font-size: 18px; 363 | font-weight: 300; 364 | line-height: 24px; 365 | margin: -18px 0 27px; 366 | text-align: center; 367 | } 368 | .scrollspy-example { 369 | height: 200px; 370 | overflow: auto; 371 | position: relative; 372 | } 373 | form.well { 374 | padding: 14px; 375 | } 376 | .well hr { 377 | margin: 18px 0; 378 | } 379 | .focused { 380 | border-color: rgba(82, 168, 236, 0.8); 381 | box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1) inset, 0 0 8px rgba(82, 168, 236, 0.6); 382 | outline: 0 none; 383 | } 384 | .docs-input-sizes select, .docs-input-sizes input[type="text"] { 385 | display: block; 386 | margin-bottom: 9px; 387 | } 388 | .the-icons { 389 | list-style: none outside none; 390 | margin-left: 0; 391 | } 392 | .the-icons i:hover { 393 | background-color: rgba(255, 0, 0, 0.25); 394 | } 395 | .bootstrap-examples .thumbnail { 396 | background-color: #FFFFFF; 397 | margin-bottom: 9px; 398 | } 399 | .responsive-utilities th small { 400 | color: #999999; 401 | display: block; 402 | font-weight: normal; 403 | } 404 | .responsive-utilities tbody th { 405 | font-weight: normal; 406 | } 407 | .responsive-utilities td { 408 | text-align: center; 409 | } 410 | .responsive-utilities td.is-visible { 411 | background-color: #DFF0D8 !important; 412 | color: #468847; 413 | } 414 | .responsive-utilities td.is-hidden { 415 | background-color: #F9F9F9 !important; 416 | color: #CCCCCC; 417 | } 418 | .responsive-utilities-test { 419 | list-style: none outside none; 420 | margin-left: 0; 421 | margin-top: 5px; 422 | overflow: hidden; 423 | } 424 | .responsive-utilities-test li { 425 | border: 1px solid #DDDDDD; 426 | border-radius: 4px 4px 4px 4px; 427 | color: #999999; 428 | float: left; 429 | font-size: 14px; 430 | font-weight: bold; 431 | height: 43px; 432 | line-height: 43px; 433 | position: relative; 434 | text-align: center; 435 | width: 25%; 436 | } 437 | .responsive-utilities-test li + li { 438 | margin-left: 10px; 439 | } 440 | .responsive-utilities-test span { 441 | border-radius: 4px 4px 4px 4px; 442 | bottom: -1px; 443 | left: -1px; 444 | position: absolute; 445 | right: -1px; 446 | top: -1px; 447 | } 448 | .responsive-utilities-test span { 449 | background-color: #DFF0D8; 450 | border: 1px solid #D6E9C6; 451 | color: #468847; 452 | } 453 | body { 454 | padding-top: 70px; 455 | } 456 | h2 { 457 | margin-top: 27px; 458 | } 459 | h2 small { 460 | display: block; 461 | line-height: 18px; 462 | } 463 | h3 { 464 | margin-top: 18px; 465 | } 466 | .jumbotron h1, .jumbotron p { 467 | margin-right: 0; 468 | text-align: center; 469 | } 470 | .jumbotron h1 { 471 | font-size: 45px; 472 | margin-right: 0; 473 | } 474 | .jumbotron p { 475 | font-size: 18px; 476 | line-height: 24px; 477 | margin-left: 0; 478 | margin-right: 0; 479 | } 480 | .jumbotron .btn { 481 | display: block; 482 | font-size: 18px; 483 | margin: 0 auto 10px; 484 | padding: 10px 14px; 485 | } 486 | .masthead { 487 | padding-top: 0; 488 | } 489 | .quick-links { 490 | margin: 40px 0 0; 491 | } 492 | .quick-links .divider { 493 | display: none; 494 | } 495 | .example-sites { 496 | margin-left: 0; 497 | } 498 | .example-sites > li { 499 | display: block; 500 | float: none; 501 | margin: 0 auto 18px; 502 | max-width: 280px; 503 | text-align: center; 504 | } 505 | .example-sites .thumbnail > img { 506 | max-width: 270px; 507 | } 508 | table code { 509 | white-space: normal; 510 | word-wrap: break-word; 511 | } 512 | .modal-example .modal { 513 | bottom: auto; 514 | left: auto; 515 | position: relative; 516 | right: auto; 517 | top: auto; 518 | } 519 | body { 520 | padding-top: 0; 521 | } 522 | .jumbotron .btn { 523 | margin-bottom: 10px; 524 | } 525 | .subnav { 526 | background: none repeat scroll 0 0 #FFFFFF; 527 | box-shadow: none; 528 | height: auto; 529 | position: static; 530 | top: auto; 531 | width: auto; 532 | z-index: auto; 533 | } 534 | .subnav .nav > li { 535 | float: none; 536 | } 537 | .subnav .nav > li > a { 538 | border: 0 none; 539 | } 540 | .subnav .nav > li + li > a { 541 | border-top: 1px solid #E5E5E5; 542 | } 543 | .subnav .nav > li:first-child > a, .subnav .nav > li:first-child > a:hover { 544 | border-radius: 4px 4px 0 0; 545 | } 546 | .large-bird { 547 | display: none; 548 | } 549 | .popover-well .popover-wrapper { 550 | margin-left: 0; 551 | } 552 | .show-grid [class*="span"] { 553 | margin-bottom: 5px; 554 | } 555 | .footer .pull-right { 556 | float: none; 557 | } 558 | .footer p { 559 | margin-bottom: 9px; 560 | } 561 | .jumbotron h1 { 562 | font-size: 54px; 563 | } 564 | .jumbotron p { 565 | margin-left: 0; 566 | margin-right: 0; 567 | } 568 | body { 569 | padding-top: 0; 570 | } 571 | .jumbotron h1 { 572 | font-size: 72px; 573 | } 574 | .navbar-fixed-top .brand { 575 | float: left; 576 | margin-left: 0; 577 | padding-left: 10px; 578 | padding-right: 10px; 579 | } 580 | .quick-links li { 581 | display: inline-block; 582 | margin: 5px; 583 | } 584 | .subnav-fixed .nav { 585 | width: 1168px; 586 | } 587 | -------------------------------------------------------------------------------- /static/bootstrap/js/bootstrap.min.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Bootstrap.js by @fat & @mdo 3 | * Copyright 2012 Twitter, Inc. 4 | * http://www.apache.org/licenses/LICENSE-2.0.txt 5 | */ 6 | !function(a){a(function(){"use strict",a.support.transition=function(){var b=document.body||document.documentElement,c=b.style,d=c.transition!==undefined||c.WebkitTransition!==undefined||c.MozTransition!==undefined||c.MsTransition!==undefined||c.OTransition!==undefined;return d&&{end:function(){var b="TransitionEnd";return a.browser.webkit?b="webkitTransitionEnd":a.browser.mozilla?b="transitionend":a.browser.opera&&(b="oTransitionEnd"),b}()}}()})}(window.jQuery),!function(a){"use strict";var b='[data-dismiss="alert"]',c=function(c){a(c).on("click",b,this.close)};c.prototype={constructor:c,close:function(b){function f(){e.trigger("closed").remove()}var c=a(this),d=c.attr("data-target"),e;d||(d=c.attr("href"),d=d&&d.replace(/.*(?=#[^\s]*$)/,"")),e=a(d),e.trigger("close"),b&&b.preventDefault(),e.length||(e=c.hasClass("alert")?c:c.parent()),e.trigger("close").removeClass("in"),a.support.transition&&e.hasClass("fade")?e.on(a.support.transition.end,f):f()}},a.fn.alert=function(b){return this.each(function(){var d=a(this),e=d.data("alert");e||d.data("alert",e=new c(this)),typeof b=="string"&&e[b].call(d)})},a.fn.alert.Constructor=c,a(function(){a("body").on("click.alert.data-api",b,c.prototype.close)})}(window.jQuery),!function(a){"use strict";var b=function(b,c){this.$element=a(b),this.options=a.extend({},a.fn.button.defaults,c)};b.prototype={constructor:b,setState:function(a){var b="disabled",c=this.$element,d=c.data(),e=c.is("input")?"val":"html";a+="Text",d.resetText||c.data("resetText",c[e]()),c[e](d[a]||this.options[a]),setTimeout(function(){a=="loadingText"?c.addClass(b).attr(b,b):c.removeClass(b).removeAttr(b)},0)},toggle:function(){var a=this.$element.parent('[data-toggle="buttons-radio"]');a&&a.find(".active").removeClass("active"),this.$element.toggleClass("active")}},a.fn.button=function(c){return this.each(function(){var d=a(this),e=d.data("button"),f=typeof c=="object"&&c;e||d.data("button",e=new b(this,f)),c=="toggle"?e.toggle():c&&e.setState(c)})},a.fn.button.defaults={loadingText:"loading..."},a.fn.button.Constructor=b,a(function(){a("body").on("click.button.data-api","[data-toggle^=button]",function(b){var c=a(b.target);c.hasClass("btn")||(c=c.closest(".btn")),c.button("toggle")})})}(window.jQuery),!function(a){"use strict";var b=function(b,c){this.$element=a(b),this.options=a.extend({},a.fn.carousel.defaults,c),this.options.slide&&this.slide(this.options.slide),this.options.pause=="hover"&&this.$element.on("mouseenter",a.proxy(this.pause,this)).on("mouseleave",a.proxy(this.cycle,this))};b.prototype={cycle:function(){return this.interval=setInterval(a.proxy(this.next,this),this.options.interval),this},to:function(b){var c=this.$element.find(".active"),d=c.parent().children(),e=d.index(c),f=this;if(b>d.length-1||b<0)return;return this.sliding?this.$element.one("slid",function(){f.to(b)}):e==b?this.pause().cycle():this.slide(b>e?"next":"prev",a(d[b]))},pause:function(){return clearInterval(this.interval),this.interval=null,this},next:function(){if(this.sliding)return;return this.slide("next")},prev:function(){if(this.sliding)return;return this.slide("prev")},slide:function(b,c){var d=this.$element.find(".active"),e=c||d[b](),f=this.interval,g=b=="next"?"left":"right",h=b=="next"?"first":"last",i=this;this.sliding=!0,f&&this.pause(),e=e.length?e:this.$element.find(".item")[h]();if(e.hasClass("active"))return;return!a.support.transition&&this.$element.hasClass("slide")?(this.$element.trigger("slide"),d.removeClass("active"),e.addClass("active"),this.sliding=!1,this.$element.trigger("slid")):(e.addClass(b),e[0].offsetWidth,d.addClass(g),e.addClass(g),this.$element.trigger("slide"),this.$element.one(a.support.transition.end,function(){e.removeClass([b,g].join(" ")).addClass("active"),d.removeClass(["active",g].join(" ")),i.sliding=!1,setTimeout(function(){i.$element.trigger("slid")},0)})),f&&this.cycle(),this}},a.fn.carousel=function(c){return this.each(function(){var d=a(this),e=d.data("carousel"),f=typeof c=="object"&&c;e||d.data("carousel",e=new b(this,f)),typeof c=="number"?e.to(c):typeof c=="string"||(c=f.slide)?e[c]():e.cycle()})},a.fn.carousel.defaults={interval:5e3,pause:"hover"},a.fn.carousel.Constructor=b,a(function(){a("body").on("click.carousel.data-api","[data-slide]",function(b){var c=a(this),d,e=a(c.attr("data-target")||(d=c.attr("href"))&&d.replace(/.*(?=#[^\s]+$)/,"")),f=!e.data("modal")&&a.extend({},e.data(),c.data());e.carousel(f),b.preventDefault()})})}(window.jQuery),!function(a){"use strict";var b=function(b,c){this.$element=a(b),this.options=a.extend({},a.fn.collapse.defaults,c),this.options.parent&&(this.$parent=a(this.options.parent)),this.options.toggle&&this.toggle()};b.prototype={constructor:b,dimension:function(){var a=this.$element.hasClass("width");return a?"width":"height"},show:function(){var b=this.dimension(),c=a.camelCase(["scroll",b].join("-")),d=this.$parent&&this.$parent.find(".in"),e;d&&d.length&&(e=d.data("collapse"),d.collapse("hide"),e||d.data("collapse",null)),this.$element[b](0),this.transition("addClass","show","shown"),this.$element[b](this.$element[0][c])},hide:function(){var a=this.dimension();this.reset(this.$element[a]()),this.transition("removeClass","hide","hidden"),this.$element[a](0)},reset:function(a){var b=this.dimension();return this.$element.removeClass("collapse")[b](a||"auto")[0].offsetWidth,this.$element[a?"addClass":"removeClass"]("collapse"),this},transition:function(b,c,d){var e=this,f=function(){c=="show"&&e.reset(),e.$element.trigger(d)};this.$element.trigger(c)[b]("in"),a.support.transition&&this.$element.hasClass("collapse")?this.$element.one(a.support.transition.end,f):f()},toggle:function(){this[this.$element.hasClass("in")?"hide":"show"]()}},a.fn.collapse=function(c){return this.each(function(){var d=a(this),e=d.data("collapse"),f=typeof c=="object"&&c;e||d.data("collapse",e=new b(this,f)),typeof c=="string"&&e[c]()})},a.fn.collapse.defaults={toggle:!0},a.fn.collapse.Constructor=b,a(function(){a("body").on("click.collapse.data-api","[data-toggle=collapse]",function(b){var c=a(this),d,e=c.attr("data-target")||b.preventDefault()||(d=c.attr("href"))&&d.replace(/.*(?=#[^\s]+$)/,""),f=a(e).data("collapse")?"toggle":c.data();a(e).collapse(f)})})}(window.jQuery),!function(a){function d(){a(b).parent().removeClass("open")}"use strict";var b='[data-toggle="dropdown"]',c=function(b){var c=a(b).on("click.dropdown.data-api",this.toggle);a("html").on("click.dropdown.data-api",function(){c.parent().removeClass("open")})};c.prototype={constructor:c,toggle:function(b){var c=a(this),e=c.attr("data-target"),f,g;return e||(e=c.attr("href"),e=e&&e.replace(/.*(?=#[^\s]*$)/,"")),f=a(e),f.length||(f=c.parent()),g=f.hasClass("open"),d(),!g&&f.toggleClass("open"),!1}},a.fn.dropdown=function(b){return this.each(function(){var d=a(this),e=d.data("dropdown");e||d.data("dropdown",e=new c(this)),typeof b=="string"&&e[b].call(d)})},a.fn.dropdown.Constructor=c,a(function(){a("html").on("click.dropdown.data-api",d),a("body").on("click.dropdown.data-api",b,c.prototype.toggle)})}(window.jQuery),!function(a){function c(){var b=this,c=setTimeout(function(){b.$element.off(a.support.transition.end),d.call(b)},500);this.$element.one(a.support.transition.end,function(){clearTimeout(c),d.call(b)})}function d(a){this.$element.hide().trigger("hidden"),e.call(this)}function e(b){var c=this,d=this.$element.hasClass("fade")?"fade":"";if(this.isShown&&this.options.backdrop){var e=a.support.transition&&d;this.$backdrop=a('