diff --git a/doc/conf.py b/doc/conf.py index 59ebc8a0..4cc5ee72 100644 --- a/doc/conf.py +++ b/doc/conf.py @@ -47,7 +47,7 @@ # General information about the project. project = 'Polemarch' -copyright = '2017, VST Consulting' +copyright = '2018, VST Consulting' author = 'VST Consulting' # The version info for the project you're documenting, acts as replacement for @@ -83,35 +83,23 @@ # The theme to use for HTML and HTML Help pages. See the documentation for # a list of builtin themes. # -html_theme = 'alabaster' - +html_theme_path=['.'] +html_theme = 'polemarch-sphinx-theme' # Theme options are theme-specific and customize the look and feel of a theme # further. For a list of options available for each theme, see the # documentation. -# + html_theme_options = { - "description": "Polemarch Documentation", "github_user": "vstconsulting", "github_repo": "polemarch", "github_button": True, "github_banner": True, - "show_powered_by": False, -} - -html_sidebars = { - '**': [ - 'about.html', - 'navigation.html', - 'relations.html', - 'searchbox.html', - 'donate.html', - ] } # Add any paths that contain custom static files (such as style sheets) here, # relative to this directory. They are copied after the builtin static files, # so a file named "default.css" will overwrite the builtin "default.css". -html_static_path = ['_static'] +html_static_path = ['_static', '../polemarch/static'] # -- Options for HTMLHelp output ------------------------------------------ diff --git a/doc/gui.rst b/doc/gui.rst index 5421d8f5..8766ad36 100644 --- a/doc/gui.rst +++ b/doc/gui.rst @@ -1,6 +1,38 @@ GUI workflow ============== +Welcome to Polemarch GUI +------------------------ + +In this section of our documentation we will tell you about Polemarch GUI's opportunities. + +Let's begin with Dashboard page: + +.. image:: gui_png/new-dashboard-1.png + +As you can see, Polemarch GUI provides user with 2 menus: + +* the first one is located in the left sidebar and it is aimed + to provide user with navigation between main system objects, like projects, templates, history records and ect. + +* the second one is located in the top right conner of browser window and it is aimed + to navigate user to API section, to user's page and to logout page. + +Also there are some widgets on content block of Dashboard page. User can customize Dashboard page as he wish. +For example, user can change widgets' position, hide some widgets or collapse them. + +To change widgets' position user should click on 'lock' button. After this button has been clicked, +user is able to change widgets' position by Drag and Drop. + +To collapse or to hide/show some widgets user should click on 'cogwheel' button. After this button has been clicked, +Polemarch opens modal window, where user can activate or collapse some widgets. + +To see all these features in work look at next gif-image: + +.. image:: gui_gif1/new-dashboard-1.gif + + + Before you start ---------------- @@ -8,43 +40,58 @@ Before you can do any job with Polemarch you should create at least one inventory with your servers enumeration and at least one project, because all Polemarch's functions are linked to the project. -Here how you can create inventory and place your hosts and groups there: +Let's start with creation of inventory: -.. image:: gui_gif/create_inventory.gif +.. image:: gui_png/new-inventories_page.png -Also you can import Ansible inventory file: +There are 2 ways of inventory's creation: -.. image:: gui_gif/import_inventory.gif +* the first one is to create inventory manually. To do it user should click on 'Create' button. -Create project. Be aware that your project must have "OK" status, because your -playbooks won't work until Polemarch done synchronization with repository. -If you made everything right, project playbooks will be shown in suggestions -in playbook execution page. +* the second on is to import inventory from text file. To do it user should click on 'Import from file' button. -If you don't have git repository, you can upload tar archive with project files -from any http url. +By inventory's creation, in this case, we understand creation of inventory, which includes at least one group, +which, in it's turn, includes at least one host. In other words, beside inventory user should create host and group. -Also there is special type of project - empty project (MANUAL). In case you -want run only modules without any playbooks or something. Polemarch will -create folder for your project files in `projects_dir` (you can specify -in settings) named as project id (you can see at URL), where you manually -place your project files (like playbooks or something). +To understand it better let's look at next gif-images: -Polemarch will scan project dir root for any .yml file and provide possibility -to run them. So place available playbook targets at root of your Git repository -or tar-archive or folder with your project files. +Here you can see how user can create inventory and place his hosts and groups there manually: -.. image:: gui_gif/create_projects.gif +.. image:: gui_gif1/new-create_inventory_manually.gif -If you update something in your git repository, don't forget to run sync in -Polemarch for pulling your changes. +And here you can see how user can import Ansible inventory file: + +.. image:: gui_gif1/new-import_inventory.gif + +Futher to start your work with Polemarch you should create project. -.. image:: gui_gif/sync_project.gif +There are 3 project types in Polemarch: -Also maybe you want create separate user for yourself and some other members of -your team: +* GIT - Polemarch can sync with your git project. -.. image:: gui_gif/create_user.gif +* TAR - If you don't have git repository, you can upload tar archive with project files + from any http url. + +* MANUAL - Empty project. This kind of project will be appropriate for you, + if you want run only modules without any playbooks or something. Polemarch will + create folder for your project files in `projects_dir` (you can specify it + in settings) named as project id (you can see at URL), where you + place manually your project files (like playbooks or something). + +Let's look at the example of GIT project's creation: + +.. image:: gui_gif1/new-create_git_project.gif + +As you can see at the gif-image above for GIT project +it is possible to choose a branch to what user want to sync. In this example user has synced +his GIT project from 'master' branch to 'other' branch. 'Arrow' icon in the branch input field +shows us, that project will be sync from one branch to another. If there is no 'arrow' icon, it means, +that next time project will be sync to the same branch as you can see it in 'Branch' input field. + +If you update something in your GIT repository, don't forget to run sync in +Polemarch for pulling your changes. + +After your project's status has changed into "OK" you can confidently start working with Polemarch. Execution of playbook and modules --------------------------------- @@ -52,44 +99,65 @@ Execution of playbook and modules Ok, we made all preparations and ready to do some real work. Let's start by executing some command on your servers: -.. image:: gui_gif/quick_run_command.gif +.. image:: gui_gif1/new-run_shell_command.gif -Of course you can run any Ansible modules and any of playbooks in your project. -Also you can cancel your tasks if you want: +As you can see at the gif-image above +when task has stopped running it become possible to clear ansible stdout. -.. image:: gui_gif/run_something.gif +Also you can run any Ansible modules and any of playbooks in your project. -Almost everywhere in Polemarch you can filter your data. Let see for example -how filter your execution history records to find result of needed action: +Polemarch will scan project dir root for any .yml file and provide possibility +to run them. So place available playbook targets at root of your Git repository +or tar-archive or folder with your project files. + +Be aware that your project must have "OK" status, because your +playbooks won't work until Polemarch done synchronization with repository. +If you made everything right, project playbooks will be shown in suggestions +in playbook execution page. + +Let's look at the example of running some playbook, which Polemarch imported from GIT repository +of our project: + +.. image:: gui_gif1/new-running_playbook.gif -.. image:: gui_gif/filter_history.gif Templates --------- If you have many arguments, which you pass to Ansible at every task run (like -extra-vars, forks number ans so on), you can create template for such action +extra-vars, forks number and so on), you can create template for such action to minimize hand work (either module run or playbook): -.. image:: gui_gif/module_template.gif +.. image:: gui_gif1/new-create_template.gif + +Sometimes your need to keep some similar templates, which are different by only several parameters. +In this case template options will be extremly useful for you. In every template you can create +a lot of options which can modify this template by some parameters. Let's look at the example: + +.. image:: gui_gif1/new-create_template_option.gif -.. image:: gui_gif/task_template.gif Also you can backup/share your templates using import/export mechanism: -.. image:: gui_gif/import_template.gif +.. image:: gui_gif1/new-export-import-template.gif Periodic tasks -------------- -If you want to run some actions to run by schedule without any control from -you, it possible with Polemarch. You can create periodic tasks, which runs -every X seconds (interval based). You can even check those tasks using -"Run now" command: +If you want to run some actions by schedule without any control from +you, it is possible with Polemarch. You can create periodic tasks, which runs +every X seconds (interval based): -.. image:: gui_gif/ptask_module_interval.gif +.. image:: gui_gif1/new-create-periodic-task-interval.gif Also you can create periodic tasks with more advancing scheduling options (days of week, hours, month and so on) by using cron-style periodic tasks: -.. image:: gui_gif/ptask_playbook_cron.gif \ No newline at end of file +.. image:: gui_gif1/new-create-periodic-schedule.gif + +Search +------ +Almost everywhere in Polemarch you can filter your data. Let see for example +how to filter your execution history records to find result of needed action: + +.. image:: gui_gif1/new-search2.gif \ No newline at end of file diff --git a/doc/gui_gif1/create-periodic-schedule.gif b/doc/gui_gif1/create-periodic-schedule.gif new file mode 100644 index 00000000..a030ad26 Binary files /dev/null and b/doc/gui_gif1/create-periodic-schedule.gif differ diff --git a/doc/gui_gif1/create-periodic-task-interval.gif b/doc/gui_gif1/create-periodic-task-interval.gif new file mode 100644 index 00000000..d563cc22 Binary files /dev/null and b/doc/gui_gif1/create-periodic-task-interval.gif differ diff --git a/doc/gui_gif1/create_git_project.gif b/doc/gui_gif1/create_git_project.gif new file mode 100644 index 00000000..b79bbfeb Binary files /dev/null and b/doc/gui_gif1/create_git_project.gif differ diff --git a/doc/gui_gif1/create_inventory_manually.gif b/doc/gui_gif1/create_inventory_manually.gif new file mode 100644 index 00000000..58f9c3ac Binary files /dev/null and b/doc/gui_gif1/create_inventory_manually.gif differ diff --git a/doc/gui_gif1/create_template.gif b/doc/gui_gif1/create_template.gif new file mode 100644 index 00000000..c8011a80 Binary files /dev/null and b/doc/gui_gif1/create_template.gif differ diff --git a/doc/gui_gif1/create_template_option.gif b/doc/gui_gif1/create_template_option.gif new file mode 100644 index 00000000..e51700b4 Binary files /dev/null and b/doc/gui_gif1/create_template_option.gif differ diff --git a/doc/gui_gif1/dashboard-1.gif b/doc/gui_gif1/dashboard-1.gif new file mode 100644 index 00000000..7ac7e58c Binary files /dev/null and b/doc/gui_gif1/dashboard-1.gif differ diff --git a/doc/gui_gif1/export-import-template.gif b/doc/gui_gif1/export-import-template.gif new file mode 100644 index 00000000..4e125483 Binary files /dev/null and b/doc/gui_gif1/export-import-template.gif differ diff --git a/doc/gui_gif1/history_search.gif b/doc/gui_gif1/history_search.gif new file mode 100644 index 00000000..4d7e91fc Binary files /dev/null and b/doc/gui_gif1/history_search.gif differ diff --git a/doc/gui_gif1/import_inventory.gif b/doc/gui_gif1/import_inventory.gif new file mode 100644 index 00000000..43cdfca8 Binary files /dev/null and b/doc/gui_gif1/import_inventory.gif differ diff --git a/doc/gui_gif1/new-create-periodic-schedule.gif b/doc/gui_gif1/new-create-periodic-schedule.gif new file mode 100644 index 00000000..59dc28ac Binary files /dev/null and b/doc/gui_gif1/new-create-periodic-schedule.gif differ diff --git a/doc/gui_gif1/new-create-periodic-task-interval.gif b/doc/gui_gif1/new-create-periodic-task-interval.gif new file mode 100644 index 00000000..edfe77b3 Binary files /dev/null and b/doc/gui_gif1/new-create-periodic-task-interval.gif differ diff --git a/doc/gui_gif1/new-create_git_project.gif b/doc/gui_gif1/new-create_git_project.gif new file mode 100644 index 00000000..ab4853c8 Binary files /dev/null and b/doc/gui_gif1/new-create_git_project.gif differ diff --git a/doc/gui_gif1/new-create_inventory_manually.gif b/doc/gui_gif1/new-create_inventory_manually.gif new file mode 100644 index 00000000..cc687fcf Binary files /dev/null and b/doc/gui_gif1/new-create_inventory_manually.gif differ diff --git a/doc/gui_gif1/new-create_template.gif b/doc/gui_gif1/new-create_template.gif new file mode 100644 index 00000000..0b0e59d3 Binary files /dev/null and b/doc/gui_gif1/new-create_template.gif differ diff --git a/doc/gui_gif1/new-create_template_option.gif b/doc/gui_gif1/new-create_template_option.gif new file mode 100644 index 00000000..c956d677 Binary files /dev/null and b/doc/gui_gif1/new-create_template_option.gif differ diff --git a/doc/gui_gif1/new-dashboard-1.gif b/doc/gui_gif1/new-dashboard-1.gif new file mode 100644 index 00000000..c9f81623 Binary files /dev/null and b/doc/gui_gif1/new-dashboard-1.gif differ diff --git a/doc/gui_gif1/new-export-import-template.gif b/doc/gui_gif1/new-export-import-template.gif new file mode 100644 index 00000000..cee302fe Binary files /dev/null and b/doc/gui_gif1/new-export-import-template.gif differ diff --git a/doc/gui_gif1/new-import_inventory.gif b/doc/gui_gif1/new-import_inventory.gif new file mode 100644 index 00000000..1fa2690a Binary files /dev/null and b/doc/gui_gif1/new-import_inventory.gif differ diff --git a/doc/gui_gif1/new-run_shell_command.gif b/doc/gui_gif1/new-run_shell_command.gif new file mode 100644 index 00000000..0682c164 Binary files /dev/null and b/doc/gui_gif1/new-run_shell_command.gif differ diff --git a/doc/gui_gif1/new-running_playbook.gif b/doc/gui_gif1/new-running_playbook.gif new file mode 100644 index 00000000..45720512 Binary files /dev/null and b/doc/gui_gif1/new-running_playbook.gif differ diff --git a/doc/gui_gif1/new-search2.gif b/doc/gui_gif1/new-search2.gif new file mode 100644 index 00000000..1bedd125 Binary files /dev/null and b/doc/gui_gif1/new-search2.gif differ diff --git a/doc/gui_gif1/run_shell_command.gif b/doc/gui_gif1/run_shell_command.gif new file mode 100644 index 00000000..1e0957b6 Binary files /dev/null and b/doc/gui_gif1/run_shell_command.gif differ diff --git a/doc/gui_gif1/running_playbook.gif b/doc/gui_gif1/running_playbook.gif new file mode 100644 index 00000000..d3d654c8 Binary files /dev/null and b/doc/gui_gif1/running_playbook.gif differ diff --git a/doc/gui_gif1/search2.gif b/doc/gui_gif1/search2.gif new file mode 100644 index 00000000..b2c82cb5 Binary files /dev/null and b/doc/gui_gif1/search2.gif differ diff --git a/doc/gui_png/dashboard-1.png b/doc/gui_png/dashboard-1.png new file mode 100644 index 00000000..384a165f Binary files /dev/null and b/doc/gui_png/dashboard-1.png differ diff --git a/doc/gui_png/inventories_page.png b/doc/gui_png/inventories_page.png new file mode 100644 index 00000000..51ddf662 Binary files /dev/null and b/doc/gui_png/inventories_page.png differ diff --git a/doc/gui_png/new-dashboard-1.png b/doc/gui_png/new-dashboard-1.png new file mode 100644 index 00000000..06ec3ba8 Binary files /dev/null and b/doc/gui_png/new-dashboard-1.png differ diff --git a/doc/gui_png/new-inventories_page.png b/doc/gui_png/new-inventories_page.png new file mode 100644 index 00000000..411acce0 Binary files /dev/null and b/doc/gui_png/new-inventories_page.png differ diff --git a/doc/polemarch-sphinx-theme/layout.html b/doc/polemarch-sphinx-theme/layout.html new file mode 100644 index 00000000..36114ed5 --- /dev/null +++ b/doc/polemarch-sphinx-theme/layout.html @@ -0,0 +1,126 @@ + + + +{%- macro script() %} + + {%- for scriptfile in script_files %} + + {%- endfor %} + + + + +{%- endmacro %} + +{%- block extrahead %} + + + + + Polemarch documantation + + + {%- block scripts %} + {{- script() }} + {%- endblock %} +{% endblock %} + + +
+
+
+
+ {% if theme_github_banner|lower != 'false' %} + + Fork me on GitHub + + {% endif %} +
+ + + + +
+ + + + +
+ +
+

