Skip to content

Commit

Permalink
Release 0.0.7
Browse files Browse the repository at this point in the history
Changelog:
* Autocomplete parametrs and help from API
* Add links for issues
* Add docs for GUI (in gif-animations with annotation text)
* Tested with Oracle Linux

Bugfixes:
* 'Execute now' does not send args
* Fixed day of week in cron gui
* Change page title when saving an object, show initiator in history
* DB timeout fix

See merge request !23
  • Loading branch information
onegreyonewhite committed Sep 5, 2017
2 parents a3b66c0 + 0cd0b96 commit 901b31b
Show file tree
Hide file tree
Showing 57 changed files with 1,258 additions and 837 deletions.
26 changes: 18 additions & 8 deletions .gitlab-ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ stages:
only:
- /^issue_.*$/
- developer
- master
- make_bin_packages


Expand Down Expand Up @@ -82,6 +83,15 @@ default_rpm_tests:
- sudo -H -u polemarch /opt/polemarch/bin/pip install -r requirements-test.txt
- sudo -H -u polemarch /opt/polemarch/bin/polemarchctl test -v2 polemarch.main.tests

default_oracle_tests:
<<: *packing-test
image: onegreyonewhite/tox:oracle
script:
- make rpm RELEASE=${CI_BUILD_ID}
- sudo yum install dist/*.rpm -y -q
- sudo -H -u polemarch /opt/polemarch/bin/pip install -r requirements-test.txt
- sudo -H -u polemarch /opt/polemarch/bin/polemarchctl test -v2 polemarch.main.tests

default_deb_tests:
<<: *packing-test
image: onegreyonewhite/tox:ubuntu
Expand Down Expand Up @@ -173,11 +183,11 @@ release_deb:
paths:
- dist/

#release_pypi:
# stage: release
# only:
# - tags
# script:
# - python2 setup.py compile
# - twine upload -u ${PYPI_UPLOAD_NAME} -p ${PYPI_UPLOAD_PASSWORD} dist/*.tar.gz
# allow_failure: false
release_pypi:
stage: release
only:
- manual
script:
- python2 setup.py compile
- twine upload -u ${PYPI_UPLOAD_NAME} -p ${PYPI_UPLOAD_PASSWORD} dist/*.tar.gz
allow_failure: true
88 changes: 88 additions & 0 deletions doc/gui.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
GUI desription
==============

Before you start
----------------

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:

.. image:: gui_gif/create_inventory.gif

Also you can import Ansible inventory file:

.. image:: gui_gif/import_inventory.gif

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.

If you don't have git repository, you can upload tar archive with project files
from any http url.

Also there is special type of project - empty project. In case you want run
only modules without any playbooks or something.

.. image:: gui_gif/create_projects.gif

If you update something in your git repository, don't forget to run sync in
Polemarch for pulling your changes.

.. image:: gui_gif/sync_project.gif

Also maybe you want create separate user for yourself and some other members of
your team:

.. image:: gui_gif/create_user.gif

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

Of course you can run any Ansible modules and any of playbooks in your project.
Also you can cancel your tasks if you want:

.. image:: gui_gif/run_something.gif

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:

.. 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
to minimize hand work (either module run or playbook):

.. image:: gui_gif/module_template.gif

.. image:: gui_gif/task_template.gif

Also you can backup/share your templates using import/export mechanism:

.. image:: gui_gif/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:

.. image:: gui_gif/ptask_module_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
Binary file added doc/gui_gif/create_inventory.gif
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added doc/gui_gif/create_projects.gif
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added doc/gui_gif/create_user.gif
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added doc/gui_gif/filter_history.gif
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added doc/gui_gif/import_inventory.gif
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added doc/gui_gif/import_template.gif
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added doc/gui_gif/module_template.gif
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added doc/gui_gif/ptask_module_interval.gif
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added doc/gui_gif/ptask_playbook_cron.gif
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added doc/gui_gif/quick_run_command.gif
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added doc/gui_gif/run_something.gif
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added doc/gui_gif/sync_project.gif
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added doc/gui_gif/task_template.gif
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
1 change: 1 addition & 0 deletions doc/index.rst
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ Official site: https://gitlab.com/vstconsulting/polemarch
pipinstall
restapi
config
gui



Expand Down
Binary file modified doc/screencast.gif
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
2 changes: 1 addition & 1 deletion polemarch/__init__.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
__version__ = "0.0.6"
__version__ = "0.0.7"

def _main(settings="polemarch.main.settings"):
# pylint: disable=unused-variable
Expand Down
26 changes: 11 additions & 15 deletions polemarch/api/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -93,19 +93,16 @@ def filter_route_queryset(self, queryset, filter_classes=None):
request=self.request).qs
return queryset

def get_paginated_route_response(self, queryset, serializer_class=None,
filter_classes=None):
def get_paginated_route_response(self, queryset, serializer_class,
filter_classes=None, **kwargs):
queryset = self.filter_route_queryset(queryset, filter_classes)

if serializer_class is None:
serializer_class = self.get_serializer_class()

page = self.paginate_queryset(queryset)
if page is not None:
serializer = serializer_class(page, many=True)
serializer = serializer_class(page, many=True, **kwargs)
return self.get_paginated_response(serializer.data)

serializer = serializer_class(queryset, many=True)
serializer = serializer_class(queryset, many=True, **kwargs)
return RestResponse(serializer.data)

@detail_route(methods=["post", "put", "delete", "get"])
Expand All @@ -120,13 +117,11 @@ def filter(self, request):
queryset = queryset.filter(**request.data.get("filter", {}))
queryset = queryset.exclude(**request.data.get("exclude", {}))

page = self.paginate_queryset(queryset)
if page is not None:
serializer = self.get_serializer(page, many=True)
return self.get_paginated_response(serializer.data)

serializer = self.get_serializer(queryset, many=True)
return RestResponse(serializer.data)
return self.get_paginated_route_response(
queryset=queryset,
serializer_class=self.get_serializer_class(),
context=self.get_serializer_context()
)


class ReadOnlyModelViewSet(GenericViewSet,
Expand All @@ -148,11 +143,12 @@ class NonModelsViewSet(GenericViewSet):
base_name = None

def get_queryset(self):
return QuerySet()
return QuerySet() # nocv


class ListNonModelViewSet(NonModelsViewSet,
viewsets.mixins.ListModelMixin):
# pylint: disable=abstract-method

@property
def methods(self):
Expand Down
2 changes: 1 addition & 1 deletion polemarch/api/handlers.py
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ def polemarch_exception_handler(exc, context):
status=status.HTTP_404_NOT_FOUND)
elif isinstance(exc, djexcs.ValidationError):
errors = dict(exc).get('__all__', dict(exc)) if isinstance(exc, dict)\
else exc
else str(exc)
if isinstance(errors, list):
errors = {'other_errors': errors} # pragma: no cover
return Response({"detail": errors},
Expand Down
2 changes: 1 addition & 1 deletion polemarch/api/permissions.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ def has_object_permission(self, request, view, obj):
if request.user.is_staff:
return True
elif request.user == obj:
return True
return True # nocv
return bool(view.get_queryset().filter(id=obj.id).count())


Expand Down
9 changes: 7 additions & 2 deletions polemarch/api/routers.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,10 @@ def get_default_base_name(self, viewset):
'not automatically determine the name from the viewset, as ' \
'it does not have a `.queryset` or `.model` attribute.'
return model._meta.object_name.lower()
return super(_AbstractRouter, self).get_default_base_name(viewset)
# can't be tested because this initialization takes place before any
# test code can be run
return super(_AbstractRouter,
self).get_default_base_name(viewset) # nocv

def register_view(self, prefix, view, name=None):
if name is None:
Expand Down Expand Up @@ -114,5 +117,7 @@ def get_urls(self):
for prefix, router, _ in self.routers:
urls.append(url(prefix, include(router.urls)))
for prefix, view, _ in self.custom_urls:
urls.append(url(prefix, view.as_view()))
# can't be tested because this initialization takes place before
# any test code can be run
urls.append(url(prefix, view.as_view())) # nocv
return urls
10 changes: 5 additions & 5 deletions polemarch/api/v1/filters.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,11 +24,11 @@ def name_filter(queryset, field, value):


def variables_filter(queryset, field, value):
if field == "variables":
items = value.split(",")
kwargs = {item.split(":")[0]: item.split(":")[1] for item in items}
return queryset.var_filter(**kwargs)
return queryset.filter(**dict(field=value))
# filter applicable only to variables
# pylint: disable=unused-argument
items = value.split(",")
kwargs = {item.split(":")[0]: item.split(":")[1] for item in items}
return queryset.var_filter(**kwargs)


class _BaseFilter(filters.FilterSet):
Expand Down
11 changes: 9 additions & 2 deletions polemarch/api/v1/serializers.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,10 @@
from ..base import Response


# NOTE: we can freely remove that because according to real behaviour all our
# models always have queryset at this stage, so this code actually doing
# nothing
#
# Serializers field for usability
class ModelRelatedField(serializers.PrimaryKeyRelatedField):
def __init__(self, **kwargs):
Expand Down Expand Up @@ -107,7 +111,9 @@ def is_valid(self, raise_exception=False):
def update(self, instance, validated_data):
if not self.context['request'].user.is_staff and \
instance.id != self.context['request'].user.id:
raise exceptions.PermissionDenied
# can't be tested because PATCH from non privileged user to other
# user fails at self.get_object() in View
raise exceptions.PermissionDenied # nocv
instance.username = validated_data.get('username',
instance.username)
instance.is_active = validated_data.get('is_active',
Expand Down Expand Up @@ -197,7 +203,8 @@ class Meta:
'value',)

def to_representation(self, instance):
return {instance.key: instance.value}
# we are not using that. This function here just in case.
return {instance.key: instance.value} # nocv


class _WithVariablesSerializer(serializers.ModelSerializer):
Expand Down
1 change: 1 addition & 0 deletions polemarch/api/v1/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -259,6 +259,7 @@ def get(self, request):


class AnsibleViewSet(base.ListNonModelViewSet):
# pylint: disable=abstract-method
base_name = "ansible"

@list_route(methods=["get"])
Expand Down
14 changes: 7 additions & 7 deletions polemarch/main/management/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ def add_arguments(self, parser):
def handle(self, *args, **options):
LOG_LEVEL = settings.LOG_LEVEL
if options.get('log-level', False):
LOG_LEVEL = options.get('log-level', LOG_LEVEL) # pragma: no cover
LOG_LEVEL = options.get('log-level', LOG_LEVEL)
logger.setLevel(LOG_LEVEL.upper())
self.LOG_LEVEL = LOG_LEVEL.upper()

Expand All @@ -47,19 +47,19 @@ def get_version(self):
return vstr.format(c=__version__, d=django, r=celery)

def _print(self, info=""):
self.stdout.write(str(info)) # pragma: no cover
self.stdout.write(str(info)) # nocv

def _success(self, info=""):
try: # pragma: no cover
def _success(self, info=""): # nocv
try:
self._print(self.style.SUCCESS(info))
except:
self._print(self.style.MIGRATE_SUCCESS(info))

def _info(self, info=""):
self._print(self.style.HTTP_INFO(info))
self._print(self.style.HTTP_INFO(info)) # nocv

def _error(self, info=""):
self._print(self.style.ERROR(info))
self._print(self.style.ERROR(info)) # nocv

def _warning(self, info=""):
self._print(self.style.WARNING(info))
self._print(self.style.WARNING(info)) # nocv
26 changes: 9 additions & 17 deletions polemarch/main/models/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
from .projects import Project
from .users import TypesPermissions
from .tasks import Task, PeriodicTask, History, HistoryLines, Template
from ..validators import validate_hostname, RegexValidator
from ..validators import RegexValidator
from ..exceptions import UnknownTypeException
from ..utils import raise_context, AnsibleArgumentsReference

Expand Down Expand Up @@ -61,20 +61,6 @@ def validate_crontab(instance, **kwargs):
raise ValidationError(msg)


@receiver(signals.pre_save, sender=Host)
def validate_hosts(instance, **kwargs):
if instance.variables.filter(key="ansible_host").count():
validate_hostname(instance.variables.get("ansible_host"))
elif instance.type == "HOST":
validate_hostname(instance.name)
elif instance.type == "RANGE":
validate_name = RegexValidator(
regex=r'^[a-zA-Z0-9\-\._\[\]\:]*$',
message='Name must be Alphanumeric'
)
validate_name(instance.name)


@receiver(signals.pre_save, sender=Host)
def validate_type(instance, **kwargs):
if instance.type not in instance.types:
Expand Down Expand Up @@ -132,5 +118,11 @@ def save_to_beat(instance, **kwargs):

@receiver(signals.post_delete, sender=PeriodicTask)
def delete_from_beat(instance, **kwargs):
django_celery_beat.models.PeriodicTask.objects.\
filter(name=str(instance.id)).delete()
manager = django_celery_beat.models.PeriodicTask.objects
celery_tasks = manager.filter(name=str(instance.id))
if instance.type == "CRONTAB" and celery_tasks.count() > 0:
crontab_id = celery_tasks[0].crontab_id
others = manager.filter(crontab_id=crontab_id)
if others.count() == 1:
CrontabSchedule.objects.get(id=crontab_id).delete()
celery_tasks.delete()
Loading

0 comments on commit 901b31b

Please sign in to comment.