Horizon tab for PTP configuration

Expose Precision Time Protocol (PTP) configuration on StarlingX GUI.
4 parameters are available to play with there:
    - Enabled: allows to turn PTP on or off.
    - Mode: selects time stamping mechanism. It could be hardware,
    software or legacy hardware time stamping.
    - Transport: switches between IEEE 802.3 or UDP network transport
    for PTP messaging.
    - Mechanism: sets the PTP delay mechanism. There are delay
    request-response mechanism and peer delay mechanism.
Also, adding visualization of NTP status as well.

Change-Id: Ie7ffbeef2ec1179ad5ffc909418d9c2218904dc7
Story: 2002935
Task: 24875
Signed-off-by: Alex Kozyrev <alex.kozyrev@windriver.com>
This commit is contained in:
Alex Kozyrev 2018-08-29 17:58:35 -04:00
parent 86e815ea60
commit 825178fb53
10 changed files with 396 additions and 20 deletions

View File

@ -1153,7 +1153,7 @@ def dns_list(request):
class NTP(base.APIResourceWrapper):
"""..."""
_attrs = ['isystem_uuid', 'ntpservers', 'uuid', 'link']
_attrs = ['isystem_uuid', 'enabled', 'ntpservers', 'uuid', 'link']
def __init__(self, apiresource):
super(NTP, self).__init__(apiresource)
@ -1188,6 +1188,43 @@ def ntp_list(request):
return [NTP(n) for n in ntp]
class PTP(base.APIResourceWrapper):
"""..."""
_attrs = ['isystem_uuid', 'enabled', 'mode',
'transport', 'mechanism', 'uuid', 'link']
def __init__(self, apiresource):
super(PTP, self).__init__(apiresource)
def ptp_update(request, ptp_id, **kwargs):
LOG.debug("ptp_update(): ptp_id=%s, kwargs=%s", ptp_id, kwargs)
mypatch = []
for key, value in kwargs.iteritems():
mypatch.append(dict(path='/' + key, value=value, op='replace'))
return cgtsclient(request).ptp.update(ptp_id, mypatch)
def ptp_delete(request, ptp_id):
LOG.debug("ptp_delete(): ptp_id=%s", ptp_id)
return cgtsclient(request).ptp.delete(ptp_id)
def ptp_get(request, ptp_id):
ptp = cgtsclient(request).ptp.get(ptp_id)
if not ptp:
raise ValueError('No match found for ptp_id "%s".' % ptp_id)
return PTP(ptp)
def ptp_list(request):
ptp = cgtsclient(request).ptp.list()
return [PTP(n) for n in ptp]
class EXTOAM(base.APIResourceWrapper):
"""..."""

View File

