Add proper status page for datasync
or rather, it's a good start.. plenty more could be added
This commit is contained in:
parent
839c4e0c28
commit
065f845707
|
@ -4,7 +4,7 @@
|
||||||
<%def name="context_menu_items()">
|
<%def name="context_menu_items()">
|
||||||
${parent.context_menu_items()}
|
${parent.context_menu_items()}
|
||||||
% if request.has_perm('datasync.list'):
|
% if request.has_perm('datasync.list'):
|
||||||
<li>${h.link_to("View DataSync Threads", url('datasync'))}</li>
|
<li>${h.link_to("View DataSync Status", url('datasync.status'))}</li>
|
||||||
% endif
|
% endif
|
||||||
</%def>
|
</%def>
|
||||||
|
|
||||||
|
|
|
@ -53,29 +53,30 @@
|
||||||
<p class="block">
|
<p class="block">
|
||||||
This tool works by modifying settings in the DB. It
|
This tool works by modifying settings in the DB. It
|
||||||
does <span class="is-italic">not</span> modify any config
|
does <span class="is-italic">not</span> modify any config
|
||||||
files. If you intend to manage datasync config via files
|
files. If you intend to manage datasync watcher/consumer
|
||||||
only then you should
|
config via files only then you should be sure to UNCHECK the
|
||||||
<span class="is-italic">not</span> use this tool!
|
"Use these Settings.." checkbox near the top of page.
|
||||||
</p>
|
</p>
|
||||||
<p class="block">
|
<p class="block">
|
||||||
If you have managed config via files thus far, and want to use
|
If you have managed config via files thus far, and want to
|
||||||
this tool anyway/instead, that's fine - but after saving
|
start using this tool to manage via DB settings instead,
|
||||||
the settings via this tool you should probably remove all
|
that's fine - but after saving the settings via this tool
|
||||||
|
you should probably remove all
|
||||||
<span class="is-family-code">[rattail.datasync]</span> entries
|
<span class="is-family-code">[rattail.datasync]</span> entries
|
||||||
from your config file (and restart apps) so as to avoid
|
from your config file (and restart apps) so as to avoid
|
||||||
confusion.
|
confusion.
|
||||||
</p>
|
</p>
|
||||||
<p class="block">
|
|
||||||
Finally, you should know that this tool will
|
|
||||||
<span class="is-italic">overwrite</span> the entire
|
|
||||||
<span class="is-family-code">rattail.datasync</span> namespace
|
|
||||||
within the DB settings. In other words if you have
|
|
||||||
manually created any ${h.link_to("Raw Settings", url('settings'))}
|
|
||||||
within that namepsace, they will be lost when you save settings
|
|
||||||
with this tool.
|
|
||||||
</p>
|
|
||||||
</b-notification>
|
</b-notification>
|
||||||
|
|
||||||
|
<b-field>
|
||||||
|
<b-checkbox name="use_profile_settings"
|
||||||
|
v-model="useProfileSettings"
|
||||||
|
native-value="true"
|
||||||
|
@input="settingsNeedSaved = true">
|
||||||
|
Use these Settings to configure watchers and consumers
|
||||||
|
</b-checkbox>
|
||||||
|
</b-field>
|
||||||
|
|
||||||
<div class="level">
|
<div class="level">
|
||||||
<div class="level-left">
|
<div class="level-left">
|
||||||
<div class="level-item">
|
<div class="level-item">
|
||||||
|
@ -83,7 +84,8 @@
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="level-right">
|
<div class="level-right">
|
||||||
<div class="level-item">
|
<div class="level-item"
|
||||||
|
v-show="useProfileSettings">
|
||||||
<b-button type="is-primary"
|
<b-button type="is-primary"
|
||||||
@click="newProfile()"
|
@click="newProfile()"
|
||||||
icon-pack="fas"
|
icon-pack="fas"
|
||||||
|
@ -130,7 +132,8 @@
|
||||||
<b-table-column field="enabled" label="Enabled">
|
<b-table-column field="enabled" label="Enabled">
|
||||||
{{ props.row.enabled ? "Yes" : "No" }}
|
{{ props.row.enabled ? "Yes" : "No" }}
|
||||||
</b-table-column>
|
</b-table-column>
|
||||||
<b-table-column label="Actions">
|
<b-table-column label="Actions"
|
||||||
|
v-if="useProfileSettings">
|
||||||
<a href="#"
|
<a href="#"
|
||||||
class="grid-action"
|
class="grid-action"
|
||||||
@click.prevent="editProfile(props.row)">
|
@click.prevent="editProfile(props.row)">
|
||||||
|
@ -397,15 +400,22 @@
|
||||||
|
|
||||||
<h3 class="is-size-3">Misc.</h3>
|
<h3 class="is-size-3">Misc.</h3>
|
||||||
|
|
||||||
<b-field grouped>
|
<b-field label="Supervisor Process Name"
|
||||||
<b-field label="Restart Command"
|
message="This should be the complete name, including group - e.g. poser:poser_datasync"
|
||||||
message="This will run as '${system_user}' system user - please configure sudoers as needed. Typical command is like: sudo supervisorctl restart poser:poser_datasync"
|
expanded>
|
||||||
expanded>
|
<b-input name="supervisor_process_name"
|
||||||
<b-input name="restart_command"
|
v-model="supervisorProcessName"
|
||||||
v-model="restartCommand"
|
@input="settingsNeedSaved = true">
|
||||||
@input="settingsNeedSaved = true">
|
</b-input>
|
||||||
</b-input>
|
</b-field>
|
||||||
</b-field>
|
|
||||||
|
<b-field label="Restart Command"
|
||||||
|
message="This will run as '${system_user}' system user - please configure sudoers as needed. Typical command is like: sudo supervisorctl restart poser:poser_datasync"
|
||||||
|
expanded>
|
||||||
|
<b-input name="restart_command"
|
||||||
|
v-model="restartCommand"
|
||||||
|
@input="settingsNeedSaved = true">
|
||||||
|
</b-input>
|
||||||
</b-field>
|
</b-field>
|
||||||
|
|
||||||
</%def>
|
</%def>
|
||||||
|
@ -417,6 +427,7 @@
|
||||||
ThisPageData.showConfigFilesNote = false
|
ThisPageData.showConfigFilesNote = false
|
||||||
ThisPageData.profilesData = ${json.dumps(profiles_data)|n}
|
ThisPageData.profilesData = ${json.dumps(profiles_data)|n}
|
||||||
ThisPageData.showDisabledProfiles = false
|
ThisPageData.showDisabledProfiles = false
|
||||||
|
ThisPageData.useProfileSettings = ${json.dumps(use_profile_settings)|n}
|
||||||
|
|
||||||
ThisPageData.editProfileShowDialog = false
|
ThisPageData.editProfileShowDialog = false
|
||||||
ThisPageData.editingProfile = null
|
ThisPageData.editingProfile = null
|
||||||
|
@ -441,6 +452,7 @@
|
||||||
ThisPageData.editingConsumerRunas = null
|
ThisPageData.editingConsumerRunas = null
|
||||||
ThisPageData.editingConsumerEnabled = true
|
ThisPageData.editingConsumerEnabled = true
|
||||||
|
|
||||||
|
ThisPageData.supervisorProcessName = ${json.dumps(supervisor_process_name)|n}
|
||||||
ThisPageData.restartCommand = ${json.dumps(restart_command)|n}
|
ThisPageData.restartCommand = ${json.dumps(restart_command)|n}
|
||||||
|
|
||||||
ThisPage.computed.filteredProfilesData = function() {
|
ThisPage.computed.filteredProfilesData = function() {
|
||||||
|
|
|
@ -1,19 +0,0 @@
|
||||||
## -*- coding: utf-8; -*-
|
|
||||||
<%inherit file="/master/index.mako" />
|
|
||||||
|
|
||||||
<%def name="context_menu_items()">
|
|
||||||
${parent.context_menu_items()}
|
|
||||||
% if request.has_perm('datasync_changes.list'):
|
|
||||||
<li>${h.link_to("View DataSync Changes", url('datasyncchanges'))}</li>
|
|
||||||
% endif
|
|
||||||
</%def>
|
|
||||||
|
|
||||||
<%def name="render_grid_component()">
|
|
||||||
<b-notification :closable="false">
|
|
||||||
TODO: this page coming soon...
|
|
||||||
</b-notification>
|
|
||||||
${parent.render_grid_component()}
|
|
||||||
</%def>
|
|
||||||
|
|
||||||
|
|
||||||
${parent.body()}
|
|
121
tailbone/templates/datasync/status.mako
Normal file
121
tailbone/templates/datasync/status.mako
Normal file
|
@ -0,0 +1,121 @@
|
||||||
|
## -*- coding: utf-8; -*-
|
||||||
|
<%inherit file="/page.mako" />
|
||||||
|
|
||||||
|
<%def name="content_title()"></%def>
|
||||||
|
|
||||||
|
<%def name="context_menu_items()">
|
||||||
|
${parent.context_menu_items()}
|
||||||
|
% if request.has_perm('datasync_changes.list'):
|
||||||
|
<li>${h.link_to("View DataSync Changes", url('datasyncchanges'))}</li>
|
||||||
|
% endif
|
||||||
|
</%def>
|
||||||
|
|
||||||
|
<%def name="page_content()">
|
||||||
|
<b-field label="Supervisor Status">
|
||||||
|
<div style="display: flex;">
|
||||||
|
|
||||||
|
% if process_info:
|
||||||
|
<pre class="has-background-${'success' if process_info['statename'] == 'RUNNING' else 'danger'}">${process_info['group']}:${process_info['name']} ${process_info['statename']} ${process_info['description']}</pre>
|
||||||
|
% else:
|
||||||
|
<pre class="has-background-warning">${supervisor_error}</pre>
|
||||||
|
% endif
|
||||||
|
|
||||||
|
<div style="margin-left: 1rem;">
|
||||||
|
% if request.has_perm('datasync.restart'):
|
||||||
|
${h.form(url('datasync.restart'), **{'@submit': 'restartProcess'})}
|
||||||
|
${h.csrf_token(request)}
|
||||||
|
<b-button type="is-primary"
|
||||||
|
native-type="submit"
|
||||||
|
icon-pack="fas"
|
||||||
|
icon-left="redo"
|
||||||
|
:disabled="restartingProcess">
|
||||||
|
{{ restartingProcess ? "Working, please wait..." : "Restart Process" }}
|
||||||
|
</b-button>
|
||||||
|
${h.end_form()}
|
||||||
|
% endif
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
</b-field>
|
||||||
|
|
||||||
|
<b-field label="Watcher Status">
|
||||||
|
<b-table :data="watchers">
|
||||||
|
<template slot-scope="props">
|
||||||
|
<b-table-column field="key"
|
||||||
|
label="Watcher">
|
||||||
|
{{ props.row.key }}
|
||||||
|
</b-table-column>
|
||||||
|
<b-table-column field="spec"
|
||||||
|
label="Spec">
|
||||||
|
{{ props.row.spec }}
|
||||||
|
</b-table-column>
|
||||||
|
<b-table-column field="dbkey"
|
||||||
|
label="DB Key">
|
||||||
|
{{ props.row.dbkey }}
|
||||||
|
</b-table-column>
|
||||||
|
<b-table-column field="delay"
|
||||||
|
label="Delay">
|
||||||
|
{{ props.row.delay }} second(s)
|
||||||
|
</b-table-column>
|
||||||
|
<b-table-column field="lastrun"
|
||||||
|
label="Last Watched">
|
||||||
|
<span v-html="props.row.lastrun"></span>
|
||||||
|
</b-table-column>
|
||||||
|
<b-table-column field="status"
|
||||||
|
label="Status"
|
||||||
|
:class="props.row.status == 'okay' ? 'has-background-success' : 'has-background-warning'">
|
||||||
|
{{ props.row.status }}
|
||||||
|
</b-table-column>
|
||||||
|
</template>
|
||||||
|
</b-table>
|
||||||
|
</b-field>
|
||||||
|
|
||||||
|
<b-field label="Consumer Status">
|
||||||
|
<b-table :data="consumers">
|
||||||
|
<template slot-scope="props">
|
||||||
|
<b-table-column field="key"
|
||||||
|
label="Consumer">
|
||||||
|
{{ props.row.key }}
|
||||||
|
</b-table-column>
|
||||||
|
<b-table-column field="spec"
|
||||||
|
label="Spec">
|
||||||
|
{{ props.row.spec }}
|
||||||
|
</b-table-column>
|
||||||
|
<b-table-column field="dbkey"
|
||||||
|
label="DB Key">
|
||||||
|
{{ props.row.dbkey }}
|
||||||
|
</b-table-column>
|
||||||
|
<b-table-column field="delay"
|
||||||
|
label="Delay">
|
||||||
|
{{ props.row.delay }} second(s)
|
||||||
|
</b-table-column>
|
||||||
|
<b-table-column field="changes"
|
||||||
|
label="Pending Changes">
|
||||||
|
{{ props.row.changes }}
|
||||||
|
</b-table-column>
|
||||||
|
<b-table-column field="status"
|
||||||
|
label="Status"
|
||||||
|
:class="props.row.status == 'okay' ? 'has-background-success' : 'has-background-warning'">
|
||||||
|
{{ props.row.status }}
|
||||||
|
</b-table-column>
|
||||||
|
</template>
|
||||||
|
</b-table>
|
||||||
|
</b-field>
|
||||||
|
</%def>
|
||||||
|
|
||||||
|
<%def name="modify_this_page_vars()">
|
||||||
|
<script type="text/javascript">
|
||||||
|
|
||||||
|
ThisPageData.restartingProcess = false
|
||||||
|
ThisPageData.watchers = ${json.dumps(watcher_data)|n}
|
||||||
|
ThisPageData.consumers = ${json.dumps(consumer_data)|n}
|
||||||
|
|
||||||
|
ThisPage.methods.restartProcess = function() {
|
||||||
|
this.restartingProcess = true
|
||||||
|
}
|
||||||
|
|
||||||
|
</script>
|
||||||
|
</%def>
|
||||||
|
|
||||||
|
|
||||||
|
${parent.body()}
|
|
@ -127,6 +127,8 @@ def raw_datetime(config, value, verbose=False, as_date=False):
|
||||||
if not value:
|
if not value:
|
||||||
return ''
|
return ''
|
||||||
|
|
||||||
|
app = config.get_app()
|
||||||
|
|
||||||
# Make sure we're dealing with a tz-aware value. If we're given a naive
|
# Make sure we're dealing with a tz-aware value. If we're given a naive
|
||||||
# value, we assume it to be local to the UTC timezone.
|
# value, we assume it to be local to the UTC timezone.
|
||||||
if not value.tzinfo:
|
if not value.tzinfo:
|
||||||
|
@ -150,10 +152,8 @@ def raw_datetime(config, value, verbose=False, as_date=False):
|
||||||
else:
|
else:
|
||||||
kwargs['c'] = six.text_type(value)
|
kwargs['c'] = six.text_type(value)
|
||||||
|
|
||||||
# avoid humanize error when calculating huge time diff
|
time_diff = app.render_time_ago(time_ago, fallback=None)
|
||||||
time_diff = None
|
if time_diff is not None:
|
||||||
if abs(time_ago.days) < 100000:
|
|
||||||
time_diff = humanize.naturaltime(time_ago)
|
|
||||||
|
|
||||||
# by "verbose" we mean the result text to look like "YYYY-MM-DD (X days ago)"
|
# by "verbose" we mean the result text to look like "YYYY-MM-DD (X days ago)"
|
||||||
if verbose:
|
if verbose:
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
################################################################################
|
################################################################################
|
||||||
#
|
#
|
||||||
# Rattail -- Retail Software Framework
|
# Rattail -- Retail Software Framework
|
||||||
# Copyright © 2010-2021 Lance Edgar
|
# Copyright © 2010-2022 Lance Edgar
|
||||||
#
|
#
|
||||||
# This file is part of Rattail.
|
# This file is part of Rattail.
|
||||||
#
|
#
|
||||||
|
@ -31,11 +31,15 @@ import json
|
||||||
import subprocess
|
import subprocess
|
||||||
import logging
|
import logging
|
||||||
|
|
||||||
|
import six
|
||||||
|
import sqlalchemy as sa
|
||||||
|
|
||||||
from rattail.db import model
|
from rattail.db import model
|
||||||
from rattail.datasync.config import load_profiles
|
|
||||||
from rattail.datasync.util import purge_datasync_settings
|
from rattail.datasync.util import purge_datasync_settings
|
||||||
|
from rattail.util import simple_error
|
||||||
|
|
||||||
from tailbone.views import MasterView
|
from tailbone.views import MasterView
|
||||||
|
from tailbone.util import raw_datetime
|
||||||
|
|
||||||
|
|
||||||
log = logging.getLogger(__name__)
|
log = logging.getLogger(__name__)
|
||||||
|
@ -49,11 +53,12 @@ class DataSyncThreadView(MasterView):
|
||||||
index view, with status for each, sort of akin to "dashboard".
|
index view, with status for each, sort of akin to "dashboard".
|
||||||
For now it only serves the config view.
|
For now it only serves the config view.
|
||||||
"""
|
"""
|
||||||
normalized_model_name = 'datasyncthread'
|
|
||||||
model_title = "DataSync Thread"
|
model_title = "DataSync Thread"
|
||||||
|
model_title_plural = "DataSync Daemon"
|
||||||
model_key = 'key'
|
model_key = 'key'
|
||||||
route_prefix = 'datasync'
|
route_prefix = 'datasync'
|
||||||
url_prefix = '/datasync'
|
url_prefix = '/datasync'
|
||||||
|
listable = False
|
||||||
viewable = False
|
viewable = False
|
||||||
creatable = False
|
creatable = False
|
||||||
editable = False
|
editable = False
|
||||||
|
@ -68,26 +73,122 @@ class DataSyncThreadView(MasterView):
|
||||||
'key',
|
'key',
|
||||||
]
|
]
|
||||||
|
|
||||||
|
def __init__(self, request, context=None):
|
||||||
|
super(DataSyncThreadView, self).__init__(request, context=context)
|
||||||
|
app = self.get_rattail_app()
|
||||||
|
self.datasync_handler = app.get_datasync_handler()
|
||||||
|
|
||||||
|
def status(self):
|
||||||
|
"""
|
||||||
|
View to list/filter/sort the model data.
|
||||||
|
|
||||||
|
If this view receives a non-empty 'partial' parameter in the query
|
||||||
|
string, then the view will return the rendered grid only. Otherwise
|
||||||
|
returns the full page.
|
||||||
|
"""
|
||||||
|
app = self.get_rattail_app()
|
||||||
|
model = self.model
|
||||||
|
|
||||||
|
try:
|
||||||
|
process_info = self.datasync_handler.get_supervisor_process_info()
|
||||||
|
supervisor_error = None
|
||||||
|
except Exception as error:
|
||||||
|
process_info = None
|
||||||
|
supervisor_error = simple_error(error)
|
||||||
|
|
||||||
|
profiles = self.datasync_handler.get_configured_profiles()
|
||||||
|
|
||||||
|
sql = """
|
||||||
|
select source, consumer, count(*) as changes
|
||||||
|
from datasync_change
|
||||||
|
group by source, consumer
|
||||||
|
"""
|
||||||
|
result = self.Session.execute(sql)
|
||||||
|
all_changes = {}
|
||||||
|
for row in result:
|
||||||
|
all_changes[(row.source, row.consumer)] = row.changes
|
||||||
|
|
||||||
|
watcher_data = []
|
||||||
|
consumer_data = []
|
||||||
|
now = app.localtime()
|
||||||
|
for key, profile in six.iteritems(profiles):
|
||||||
|
watcher = profile.watcher
|
||||||
|
|
||||||
|
lastrun = self.datasync_handler.get_watcher_lastrun(
|
||||||
|
watcher.key, local=True, session=self.Session())
|
||||||
|
|
||||||
|
status = "okay"
|
||||||
|
if (now - lastrun).total_seconds() >= (watcher.delay * 2):
|
||||||
|
status = "dead watcher"
|
||||||
|
|
||||||
|
watcher_data.append({
|
||||||
|
'key': watcher.key,
|
||||||
|
'spec': profile.watcher_spec,
|
||||||
|
'dbkey': watcher.dbkey,
|
||||||
|
'delay': watcher.delay,
|
||||||
|
'lastrun': raw_datetime(self.rattail_config, lastrun, verbose=True),
|
||||||
|
'status': status,
|
||||||
|
})
|
||||||
|
|
||||||
|
for consumer in profile.consumers:
|
||||||
|
if consumer.watcher is watcher:
|
||||||
|
|
||||||
|
changes = all_changes.get((watcher.key, consumer.key), 0)
|
||||||
|
if changes:
|
||||||
|
oldest = self.Session.query(sa.func.min(model.DataSyncChange.obtained))\
|
||||||
|
.filter(model.DataSyncChange.source == watcher.key)\
|
||||||
|
.filter(model.DataSyncChange.consumer == consumer.key)\
|
||||||
|
.scalar()
|
||||||
|
oldest = app.localtime(oldest, from_utc=True)
|
||||||
|
changes = "{} (oldest from {})".format(
|
||||||
|
changes,
|
||||||
|
app.render_time_ago(now - oldest))
|
||||||
|
|
||||||
|
status = "okay"
|
||||||
|
if changes:
|
||||||
|
status = "processing changes"
|
||||||
|
|
||||||
|
consumer_data.append({
|
||||||
|
'key': '{} -> {}'.format(watcher.key, consumer.key),
|
||||||
|
'spec': consumer.spec,
|
||||||
|
'dbkey': consumer.dbkey,
|
||||||
|
'delay': consumer.delay,
|
||||||
|
'changes': changes,
|
||||||
|
'status': status,
|
||||||
|
})
|
||||||
|
|
||||||
|
watcher_data.sort(key=lambda w: w['key'])
|
||||||
|
consumer_data.sort(key=lambda c: c['key'])
|
||||||
|
|
||||||
|
context = {
|
||||||
|
'index_title': "DataSync Status",
|
||||||
|
'index_url': None,
|
||||||
|
'process_info': process_info,
|
||||||
|
'supervisor_error': supervisor_error,
|
||||||
|
'watcher_data': watcher_data,
|
||||||
|
'consumer_data': consumer_data,
|
||||||
|
}
|
||||||
|
return self.render_to_response('status', context)
|
||||||
|
|
||||||
def get_data(self, session=None):
|
def get_data(self, session=None):
|
||||||
data = []
|
data = []
|
||||||
return data
|
return data
|
||||||
|
|
||||||
def restart(self):
|
def restart(self):
|
||||||
cmd = self.rattail_config.getlist('tailbone', 'datasync.restart',
|
try:
|
||||||
# nb. simulate by default
|
self.datasync_handler.restart_supervisor_process()
|
||||||
default='/bin/sleep 3')
|
|
||||||
log.debug("attempting datasync restart with command: %s", cmd)
|
|
||||||
result = subprocess.call(cmd)
|
|
||||||
if result == 0:
|
|
||||||
self.request.session.flash("DataSync daemon has been restarted.")
|
self.request.session.flash("DataSync daemon has been restarted.")
|
||||||
else:
|
|
||||||
self.request.session.flash("DataSync daemon could not be restarted; result was: {}".format(result), 'error')
|
except Exception as error:
|
||||||
return self.redirect(self.request.get_referrer(default=self.request.route_url('datasyncchanges')))
|
self.request.session.flash(simple_error(error), 'error')
|
||||||
|
|
||||||
|
return self.redirect(self.request.get_referrer(
|
||||||
|
default=self.request.route_url('datasyncchanges')))
|
||||||
|
|
||||||
def configure_get_context(self):
|
def configure_get_context(self):
|
||||||
profiles = load_profiles(self.rattail_config,
|
profiles = self.datasync_handler.get_configured_profiles(
|
||||||
include_disabled=True,
|
include_disabled=True,
|
||||||
ignore_problems=True)
|
ignore_problems=True)
|
||||||
|
|
||||||
profiles_data = []
|
profiles_data = []
|
||||||
for profile in sorted(profiles.values(), key=lambda p: p.key):
|
for profile in sorted(profiles.values(), key=lambda p: p.key):
|
||||||
|
@ -125,7 +226,12 @@ class DataSyncThreadView(MasterView):
|
||||||
return {
|
return {
|
||||||
'profiles': profiles,
|
'profiles': profiles,
|
||||||
'profiles_data': profiles_data,
|
'profiles_data': profiles_data,
|
||||||
'restart_command': self.rattail_config.get('tailbone', 'datasync.restart'),
|
'use_profile_settings': self.rattail_config.getbool(
|
||||||
|
'rattail.datasync', 'use_profile_settings'),
|
||||||
|
'supervisor_process_name': self.rattail_config.get(
|
||||||
|
'rattail.datasync', 'supervisor_process_name'),
|
||||||
|
'restart_command': self.rattail_config.get(
|
||||||
|
'tailbone', 'datasync.restart'),
|
||||||
'system_user': getpass.getuser(),
|
'system_user': getpass.getuser(),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -133,58 +239,67 @@ class DataSyncThreadView(MasterView):
|
||||||
settings = []
|
settings = []
|
||||||
watch = []
|
watch = []
|
||||||
|
|
||||||
for profile in json.loads(data['profiles']):
|
use_profile_settings = data.get('use_profile_settings') == 'true'
|
||||||
pkey = profile['key']
|
settings.append({'name': 'rattail.datasync.use_profile_settings',
|
||||||
if profile['enabled']:
|
'value': 'true' if use_profile_settings else 'false'})
|
||||||
watch.append(pkey)
|
|
||||||
|
|
||||||
settings.extend([
|
if use_profile_settings:
|
||||||
{'name': 'rattail.datasync.{}.watcher'.format(pkey),
|
|
||||||
'value': profile['watcher_spec']},
|
|
||||||
{'name': 'rattail.datasync.{}.watcher.db'.format(pkey),
|
|
||||||
'value': profile['watcher_dbkey']},
|
|
||||||
{'name': 'rattail.datasync.{}.watcher.delay'.format(pkey),
|
|
||||||
'value': profile['watcher_delay']},
|
|
||||||
{'name': 'rattail.datasync.{}.watcher.retry_attempts'.format(pkey),
|
|
||||||
'value': profile['watcher_retry_attempts']},
|
|
||||||
{'name': 'rattail.datasync.{}.watcher.retry_delay'.format(pkey),
|
|
||||||
'value': profile['watcher_retry_delay']},
|
|
||||||
{'name': 'rattail.datasync.{}.consumers.runas'.format(pkey),
|
|
||||||
'value': profile['watcher_default_runas']},
|
|
||||||
])
|
|
||||||
|
|
||||||
consumers = []
|
for profile in json.loads(data['profiles']):
|
||||||
if profile['watcher_consumes_self']:
|
pkey = profile['key']
|
||||||
consumers = ['self']
|
if profile['enabled']:
|
||||||
else:
|
watch.append(pkey)
|
||||||
|
|
||||||
for consumer in profile['consumers_data']:
|
settings.extend([
|
||||||
ckey = consumer['key']
|
{'name': 'rattail.datasync.{}.watcher'.format(pkey),
|
||||||
if consumer['enabled']:
|
'value': profile['watcher_spec']},
|
||||||
consumers.append(ckey)
|
{'name': 'rattail.datasync.{}.watcher.db'.format(pkey),
|
||||||
settings.extend([
|
'value': profile['watcher_dbkey']},
|
||||||
{'name': 'rattail.datasync.{}.consumer.{}'.format(pkey, ckey),
|
{'name': 'rattail.datasync.{}.watcher.delay'.format(pkey),
|
||||||
'value': consumer['consumer_spec']},
|
'value': profile['watcher_delay']},
|
||||||
{'name': 'rattail.datasync.{}.consumer.{}.db'.format(pkey, ckey),
|
{'name': 'rattail.datasync.{}.watcher.retry_attempts'.format(pkey),
|
||||||
'value': consumer['consumer_dbkey']},
|
'value': profile['watcher_retry_attempts']},
|
||||||
{'name': 'rattail.datasync.{}.consumer.{}.delay'.format(pkey, ckey),
|
{'name': 'rattail.datasync.{}.watcher.retry_delay'.format(pkey),
|
||||||
'value': consumer['consumer_delay']},
|
'value': profile['watcher_retry_delay']},
|
||||||
{'name': 'rattail.datasync.{}.consumer.{}.retry_attempts'.format(pkey, ckey),
|
{'name': 'rattail.datasync.{}.consumers.runas'.format(pkey),
|
||||||
'value': consumer['consumer_retry_attempts']},
|
'value': profile['watcher_default_runas']},
|
||||||
{'name': 'rattail.datasync.{}.consumer.{}.retry_delay'.format(pkey, ckey),
|
])
|
||||||
'value': consumer['consumer_retry_delay']},
|
|
||||||
{'name': 'rattail.datasync.{}.consumer.{}.runas'.format(pkey, ckey),
|
|
||||||
'value': consumer['consumer_runas']},
|
|
||||||
])
|
|
||||||
|
|
||||||
settings.extend([
|
consumers = []
|
||||||
{'name': 'rattail.datasync.{}.consumers'.format(pkey),
|
if profile['watcher_consumes_self']:
|
||||||
'value': ', '.join(consumers)},
|
consumers = ['self']
|
||||||
])
|
else:
|
||||||
|
|
||||||
if watch:
|
for consumer in profile['consumers_data']:
|
||||||
settings.append({'name': 'rattail.datasync.watch',
|
ckey = consumer['key']
|
||||||
'value': ', '.join(watch)})
|
if consumer['enabled']:
|
||||||
|
consumers.append(ckey)
|
||||||
|
settings.extend([
|
||||||
|
{'name': 'rattail.datasync.{}.consumer.{}'.format(pkey, ckey),
|
||||||
|
'value': consumer['consumer_spec']},
|
||||||
|
{'name': 'rattail.datasync.{}.consumer.{}.db'.format(pkey, ckey),
|
||||||
|
'value': consumer['consumer_dbkey']},
|
||||||
|
{'name': 'rattail.datasync.{}.consumer.{}.delay'.format(pkey, ckey),
|
||||||
|
'value': consumer['consumer_delay']},
|
||||||
|
{'name': 'rattail.datasync.{}.consumer.{}.retry_attempts'.format(pkey, ckey),
|
||||||
|
'value': consumer['consumer_retry_attempts']},
|
||||||
|
{'name': 'rattail.datasync.{}.consumer.{}.retry_delay'.format(pkey, ckey),
|
||||||
|
'value': consumer['consumer_retry_delay']},
|
||||||
|
{'name': 'rattail.datasync.{}.consumer.{}.runas'.format(pkey, ckey),
|
||||||
|
'value': consumer['consumer_runas']},
|
||||||
|
])
|
||||||
|
|
||||||
|
settings.extend([
|
||||||
|
{'name': 'rattail.datasync.{}.consumers'.format(pkey),
|
||||||
|
'value': ', '.join(consumers)},
|
||||||
|
])
|
||||||
|
|
||||||
|
if watch:
|
||||||
|
settings.append({'name': 'rattail.datasync.watch',
|
||||||
|
'value': ', '.join(watch)})
|
||||||
|
|
||||||
|
settings.append({'name': 'rattail.datasync.supervisor_process_name',
|
||||||
|
'value': data['supervisor_process_name']})
|
||||||
|
|
||||||
settings.append({'name': 'tailbone.datasync.restart',
|
settings.append({'name': 'tailbone.datasync.restart',
|
||||||
'value': data['restart_command']})
|
'value': data['restart_command']})
|
||||||
|
@ -204,6 +319,25 @@ class DataSyncThreadView(MasterView):
|
||||||
permission_prefix = cls.get_permission_prefix()
|
permission_prefix = cls.get_permission_prefix()
|
||||||
route_prefix = cls.get_route_prefix()
|
route_prefix = cls.get_route_prefix()
|
||||||
url_prefix = cls.get_url_prefix()
|
url_prefix = cls.get_url_prefix()
|
||||||
|
index_title = cls.get_index_title()
|
||||||
|
|
||||||
|
# view status
|
||||||
|
config.add_tailbone_permission(permission_prefix,
|
||||||
|
'{}.status'.format(permission_prefix),
|
||||||
|
"View status for DataSync daemon")
|
||||||
|
# nb. simple 'datasync' route points to 'datasync.status' for now..
|
||||||
|
config.add_route(route_prefix,
|
||||||
|
'{}/status/'.format(url_prefix))
|
||||||
|
config.add_route('{}.status'.format(route_prefix),
|
||||||
|
'{}/status/'.format(url_prefix))
|
||||||
|
config.add_view(cls, attr='status',
|
||||||
|
route_name=route_prefix,
|
||||||
|
permission='{}.status'.format(permission_prefix))
|
||||||
|
config.add_view(cls, attr='status',
|
||||||
|
route_name='{}.status'.format(route_prefix),
|
||||||
|
permission='{}.status'.format(permission_prefix))
|
||||||
|
config.add_tailbone_index_page(route_prefix, index_title,
|
||||||
|
'{}.status'.format(permission_prefix))
|
||||||
|
|
||||||
# restart
|
# restart
|
||||||
config.add_tailbone_permission(permission_prefix,
|
config.add_tailbone_permission(permission_prefix,
|
||||||
|
|
Loading…
Reference in a new issue