Polemarch documentation

+
+
+
+
+ {%- block content %} + {%- block document %} + {% block body %} {% endblock %} + {%- endblock %} + {%- endblock %} +
+
+
+
+ + +
+ + + + + + diff --git a/doc/polemarch-sphinx-theme/search.html b/doc/polemarch-sphinx-theme/search.html new file mode 100644 index 00000000..d26e8948 --- /dev/null +++ b/doc/polemarch-sphinx-theme/search.html @@ -0,0 +1,65 @@ +{# + basic/search.html + ~~~~~~~~~~~~~~~~~ + + Template for the search page. + + :copyright: Copyright 2007-2018 by the Sphinx team, see AUTHORS. + :license: BSD, see LICENSE for details. +#} +{%- extends "layout.html" %} +{% set title = _('Search') %} +{% set script_files = script_files + ['_static/searchtools.js'] %} +{% block extrahead %} + {%- for scriptfile in script_files %} + + {%- endfor %} + + {# this is used when loading the search index using $.ajax fails, + such as on Chrome for documents on localhost #} + + {{ super() }} +{% endblock %} +{% block body %} +

{{ _('Search') }}

+
+ +

+ {% trans %}Please activate JavaScript to enable the search + functionality.{% endtrans %} +

+
+

+ {% trans %}From here you can search these documents. Enter your search + words into the box below and click "search". Note that the search + function will automatically search for all of the words. Pages + containing fewer words won't appear in the result list.{% endtrans %} +

+
+ + + + + + +
+ {% if search_performed %} +

{{ _('Search Results') }}

+ {% if not search_results %} +

{{ _('Your search did not match any documents. Please make sure that all words are spelled correctly and that you\'ve selected enough categories.') }}

+ {% endif %} + {% endif %} +
+ {% if search_results %} + + {% endif %} +
+{% endblock %} \ No newline at end of file diff --git a/doc/polemarch-sphinx-theme/searchbox.html b/doc/polemarch-sphinx-theme/searchbox.html new file mode 100644 index 00000000..1b78f6b5 --- /dev/null +++ b/doc/polemarch-sphinx-theme/searchbox.html @@ -0,0 +1,27 @@ +{# + basic/searchbox.html + ~~~~~~~~~~~~~~~~~~~~ + + Sphinx sidebar template: quick search box. + + :copyright: Copyright 2007-2018 by the Sphinx team, see AUTHORS. + :license: BSD, see LICENSE for details. +#} +{%- if pagename != "search" and builder != "singlehtml" %} + + +{%- endif %} \ No newline at end of file diff --git a/doc/polemarch-sphinx-theme/static/polemarch-style.css b/doc/polemarch-sphinx-theme/static/polemarch-style.css new file mode 100644 index 00000000..e80fa9fd --- /dev/null +++ b/doc/polemarch-sphinx-theme/static/polemarch-style.css @@ -0,0 +1,279 @@ +@import url(bootstrap/css/bootstrap.css); +@import url(bootstrap/css/bootstrap-theme.css); +@import url(css/alt/AdminLTE-bootstrap-social.css); +@import url(css/alt/AdminLTE-fullcalendar.css); +@import url(css/alt/AdminLTE-select2.css); +@import url(css/alt/AdminLTE-without-plugins.css); +@import url(css/AdminLTE.css); +@import url(css/ansi-colors.css); +@import url(css/font-awesome.min.css); +/*@import url(/polemarch/static/css/gui.css);*/ +@import url(css/ionicons.min.css); +@import url(bootstrap/fonts/glyphicons-halflings-regular.eot); +@import url(bootstrap/fonts/glyphicons-halflings-regular.svg); +@import url(bootstrap/fonts/glyphicons-halflings-regular.ttf); +@import url(bootstrap/fonts/glyphicons-halflings-regular.woff); +@import url(bootstrap/fonts/glyphicons-halflings-regular.woff2); + +@import url(css/skins/_all-skins.css); +@import url(css/skins/skin-blue.css); +@import url(css/skins/skin-blue-light.css); + +.sidebar-menu ul { + list-style: none; + margin: 0; + padding:0; + word-wrap: break-word; + overflow-wrap: break-word; +} + +.sidebar-menu ul li { + margin: 0; + padding: 0; +} + +.sidebar-menu ul li a +{ + font-weight: 600; + display:block; + padding-top:7px; + padding-bottom:7px; + padding-left:15px; +} + +.sidebar-menu .caption { + display: none; +} + +.sidebar-menu ul li a:hover +{ + background-color: #f4f4f5; +} + +.sidebar-menu ul li ul { + margin: 0; +} + + +.sidebar-menu ul li ul li a +{ + font-weight: 500; + display:block; + padding-top:7px; + padding-bottom:7px; + padding-left:35px; +} + +.sidebar-menu, +.main-sidebar .user-panel, +.sidebar-menu > li.header { + white-space: normal!important; + overflow: visible!important; +} + +.headerlink { + opacity:0; +} + +.headerlink:hover { + opacity:1; +} + +img { + width: 100%; +} + +@media (min-width: 992px) { + img { + max-width: 650px; + } +} + +.section h1 { + margin-top: 0px; + margin-bottom: 10px; + border-bottom: 1px solid #eee; + padding-bottom: 10px; + font-size: 22px; +} + +.section h2 { + font-size: 22px; +} + +.highlight pre { + background-color: #f7f7f9; +} + +.highlight pre p { + color: #333; +} + +.highlight .mi { + color: #208050; +} + +.highlight .s2 { + /* color: #4070a0;*/ + color: #D14; +} + +.highlight .kc { + color: #000081; +} + +.highlight .nf, +.highlight .na { + font-weight: bold; +} + +.highlight .l, +.highlight .nn, +.highlight .kr, +.highlight .m { + color: #195f91; +} + +.sidebar-collapse .wrapper .main-sidebar .sidebar +{ + opacity:0; +} + +.sidebar h3, .sidebar p { + padding-left:15px; + color: #444444; + font-size: 15px; + font-weight: 600; +} + +.sidebar h3 { + margin-top:20px; +} + +#search-documentation, #search-results h2{ + font-size: 22px; +} + +#search-documentation { + margin-top: 0px; + padding-top: 0px; + padding-bottom:7px; + border-bottom: 1px solid #eee; +} + +#searchbox h3 { + font-weight: 600; + display:block; + padding-left:15px; + color: #444444; + font-size: 15px; +} + +#search-results h2:first-child { + display: none; +} + +.search-input-pm { + float:left; + border:1px solid #d2d6de; + height: 26px; +} + +#big-search-input-pm { + width: 300px; +} + +.search-button-wrapper { + position:relative; + float:left; + height: 26px; + width:30px; + border: 1px solid #d2d6de; + border-left:none; + background-color: #fff; +} + +.search-button-icon { + position:absolute; + left: 7px; + top:5px; +} + +.search-button-input { + height: 26px; + width:30px; + opacity:0; + z-index:10; +} + +form.search { + padding-left:15px; +} + +.bg-logo-doc { + background-image: url(img/logo/horizontal.png); + background-position: center center; + background-repeat: no-repeat; + -webkit-background-size:150px 40px; + -o-background-size: 150px 40px; + -moz-background-size:150px 40px; + background-size: 150px 40px; + display: block; + width:150px; + height: 40px; + margin:0px; + margin-top: 5px; + float: left; + + +} + +.bg-logo-min-doc { + background-image: url(img/logo/logo.png); + background-position: center center; + background-repeat: no-repeat; + -webkit-background-size:40px 40px; + -o-background-size: 40px 40px; + -moz-background-size:40px 40px; + background-size: 40px 40px; + display: block; + width: 40px; + height: 40px; + float: left; + margin:0px; + margin-top: 5px; + margin-left: -10px; +} + +.logo-span-doc { + width: 190px; + margin-right: auto; + margin-left: auto; +} + +.fork-me img { + position: absolute; + top: 0; + right: 0; + border: 0; + width:130px; + height:130px; + z-index:2000; +} + +@media (max-width: 767px) { + .bg-logo-doc { + background-image: url(img/logo/horizontal-bw.png); + } + .bg-logo-min-doc { + background-image: url(img/logo/logo-bw.png); + } + .fork-me img { + width:112px; + height:112px; + } + + #big-search-input-pm { + width: 150px; + } +} diff --git a/doc/polemarch-sphinx-theme/theme.conf b/doc/polemarch-sphinx-theme/theme.conf new file mode 100644 index 00000000..fea96544 --- /dev/null +++ b/doc/polemarch-sphinx-theme/theme.conf @@ -0,0 +1,12 @@ +[theme] +inherit = basic +stylesheet = polemarch-style.css +pygments_style = sphinx + +[options] +github_user = +github_repo = +github_button = true +github_banner = false +github_type = watch +github_count = true \ No newline at end of file diff --git a/doc/quickstart.rst b/doc/quickstart.rst index 56f990d5..9ee87e7c 100644 --- a/doc/quickstart.rst +++ b/doc/quickstart.rst @@ -67,7 +67,8 @@ always configure Polemarch like said in :doc:`Configuration manual `. Backup ------ -Regular uploading backups is a guarantee of the reliability of the application. +Regular uploading of backups is a guarantee of the reliability of the application. +There are several ways of making a backup. The first one is not very reliable, but if you want, you can use it. To upload the data, use the command: .. sourcecode:: bash @@ -80,10 +81,84 @@ To load the saved data, use: sudo -u polemarch /opt/polemarch/bin/polemarchctl loaddata /home/polemarch/backup.json -But more faster backup is SQL backup and projects dir. We strongly recommended this manual backup in production. +The second way is to use SQL backup or to copy you database manually. +We strongly recommend to use this way of making a backup, because +it is faster and more reliale, than first one. +There are examples of SQL backup for MySQL and PostgreSQL below. + +Making backup in MySQL: + + .. sourcecode:: mysql + + shell> mysqldump dbname > dump.sql + +Here dbname is the name of your database, dump.sql is the file, where all SQL backup statements +will be saved. + +Uploading of backup in MySQL: + + .. sourcecode:: mysql + + shell> mysqladmin create dbname + shell> mysql dbname < dump.sql + +Making backup in PostgreSQL: + + .. sourcecode:: postgresql + + pg_dump dbname > dump.sql + +Uploading of backup in PostgreSQL: + + .. sourcecode:: postgresql + + createdb dbname + psql dbname < dump.sql + Update ------ Before updating any type of package strongly recommended to stop all services and create backup for safe. + + +Migrate +------- + +Migrations are Django’s way of propagating changes you make to your models (adding a field, deleting a model, etc.) +into your database schema. They’re designed to be mostly automatic, but you’ll need to know when to make migrations, +when to run them, and the common problems you might run into. + +To run a ``migrate`` command you should run follow code: + +.. sourcecode:: python + + sudo -u polemarch /opt/polemarch/bin/polemarchctl migrate + +Create superuser +---------------- + +A superuser is the user, who has all permissions. + +To create a superuser account use the follow command: + +.. sourcecode:: python + + sudo -u polemarch /opt/polemarch/bin/polemarchctl createsuperuser + +This command will promt for all required user's options. + +Change password +--------------- + +To change password use the follow command: + +.. sourcecode:: python + + sudo -u polemarch /opt/polemarch/bin/polemarchctl changepassword [] + +It prompts you to enter a new password twice for the given user. +If the entries are identical, this immediately becomes the new password. +If you do not supply a user, the command will attempt to change the password whose username matches +the current user. \ No newline at end of file diff --git a/doc/restapi.rst b/doc/restapi.rst index 81dd6911..7f54ff45 100644 --- a/doc/restapi.rst +++ b/doc/restapi.rst @@ -1,14 +1,13 @@ - Rest API ======== -Polemarch provides REST API for all its functionality accessible via web GUI, -because our GUI also uses this API to work. Below comes information about every +Polemarch provides REST API for all it's functionality accessible via web GUI, +because our GUI also uses this API to work. Below there is information about every entity we have in Polemarch and methods applicable to it. -All methods urls stated with ``/api/v1/`` for first api version. -With other versions number will be changed. Current documentation wrote for -version 1. All methods here placed with this prefix to simplify copy & pasting. +All urls methods are stated with ``/api/v1/`` for first API version. +With other versions number will be changed. Current documentation is writen for +version 1. In this documentation all methods are placed with this prefix to simplify copy & pasting. .. _pagination: @@ -18,11 +17,11 @@ Pagination .. |pagination_def| replace:: :ref:`pagination` is used for this list. For all kinds of objects in Polemarch pagination is used. So for every list of -objects of any kind result will look like: +objects of every kind result will look like: .. http:get:: /api/v1/{something}/ - List of something. + Gets list of something. Results: @@ -37,7 +36,7 @@ objects of any kind result will look like: ] } - :>json number count: how many objects exists at all. + :>json number count: how many objects exist at all. :>json string next: link to next page with objects (``null`` if we at last). :>json string previous: link to previous page with objects (``null`` if we at first). @@ -50,7 +49,7 @@ Hosts .. http:get:: /api/v1/hosts/{id}/ - Get details about one host. + Gets details about one host. :arg id: id of host. @@ -72,6 +71,13 @@ Hosts "type":"HOST", "vars":{ + }, + "owner": { + "id": 1, + "username": "admin", + "is_active": true, + "is_staff": true, + "url": "http://localhost:8080/api/v1/users/1/" }, "url":"http://localhost:8080/api/v1/hosts/12/?format=json" } @@ -79,22 +85,27 @@ Hosts :>json number id: id of host. :>json string name: |host_name_def| :>json string type: |host_type_def| - :>json string url: url to this specific host. :>json object vars: |obj_vars_def| + :>json object owner: |host_owner_details| + :>json string url: url to this specific host. + .. |host_type_def| replace:: it is ``RANGE`` if name is range of IPs or hosts, otherwise is ``HOST``. .. |host_name_def| replace:: either human-readable name or hostname/IP or range - of them (it is depends at context of using this host during playbooks run). -.. |hosts_details_ref| replace:: **Response JSON Object:** response json fields + of them (it is depends on context of using this host during playbooks running). +.. |host_owner_details| replace:: owner of host. Supported fields + could be seen in :http:get:`/api/v1/users/{id}/`. +.. |hosts_details_ref| replace:: **Response JSON Object:** response json fields are the same as in :http:get:`/api/v1/hosts/{id}/`. .. http:get:: /api/v1/hosts/ - List of hosts. |pagination_def| + Gets list of hosts. |pagination_def| - :query id: id of host if we want to filter by it. - :query name: name of host if we want to filter by it. + :query id: id of host, if we want to filter by it. + :query name: name of host, if we want to filter by it. + :query type: type of host, if we want to filter by it. :query id__not: id of host, which we want to filter out. :query name__not: name of host, which we want to filter out. @@ -146,13 +157,13 @@ Hosts .. http:delete:: /api/v1/hosts/{id}/ - Delete host. + Deletes host. :arg id: id of host. .. http:post:: /api/v1/hosts/ - Create host. + Creates host. :json array groups: |group_groups_def| :>json object vars: |obj_vars_def| :>json boolean children: |group_children_def| + :>json object owner: |group_owner_details| :>json string url: url to this specific group. -.. |group_hosts_def| replace:: list of hosts in group if ``children`` is +.. |group_hosts_def| replace:: list of hosts in group, if ``children`` is ``false``, otherwise empty. See :ref:`hosts` for fields explanation. -.. |group_groups_def| replace:: list of subgroups in group if ``children`` is +.. |group_groups_def| replace:: list of subgroups in group, if ``children`` is ``true``, otherwise empty. .. |group_children_def| replace:: either this group of subgroups or group of hosts. -.. |group_details_ref| replace:: **Response JSON Object:** response json fields +.. |group_owner_details| replace:: owner of group. Supported fields + could be seen in :http:get:`/api/v1/users/{id}/`. +.. |group_details_ref| replace:: **Response JSON Object:** response json fields are the same as in :http:get:`/api/v1/groups/{id}/`. .. http:get:: /api/v1/groups/ - List of groups. |pagination_def| + Gets list of groups. |pagination_def| - :query id: id of group if we want to filter by it. - :query name: name of group if we want to filter by it. + :query id: id of group, if we want to filter by it. + :query name: name of group, if we want to filter by it. :query id__not: id of group, which we want to filter out. :query name__not: name of group, which we want to filter out. @@ -343,13 +374,13 @@ Groups .. http:delete:: /api/v1/groups/{id}/ - Delete group. + Deletes group. :arg id: id of group. .. http:post:: /api/v1/groups/ - Create group. + Creates group. :json number id: id of inventory. :>json string name: name of inventory. :>json array hosts: |inventory_hosts_def| + :>json array all_hosts: |inventory_all_hosts_def| :>json array groups: |inventory_groups_def| :>json object vars: |obj_vars_def| + :>json object owner: |inventory_owner_details| :>json string url: url to this specific inventory. .. |inventory_hosts_def| replace:: list of hosts in inventory. See :ref:`hosts` for fields explanation. +.. |inventory_all_hosts_def| replace:: list of all hosts in inventory(includes also hosts from this + inventory's groups) . See :ref:`hosts` for fields explanation. .. |inventory_groups_def| replace:: list of groups in inventory. See :ref:`groups` for fields explanation. +.. |inventory_owner_details| replace:: owner of inventory. Supported fields + could be seen in :http:get:`/api/v1/users/{id}/`. .. |inventory_details_ref| replace:: **Response JSON Object:** response json - fields same as in :http:get:`/api/v1/inventories/{id}/`. + fields are the same as in :http:get:`/api/v1/inventories/{id}/`. .. http:get:: /api/v1/inventories/ - List of inventories. |pagination_def| + Gets list of inventories. |pagination_def| - :query id: id of inventory if we want to filter by it. + :query id: id of inventory, if we want to filter by it. :query name: name of inventory if we want to filter by it. :query id__not: id of inventory, which we want to filter out. :query name__not: name of inventory, which we want to filter out. @@ -559,7 +643,7 @@ Inventories .. sourcecode:: js { - "count":1, + "count":2, "next":null, "previous":null, "results":[ @@ -567,6 +651,11 @@ Inventories "id":8, "name":"Inventory1", "url":"http://localhost:8080/api/v1/inventories/8/" + }, + { + "id":9, + "name":"Inventory2", + "url":"http://localhost:8080/api/v1/inventories/9/" } ] } @@ -575,13 +664,13 @@ Inventories .. http:delete:: /api/v1/inventories/{id}/ - Delete inventory. + Deletes inventory. :arg id: id of inventory. .. http:post:: /api/v1/inventories/ - Create inventory. + Creates inventory. :json number id: id of project. :>json string name: name of project. :>json string repository: |project_repository_def| - :>json string status: current state of project. Possible values are: + :>json string status: current status of project. Possible values are: ``NEW`` - newly created project, ``WAIT_SYNC`` - repository - synchronization scheduled but not yet started to perform, ``SYNC`` - - synchronization in progress, ``ERROR`` - synchronization failed (cvs + synchronization has been scheduled, but has not started to perform yet, ``SYNC`` - + synchronization is in progress, ``ERROR`` - synchronization failed (cvs failure? incorrect credentials?), ``OK`` - project is synchronized. :>json array hosts: |project_hosts_def| :>json array groups: |project_groups_def| :>json object vars: |obj_vars_def| |project_vars_rem| + :>json object owner: |project_owner_details| + :>json string revision: ``GIT`` revision + :>json string branch: current branch of project, to which project has been synced last time. :>json string url: url to this specific inventory. .. |project_repository_def| replace:: URL of repository (repo-specific URL). @@ -754,20 +873,26 @@ Projects for fields explanation. .. |project_groups_def| replace:: list of groups in project. See :ref:`groups` for fields explanation. -.. |project_vars_rem| replace:: In this special case always exists - variables ``repo_password`` to store password for repository and - ``repo_type`` to store type of repository. Currently implemented types - are ``GIT`` for Git repositories. And ``TAR`` for uploading tar archive - with project files. +.. |project_vars_rem| replace:: In this special case variable ``repo_type`` always exists + to store type of repository. Currently implemented types + are ``GIT`` - for Git repositories, ``TAR`` - for uploading tar archive + with project files and ``MANUAL`` - for creating empty project or for uploading + project files from server 'manually'. + For ``GIT`` projects such variables, as ``repo_password`` and ``repo_branch``, are also available. + ``repo_password`` is needed to store password for repository(if it exists) + and ``repo_branch`` means a branch of git project with which next + synchronization will be done. +.. |project_owner_details| replace:: owner of project. Supported fields + could be seen in :http:get:`/api/v1/users/{id}/`. .. |project_details_ref| replace:: **Response JSON Object:** response json - fields same as in :http:get:`/api/v1/projects/{id}/`. + fields are the same as in :http:get:`/api/v1/projects/{id}/`. .. http:get:: /api/v1/projects/ - List of projects. |pagination_def| + Gets list of projects. |pagination_def| - :query id: id of project if we want to filter by it. - :query name: name of project if we want to filter by it. + :query id: id of project, if we want to filter by it. + :query name: name of project, if we want to filter by it. :query id__not: id of project, which we want to filter out. :query name__not: name of project, which we want to filter out. :query status: ``status`` of projects to show in list @@ -813,13 +938,13 @@ Projects .. http:delete:: /api/v1/projects/{id}/ - Delete project. + Deletes project. :arg id: id of project. .. http:post:: /api/v1/projects/ - Create project. Operation automatically triggers synchronization. Details + Creates project. Operation automatically triggers synchronization. Details about what it is you can see in description :http:post:`/api/v1/projects/{id}/sync/` @@ -837,10 +962,9 @@ Projects { "name":"project_owl", - "repository":"somewhere-in-emptiness", + "repository":"http://example.com/project.tar", "vars":{ - "repo_type":"TAR", - "repo_password":"" + "repo_type":"TAR" } } @@ -852,7 +976,7 @@ Projects "id":9, "name":"project_owl", "status":"WAIT_SYNC", - "repository":"somewhere-in-emptiness", + "repository":"http://example.com/project.tar", "hosts":[ ], @@ -863,9 +987,17 @@ Projects ], "vars":{ - "repo_password":"", "repo_type":"TAR" }, + "owner": { + "id": 1, + "username": "admin", + "is_active": true, + "is_staff": true, + "url": "http://localhost:8080/api/v1/users/1/" + }, + "revision": "NO VCS", + "branch": "NO VCS", "url":"http://localhost:8080/api/v1/projects/9/" } @@ -873,14 +1005,14 @@ Projects .. http:patch:: /api/v1/projects/{id}/ - Update project. Operation does not start synchronization again. - If you want synchronize, you must do it by + Updates project. Operation does not start synchronization again. + If you want to synchronize, you should do it by using :http:post:`/api/v1/projects/{id}/sync/` |patch_reminder| :arg id: id of project. **Request JSON Object:** - request json fields same as in :http:post:`/api/v1/projects/` + request json fields are the same as in :http:post:`/api/v1/projects/` Example request: @@ -891,12 +1023,7 @@ Projects Accept: application/json, text/javascript { - "name":"project_owl", - "repository":"somewhere-in-emptiness", - "vars":{ - "repo_type":"TAR", - "repo_password":"" - } + "name":"project_tar" } Results: @@ -905,9 +1032,9 @@ Projects { "id":9, - "name":"project_owl", + "name":"project_tar", "status":"WAIT_SYNC", - "repository":"somewhere-in-emptiness", + "repository":"http://example.com/project.tar", "hosts":[ ], @@ -918,9 +1045,17 @@ Projects ], "vars":{ - "repo_password":"", "repo_type":"TAR" }, + "owner": { + "id": 1, + "username": "admin", + "is_active": true, + "is_staff": true, + "url": "http://localhost:8080/api/v1/users/1/" + }, + "revision": "NO VCS", + "branch": "NO VCS", "url":"http://localhost:8080/api/v1/projects/9/" } @@ -928,39 +1063,39 @@ Projects .. http:post:: /api/v1/projects/{project_id}/hosts/ - Add hosts to project. |sublists_details| + Adds hosts to project. |sublists_details| .. http:put:: /api/v1/projects/{project_id}/hosts/ - Replace sublist of hosts with new one. |sublists_details| + Replaces sublist of hosts with new one. |sublists_details| .. http:delete:: /api/v1/projects/{project_id}/hosts/ - Remove those hosts from project. |sublists_details| + Removes those hosts from project. |sublists_details| .. http:post:: /api/v1/projects/{project_id}/groups/ - Add groups to project. |sublists_details| + Adds groups to project. |sublists_details| .. http:put:: /api/v1/projects/{project_id}/groups/ - Replace sublist of groups with new one. |sublists_details| + Replaces sublist of groups with new one. |sublists_details| .. http:delete:: /api/v1/projects/{project_id}/groups/ - Remove those groups from project. |sublists_details| + Removes those groups from project. |sublists_details| .. http:post:: /api/v1/projects/{project_id}/inventories/ - Add inventories to project. |sublists_details| + Adds inventories to project. |sublists_details| .. http:put:: /api/v1/projects/{project_id}/inventories/ - Replace sublist of inventories with new one. |sublists_details| + Replaces sublist of inventories with new one. |sublists_details| .. http:delete:: /api/v1/projects/{project_id}/inventories/ - Remove those inventories from project. |sublists_details| + Removes those inventories from project. |sublists_details| .. http:get:: /api/v1/projects/supported-repos/ @@ -971,16 +1106,17 @@ Projects .. sourcecode:: js [ - "TAR", - "GIT" + "GIT", + "MANUAL", + "TAR" ] .. http:post:: /api/v1/projects/{id}/sync/ - Starts synchronization. During that process project files uploading from + Starts synchronization. During this process project files are uploading from repository. Concrete details of process highly depends on project type. For ``GIT`` is ``git pull``, for ``TAR`` it just downloading archive from - URL again and unpacking it with rewrite of old files. And so on. + URL again and unpacking it with rewriting of old files. :arg id: id of project. @@ -994,16 +1130,16 @@ Projects .. http:post:: /api/v1/projects/{id}/execute-playbook/ - Execute ansible playbook. Returns history id for watching execution process. + Executes ansible playbook. Returns history id for watching execution process. :arg id: id of project. :json string type: |ptask_type_details| :>json string schedule: |ptask_schedule_details| :>json string mode: playbook or module to run periodically. - :>json string kind: either this task is playbook run (``PLAYBOOK``) or - module run (``MODULE``). - :>json number project: id of project which this task belongs to. + :>json string kind: either this task is playbook running (``PLAYBOOK``) or + module running (``MODULE``). + :>json number project: id of project, which this task belongs to. :>json number inventory: id of inventory for which must execute_playbook playbook. + :>json boolean save_result: if ``save_result`` is true, the result will be saved. + :>json boolean enabled: if ``enabled`` is true, the periodic task will be enabled. :>json object vars: |ptask_vars_def| :>json string url: url to this specific periodic task. .. |ptask_details_ref| replace:: **Response JSON Object:** response json - fields same as in :http:get:`/api/v1/periodic-tasks/{id}/`. + fields are the same as in :http:get:`/api/v1/periodic-tasks/{id}/`. .. |ptask_schedule_details| replace:: string with integer value or string in cron format, what depends on ``type`` value. Look at ``type`` description for details. .. |ptask_type_details| replace:: type of periodic task. Either ``INTERVAL`` - for tasks that runs every N seconds or ``CRONTAB`` for tasks, which runs - according by more complex rules. According to that ``schedule`` field will + for tasks that run every N seconds or ``CRONTAB`` for tasks, which run + according to more complex rules. According to that ``schedule`` field will be interpreted as integer - number of seconds between runs. Or string in - cron format with one small exception - Polemarch expect string without year, - because years is not supported. You can easily find documentation for cron + cron format with one small exception - Polemarch expects string without year, + because year format is not supported. You can easily find documentation for cron format in web. Like those, for example: https://linux.die.net/man/5/crontab and http://www.nncron.ru/help/EN/working/cron-format.htm .. |ptask_vars_def| replace:: those vars have special meaning. All those - parameters just passes as additional command line arguments to + parameters just pass as additional command line arguments to ``ansible-playbook`` utility during execution, so you can use this feature - to widely customize of ansible behaviour. For any ``key:value`` in command - line will be ``--key value``. If you want only key without a value + for wide customization of ansible behaviour. For any ``key:value`` in command + line there will be ``--key value``. If you want to post only key without a value (``--become`` option for example), just pass ``null`` as value. In all other - aspects this field works like usual ``vars``: |obj_vars_def| + cases this field works like usual ``vars``: |obj_vars_def| .. http:get:: /api/v1/periodic-tasks/ - List of periodic tasks. |pagination_def| + Gets list of periodic tasks. |pagination_def| - :query id: id of template if we want to filter by it. + :query id: id of template, if we want to filter by it. :query id__not: id of template, which we want to filter out. :query mode: filter by playbook or module name. :query kind: filter by kind of task. @@ -1251,11 +1401,15 @@ Periodic tasks "results":[ { "id":10, + "name":"periodic-test", "type":"INTERVAL", "schedule":"60", "mode":"collect_data.yml", "kind":"PLAYBOOK", + "project": 12, "inventory":8, + "save_result": true, + "enabled": true, "vars":{ }, @@ -1263,11 +1417,15 @@ Periodic tasks }, { "id":11, + "name":"periodic-test2", "type":"CRONTAB", "schedule":"* */2 sun,fri 1-15 *", "mode":"do_greatest_evil.yml", "kind":"PLAYBOOK", + "project": 12, "inventory":8, + "save_result": true, + "enabled": true, "vars":{ }, @@ -1280,27 +1438,14 @@ Periodic tasks .. http:delete:: /api/v1/periodic-tasks/{id}/ - Delete periodic task. + Deletes periodic task. :arg id: id of periodic task. .. http:post:: /api/v1/periodic-tasks/ - Create periodic task + Creates periodic task - :json number id: id of template. :>json string name: name of template. :>json string kind: |template_kind_details| + :>json object owner: |template_owner_details| :>json string data: |template_data_details| + :>json object options: tepmlate options, which can update some template's settings before new execution. + :>json array options_list: list of options' names for this template. .. |template_details_ref| replace:: **Response JSON Object:** response json - fields same as in :http:get:`/api/v1/templates/{id}/`. + fields are the same as in :http:get:`/api/v1/templates/{id}/`. + +.. |template_kind_details| replace:: kind of template. Supported kinds + could be seen in :http:get:`/api/v1/templates/supported-kinds/`. -.. |template_kind_details| replace:: Kind of template. Supported kinds - could see in :http:get:`/api/v1/templates/supported-kinds/`. +.. |template_owner_details| replace:: owner of template. Supported fields + could be seen in :http:get:`/api/v1/users/{id}/`. .. |template_data_details| replace:: JSON structure of template. Supported fields could see in :http:get:`/api/v1/templates/supported-kinds/`. @@ -1435,9 +1658,9 @@ Templates .. http:get:: /api/v1/templates/ - Get list of templates. |pagination_def| + Gets list of templates. |pagination_def| - :query id: id of project if we want to filter by it. + :query id: id of project, if we want to filter by it. :query id__not: id of project, which we want to filter out. :query name: filter by name. :query name__not: filter by name, which we want to filter out. @@ -1458,14 +1681,26 @@ Templates .. sourcecode:: js { - "count": 1, + "count": 2, "next": null, "previous": null, "results": [ { "id": 1, "name": "test_tmplt", - "kind": "Task" + "kind": "Task", + "options_list": [ + "only-local", + "only-server" + ] + }, + { + "id": 2, + "name": "test_tmplm", + "kind": "Module", + "options_list": [ + + ] } ] } @@ -1474,17 +1709,18 @@ Templates .. http:delete:: /api/v1/templates/{id}/ - Delete periodic task. + Deletes periodic task. :arg id: id of periodic task. .. http:post:: /api/v1/templates/ - Create template + Creates template :json number id: id of history record. :>json number project: id of project, which record belongs to. :>json string mode: name of executed playbook or module. - :>json string kind: either was run of ``ansible-playbook`` (``PLAYBOOK``) or - ``ansible`` (``MODULE``). + :>json string kind: kind of task: ``PLAYBOOK`` or ``MODULE``. :>json string status: either ``DELAY``, ``OK``, ``INTERRUPTED``, ``RUN``, ``OFFLINE`` or ``ERROR``, which indicates different results of execution - (scheduled for run, good, interrupted by user, currently running, + (scheduled for run, successful run, interrupted by user, currently running, can't connect to node, failure). :>json string start_time: time, when playbook execution was started. :>json string stop_time: time, when playbook execution was ended (normally or not) - :>json string raw_inventory: Ansible inventory, which used for execution. It - is generates from on of Polemarch's :ref:`inventory` + :>json number inventory: id of inventory. + :>json string raw_inventory: ansible inventory, which was used for execution. It + was generated from Polemarch's :ref:`inventory` :>json string raw_args: ansible command line during execution. - :>json string raw_stdout: what Ansible wrote to stdout and stderr during + :>json string raw_stdout: what Ansible has written to stdout and stderr during execution. The size is limited to 10M characters. Full output in :http:get:`/api/v1/history/{id}/raw/`. :>json number initiator: initiator id. :>json string initiator_type: initiator type like in api url. + :>json object execute_args: arguments, which were used during execution. + :>json string revision: project revision. :>json string url: url to this specific history record. -.. |history_details_ref| replace:: **Response JSON Object:** response json fields +.. |history_details_ref| replace:: **Response JSON Object:** response json fields are the same as in :http:get:`/api/v1/history/{id}/`. .. http:post:: /api/v1/history/{id}/cancel/ - Cancel currently executed task. + Cancels currently executed task. :arg id: id of history record. @@ -1693,11 +2003,11 @@ History records .. http:get:: /api/v1/history/{id}/raw/ - Get full output of executed task. + Gets full output of executed task. :arg id: id of history record. - :query color: Default is ``no``. If ``yes`` you will get output with ANSI + :query color: Default is ``no``. If it is ``yes``, you will get output with ANSI Esc color codes printed by Ansible in addition to text itself. Example request: @@ -1735,7 +2045,7 @@ History records .. http:get:: /api/v1/history/{id}/lines/ - List of history record lines. |pagination_def| + Gets list of history record lines. |pagination_def| :query after: filter lines to return lines after this number. :query before: filter lines to return lines before this number. @@ -1768,11 +2078,35 @@ History records ] } +.. http:delete:: /api/v1/history/{id}/clear/ + + Deletes full output of executed task. + + :arg id: id of history record. + + Example request: + + .. sourcecode:: http + + DELETE /api/v1/history/1/clear/ HTTP/1.1 + Host: example.com + Accept: application/json, text/javascript + + Results: + + .. sourcecode:: js + + { + "detail": "Output trancated.\n" + } + + :>json string detail: new output for history record's stdout. + .. http:get:: /api/v1/history/ - List of history records. |pagination_def| + Gets list of history records. |pagination_def| - :query id: id of inventory if we want to filter by it. + :query id: id of inventory, if we want to filter by it. :query id__not: id of inventory, which we want to filter out. :query start_time__gt: filter records whose ``start_time`` greater than specified. @@ -1796,6 +2130,8 @@ History records :query status: filter by ``status``. :query start_time: get records only with ``start_time`` equal to specified. :query stop_time: get records only with ``stop_time`` equal to specified. + :query initiator: filter by ``initiator``. + :query initiator_type: filter by ``initiator_type``. Example request: @@ -1819,19 +2155,25 @@ History records "project": 3, "mode": "main.yml", "kind": "PLAYBOOK", + "inventory": 6, "status": "OK", "start_time": "2017-07-24T06:39:52.052504Z", "stop_time": "2017-07-24T06:41:06.521813Z", + "initiator": 1, + "initiator_type": "users", "url": "http://localhost:8000/api/v1/history/121/" }, { "id": 118, - "project": null, + "project": 4, "mode": "ping", "kind": "MODULE", + "inventory": 7, "status": "OK", "start_time": "2017-07-24T06:27:40.481588Z", "stop_time": "2017-07-24T06:27:42.499873Z", + "initiator": 1, + "initiator_type": "users", "url": "http://localhost:8000/api/v1/history/118/" } ] @@ -1841,13 +2183,13 @@ History records .. http:delete:: /api/v1/history/{id}/ - Delete history record. + Deletes history record. :arg id: id of record. .. http:get:: /api/v1/history/{id}/facts/ - Get facts gathered during execution of ``setup`` module. + Gets facts gathered during execution of ``setup`` module. :arg id: id of history record. @@ -1900,7 +2242,7 @@ History records by running ``setup`` module. See :http:post:`/api/v1/projects/{id}/execute-module/` for details about modules run. - :statuscode 424: facts still not ready because module is currently running + :statuscode 424: facts are still not ready, because module is currently running or only scheduled for run. Ansible @@ -1908,7 +2250,7 @@ Ansible .. http:get:: /api/v1/ansible/ - Get list of available methods in that category. All methods under + Gets list of available methods in that category. All methods under `/ansible/` designed to provide information about ansible installation which Polemarch is currently using. @@ -1931,17 +2273,17 @@ Ansible .. http:get:: /api/v1/ansible/cli_reference/ - Get list of available ansible command line tools arguments with their type + Gets list of available ansible command line tools arguments with their type and hint. - :query filter: filter by tool, for which you want get help (either `ansible` - or `ansible-playbook`). + :query filter: filter by tool, for which you want get help (for exapmle, `periodic_playbook` + or `periodic_module`). Example request: .. sourcecode:: http - GET /api/v1/ansible/cli_reference/?filter=ansible HTTP/1.1 + GET /api/v1/ansible/cli_reference/ HTTP/1.1 Host: example.com Accept: application/json, text/javascript @@ -1950,12 +2292,56 @@ Ansible .. sourcecode:: js { - "ansible": { + "periodic_playbook": { + "flush-cache": { + "shortopts": [], + "type": "boolean", + "help": "clear the fact cache" + }, + "extra-vars": { + "type": "text", + "help": "set additional variables as key=value or YAML/JSON" + }, + // there is much more arguments to type it here + // ... + }, + "playbook": { + "flush-cache": { + "shortopts": [], + "type": "boolean", + "help": "clear the fact cache" + }, + "extra-vars": { + "type": "text", + "help": "set additional variables as key=value or YAML/JSON" + }, + // there is much more arguments to type it here + // ... + }, + "module": { + "extra-vars": { + "type": "text", + "help": "set additional variables as key=value or YAML/JSON" + }, + "help": { + "shortopts": [ + "h" + ], + "type": "boolean", + "help": "show this help message and exit" + }, + // there is much more arguments to type it here + // ... + }, + "periodic_module": { "extra-vars": { "type": "text", "help": "set additional variables as key=value or YAML/JSON" }, "help": { + "shortopts": [ + "h" + ], "type": "boolean", "help": "show this help message and exit" }, @@ -1966,7 +2352,7 @@ Ansible .. http:get:: /api/v1/ansible/modules/ - Get list of installed ansible modules. + Gets list of installed ansible modules. :query filter: filter to search by module name. It is Python regular expression. @@ -1984,16 +2370,155 @@ Ansible .. sourcecode:: js [ - "extras.source_control.git_config", - "extras.source_control.github_release", - "extras.source_control.github_hooks", - "extras.source_control.gitlab_user", - "extras.source_control.github_key", - "extras.source_control.gitlab_group", - "extras.source_control.gitlab_project", - "core.source_control.git" + "source_control.github_hooks", + "source_control.git_config", + "source_control.github_issue", + "source_control.git", + "source_control.github_deploy_key", + "source_control.gitlab_project", + "source_control.github_release", + "source_control.gitlab_group", + "source_control.github_key", + "source_control.gitlab_user" ] + +.. _stats: + +Statistic list +-------------- + +Sometimes application needs to provide user with some statistic information +like amount of different objects or frequency of executing some tasks and so on. +For such kind of work we use our API's statistic list, which can provide user with information +about amount of templates, users, teams, hosts, groups, inventories, projects in system in current moment +and to tell him how many tasks of each status have been executed during last days, months and years. + +.. http:get:: /api/v1/stats/ + + Gets statistic list. + + :query last: filter to search statistic information for certain amount of past days (by default the last is 14, this filter is measured in days). + + + Example request: + + .. sourcecode:: http + + GET /api/v1/stats/ HTTP/1.1 + Host: example.com + Accept: application/json, text/javascript + + Results: + + .. sourcecode:: js + + { + "templates": 7, + "users": 1, + "teams": 0, + "hosts": 10, + "groups": 5, + "inventories": 4, + "projects": 3, + "jobs": { + "month": [ + { + "status": "ERROR", + "sum": 6, + "all": 47, + "month": "2018-03-01T00:00:00Z" + }, + { + "status": "OFFLINE", + "sum": 5, + "all": 47, + "month": "2018-03-01T00:00:00Z" + }, + { + "status": "OK", + "sum": 36, + "all": 47, + "month": "2018-03-01T00:00:00Z" + } + ], + "day": [ + { + "status": "ERROR", + "sum": 3, + "day": "2018-03-05T00:00:00Z", + "all": 10 + }, + { + "status": "OK", + "sum": 7, + "day": "2018-03-05T00:00:00Z", + "all": 10 + }, + { + "status": "ERROR", + "sum": 2, + "day": "2018-03-06T00:00:00Z", + "all": 30 + }, + { + "status": "OFFLINE", + "sum": 5, + "day": "2018-03-06T00:00:00Z", + "all": 30 + }, + { + "status": "OK", + "sum": 23, + "day": "2018-03-06T00:00:00Z", + "all": 30 + }, + { + "status": "ERROR", + "sum": 1, + "day": "2018-03-07T00:00:00Z", + "all": 7 + }, + { + "status": "OK", + "sum": 6, + "day": "2018-03-07T00:00:00Z", + "all": 7 + } + ], + "year": [ + { + "status": "ERROR", + "sum": 6, + "all": 47, + "year": "2018-01-01T00:00:00Z" + }, + { + "status": "OFFLINE", + "sum": 5, + "all": 47, + "year": "2018-01-01T00:00:00Z" + }, + { + "status": "OK", + "sum": 36, + "all": 47, + "year": "2018-01-01T00:00:00Z" + } + ] + } + } + + :>json number templates: amount of templates in system in current moment. + :>json number users: amount of users in system in current moment. + :>json number teams: amount of teams in system in current moment. (Polemarch+ only) + :>json number hosts: amount of hosts in system in current moment. + :>json number groups: amount of groups in system in current moment. + :>json number inventories: amount of inventories in system in current moment. + :>json number projects: amount of projects in system in current moment. + :>json object jobs: amount of executed tasks during last days, months, years. + + .. _variables: Variables @@ -2002,24 +2527,23 @@ Variables .. |obj_vars_def| replace:: dictionary of variables associated with this object. See :ref:`variables` for details. -Hosts, groups, inventories, projects in Polemarch may have variables +Hosts, groups, inventories and projects in Polemarch may have variables associated with them. Usually (with one exception - variables for additional -repository data in :ref:`projects`) those variables passes to Ansible to -somehow customize his behaviour or playbook logic. In all this kinds of -objects variables works in same way, so here additional chapter which describes -their behaviour abstracting from details related to every concrete type of +repository data in :ref:`projects`) those variables pass to Ansible to +customize his behaviour or playbook logic in certain way. In all these kinds of +objects variables work in the same way, so there is an additional chapter, which describes +their behaviour, abstracting from details related to every concrete type of object. In JSON responses related to those objects variables are placed in field ``vars``. This field is just key-value dictionary of existent variables for -object. It can be saved in ``POST`` and ``PATCH`` request completely -overwriting previous dictionary. +object. It can be saved by ``POST`` request and can be completely owerwritted by ``PATCH`` request. It can be represented in such more formal way: .. http:get:: /api/v1/{object_kind}/{object_id} - Get details about one object. + Gets details about one object. :arg id: id of this object. @@ -2048,11 +2572,11 @@ It can be represented in such more formal way: .. http:patch:: /api/v1/{object_kind}/{object_id} - Update object. + Updates object. :arg id: id of object. - :json number not_found: count of processed with error (not exists or no + :>json number not_found: number of items processed with error (not exists or no access). - :>json number operated: count of processed successfully. - :>json number total: count of all sent ids. + :>json number operated: number of items processed successfully. + :>json number total: number of all sent ids. + +.. _hooks: + +Hooks +----- + +Polemarch has his own system of hooks. + +.. http:get:: /api/v1/hooks/ + + Gets hooks list. + + :arg id: filter by id of hook. + :arg id__not: filter by id of hook (except this id). + :arg name: filter by name of hook. + :arg type: filter by type of hook. + + Example request: + + .. sourcecode:: http + + GET /api/v1/hooks/ HTTP/1.1 + Host: example.com + Accept: application/json, text/javascript + + Results: + + .. sourcecode:: js + + { + { + "id": 1, + "name": "test hook on user add", + "type": "HTTP", + "when": "on_user_add", + "recipients": "http://localhost:8000/hook_trigger" + }, + { + "id": 2, + "name": "all hooks", + "type": "HTTP", + "when": null, + "recipients": "http://localhost:8000/hook_trigger_another" + }, + { + "id": 3, + "name": "Script hooks", + "type": "SCRIPT", + "when": null, + "recipients": "test.sh" + } + } + + :>json number id: id of hook. + :>json string name: name of hook. + :>json string type: type of hook. For more details look :http:get:`/api/v1/hooks/types/`. + :>json string when: type of event on which hook will be executed. If ``when`` is ``null``, this hook will be + executed for every type of event. For more details look :http:get:`/api/v1/hooks/types/`. + :>json string recipients: recipients of hook. + +.. http:get:: /api/v1/hooks/{id}/ + + Gets details about one hook. + + :arg id: id of hook. + + Example request: + + .. sourcecode:: http + + GET /api/v1/hooks/1/ HTTP/1.1 + Host: example.com + Accept: application/json, text/javascript + + Results: + + .. sourcecode:: js + + { + "id": 1, + "name": "test hook on user add", + "type": "HTTP", + "when": "on_user_add", + "recipients": "http://localhost:8000/hook_trigger" + } + + **Response JSON Object:** response json fields are the + same as in :http:get:`/api/v1/hooks/`. + +.. http:post:: /api/v1/hooks/ + + Creates new hook. + + Example request: + + .. sourcecode:: http + + POST /api/v1/hooks/ HTTP/1.1 + Host: example.com + Accept: application/json, text/javascript + + { + "name": "new hook", + "type": "HTTP", + "when": "on_execution", + "recipients": "http://localhost:8000/new_hook_trigger" + } + + Results: + + .. sourcecode:: js + + { + "id": 4, + "name": "new hook", + "type": "SCRIPT", + "when": "on_execution", + "recipients": "new-test.sh" + } + + **Response JSON Object:** response json fields are the + same as in :http:get:`/api/v1/hooks/`. + + If ``type`` is ``SCRIPT`` and there is no ``'new-test.sh'`` in hooks dir, + ``POST`` request will return 400 Bad Request. + + +.. http:get:: /api/v1/hooks/types/ + + Returns list of supported hook's types and events. + + Example request: + + .. sourcecode:: http + + GET /api/v1/hooks/types/ HTTP/1.1 + Host: example.com + Accept: application/json, text/javascript + + Results: + + .. sourcecode:: js + + { + "when": { + "after_execution": "After end task", + "on_user_add": "When new user register", + "on_user_del": "When user was removed", + "on_execution": "Before start task", + "on_object_add": "When new Polemarch object was added", + "on_object_upd": "When Polemarch object was updated", + "on_object_del": "When Polemarch object was removed", + "on_user_upd": "When user update data" + }, + "types": [ + "HTTP", + "SCRIPT" + ] + } + + :>json string when: type of event on which hook will be executed. + :>json string types: type of hook. If ``type`` is ``HTTP``, hook will send JSON by ``POST`` request + to url, which is in ``recipients`` field of hook. + If ``type`` is ``SCRIPT``, hook will send a temporery file with JSON to script, name of which is in + ``recipients`` field of hook. + + +.. _token: + +Token +----- + +.. http:post:: /api/v1/token/ + + Creates new token. + + Example request: + + .. sourcecode:: http + + POST /api/v1/token/ HTTP/1.1 + Host: example.com + Accept: application/json, text/javascript + + { + "username": "test-user", + "password": "password" + } + + Results: + + .. sourcecode:: js + + { + "token": "f9e983ef5f67725b60f5a4a1aa0f32912ebe05fb" + } + +.. http:delete:: /api/v1/token/ + + Deletes token. + + Example request: + + .. sourcecode:: http + + DELETE /api/v1/token/ HTTP/1.1 + Host: example.com + Accept: application/json, text/javascript + + { + "token": "f9e983ef5f67725b60f5a4a1aa0f32912ebe05fb" + } + + Results: + + .. sourcecode:: js + + { + + } .. _users: @@ -2223,7 +2984,7 @@ Users .. http:get:: /api/v1/users/{id}/ - Get details about one user. + Gets details about one user. :arg id: id of user. @@ -2254,22 +3015,22 @@ Users :>json number id: id of user. :>json string username: login. :>json string password: hash of password. - :>json boolean is_active: is account enabled. - :>json boolean is_staff: is it superuser. Superuser have access to all + :>json boolean is_active: if it is ``true``, account is enabled. + :>json boolean is_staff: if it is ``true``, this user is superuser. Superuser has access to all objects/records despite of access rights. :>json string first_name: name. :>json string last_name: last name. :>json string email: email. :>json string url: url to this specific user. -.. |users_details_ref| replace:: **Response JSON Object:** response json fields +.. |users_details_ref| replace:: **Response JSON Object:** response json fields are the same as in :http:get:`/api/v1/users/{id}/`. .. http:get:: /api/v1/users/ - List of users. |pagination_def| + Gets list of users. |pagination_def| - :query id: id of host if we want to filter by it. + :query id: id of host, if we want to filter by it. :query id__not: id of host, which we want to filter out. :query username: filter by login. :query is_active: filter enabled users. @@ -2298,12 +3059,14 @@ Users "id":1, "username":"admin", "is_active":true, + "is_staff": true, "url":"http://127.0.0.1:8080/api/v1/users/1/" }, { "id":3, "username":"petya", "is_active":true, + "is_staff": true, "url":"http://127.0.0.1:8080/api/v1/users/3/" } ] @@ -2313,18 +3076,18 @@ Users .. http:delete:: /api/v1/users/{id}/ - Delete user. + Deletes user. :arg id: id of user. .. http:post:: /api/v1/users/ - Create user. + Creates user. :json string pmw{widget_Name}: widget name. + :>json boolean active: |users_settings_active| + :>json number sortNum: |users_settings_sortNum| + :>json boolean collapse: |users_settings_collapse| + +.. |users_settings_active| replace:: one of widget's settings, if ``active`` is ``true``, this widget will be visible on Dashboard. + +.. |users_settings_sortNum| replace:: one of widget's settings, it means order number of widget on Dashboard. + +.. |users_settings_collapse| replace:: one of widget's settings, if ``collapse`` is ``true``, this widget will be collapsed on Dashboard. + + +.. http:get:: /api/v1/users/{id}/settings/ + + Gets user's view settings of Dashboard's widgets. + + :arg id: id of user. + + + + Example request: + + .. sourcecode:: http + + GET /api/v1/users/{id}/settings/ HTTP/1.1 + Host: example.com + Accept: application/json, text/javascript + + { + + } + + Results: + + .. sourcecode:: js + + { + "pmwTasksTemplatesWidget": { + "active": true, + "sortNum": 8, + "collapse": true + }, + "pmwUsersCounter": { + "active": true, + "sortNum": 5, + "collapse": false + }, + "pmwProjectsCounter": { + "active": true, + "sortNum": 4, + "collapse": false + }, + "pmwHostsCounter": { + "active": true, + "sortNum": 0, + "collapse": false + }, + "pmwInventoriesCounter": { + "active": true, + "sortNum": 2, + "collapse": false + }, + "pmwGroupsCounter": { + "active": true, + "sortNum": 1, + "collapse": false + }, + "pmwChartWidget": { + "active": true, + "sortNum": 6, + "collapse": false + }, + "pmwModulesTemplatesWidget": { + "active": true, + "sortNum": 9, + "collapse": true + }, + "pmwTemplatesCounter": { + "active": true, + "sortNum": 3, + "collapse": false + }, + "pmwAnsibleModuleWidget": { + "active": true, + "sortNum": 7, + "collapse": true + } + } + + :>json string pmw{widget_Name}: widget name. + :>json boolean active: |users_settings_active| + :>json number sortNum: |users_settings_sortNum| + :>json boolean collapse: |users_settings_collapse| + + +.. http:delete:: /api/v1/users/{id}/settings/ + + Deletes user's view settings of Dashboard's widgets. + + :arg id: id of user. + + + +.. _teams: Teams (Polemarch+ only) ----------------------- -Teams is groups of users to which you can collectively assign rights to objects +Team is a group of users to which you can collectively assign rights to objects in ACL system. .. http:get:: /api/v1/teams/{id}/ - Get details about one team. + Gets details about one team. :arg id: id of team. @@ -2448,10 +3436,18 @@ in ACL system. "is_active": true, "is_staff": true, "url": "http://localhost:8000/api/v1/users/1/" + }, + { + "id": 2, + "username": "test-user", + "is_active": true, + "is_staff": false, + "url": "http://localhost:8081/api/v1/users/2/" } ], "users_list": [ - 1 + 1, + 2 ], "owner": { "id": 1, @@ -2471,12 +3467,12 @@ in ACL system. :>json object owner: owner of team. See :ref:`users` for fields explanation. :>json string url: url to this specific team. -.. |team_details_ref| replace:: **Response JSON Object:** response json fields +.. |team_details_ref| replace:: **Response JSON Object:** response json fields are the same as in :http:get:`/api/v1/teams/{id}/`. .. http:get:: /api/v1/teams/ - List of teams. |pagination_def| + Gets list of teams. |pagination_def| :query id: id of team if we want to filter by it. :query name: name of team if we want to filter by it. @@ -2512,13 +3508,13 @@ in ACL system. .. http:delete:: /api/v1/teams/{id}/ - Delete team. + Deletes team. :arg id: id of team. .. http:post:: /api/v1/teams/ - Create team. + Creates team. :json array permissions: list of actual object permissions after operation. - Every permission is same object as in request, so you can look request + Every permission is the same object as in request, so you can look request fields explanation for details. .. http:put:: /api/v1/{object_kind}/{id}/permissions/ - Replace permissions to object with provided. + Replaces permissions to object with provided. :arg object_kind: |perm_kind_def| :arg id: Id of object. @@ -2741,7 +3740,7 @@ Polemarch+ have such methods to control ownership and permissions information: .. sourcecode:: http - PUT /api/v1/hosts/123/permissions/ HTTP/1.1 + PUT /api/v1/teams/1/permissions/ HTTP/1.1 Host: example.com Accept: application/json, text/javascript @@ -2780,19 +3779,18 @@ Polemarch+ have such methods to control ownership and permissions information: .. http:delete:: /api/v1/{object_kind}/{id}/permissions/ - Remove access to object for users, who listed in json array in body of - request. + Removes access to object for users, which are listed in json array in request's body. :arg object_kind: |perm_kind_def| - :arg id: Id of object. - :json number id: id of owner. -.. |perm_kind_def| replace:: Kind of objects to perform operation. It can be - any present objects type in system: ``hosts``, ``groups``, - ``inventories``, ``projects``, ``templates``. +.. |perm_kind_def| replace:: kind of objects to perform operation. It can be one of + any presented object type in system: ``hosts``, ``groups``, + ``inventories``, ``projects``, ``templates``, ``teams``. License (Polemarch+ only) ------------------------- .. http:get:: /api/v1/license/ - Get details about your license. + Gets details about your license. Example request: @@ -2888,12 +3899,12 @@ License (Polemarch+ only) :>json string expiry: date, when license will be (or was) expired. If `null` license is endless. - :>json number users: number of users available with this license. If `null` + :>json number users: number of users are available with this license. If `null` - unlimited. :>json string organization: to whom this license is provided. - :>json string contacts: contatc information of license owner. - :>json number hosts: number of hosts available with this license. If `null` + :>json string contacts: license owner's contact information . + :>json number hosts: number of hosts which are available with this license. If `null` - unlimited. - :>json number actual_hosts: how many hosts (RANGE calculates appropriately) + :>json number actual_hosts: how many hosts are (RANGE calculates appropriately) currently in system. - :>json number actual_users: how many users currently in system. \ No newline at end of file + :>json number actual_users: how many users are currently in system. \ No newline at end of file diff --git a/doc/screencast.gif b/doc/screencast.gif index 9f37cb8c..7a25993d 100644 Binary files a/doc/screencast.gif and b/doc/screencast.gif differ diff --git a/polemarch/__init__.py b/polemarch/__init__.py index 77136832..4f19d3a1 100644 --- a/polemarch/__init__.py +++ b/polemarch/__init__.py @@ -1,6 +1,6 @@ from .environment import prepare_environment -__version__ = "0.1.4" +__version__ = "0.1.5" def _main(**kwargs): # pylint: disable=unused-variable diff --git a/polemarch/main/context_processors.py b/polemarch/main/context_processors.py index 01c90757..34a5c5c0 100644 --- a/polemarch/main/context_processors.py +++ b/polemarch/main/context_processors.py @@ -6,6 +6,7 @@ def settings_constants(request): data = {"login_url": getattr(settings, 'LOGIN_URL', '/login/'), "logout_url": getattr(settings, 'LOGOUT_URL', '/logout/'), "docs_url": getattr(settings, 'DOC_URL', '/docs/'), + "timezone": getattr(settings, 'TIME_ZONE', 'UTC'), "debug": getattr(settings, 'DEBUG', False)} return data diff --git a/polemarch/main/models/__init__.py b/polemarch/main/models/__init__.py index df3fb6a2..39535e91 100644 --- a/polemarch/main/models/__init__.py +++ b/polemarch/main/models/__init__.py @@ -1,6 +1,7 @@ # pylint: disable=unused-argument,no-member from __future__ import absolute_import import os +import sys import json from collections import OrderedDict import django_celery_beat @@ -62,7 +63,7 @@ def validate_group_name(instance, **kwargs): @receiver(signals.m2m_changed, sender=Group.parents.through) def check_circular_deps(instance, action, pk_set, *args, **kw): - if action in ["pre_add", "post_add"]: + if action in ["pre_add", "post_add"] and 'loaddata' not in sys.argv: if instance.id in pk_set: raise instance.CiclicDependencyError("The group can " "not refer to itself.") diff --git a/polemarch/main/models/tasks.py b/polemarch/main/models/tasks.py index aa481a33..b6aed254 100644 --- a/polemarch/main/models/tasks.py +++ b/polemarch/main/models/tasks.py @@ -385,7 +385,7 @@ def __init__(self): class Meta: default_related_name = "history" - ordering = ["-id"] + ordering = ["-start_time"] index_together = [ ["id", "project", "mode", "status", "inventory", "start_time", "stop_time", "initiator", "initiator_type"] diff --git a/polemarch/main/templates/auth/login.html b/polemarch/main/templates/auth/login.html index 76279203..5098dd9a 100644 --- a/polemarch/main/templates/auth/login.html +++ b/polemarch/main/templates/auth/login.html @@ -2,7 +2,14 @@ {% load staticfiles %} {% load i18n %} -{% block style %} +{% block style %} + + + + + +

Manage servers with comfort