@ -219,6 +219,10 @@ class UpdatecDNS(forms.SelfHandlingForm):
class UpdatecNTP(forms.SelfHandlingForm):
uuid = forms.CharField(widget=forms.widgets.HiddenInput)
enabled = forms.BooleanField(
label=_("Enabled"),
help_text=_('Enable NTP service.'),
required=False)
NTP_SERVER_1 = forms.CharField(label=_("NTP Server 1 Address"),
initial='NTP_SERVER_1',
@ -262,6 +266,12 @@ class UpdatecNTP(forms.SelfHandlingForm):
else:
data['uuid'] = ' '
if 'enabled' in data.keys():
if not data['enabled']:
data['enabled'] = 'False'
else:
data['enabled'] = 'False'
for index in range(1, max_ntp_servers + 1):
if data['NTP_SERVER_%s' % index] and data[
'NTP_SERVER_%s' % index] != ' ':
@ -273,28 +283,32 @@ class UpdatecNTP(forms.SelfHandlingForm):
if hasattr(ntp_config, 'uuid'):
ntp_config_uuid = ntp_config.uuid
ntp_enabled = ntp_config.enabled
if ntp_config.ntpservers:
ntpservers = ntp_config.ntpservers.split(',')
else:
ntpservers = []
# if their sizes are different, then action=apply
if len(NTPSERVERS) != len(ntpservers):
if ntp_enabled != data['enabled']:
data['action'] = 'apply'
data['enabled'] = str(data['enabled'])
send_to_sysinv = True
else:
# If lengths are same,
# check if order of values have been changed
if set(ntpservers) != set(NTPSERVERS.values()):
for index in range(len(ntpservers)):
if ntpservers[index] != NTPSERVERS[
'NTP_SERVER_%s' % (index + 1)]:
data['action'] = 'apply'
send_to_sysinv = True
# we need to do action=apply only once
break
# if their sizes are different, then action=apply
if len(NTPSERVERS) != len(ntpservers):
data['action'] = 'apply'
send_to_sysinv = True
else:
# If lengths are same,
# check if order of values have been changed
if set(ntpservers) != set(NTPSERVERS.values()):
for index in range(len(ntpservers)):
if ntpservers[index] != NTPSERVERS[
'NTP_SERVER_%s' % (index + 1)]:
data['action'] = 'apply'
send_to_sysinv = True
# we need to do action=apply only once
break
# sysinv accepts csv values as the ntpservers
data['ntpservers'] = ','.join(NTPSERVERS.values())
@ -309,7 +323,7 @@ class UpdatecNTP(forms.SelfHandlingForm):
else:
ntp_config_uuid = ' '
data = {'ntpservers': ''}
data = {'enabled': 'False', 'ntpservers': ''}
LOG.debug(data)
@ -326,7 +340,7 @@ class UpdatecNTP(forms.SelfHandlingForm):
return False
else:
msg = _('No NTP Server changes have been made.')
msg = _('No NTP configuration changes have been made.')
LOG.debug(msg)
messages.info(request, msg)
return True
@ -346,6 +360,141 @@ class UpdatecNTP(forms.SelfHandlingForm):
return False
class UpdatecPTP(forms.SelfHandlingForm):
uuid = forms.CharField(widget=forms.widgets.HiddenInput)
enabled = forms.BooleanField(
label=_("Enabled"),
help_text=_('Enable PTP service.'),
required=False)
PTP_MODE_CHOICES = (
('hardware', _('Hardware time stamping')),
('software', _('Software time stamping')),
('legacy', _('Legacy hardware time stamping')),
)
mode = forms.ChoiceField(label=_("PTP Time Stamping Mode"),
required=False,
choices=PTP_MODE_CHOICES)
PTP_TRANSPORT_CHOICES = (
('l2', _('IEEE 802.3 network transport (L2)')),
('udp', _('UDP IPv4/v6 network transport(UDP)')),
)
transport = forms.ChoiceField(label=_("PTP Network Transport"),
required=False,
choices=PTP_TRANSPORT_CHOICES)
PTP_MECHANISM_CHOICES = (
('e2e', _('Delay request-response (E2E)')),
('p2p', _('Peer delay (P2P)')),
)
mechanism = forms.ChoiceField(label=_("PTP Delay Mechanism"),
required=False,
choices=PTP_MECHANISM_CHOICES)
failure_url = 'horizon:admin:system_config:index'
failure_message = 'Failed to update PTP configurations.'
def __init__(self, request, *args, **kwargs):
super(UpdatecPTP, self).__init__(request, *args, **kwargs)
def handle(self, request, data):
send_to_sysinv = False
try:
if data:
if 'uuid' in data.keys():
if not data['uuid']:
data['uuid'] = ' '
else:
data['uuid'] = ' '
if 'enabled' in data.keys():
if not data['enabled']:
data['enabled'] = 'False'
else:
data['enabled'] = 'False'
if 'mode' in data.keys():
if not data['mode']:
data['mode'] = 'hardware'
else:
data['mode'] = 'False'
if 'transport' in data.keys():
if not data['transport']:
data['transport'] = 'l2'
else:
data['transport'] = 'l2'
if 'mechanism' in data.keys():
if not data['mechanism']:
data['mechanism'] = 'e2e'
else:
data['mechanism'] = 'e2e'
ptp_config = stx_api.sysinv.ptp_get(request, data['uuid'])
if hasattr(ptp_config, 'uuid'):
ptp_config_uuid = ptp_config.uuid
ptp_enabled = ptp_config.enabled
if ptp_enabled != data['enabled']:
data['enabled'] = str(data['enabled'])
send_to_sysinv = True
if ptp_config.mode != data['mode']:
send_to_sysinv = True
if ptp_config.transport != data['transport']:
send_to_sysinv = True
if ptp_config.mechanism != data['mechanism']:
send_to_sysinv = True
else:
ptp_config_uuid = ' '
data.pop('uuid')
else:
ptp_config_uuid = ' '
data = {'enabled': 'False',
'mode': '',
'transport': '',
'mechanism': ''}
LOG.debug(data)
if send_to_sysinv:
my_ptp = \
stx_api.sysinv.ptp_update(request, ptp_config_uuid, **data)
if my_ptp:
msg = _('PTP configuration was successfully updated. ')
LOG.debug(msg)
messages.success(request, msg)
return True
else:
return False
else:
msg = _('No PTP configuratiom changes have been made.')
LOG.debug(msg)
messages.info(request, msg)
return True
except exc.ClientException as ce:
messages.error(request, ce)
# Display REST API error on the GUI
LOG.error(ce)
# redirect = reverse(self.failure_url)
return False
except Exception:
msg = _('Failed to update PTP configuration.')
messages.error(request, msg)
LOG.info(msg)
# redirect = reverse(self.failure_url)
return False
class UpdatecEXT_OAM(forms.SelfHandlingForm):
uuid = forms.CharField(widget=forms.widgets.HiddenInput)

View File

@ -89,6 +89,13 @@ class EditcNTP(tables.LinkAction):
classes = ("ajax-modal", "btn-edit")
class EditcPTP(tables.LinkAction):
name = "update_cptp"
verbose_name = _("Edit PTP")
url = "horizon:admin:system_config:update_cptp_table"
classes = ("ajax-modal", "btn-edit")
class EditcOAM(tables.LinkAction):
name = "update_coam"
verbose_name = _("Edit OAM IP")
@ -156,6 +163,10 @@ class UpdateNTPRow(tables.Row):
class cNTPTable(tables.DataTable):
enabled = tables.Column(
'enabled',
verbose_name=_('NTP Enabled'))
ntpserver_1 = tables.Column(
'ntpserver_1',
# link="horizon:admin:system_config:detail_cdns",
@ -185,6 +196,44 @@ class cNTPTable(tables.DataTable):
table_actions = (EditcNTP,)
class UpdatePTPRow(tables.Row):
ajax = True
def get_data(self, request, obj_id):
return stx_api.sysinv.ptp_get(request, obj_id)
class cPTPTable(tables.DataTable):
enabled = tables.Column(
'enabled',
verbose_name=_('PTP Enabled'))
mode = tables.Column(
'mode',
verbose_name=_('PTP Time Stamping Mode'))
transport = tables.Column(
'transport',
verbose_name=_('PTP Network Transport'))
mechanism = tables.Column(
'mechanism',
verbose_name=_('PTP Delay Mechanism'))
def get_object_id(self, datum):
return unicode(datum.uuid)
def get_object_display(self, datum):
return datum.uuid
class Meta(object):
name = "cptp_table"
verbose_name = _("PTP")
row_class = UpdatePTPRow
multi_select = False
table_actions = (EditcPTP,)
class UpdateOAMRow(tables.Row):
ajax = True

View File

@ -105,6 +105,7 @@ class cNTPTab(tabs.TableTab):
try:
ntp_data = {'uuid': ' ',
'enabled': False,
'ntpserver_1': ' ',
'ntpserver_2': ' ',
'ntpserver_3': ' '}
@ -114,6 +115,7 @@ class cNTPTab(tabs.TableTab):
ntp = ntp_list[0]
ntp_data['uuid'] = ntp.uuid
ntp_data['enabled'] = ntp.enabled
if ntp.ntpservers:
servers = ntp.ntpservers.split(",")
for index, server in enumerate(servers):
@ -128,6 +130,42 @@ class cNTPTab(tabs.TableTab):
return data
class cPTPTab(tabs.TableTab):
table_classes = (toplevel_tables.cPTPTable, )
name = _("PTP")
slug = "cptp_table"
template_name = ("admin/system_config/_cptp_table.html")
def get_cptp_table_data(self):
request = self.request
data = []
try:
ptp_data = {'uuid': ' ',
'enabled': False,
'mode': ' ',
'transport': ' ',
'mechanism': ' '}
ptp_list = stx_api.sysinv.ptp_list(request)
if ptp_list:
ptp = ptp_list[0]
ptp_data['uuid'] = ptp.uuid
ptp_data['enabled'] = ptp.enabled
ptp_data['mode'] = ptp.mode
ptp_data['transport'] = ptp.transport
ptp_data['mechanism'] = ptp.mechanism
data.append(type('PTP', (object,), ptp_data)())
except Exception:
exceptions.handle(request,
_('Unable to retrieve ptp list.'))
return data
class cEXTOAMTab(tabs.TableTab):
table_classes = (toplevel_tables.cOAMTable, )
name = _("OAM IP")
@ -251,7 +289,7 @@ class CeilometerConfigTab(tabs.TableTab):
class ConfigTabs(tabs.TabGroup):
slug = "system_config_tab"
tabs = (SystemsTab, AddressPoolsTab, cDNSTab, cNTPTab, cEXTOAMTab,
iStorageTab, iStoragePoolsTab, SDNControllerTab,
tabs = (SystemsTab, AddressPoolsTab, cDNSTab, cNTPTab, cPTPTab,
cEXTOAMTab, iStorageTab, iStoragePoolsTab, SDNControllerTab,
CeilometerConfigTab)
sticky = True

View File

@ -0,0 +1,7 @@
{% load i18n sizeformat %}
{% block main %}
<div id="cptp_table">
{{ cptp_table_table.render }}
</div>
{% endblock %}

View File

@ -15,7 +15,7 @@
</div>
<div class="right">
<h3>{% trans "Description" %}:</h3>
<p>{% trans "From here you can update the configuration of the NTP servers." %}</p>
<p>{% trans "From here you can update the configuration of the NTP service." %}</p>
<p>{% trans "WARNING: Completion of NTP configuration change will require lock and unlock of affected hosts." %}</p>
<p>{% trans "Major Alarms will be raised against the affected hosts until the lock unlock operation is successfully completed." %}</p>
</div>

View File

@ -0,0 +1,30 @@
{% extends "horizon/common/_modal_form.html" %}
{% load i18n %}
{% block form_id %}edit_iconfig_form{% endblock %}
{% block form_action %}{% url 'horizon:admin:system_config:update_cptp_table' %}{% endblock %}
{% block modal_id %}edit_iconfig_modal{% endblock %}
{% block modal-header %}{% trans "Edit PTP" %}{% endblock %}
{% block modal-body %}
<div class="left">
<fieldset>
{% include "horizon/common/_form_fields.html" %}
</fieldset>
</div>
<div class="right">
<h3>{% trans "Description" %}:</h3>
<p>{% trans "From here you can update the configuration of the PTP service." %}</p>
<p>{% trans "WARNING: Completion of PTP configuration change will require lock and unlock of affected hosts." %}</p>
<p>{% trans "Major Alarms will be raised against the affected hosts until the lock unlock operation is successfully completed." %}</p>
</div>
{% endblock %}
{% block modal-footer %}
<a class="btn btn-default cancel" data-dismiss="modal">{% trans "Cancel" %}</a>
<input class="btn btn-primary btn-danger" type="submit" onclick="return confirm('WARNING: ' +
'Completion of PTP configuration change will require lock and unlock of affected hosts. ' +
'Major Alarms will be raised against the affected hosts until the lock unlock operation is successfully completed.');"
value="{% trans "Save" %}" />
{% endblock %}

View File

@ -0,0 +1,11 @@
{% extends 'base.html' %}
{% load i18n %}
{% block title %}{% trans "Edit PTP" %}{% endblock %}
{% block page_header %}
{% include "horizon/common/_page_header.html" with title=_("Edit PTP") %}
{% endblock page_header %}
{% block main %}
{% include "admin/system_config/_update_cptp_table.html" %}
{% endblock %}

View File

@ -20,6 +20,8 @@ from starlingx_dashboard.dashboards.admin.system_config.views \
import UpdatecEXT_OAMView
from starlingx_dashboard.dashboards.admin.system_config.views \
import UpdatecNTPView
from starlingx_dashboard.dashboards.admin.system_config.views \
import UpdatecPTPView
from starlingx_dashboard.dashboards.admin.system_config.views import \
UpdateiStoragePoolsView
from starlingx_dashboard.dashboards.admin.system_config.views import \
@ -51,6 +53,8 @@ urlpatterns = [
name='update_cdns_table'),
url(r'^update_cntp_table/$', UpdatecNTPView.as_view(),
name='update_cntp_table'),
url(r'^update_cptp_table/$', UpdatecPTPView.as_view(),
name='update_cptp_table'),
url(r'^update_coam_table/$', UpdatecEXT_OAMView.as_view(),
name='update_coam_table'),

View File

@ -28,6 +28,8 @@ from starlingx_dashboard.dashboards.admin.system_config.forms \
import UpdatecEXT_OAM
from starlingx_dashboard.dashboards.admin.system_config.forms \
import UpdatecNTP
from starlingx_dashboard.dashboards.admin.system_config.forms \
import UpdatecPTP
from starlingx_dashboard.dashboards.admin.system_config.forms \
import UpdateiStorage
from starlingx_dashboard.dashboards.admin.system_config.forms \
@ -149,6 +151,7 @@ class UpdatecNTPView(forms.ModalFormView):
def get_initial(self):
ntp_form_data = {'uuid': ' ',
'enabled': False,
'NTP_SERVER_1': None,
'NTP_SERVER_2': None,
'NTP_SERVER_3': None}
@ -160,6 +163,7 @@ class UpdatecNTPView(forms.ModalFormView):
ntp = ntp_list[0]
ntp_form_data['uuid'] = ntp.uuid
ntp_form_data['enabled'] = ntp.enabled
if ntp.ntpservers:
servers = ntp.ntpservers.split(",")
for index, server in enumerate(servers):
@ -172,6 +176,53 @@ class UpdatecNTPView(forms.ModalFormView):
return ntp_form_data
class UpdatecPTPView(forms.ModalFormView):
form_class = UpdatecPTP
template_name = 'admin/system_config/update_cptp_table.html'
success_url = reverse_lazy('horizon:admin:system_config:index')
def get_context_data(self, **kwargs):
context = super(UpdatecPTPView, self).get_context_data(**kwargs)
ptp_list = stx_api.sysinv.ptp_list(self.request)
if ptp_list:
if "uuid" in ptp_list[0]._attrs:
uuid = ptp_list[0].uuid
else:
uuid = " "
else:
uuid = " "
context['uuid'] = uuid
return context
def get_initial(self):
ptp_form_data = {'uuid': ' ',
'enabled': False,
'mode': None,
'transport': None,
'mechanism': None}
try:
ptp_list = stx_api.sysinv.ptp_list(self.request)
if ptp_list:
ptp = ptp_list[0]
ptp_form_data['uuid'] = ptp.uuid
ptp_form_data['enabled'] = ptp.enabled
ptp_form_data['mode'] = ptp.mode
ptp_form_data['transport'] = ptp.transport
ptp_form_data['mechanism'] = ptp.mechanism
except Exception:
exceptions.handle(self.request,
_("Unable to retrieve PTP data."))
return ptp_form_data
class UpdatecEXT_OAMView(forms.ModalFormView):
form_class = UpdatecEXT_OAM
template_name = 'admin/system_config/update_coam_table.html'