Implementation of PTP support in SysInv and Puppet
Precision Time Protocol (PTP) support is added to StarlingX. Controller Nodes act as a Boundary Clocks and synchronize its clock to a Grand Master clock via OAM interface. Serve as a time source to other nodes. Compute/Storage Nodes are Slave Clocks (Ordinary Clocks) and synchronize its clock to a Controller Nodes via Management interface. API is provided to enable and configure PTP as follows: "system ptp-modify --enabled=<true/false>" is there to turn it on/off. Note that NTP must be disabled first before turning PTP service on. "system ptp-modify --mode=<hardware/software/legacy>" selects time stamping. Hardware timestamping is the default option and achieves best time syncing. "system ptp-modify --transport=<l2,udp>" switches between IEEE 802.3 or UDP network transport for PTP messaging. L2 is the default transport. "system ptp-modify --mechanism=<e2e,p2p>" sets the PTP delay mechanism. Options: default delay request-response (E2E) mechanism and peer delay (P2P). "system ptp-show" displays the current status of PTP service. Change-Id: I6bb6162903c70a49b55f4fc02b44e5ca8a66d5a5 Story: 2002935 Task: 22923 Signed-off-by: Alex Kozyrev <alex.kozyrev@windriver.com>
This commit is contained in:
parent
5354843163
commit
3a42372b64
@ -20,6 +20,7 @@ include ::platform::fstab
|
||||
include ::platform::password
|
||||
include ::platform::ldap::client
|
||||
include ::platform::ntp::client
|
||||
include ::platform::ptp
|
||||
include ::platform::lldp
|
||||
include ::platform::patching
|
||||
include ::platform::remotelogging
|
||||
|
@ -25,6 +25,7 @@ include ::platform::ldap::server
|
||||
include ::platform::ldap::client
|
||||
include ::platform::password
|
||||
include ::platform::ntp::server
|
||||
include ::platform::ptp
|
||||
include ::platform::lldp
|
||||
include ::platform::amqp::rabbitmq
|
||||
include ::platform::postgresql::server
|
||||
|
@ -18,6 +18,7 @@ include ::platform::fstab
|
||||
include ::platform::password
|
||||
include ::platform::ldap::client
|
||||
include ::platform::ntp::client
|
||||
include ::platform::ptp
|
||||
include ::platform::lldp
|
||||
include ::platform::patching
|
||||
include ::platform::remotelogging
|
||||
|
@ -211,6 +211,13 @@ define platform::firewall::services (
|
||||
provider => $provider,
|
||||
}
|
||||
|
||||
firewall { "203 platform accept ptp ${version}":
|
||||
proto => 'udp',
|
||||
dport => [319, 320],
|
||||
action => 'accept',
|
||||
provider => $provider,
|
||||
}
|
||||
|
||||
# allow IGMP Query traffic if IGMP Snooping is
|
||||
# enabled on the TOR switch
|
||||
firewall { "204 platform accept igmp ${version}":
|
||||
|
134
puppet-manifests/src/modules/platform/manifests/ptp.pp
Normal file
134
puppet-manifests/src/modules/platform/manifests/ptp.pp
Normal file
@ -0,0 +1,134 @@
|
||||
class platform::ptp (
|
||||
$enabled = false,
|
||||
$mode = 'hardware',
|
||||
$transport = 'l2',
|
||||
$mechanism = 'e2e',
|
||||
) {
|
||||
if $::platform::params::personality == 'controller' {
|
||||
include ::platform::network::oam::params
|
||||
$oam_interface = $::platform::network::oam::params::interface_name
|
||||
$slave_interface = split($oam_interface, '[.]')[0]
|
||||
$slave_subnet = $::platform::network::oam::params::subnet_version
|
||||
include ::platform::network::mgmt::params
|
||||
$mgmt_interface = $::platform::network::mgmt::params::interface_name
|
||||
$master_interface = split($mgmt_interface, '[.]')[0]
|
||||
$master_subnet = $::platform::network::mgmt::params::subnet_version
|
||||
if $::platform::params::system_type == 'All-in-one' {
|
||||
$slave_only = true
|
||||
} else {
|
||||
$slave_only = false
|
||||
}
|
||||
} else {
|
||||
include ::platform::network::mgmt::params
|
||||
$mgmt_interface = $::platform::network::mgmt::params::interface_name
|
||||
$slave_interface = split($mgmt_interface, '[.]')[0]
|
||||
$slave_subnet = $::platform::network::mgmt::params::subnet_version
|
||||
$slave_only = true
|
||||
}
|
||||
|
||||
if $enabled {
|
||||
$pmon_ensure = 'link'
|
||||
} else {
|
||||
$pmon_ensure = 'absent'
|
||||
}
|
||||
|
||||
file { 'ptp4l_config':
|
||||
ensure => file,
|
||||
path => '/etc/ptp4l.conf',
|
||||
mode => '0644',
|
||||
content => template('platform/ptp4l.conf.erb'),
|
||||
} ->
|
||||
file { 'ptp4l_service':
|
||||
ensure => file,
|
||||
path => '/usr/lib/systemd/system/ptp4l.service',
|
||||
mode => '0644',
|
||||
content => template('platform/ptp4l.service.erb'),
|
||||
} ->
|
||||
file { 'ptp4l_sysconfig':
|
||||
ensure => file,
|
||||
path => '/etc/sysconfig/ptp4l',
|
||||
mode => '0644',
|
||||
content => template('platform/ptp4l.erb'),
|
||||
} ->
|
||||
file { 'phc2sys_service':
|
||||
ensure => file,
|
||||
path => '/usr/lib/systemd/system/phc2sys.service',
|
||||
mode => '0644',
|
||||
content => template('platform/phc2sys.service.erb'),
|
||||
} ->
|
||||
file { 'phc2sys_sysconfig':
|
||||
ensure => file,
|
||||
path => '/etc/sysconfig/phc2sys',
|
||||
mode => '0644',
|
||||
content => template('platform/phc2sys.erb'),
|
||||
} ->
|
||||
file { 'ptp4l_pmon':
|
||||
ensure => file,
|
||||
path => '/etc/ptp4l.pmon.conf',
|
||||
mode => '0644',
|
||||
content => template('platform/ptp4l.pmon.conf.erb'),
|
||||
} ->
|
||||
file { 'phc2sys_pmon':
|
||||
ensure => file,
|
||||
path => '/etc/phc2sys.pmon.conf',
|
||||
mode => '0644',
|
||||
content => template('platform/phc2sys.pmon.conf.erb'),
|
||||
} ->
|
||||
file { 'ptp4l_pmon_link':
|
||||
ensure => $pmon_ensure,
|
||||
path => '/etc/pmon.d/ptp4l.conf',
|
||||
target => '/etc/ptp4l.pmon.conf',
|
||||
owner => 'root',
|
||||
group => 'root',
|
||||
mode => '0600',
|
||||
} ->
|
||||
file { 'phc2sys_pmon_link':
|
||||
ensure => $pmon_ensure,
|
||||
path => '/etc/pmon.d/phc2sys.conf',
|
||||
target => '/etc/phc2sys.pmon.conf',
|
||||
owner => 'root',
|
||||
group => 'root',
|
||||
mode => '0600',
|
||||
} ->
|
||||
exec { 'systemctl-daemon-reload':
|
||||
command => '/usr/bin/systemctl daemon-reload',
|
||||
}
|
||||
|
||||
if $enabled {
|
||||
exec { 'enable-ptp4l':
|
||||
command => '/usr/bin/systemctl enable ptp4l.service',
|
||||
require => Exec['systemctl-daemon-reload'],
|
||||
} ->
|
||||
exec { 'enable-phc2sys':
|
||||
command => '/usr/bin/systemctl enable phc2sys.service',
|
||||
} ->
|
||||
service { 'ptp4l':
|
||||
ensure => 'running',
|
||||
enable => true,
|
||||
name => 'ptp4l',
|
||||
hasstatus => true,
|
||||
hasrestart => true,
|
||||
} ->
|
||||
service { 'phc2sys':
|
||||
ensure => 'running',
|
||||
enable => true,
|
||||
name => 'phc2sys',
|
||||
hasstatus => true,
|
||||
hasrestart => true,
|
||||
}
|
||||
} else {
|
||||
exec { 'disable-ptp4l':
|
||||
command => '/usr/bin/systemctl disable ptp4l.service',
|
||||
require => Exec['systemctl-daemon-reload'],
|
||||
} ->
|
||||
exec { 'disable-phc2sys':
|
||||
command => '/usr/bin/systemctl disable phc2sys.service',
|
||||
}
|
||||
exec { 'stop-ptp4l':
|
||||
command => '/usr/bin/systemctl stop ptp4l.service',
|
||||
} ->
|
||||
exec { 'stop-phc2sys':
|
||||
command => '/usr/bin/systemctl stop phc2sys.service',
|
||||
}
|
||||
}
|
||||
}
|
@ -11,7 +11,7 @@ restrict -6 default kod nomodify notrap nopeer noquery
|
||||
restrict 127.0.0.1
|
||||
restrict -6 ::1
|
||||
|
||||
<%- if @enabled == true -%>
|
||||
<%- if scope['platform::ntp::enabled'] == true -%>
|
||||
# Use orphan mode if external servers are unavailable (or not configured)
|
||||
tos orphan 12
|
||||
|
||||
|
@ -11,7 +11,7 @@ restrict -6 default kod nomodify notrap nopeer noquery
|
||||
restrict 127.0.0.1
|
||||
restrict -6 ::1
|
||||
|
||||
<%- if @enabled == true -%>
|
||||
<%- if scope['platform::ntp::enabled'] == true -%>
|
||||
# orphan - Use orphan mode if external servers are unavailable (or not configured).
|
||||
# minclock - Prevent clustering algorithm from casting out any outlyers by setting
|
||||
# minclock to the maximum number of ntp servers that can be configured
|
||||
|
@ -0,0 +1 @@
|
||||
OPTIONS="-a -r -E linreg -u 60"
|
@ -0,0 +1,19 @@
|
||||
[process]
|
||||
process = phc2sys
|
||||
service = phc2sys
|
||||
pidfile = /var/run/phc2sys.pid
|
||||
style = lsb ; ocf or lsb
|
||||
severity = minor ; minor, major, critical
|
||||
restarts = 0 ; restart retries before error assertion
|
||||
interval = 10 ; number of seconds to wait between restarts
|
||||
debounce = 10 ; number of seconds that a process needs to remain
|
||||
; running before degrade is removed and retry count
|
||||
; is cleared.
|
||||
; These settings will generate a log only without attempting to restart
|
||||
; pmond will put the process into an ignore state after failure.
|
||||
|
||||
startuptime = 180 ; Seconds to wait after process start before starting the debounce monitor
|
||||
mode = passive ; Monitoring mode: passive (default) or active
|
||||
; passive: process death monitoring (default: always)
|
||||
; active : heartbeat monitoring, i.e. request / response messaging
|
||||
; ignore : do not monitor or stop monitoring
|
@ -0,0 +1,13 @@
|
||||
[Unit]
|
||||
Description=Synchronize system clock or PTP hardware clock (PHC)
|
||||
After=ntpdate.service
|
||||
|
||||
[Service]
|
||||
Type=simple
|
||||
EnvironmentFile=-/etc/sysconfig/phc2sys
|
||||
ExecStart=/usr/sbin/phc2sys $OPTIONS
|
||||
ExecStartPost=/bin/bash -c 'echo $MAINPID > /var/run/phc2sys.pid'
|
||||
ExecStopPost=/bin/rm -f /var/run/phc2sys.pid
|
||||
|
||||
[Install]
|
||||
WantedBy=multi-user.target
|
107
puppet-manifests/src/modules/platform/templates/ptp4l.conf.erb
Normal file
107
puppet-manifests/src/modules/platform/templates/ptp4l.conf.erb
Normal file
@ -0,0 +1,107 @@
|
||||
[global]
|
||||
#
|
||||
# Default Data Set
|
||||
#
|
||||
twoStepFlag 1
|
||||
<%- if @slave_only == true -%>
|
||||
slaveOnly 1
|
||||
<%- else -%>
|
||||
slaveOnly 0
|
||||
<%- end -%>
|
||||
priority1 128
|
||||
priority2 128
|
||||
domainNumber 0
|
||||
#utc_offset 37
|
||||
clockClass 248
|
||||
clockAccuracy 0xFE
|
||||
offsetScaledLogVariance 0xFFFF
|
||||
free_running 0
|
||||
freq_est_interval 1
|
||||
dscp_event 0
|
||||
dscp_general 0
|
||||
#
|
||||
# Port Data Set
|
||||
#
|
||||
logAnnounceInterval 1
|
||||
logSyncInterval 0
|
||||
logMinDelayReqInterval 0
|
||||
logMinPdelayReqInterval 0
|
||||
announceReceiptTimeout 3
|
||||
syncReceiptTimeout 0
|
||||
delayAsymmetry 0
|
||||
fault_reset_interval 4
|
||||
neighborPropDelayThresh 20000000
|
||||
#
|
||||
# Run time options
|
||||
#
|
||||
assume_two_step 0
|
||||
logging_level 6
|
||||
path_trace_enabled 0
|
||||
follow_up_info 0
|
||||
hybrid_e2e 0
|
||||
tx_timestamp_timeout 1
|
||||
use_syslog 1
|
||||
verbose 0
|
||||
summary_interval 6
|
||||
kernel_leap 1
|
||||
check_fup_sync 0
|
||||
#
|
||||
# Servo Options
|
||||
#
|
||||
pi_proportional_const 0.0
|
||||
pi_integral_const 0.0
|
||||
pi_proportional_scale 0.0
|
||||
pi_proportional_exponent -0.3
|
||||
pi_proportional_norm_max 0.7
|
||||
pi_integral_scale 0.0
|
||||
pi_integral_exponent 0.4
|
||||
pi_integral_norm_max 0.3
|
||||
step_threshold 0.0
|
||||
first_step_threshold 0.00002
|
||||
max_frequency 900000000
|
||||
clock_servo linreg
|
||||
sanity_freq_limit 200000000
|
||||
ntpshm_segment 0
|
||||
#
|
||||
# Transport options
|
||||
#
|
||||
transportSpecific 0x0
|
||||
ptp_dst_mac 01:1B:19:00:00:00
|
||||
p2p_dst_mac 01:80:C2:00:00:0E
|
||||
udp_ttl 1
|
||||
udp6_scope 0x0E
|
||||
uds_address /var/run/ptp4l
|
||||
#
|
||||
# Default interface options
|
||||
#
|
||||
network_transport L2
|
||||
delay_mechanism <%= scope['platform::ptp::mechanism'].upcase %>
|
||||
time_stamping <%= scope['platform::ptp::mode'].downcase %>
|
||||
tsproc_mode filter
|
||||
delay_filter moving_median
|
||||
delay_filter_length 10
|
||||
egressLatency 0
|
||||
ingressLatency 0
|
||||
<%- if @slave_only == true -%>
|
||||
boundary_clock_jbod 0
|
||||
<%- else -%>
|
||||
boundary_clock_jbod 1
|
||||
<%- end -%>
|
||||
#
|
||||
# Clock description
|
||||
#
|
||||
productDescription ;;
|
||||
revisionData ;;
|
||||
manufacturerIdentity 00:00:00
|
||||
userDescription ;
|
||||
timeSource 0xA0
|
||||
|
||||
<%- if scope['platform::ptp::transport'] == 'udp' -%>
|
||||
[<%= @slave_interface %>]
|
||||
network_transport UDPv<%= @slave_subnet %>
|
||||
|
||||
<%- if @slave_only == false -%>
|
||||
[<%= @master_interface %>]
|
||||
network_transport UDPv<%= @master_subnet %>
|
||||
<%- end -%>
|
||||
<%- end -%>
|
@ -0,0 +1 @@
|
||||
OPTIONS="-f /etc/ptp4l.conf"
|
@ -0,0 +1,19 @@
|
||||
[process]
|
||||
process = ptp4l
|
||||
service = ptp4l
|
||||
pidfile = /var/run/ptp4l.pid
|
||||
style = lsb ; ocf or lsb
|
||||
severity = minor ; minor, major, critical
|
||||
restarts = 0 ; restart retries before error assertion
|
||||
interval = 10 ; number of seconds to wait between restarts
|
||||
debounce = 10 ; number of seconds that a process needs to remain
|
||||
; running before degrade is removed and retry count
|
||||
; is cleared.
|
||||
; These settings will generate a log only without attempting to restart
|
||||
; pmond will put the process into an ignore state after failure.
|
||||
|
||||
startuptime = 180 ; Seconds to wait after process start before starting the debounce monitor
|
||||
mode = passive ; Monitoring mode: passive (default) or active
|
||||
; passive: process death monitoring (default: always)
|
||||
; active : heartbeat monitoring, i.e. request / response messaging
|
||||
; ignore : do not monitor or stop monitoring
|
@ -0,0 +1,12 @@
|
||||
[Unit]
|
||||
Description=Precision Time Protocol (PTP) service
|
||||
|
||||
[Service]
|
||||
Type=simple
|
||||
EnvironmentFile=-/etc/sysconfig/ptp4l
|
||||
ExecStart=/usr/sbin/ptp4l $OPTIONS
|
||||
ExecStartPost=/bin/bash -c 'echo $MAINPID > /var/run/ptp4l.pid'
|
||||
ExecStopPost=/bin/rm -f /var/run/ptp4l.pid
|
||||
|
||||
[Install]
|
||||
WantedBy=multi-user.target
|
@ -1,2 +1,2 @@
|
||||
SRC_DIR="cgts-client"
|
||||
TIS_PATCH_VER=59
|
||||
TIS_PATCH_VER=60
|
||||
|
@ -96,6 +96,7 @@ def _does_command_need_no_wrap(callback):
|
||||
|
||||
if callback.__name__ in \
|
||||
['donot_config_ntp_list',
|
||||
'donot_config_ptp_list',
|
||||
'do_host_apply_memprofile',
|
||||
'do_host_apply_cpuprofile',
|
||||
'do_host_apply_ifprofile',
|
||||
|
@ -57,6 +57,7 @@ from cgtsclient.v1 import network
|
||||
from cgtsclient.v1 import partition
|
||||
from cgtsclient.v1 import pci_device
|
||||
from cgtsclient.v1 import port
|
||||
from cgtsclient.v1 import ptp
|
||||
from cgtsclient.v1 import remotelogging
|
||||
from cgtsclient.v1 import route
|
||||
from cgtsclient.v1 import sdn_controller
|
||||
@ -102,6 +103,7 @@ class Client(http.HTTPClient):
|
||||
self.iuser = iuser.iuserManager(self)
|
||||
self.idns = idns.idnsManager(self)
|
||||
self.intp = intp.intpManager(self)
|
||||
self.ptp = ptp.ptpManager(self)
|
||||
self.iextoam = iextoam.iextoamManager(self)
|
||||
self.controller_fs = controller_fs.ControllerFsManager(self)
|
||||
self.storage_backend = storage_backend.StorageBackendManager(self)
|
||||
|
50
sysinv/cgts-client/cgts-client/cgtsclient/v1/ptp.py
Normal file
50
sysinv/cgts-client/cgts-client/cgtsclient/v1/ptp.py
Normal file
@ -0,0 +1,50 @@
|
||||
########################################################################
|
||||
#
|
||||
# Copyright (c) 2018 Wind River Systems, Inc.
|
||||
#
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
#
|
||||
########################################################################
|
||||
|
||||
from cgtsclient.common import base
|
||||
from cgtsclient import exc
|
||||
|
||||
|
||||
CREATION_ATTRIBUTES = []
|
||||
|
||||
|
||||
class ptp(base.Resource):
|
||||
def __repr__(self):
|
||||
return "<ptp %s>" % self._info
|
||||
|
||||
|
||||
class ptpManager(base.Manager):
|
||||
resource_class = ptp
|
||||
|
||||
@staticmethod
|
||||
def _path(id=None):
|
||||
return '/v1/ptp/%s' % id if id else '/v1/ptp'
|
||||
|
||||
def list(self):
|
||||
return self._list(self._path(), "ptps")
|
||||
|
||||
def get(self, ptp_id):
|
||||
try:
|
||||
return self._list(self._path(ptp_id))[0]
|
||||
except IndexError:
|
||||
return None
|
||||
|
||||
def create(self, **kwargs):
|
||||
new = {}
|
||||
for (key, value) in kwargs.items():
|
||||
if key in CREATION_ATTRIBUTES:
|
||||
new[key] = value
|
||||
else:
|
||||
raise exc.InvalidAttribute('%s' % key)
|
||||
return self._create(self._path(), new)
|
||||
|
||||
def delete(self, ptp_id):
|
||||
return self._delete(self._path(ptp_id))
|
||||
|
||||
def update(self, ptp_id, patch):
|
||||
return self._update(self._path(ptp_id), patch)
|
79
sysinv/cgts-client/cgts-client/cgtsclient/v1/ptp_shell.py
Normal file
79
sysinv/cgts-client/cgts-client/cgtsclient/v1/ptp_shell.py
Normal file
@ -0,0 +1,79 @@
|
||||
########################################################################
|
||||
#
|
||||
# Copyright (c) 2018 Wind River Systems, Inc.
|
||||
#
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
#
|
||||
########################################################################
|
||||
|
||||
from cgtsclient.common import utils
|
||||
from cgtsclient import exc
|
||||
|
||||
|
||||
def _print_ptp_show(ptp):
|
||||
fields = ['uuid', 'enabled', 'mode', 'transport', 'mechanism',
|
||||
'isystem_uuid', 'created_at', 'updated_at']
|
||||
data = [(f, getattr(ptp, f, '')) for f in fields]
|
||||
utils.print_tuple_list(data)
|
||||
|
||||
|
||||
def do_ptp_show(cc, args):
|
||||
"""Show PTP (Precision Time Protocol) attributes."""
|
||||
|
||||
ptps = cc.ptp.list()
|
||||
|
||||
_print_ptp_show(ptps[0])
|
||||
|
||||
|
||||
def donot_config_ptp_list(cc, args):
|
||||
"""List ptps."""
|
||||
|
||||
ptps = cc.ptp.list()
|
||||
|
||||
field_labels = ['uuid', 'enabled', 'mode', 'transport', 'mechanism']
|
||||
fields = ['uuid', 'enabled', 'mode', 'transport', 'mechanism']
|
||||
utils.print_list(ptps, fields, field_labels, sortby=1)
|
||||
|
||||
|
||||
@utils.arg('--enabled',
|
||||
metavar='<true/false>',
|
||||
help="PTP service enabled.")
|
||||
@utils.arg('--mode',
|
||||
metavar='<mode>',
|
||||
default=None,
|
||||
help="PTP time stamping mode.")
|
||||
@utils.arg('--transport',
|
||||
metavar='<transport>',
|
||||
default=None,
|
||||
help="PTP transport protocol.")
|
||||
@utils.arg('--mechanism',
|
||||
metavar='<mechanism>',
|
||||
default=None,
|
||||
help="PTP delay mechanism.")
|
||||
def do_ptp_modify(cc, args):
|
||||
"""Modify PTP attributes."""
|
||||
|
||||
ptps = cc.ptp.list()
|
||||
ptp = ptps[0]
|
||||
op = "replace"
|
||||
|
||||
attributes = []
|
||||
if args.enabled is not None:
|
||||
attributes.append('enabled=%s' % args.enabled)
|
||||
if args.mode is not None:
|
||||
attributes.append('mode=%s' % args.mode)
|
||||
if args.transport is not None:
|
||||
attributes.append('transport=%s' % args.transport)
|
||||
if args.mechanism is not None:
|
||||
attributes.append('mechanism=%s' % args.mechanism)
|
||||
if len(attributes) == 0:
|
||||
print "No options provided."
|
||||
return
|
||||
|
||||
patch = utils.args_array_to_patch("replace", attributes)
|
||||
try:
|
||||
ptp = cc.ptp.update(ptp.uuid, patch)
|
||||
except exc.HTTPNotFound:
|
||||
raise exc.CommandError('PTP not found: %s' % ptp.uuid)
|
||||
|
||||
_print_ptp_show(ptp)
|
@ -46,6 +46,7 @@ from cgtsclient.v1 import network_shell
|
||||
from cgtsclient.v1 import partition_shell
|
||||
from cgtsclient.v1 import pci_device_shell
|
||||
from cgtsclient.v1 import port_shell
|
||||
from cgtsclient.v1 import ptp_shell
|
||||
from cgtsclient.v1 import remotelogging_shell
|
||||
from cgtsclient.v1 import route_shell
|
||||
from cgtsclient.v1 import sdn_controller_shell
|
||||
@ -63,6 +64,7 @@ COMMAND_MODULES = [
|
||||
iuser_shell,
|
||||
idns_shell,
|
||||
intp_shell,
|
||||
ptp_shell,
|
||||
iextoam_shell,
|
||||
controller_fs_shell,
|
||||
storage_backend_shell,
|
||||
|
@ -1,2 +1,2 @@
|
||||
SRC_DIR="sysinv"
|
||||
TIS_PATCH_VER=278
|
||||
TIS_PATCH_VER=279
|
||||
|
@ -53,6 +53,7 @@ from sysinv.api.controllers.v1 import partition
|
||||
from sysinv.api.controllers.v1 import pci_device
|
||||
from sysinv.api.controllers.v1 import port
|
||||
from sysinv.api.controllers.v1 import profile
|
||||
from sysinv.api.controllers.v1 import ptp
|
||||
from sysinv.api.controllers.v1 import pv
|
||||
from sysinv.api.controllers.v1 import remotelogging
|
||||
from sysinv.api.controllers.v1 import route
|
||||
@ -137,6 +138,9 @@ class V1(base.APIBase):
|
||||
intp = [link.Link]
|
||||
"Links to the intp resource"
|
||||
|
||||
ptp = [link.Link]
|
||||
"Links to the ptp resource"
|
||||
|
||||
iextoam = [link.Link]
|
||||
"Links to the iextoam resource"
|
||||
|
||||
@ -402,6 +406,14 @@ class V1(base.APIBase):
|
||||
bookmark=True)
|
||||
]
|
||||
|
||||
v1.ptp = [link.Link.make_link('self', pecan.request.host_url,
|
||||
'ptp', ''),
|
||||
link.Link.make_link('bookmark',
|
||||
pecan.request.host_url,
|
||||
'ptp', '',
|
||||
bookmark=True)
|
||||
]
|
||||
|
||||
v1.iextoam = [link.Link.make_link('self', pecan.request.host_url,
|
||||
'iextoam', ''),
|
||||
link.Link.make_link('bookmark',
|
||||
@ -717,6 +729,7 @@ class Controller(rest.RestController):
|
||||
iuser = user.UserController()
|
||||
idns = dns.DNSController()
|
||||
intp = ntp.NTPController()
|
||||
ptp = ptp.PTPController()
|
||||
iextoam = network_oam.OAMNetworkController()
|
||||
controller_fs = controller_fs.ControllerFsController()
|
||||
storage_backend = storage_backend.StorageBackendController()
|
||||
|
@ -159,6 +159,15 @@ def _check_ntp_data(op, ntp):
|
||||
|
||||
MAX_S = 3
|
||||
|
||||
ptp_list = pecan.request.dbapi.ptp_get_by_isystem(ntp['forisystemid'])
|
||||
|
||||
if ptp_list:
|
||||
if hasattr(ptp_list[0], 'enabled'):
|
||||
if ptp_list[0].enabled is True and enabled is True:
|
||||
raise wsme.exc.ClientSideError(_(
|
||||
"NTP cannot be configured alongside with PTP."
|
||||
" Please disable PTP before enabling NTP."))
|
||||
|
||||
dns_list = pecan.request.dbapi.idns_get_by_isystem(ntp['forisystemid'])
|
||||
|
||||
if dns_list:
|
||||
|
253
sysinv/sysinv/sysinv/sysinv/api/controllers/v1/ptp.py
Normal file
253
sysinv/sysinv/sysinv/sysinv/api/controllers/v1/ptp.py
Normal file
@ -0,0 +1,253 @@
|
||||
########################################################################
|
||||
#
|
||||
# Copyright (c) 2018 Wind River Systems, Inc.
|
||||
#
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
#
|
||||
########################################################################
|
||||
|
||||
import jsonpatch
|
||||
|
||||
import pecan
|
||||
from pecan import rest
|
||||
|
||||
import wsme
|
||||
from wsme import types as wtypes
|
||||
import wsmeext.pecan as wsme_pecan
|
||||
|
||||
from sysinv.api.controllers.v1 import base
|
||||
from sysinv.api.controllers.v1 import collection
|
||||
from sysinv.api.controllers.v1 import link
|
||||
from sysinv.api.controllers.v1 import types
|
||||
from sysinv.api.controllers.v1 import utils
|
||||
from sysinv.common import exception
|
||||
from sysinv.common import utils as cutils
|
||||
from sysinv import objects
|
||||
from sysinv.openstack.common.gettextutils import _
|
||||
from sysinv.openstack.common import log
|
||||
|
||||
|
||||
LOG = log.getLogger(__name__)
|
||||
|
||||
|
||||
class PTPPatchType(types.JsonPatchType):
|
||||
|
||||
@staticmethod
|
||||
def mandatory_attrs():
|
||||
return []
|
||||
|
||||
|
||||
class PTP(base.APIBase):
|
||||
"""API representation of PTP configuration.
|
||||
|
||||
This class enforces type checking and value constraints, and converts
|
||||
between the internal object model and the API representation of
|
||||
an ptp.
|
||||
"""
|
||||
|
||||
uuid = types.uuid
|
||||
"Unique UUID for this ptp"
|
||||
|
||||
enabled = types.boolean
|
||||
"Represent the status of the ptp."
|
||||
|
||||
mode = wtypes.Enum(str, 'hardware', 'software', 'legacy')
|
||||
"Time stamping mode used by ptp."
|
||||
|
||||
transport = wtypes.Enum(str, 'l2', 'udp')
|
||||
"Network transport used by ptp."
|
||||
|
||||
mechanism = wtypes.Enum(str, 'e2e', 'p2p')
|
||||
"Messaging mechanism used by ptp."
|
||||
|
||||
links = [link.Link]
|
||||
"A list containing a self link and associated ptp links"
|
||||
|
||||
isystem_uuid = types.uuid
|
||||
"The UUID of the system this ptp belongs to"
|
||||
|
||||
created_at = wtypes.datetime.datetime
|
||||
updated_at = wtypes.datetime.datetime
|
||||
|
||||
def __init__(self, **kwargs):
|
||||
self.fields = objects.ptp.fields.keys()
|
||||
for k in self.fields:
|
||||
setattr(self, k, kwargs.get(k))
|
||||
|
||||
@classmethod
|
||||
def convert_with_links(cls, rpc_ptp, expand=True):
|
||||
|
||||
ptp = PTP(**rpc_ptp.as_dict())
|
||||
if not expand:
|
||||
ptp.unset_fields_except(['uuid',
|
||||
'enabled',
|
||||
'mode',
|
||||
'transport',
|
||||
'mechanism',
|
||||
'isystem_uuid',
|
||||
'created_at',
|
||||
'updated_at'])
|
||||
|
||||
ptp.links = [link.Link.make_link('self', pecan.request.host_url,
|
||||
'ptps', ptp.uuid),
|
||||
link.Link.make_link('bookmark',
|
||||
pecan.request.host_url,
|
||||
'ptps', ptp.uuid,
|
||||
bookmark=True)
|
||||
]
|
||||
|
||||
return ptp
|
||||
|
||||
|
||||
class ptpCollection(collection.Collection):
|
||||
"""API representation of a collection of ptps."""
|
||||
|
||||
ptps = [PTP]
|
||||
"A list containing ptp objects"
|
||||
|
||||
def __init__(self, **kwargs):
|
||||
self._type = 'ptps'
|
||||
|
||||
@classmethod
|
||||
def convert_with_links(cls, rpc_ptps, limit, url=None,
|
||||
expand=False, **kwargs):
|
||||
collection = ptpCollection()
|
||||
collection.ptps = [PTP.convert_with_links(p, expand)
|
||||
for p in rpc_ptps]
|
||||
collection.next = collection.get_next(limit, url=url, **kwargs)
|
||||
return collection
|
||||
|
||||
|
||||
##############
|
||||
# UTILS
|
||||
##############
|
||||
def _check_ptp_data(op, ptp):
|
||||
enabled = ptp['enabled']
|
||||
ntp_list = pecan.request.dbapi.intp_get_by_isystem(ptp['isystem_uuid'])
|
||||
if ntp_list:
|
||||
if hasattr(ntp_list[0], 'enabled'):
|
||||
if ntp_list[0].enabled is True and enabled is True:
|
||||
raise wsme.exc.ClientSideError(_(
|
||||
"PTP cannot be configured alongside with NTP."
|
||||
" Please disable NTP before enabling PTP."))
|
||||
return ptp
|
||||
|
||||
|
||||
LOCK_NAME = 'PTPController'
|
||||
|
||||
|
||||
class PTPController(rest.RestController):
|
||||
"""REST controller for ptps."""
|
||||
|
||||
_custom_actions = {
|
||||
'detail': ['GET'],
|
||||
}
|
||||
|
||||
def _get_ptps_collection(self, marker, limit, sort_key, sort_dir,
|
||||
expand=False, resource_url=None):
|
||||
|
||||
limit = utils.validate_limit(limit)
|
||||
sort_dir = utils.validate_sort_dir(sort_dir)
|
||||
|
||||
marker_obj = None
|
||||
if marker:
|
||||
marker_obj = objects.ptp.get_by_uuid(pecan.request.context,
|
||||
marker)
|
||||
|
||||
ptps = pecan.request.dbapi.ptp_get_list(limit, marker_obj,
|
||||
sort_key=sort_key,
|
||||
sort_dir=sort_dir)
|
||||
|
||||
return ptpCollection.convert_with_links(ptps, limit,
|
||||
url=resource_url,
|
||||
expand=expand,
|
||||
sort_key=sort_key,
|
||||
sort_dir=sort_dir)
|
||||
|
||||
@wsme_pecan.wsexpose(ptpCollection, types.uuid, int,
|
||||
wtypes.text, wtypes.text)
|
||||
def get_all(self, marker=None, limit=None,
|
||||
sort_key='id', sort_dir='asc'):
|
||||
"""Retrieve a list of ptps. Only one per system"""
|
||||
|
||||
return self._get_ptps_collection(marker, limit,
|
||||
sort_key, sort_dir)
|
||||
|
||||
@wsme_pecan.wsexpose(ptpCollection, types.uuid, int,
|
||||
wtypes.text, wtypes.text)
|
||||
def detail(self, marker=None, limit=None,
|
||||
sort_key='id', sort_dir='asc'):
|
||||
"""Retrieve a list of ptps with detail."""
|
||||
# NOTE(lucasagomes): /detail should only work agaist collections
|
||||
parent = pecan.request.path.split('/')[:-1][-1]
|
||||
if parent != "ptps":
|
||||
raise exception.HTTPNotFound
|
||||
|
||||
expand = True
|
||||
resource_url = '/'.join(['ptps', 'detail'])
|
||||
return self._get_ptps_collection(marker, limit,
|
||||
sort_key, sort_dir,
|
||||
expand, resource_url)
|
||||
|
||||
@wsme_pecan.wsexpose(PTP, types.uuid)
|
||||
def get_one(self, ptp_uuid):
|
||||
"""Retrieve information about the given ptp."""
|
||||
rpc_ptp = objects.ptp.get_by_uuid(pecan.request.context, ptp_uuid)
|
||||
return PTP.convert_with_links(rpc_ptp)
|
||||
|
||||
@wsme_pecan.wsexpose(PTP, body=PTP)
|
||||
def post(self, ptp):
|
||||
"""Create a new ptp."""
|
||||
raise exception.OperationNotPermitted
|
||||
|
||||
@cutils.synchronized(LOCK_NAME)
|
||||
@wsme.validate(types.uuid, [PTPPatchType])
|
||||
@wsme_pecan.wsexpose(PTP, types.uuid,
|
||||
body=[PTPPatchType])
|
||||
def patch(self, ptp_uuid, patch):
|
||||
"""Update the current PTP configuration."""
|
||||
|
||||
rpc_ptp = objects.ptp.get_by_uuid(pecan.request.context, ptp_uuid)
|
||||
patch_obj = jsonpatch.JsonPatch(patch)
|
||||
|
||||
state_rel_path = ['/uuid', '/id']
|
||||
if any(p['path'] in state_rel_path for p in patch_obj):
|
||||
raise wsme.exc.ClientSideError(_("The following fields can not be "
|
||||
"modified: %s" %
|
||||
state_rel_path))
|
||||
|
||||
try:
|
||||
ptp = PTP(**jsonpatch.apply_patch(rpc_ptp.as_dict(),
|
||||
patch_obj))
|
||||
|
||||
except utils.JSONPATCH_EXCEPTIONS as e:
|
||||
raise exception.PatchError(patch=patch, reason=e)
|
||||
|
||||
ptp = _check_ptp_data("modify", ptp.as_dict())
|
||||
|
||||
try:
|
||||
# Update only the fields that have changed
|
||||
for field in objects.ptp.fields:
|
||||
if rpc_ptp[field] != ptp[field]:
|
||||
rpc_ptp[field] = ptp[field]
|
||||
|
||||
delta = rpc_ptp.obj_what_changed()
|
||||
if delta:
|
||||
rpc_ptp.save()
|
||||
# perform rpc to conductor to perform config apply
|
||||
pecan.request.rpcapi.update_ptp_config(pecan.request.context)
|
||||
else:
|
||||
LOG.info("No PTP config changes")
|
||||
|
||||
return PTP.convert_with_links(rpc_ptp)
|
||||
|
||||
except exception.HTTPNotFound:
|
||||
msg = _("PTP update failed: enabled %s : %s %s %s : patch %s"
|
||||
% (ptp['enabled'], ptp['mode'], ptp['transport'],
|
||||
ptp['mechanism'], patch))
|
||||
raise wsme.exc.ClientSideError(msg)
|
||||
|
||||
@wsme_pecan.wsexpose(None, types.uuid, status_code=204)
|
||||
def delete(self, ptp_uuid):
|
||||
"""Delete a ptp."""
|
||||
raise exception.OperationNotPermitted
|
@ -478,7 +478,11 @@ class DNSAlreadyExists(Conflict):
|
||||
|
||||
|
||||
class NTPAlreadyExists(Conflict):
|
||||
message = _("An NTP with UUID %(uuid)s already exists.")
|
||||
message = _("A NTP with UUID %(uuid)s already exists.")
|
||||
|
||||
|
||||
class PTPAlreadyExists(Conflict):
|
||||
message = _("A PTP with UUID %(uuid)s already exists.")
|
||||
|
||||
|
||||
class PMAlreadyExists(Conflict):
|
||||
@ -614,6 +618,10 @@ class NTPNotFound(NotFound):
|
||||
message = _("No NTP with id %(uuid)s found.")
|
||||
|
||||
|
||||
class PTPNotFound(NotFound):
|
||||
message = _("No PTP with id %(uuid)s found.")
|
||||
|
||||
|
||||
class DiskNotFound(NotFound):
|
||||
message = _("No disk with id %(disk_id)s")
|
||||
|
||||
|
@ -227,6 +227,8 @@ class ConductorManager(service.PeriodicService):
|
||||
|
||||
# fill in empty remotelogging system_id fields
|
||||
self.dbapi.remotelogging_fill_empty_system_id(system.id)
|
||||
# fill in empty ptp system_id fields
|
||||
self.dbapi.ptp_fill_empty_system_id(system.id)
|
||||
|
||||
return system # system already configured
|
||||
except exception.NotFound:
|
||||
@ -271,9 +273,10 @@ class ConductorManager(service.PeriodicService):
|
||||
'rtt_ms': constants.DRBD_RTT_MS_DEFAULT
|
||||
})
|
||||
|
||||
# remotelogging tables have attribute 'system_id' not 'forisystemid'
|
||||
# remotelogging and ptp tables have attribute 'system_id' not 'forisystemid'
|
||||
system_id_attribute_value = {'system_id': system.id}
|
||||
self.dbapi.remotelogging_create(system_id_attribute_value)
|
||||
self.dbapi.ptp_create(system_id_attribute_value)
|
||||
|
||||
# set default storage_backend
|
||||
values.update({'backend': constants.SB_TYPE_FILE,
|
||||
@ -5156,6 +5159,13 @@ class ConductorManager(service.PeriodicService):
|
||||
personalities = [constants.CONTROLLER]
|
||||
self._config_update_hosts(context, personalities, reboot=True)
|
||||
|
||||
def update_ptp_config(self, context):
|
||||
"""Update the PTP configuration"""
|
||||
personalities = [constants.CONTROLLER,
|
||||
constants.COMPUTE,
|
||||
constants.STORAGE]
|
||||
self._config_update_hosts(context, personalities)
|
||||
|
||||
def update_system_mode_config(self, context):
|
||||
"""Update the system mode configuration"""
|
||||
personalities = [constants.CONTROLLER]
|
||||
|
@ -702,6 +702,13 @@ class ConductorAPI(sysinv.openstack.common.rpc.proxy.RpcProxy):
|
||||
return self.call(context, self.make_msg('update_ntp_config',
|
||||
service_change=service_change))
|
||||
|
||||
def update_ptp_config(self, context):
|
||||
"""Synchronously, have the conductor update the PTP configuration.
|
||||
|
||||
:param context: request context.
|
||||
"""
|
||||
return self.call(context, self.make_msg('update_ptp_config'))
|
||||
|
||||
def update_system_mode_config(self, context):
|
||||
"""Synchronously, have the conductor update the system mode
|
||||
configuration.
|
||||
|
@ -1864,7 +1864,6 @@ class Connection(object):
|
||||
def intp_create(self, values):
|
||||
"""Create a new intp for an isystem.
|
||||
|
||||
:param forisystemid: intp belongs to this isystem
|
||||
:param values: A dict containing several items used to identify
|
||||
and track the ntp settings.
|
||||
{
|
||||
@ -1878,18 +1877,18 @@ class Connection(object):
|
||||
"""
|
||||
|
||||
@abc.abstractmethod
|
||||
def intp_get(self, server):
|
||||
def intp_get(self, intp_id):
|
||||
"""Return an intp.
|
||||
|
||||
:param isystem: The id or uuid of an intp.
|
||||
:returns: A intp.
|
||||
:param intp_id: The id or uuid of an intp.
|
||||
:returns: An intp.
|
||||
"""
|
||||
|
||||
@abc.abstractmethod
|
||||
def intp_get_one(self):
|
||||
"""Return exactly one intp.
|
||||
|
||||
:returns: A intp.
|
||||
:returns: An intp.
|
||||
"""
|
||||
|
||||
@abc.abstractmethod
|
||||
@ -1910,7 +1909,7 @@ class Connection(object):
|
||||
sort_key=None, sort_dir=None):
|
||||
"""List all the intp for a given isystem.
|
||||
|
||||
:param isystem: The id or uuid of an isystem.
|
||||
:param isystem_id: The id or uuid of an isystem.
|
||||
:param limit: Maximum number of intp to return.
|
||||
:param marker: the last item of the previous page; we return the next
|
||||
result set.
|
||||
@ -1921,7 +1920,7 @@ class Connection(object):
|
||||
"""
|
||||
|
||||
@abc.abstractmethod
|
||||
def intp_update(self, server, values):
|
||||
def intp_update(self, intp_id, values):
|
||||
"""Update properties of an intp.
|
||||
|
||||
:param intp_id: The id or uuid of an intp.
|
||||
@ -1940,10 +1939,105 @@ class Connection(object):
|
||||
"""
|
||||
|
||||
@abc.abstractmethod
|
||||
def intp_destroy(self, server):
|
||||
def intp_destroy(self, intp_id):
|
||||
"""Destroy an intp.
|
||||
|
||||
:param id: The id or uuid of an intp.
|
||||
:param intp_id: The id or uuid of an intp.
|
||||
"""
|
||||
|
||||
@abc.abstractmethod
|
||||
def ptp_create(self, values):
|
||||
"""Create a new ptp for an isystem.
|
||||
|
||||
:param values: A dict containing several items used to identify
|
||||
and track the ptp settings.
|
||||
{
|
||||
'enabled': 'True',
|
||||
'mode': 'hardware',
|
||||
'transport': 'l2',
|
||||
'mechanism': 'e2e',
|
||||
}
|
||||
:returns: A ptp.
|
||||
"""
|
||||
|
||||
@abc.abstractmethod
|
||||
def ptp_get(self, ptp_id):
|
||||
"""Return a ptp.
|
||||
|
||||
:param ptp_id: The id or uuid of a ptp.
|
||||
:returns: A ptp.
|
||||
"""
|
||||
|
||||
@abc.abstractmethod
|
||||
def ptp_get_one(self):
|
||||
"""Return exactly one ptp.
|
||||
|
||||
:returns: A ptp.
|
||||
"""
|
||||
|
||||
@abc.abstractmethod
|
||||
def ptp_get_list(self, limit=None, marker=None,
|
||||
sort_key=None, sort_dir=None):
|
||||
"""Return a list of ptp.
|
||||
|
||||
:param limit: Maximum number of ptp to return.
|
||||
:param marker: the last item of the previous page; we return the next
|
||||
result set.
|
||||
:param sort_key: Attribute by which results should be sorted.
|
||||
:param sort_dir: direction in which results should be sorted.
|
||||
(asc, desc)
|
||||
"""
|
||||
|
||||
@abc.abstractmethod
|
||||
def ptp_get_by_isystem(self, isystem_id, limit=None, marker=None,
|
||||
sort_key=None, sort_dir=None):
|
||||
"""List all the ptp for a given isystem.
|
||||
|
||||
:param isystem_id: The id or uuid of an isystem.
|
||||
:param limit: Maximum number of ptp to return.
|
||||
:param marker: the last item of the previous page; we return the next
|
||||
result set.
|
||||
:param sort_key: Attribute by which results should be sorted
|
||||
:param sort_dir: direction in which results should be sorted
|
||||
(asc, desc)
|
||||
:returns: A list of ptp.
|
||||
"""
|
||||
|
||||
@abc.abstractmethod
|
||||
def ptp_update(self, ptp_id, values):
|
||||
"""Update properties of a ptp.
|
||||
|
||||
:param ptp_id: The id or uuid of a ptp.
|
||||
:param values: Dict of values to update.
|
||||
May be a partial list, eg. when setting the
|
||||
properties for capabilities. For example:
|
||||
|
||||
{
|
||||
'capabilities':
|
||||
{
|
||||
'my-field-1': val1,
|
||||
'my-field-2': val2,
|
||||
}
|
||||
}
|
||||
:returns: A ptp.
|
||||
"""
|
||||
|
||||
@abc.abstractmethod
|
||||
def ptp_destroy(self, ptp_id):
|
||||
"""Destroy a ptp.
|
||||
|
||||
:param ptp_id: The id or uuid of a ptp.
|
||||
"""
|
||||
|
||||
@abc.abstractmethod
|
||||
def ptp_fill_empty_system_id(self, system_id):
|
||||
"""fills all empty system_id in a ptp.
|
||||
ptp did not always fill this entry in properly
|
||||
so existing systems might still have no value in the
|
||||
system_id field. This function fills in the system_id
|
||||
in existing systems that were missing this value.
|
||||
|
||||
:param system_id: The value to fill system_id with
|
||||
"""
|
||||
|
||||
@abc.abstractmethod
|
||||
|
@ -3508,14 +3508,14 @@ class Connection(api.Connection):
|
||||
|
||||
query.delete()
|
||||
|
||||
def _ntp_get(self, server):
|
||||
def _ntp_get(self, intp_id):
|
||||
query = model_query(models.intp)
|
||||
query = add_identity_filter(query, server)
|
||||
query = add_identity_filter(query, intp_id)
|
||||
|
||||
try:
|
||||
return query.one()
|
||||
except NoResultFound:
|
||||
raise exception.NTPNotFound(server=server)
|
||||
raise exception.NTPNotFound(intp_id=intp_id)
|
||||
|
||||
@objects.objectify(objects.ntp)
|
||||
def intp_create(self, values):
|
||||
@ -3564,25 +3564,25 @@ class Connection(api.Connection):
|
||||
sort_key, sort_dir, query)
|
||||
|
||||
@objects.objectify(objects.ntp)
|
||||
def intp_update(self, server, values):
|
||||
def intp_update(self, intp_id, values):
|
||||
with _session_for_write() as session:
|
||||
query = model_query(models.intp, session=session)
|
||||
query = add_identity_filter(query, server)
|
||||
query = add_identity_filter(query, intp_id)
|
||||
|
||||
count = query.update(values, synchronize_session='fetch')
|
||||
if count != 1:
|
||||
raise exception.NTPNotFound(server=server)
|
||||
raise exception.NTPNotFound(intp_id=intp_id)
|
||||
return query.one()
|
||||
|
||||
def intp_destroy(self, server):
|
||||
def intp_destroy(self, intp_id):
|
||||
with _session_for_write() as session:
|
||||
query = model_query(models.intp, session=session)
|
||||
query = add_identity_filter(query, server)
|
||||
query = add_identity_filter(query, intp_id)
|
||||
|
||||
try:
|
||||
query.one()
|
||||
except NoResultFound:
|
||||
raise exception.NTPNotFound(server=server)
|
||||
raise exception.NTPNotFound(intp_id=intp_id)
|
||||
# if node_ref['reservation'] is not None:
|
||||
# raise exception.NodeLocked(node=node)
|
||||
|
||||
@ -3595,6 +3595,92 @@ class Connection(api.Connection):
|
||||
|
||||
query.delete()
|
||||
|
||||
def _ptp_get(self, ptp_id):
|
||||
query = model_query(models.PTP)
|
||||
query = add_identity_filter(query, ptp_id)
|
||||
|
||||
try:
|
||||
return query.one()
|
||||
except NoResultFound:
|
||||
raise exception.PTPNotFound(ptp_id=ptp_id)
|
||||
|
||||
@objects.objectify(objects.ptp)
|
||||
def ptp_create(self, values):
|
||||
if not values.get('uuid'):
|
||||
values['uuid'] = uuidutils.generate_uuid()
|
||||
ptp = models.PTP()
|
||||
ptp.update(values)
|
||||
with _session_for_write() as session:
|
||||
try:
|
||||
session.add(ptp)
|
||||
session.flush()
|
||||
except db_exc.DBDuplicateEntry:
|
||||
raise exception.PTPAlreadyExists(uuid=values['uuid'])
|
||||
return self._ptp_get(values['uuid'])
|
||||
|
||||
@objects.objectify(objects.ptp)
|
||||
def ptp_get(self, ptp_id):
|
||||
return self._ptp_get(ptp_id)
|
||||
|
||||
@objects.objectify(objects.ptp)
|
||||
def ptp_get_one(self):
|
||||
query = model_query(models.PTP)
|
||||
|
||||
try:
|
||||
return query.one()
|
||||
except NoResultFound:
|
||||
raise exception.NotFound()
|
||||
|
||||
@objects.objectify(objects.ptp)
|
||||
def ptp_get_list(self, limit=None, marker=None,
|
||||
sort_key=None, sort_dir=None):
|
||||
|
||||
query = model_query(models.PTP)
|
||||
|
||||
return _paginate_query(models.PTP, limit, marker,
|
||||
sort_key, sort_dir, query)
|
||||
|
||||
@objects.objectify(objects.ptp)
|
||||
def ptp_get_by_isystem(self, isystem_id, limit=None, marker=None,
|
||||
sort_key=None, sort_dir=None):
|
||||
# isystem_get() to raise an exception if the isystem is not found
|
||||
isystem_obj = self.isystem_get(isystem_id)
|
||||
query = model_query(models.PTP)
|
||||
query = query.filter_by(system_id=isystem_obj.id)
|
||||
return _paginate_query(models.PTP, limit, marker,
|
||||
sort_key, sort_dir, query)
|
||||
|
||||
@objects.objectify(objects.ptp)
|
||||
def ptp_update(self, ptp_id, values):
|
||||
with _session_for_write() as session:
|
||||
query = model_query(models.PTP, session=session)
|
||||
query = add_identity_filter(query, ptp_id)
|
||||
|
||||
count = query.update(values, synchronize_session='fetch')
|
||||
if count != 1:
|
||||
raise exception.PTPNotFound(ptp_id=ptp_id)
|
||||
return query.one()
|
||||
|
||||
def ptp_destroy(self, ptp_id):
|
||||
with _session_for_write() as session:
|
||||
query = model_query(models.PTP, session=session)
|
||||
query = add_identity_filter(query, ptp_id)
|
||||
|
||||
try:
|
||||
query.one()
|
||||
except NoResultFound:
|
||||
raise exception.PTPNotFound(ptp_id=ptp_id)
|
||||
|
||||
query.delete()
|
||||
|
||||
def ptp_fill_empty_system_id(self, system_id):
|
||||
values = {'system_id': system_id}
|
||||
with _session_for_write() as session:
|
||||
query = model_query(models.PTP,
|
||||
session=session)
|
||||
query = query.filter_by(system_id=None)
|
||||
query.update(values, synchronize_session='fetch')
|
||||
|
||||
# NOTE: method is deprecated and provided for API compatibility.
|
||||
# object class will convert Network entity to an iextoam object
|
||||
@objects.objectify(objects.oam_network)
|
||||
|
@ -0,0 +1,78 @@
|
||||
########################################################################
|
||||
#
|
||||
# Copyright (c) 2018 Wind River Systems, Inc.
|
||||
#
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
#
|
||||
########################################################################
|
||||
|
||||
import uuid
|
||||
|
||||
from datetime import datetime
|
||||
from sqlalchemy import Integer, String, Boolean, DateTime
|
||||
from sqlalchemy import Column, MetaData, Table, ForeignKey
|
||||
|
||||
ENGINE = 'InnoDB'
|
||||
CHARSET = 'utf8'
|
||||
|
||||
def _populate_ptp_table(migrate_engine, meta, ptp, i_system):
|
||||
"""This function inserts all the initial data about journals,
|
||||
into the ptp table.
|
||||
"""
|
||||
|
||||
sys = list(i_system.select().where(i_system.c.uuid is not None).execute())
|
||||
if len(sys) > 0:
|
||||
ptp_insert = ptp.insert()
|
||||
ptp_uuid = str(uuid.uuid4())
|
||||
values = {'created_at': datetime.now(),
|
||||
'updated_at': None,
|
||||
'deleted_at': None,
|
||||
'uuid': ptp_uuid,
|
||||
'enabled': False,
|
||||
'mode': 'hardware',
|
||||
'transport': 'l2',
|
||||
'mechanism': 'e2e',
|
||||
'system_id': sys[0].id,
|
||||
}
|
||||
ptp_insert.execute(values)
|
||||
|
||||
|
||||
def upgrade(migrate_engine):
|
||||
|
||||
meta = MetaData()
|
||||
meta.bind = migrate_engine
|
||||
|
||||
i_system = Table('i_system', meta, autoload=True)
|
||||
ptp = Table(
|
||||
'ptp',
|
||||
meta,
|
||||
Column('created_at', DateTime),
|
||||
Column('updated_at', DateTime),
|
||||
Column('deleted_at', DateTime),
|
||||
|
||||
Column('id', Integer, primary_key=True, nullable=False),
|
||||
Column('uuid', String(36), unique=True),
|
||||
|
||||
Column('enabled', Boolean, default=False),
|
||||
Column('mode', String(16), default='hardware'),
|
||||
Column('transport', String(4), default='l2'),
|
||||
Column('mechanism', String(4), default='e2e'),
|
||||
|
||||
Column('system_id', Integer,
|
||||
ForeignKey('i_system.id', ondelete="CASCADE"),
|
||||
nullable=True),
|
||||
|
||||
mysql_engine=ENGINE,
|
||||
mysql_charset=CHARSET,
|
||||
)
|
||||
ptp.create()
|
||||
# Populate the new ptp table with the initial data
|
||||
_populate_ptp_table(migrate_engine, meta, ptp, i_system)
|
||||
|
||||
|
||||
def downgrade(migrate_engine):
|
||||
meta = MetaData()
|
||||
meta.bind = migrate_engine
|
||||
|
||||
ptp = Table('ptp', meta, autoload=True)
|
||||
ptp.drop()
|
@ -809,6 +809,23 @@ class intp(Base):
|
||||
system = relationship("isystem", lazy="joined", join_depth=1)
|
||||
|
||||
|
||||
class PTP(Base):
|
||||
__tablename__ = 'ptp'
|
||||
|
||||
id = Column(Integer, primary_key=True)
|
||||
uuid = Column(String(36))
|
||||
|
||||
enabled = Column(Boolean, default=False)
|
||||
mode = Column(String(16), default='hardware')
|
||||
transport = Column(String(4), default='l2')
|
||||
mechanism = Column(String(4), default='e2e')
|
||||
|
||||
system_id = Column(Integer,
|
||||
ForeignKey('i_system.id', ondelete='CASCADE'))
|
||||
|
||||
system = relationship("isystem", lazy="joined", join_depth=1)
|
||||
|
||||
|
||||
class StorageTier(Base):
|
||||
__tablename__ = 'storage_tiers'
|
||||
|
||||
|
@ -50,13 +50,14 @@ from sysinv.objects import load
|
||||
from sysinv.objects import lvg
|
||||
from sysinv.objects import memory
|
||||
from sysinv.objects import network
|
||||
from sysinv.objects import network_oam
|
||||
from sysinv.objects import node
|
||||
from sysinv.objects import ntp
|
||||
from sysinv.objects import network_oam
|
||||
from sysinv.objects import pci_device
|
||||
from sysinv.objects import peer
|
||||
from sysinv.objects import port
|
||||
from sysinv.objects import profile
|
||||
from sysinv.objects import ptp
|
||||
from sysinv.objects import pv
|
||||
from sysinv.objects import remote_logging
|
||||
from sysinv.objects import route
|
||||
@ -137,6 +138,7 @@ community = community.Community
|
||||
user = user.User
|
||||
dns = dns.DNS
|
||||
ntp = ntp.NTP
|
||||
ptp = ptp.PTP
|
||||
oam_network = network_oam.OAMNetwork
|
||||
storage_backend = storage_backend.StorageBackend
|
||||
storage_ceph = storage_ceph.StorageCeph
|
||||
@ -201,6 +203,7 @@ __all__ = (system,
|
||||
user,
|
||||
dns,
|
||||
ntp,
|
||||
ptp,
|
||||
oam_network,
|
||||
storage_backend,
|
||||
storage_ceph,
|
||||
|
40
sysinv/sysinv/sysinv/sysinv/objects/ptp.py
Normal file
40
sysinv/sysinv/sysinv/sysinv/objects/ptp.py
Normal file
@ -0,0 +1,40 @@
|
||||
########################################################################
|
||||
#
|
||||
# Copyright (c) 2018 Wind River Systems, Inc.
|
||||
#
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
#
|
||||
########################################################################
|
||||
|
||||
from sysinv.db import api as db_api
|
||||
from sysinv.objects import base
|
||||
from sysinv.objects import utils
|
||||
|
||||
|
||||
class PTP(base.SysinvObject):
|
||||
|
||||
dbapi = db_api.get_instance()
|
||||
|
||||
fields = {
|
||||
'id': int,
|
||||
'uuid': utils.str_or_none,
|
||||
|
||||
'enabled': utils.bool_or_none,
|
||||
'mode': utils.str_or_none,
|
||||
'transport': utils.str_or_none,
|
||||
'mechanism': utils.str_or_none,
|
||||
|
||||
'isystem_uuid': utils.str_or_none,
|
||||
'system_id': utils.int_or_none
|
||||
}
|
||||
|
||||
_foreign_fields = {
|
||||
'isystem_uuid': 'system:uuid'
|
||||
}
|
||||
|
||||
@base.remotable_classmethod
|
||||
def get_by_uuid(cls, context, uuid):
|
||||
return cls.dbapi.ptp_get(uuid)
|
||||
|
||||
def save_changes(self, context, updates):
|
||||
self.dbapi.ptp_update(self.uuid, updates)
|
@ -62,6 +62,7 @@ class PlatformPuppet(base.BasePuppet):
|
||||
config = {}
|
||||
config.update(self._get_host_platform_config(host, config_uuid))
|
||||
config.update(self._get_host_ntp_config(host))
|
||||
config.update(self._get_host_ptp_config(host))
|
||||
config.update(self._get_host_sysctl_config(host))
|
||||
config.update(self._get_host_drbd_config(host))
|
||||
config.update(self._get_host_upgrade_config(host))
|
||||
@ -430,6 +431,20 @@ class PlatformPuppet(base.BasePuppet):
|
||||
'platform::ntp::ntpdate_timeout': ntpdate_timeout,
|
||||
}
|
||||
|
||||
def _get_host_ptp_config(self, host):
|
||||
ptp = self.dbapi.ptp_get_one()
|
||||
|
||||
return {
|
||||
'platform::ptp::enabled':
|
||||
ptp.enabled,
|
||||
'platform::ptp::mode':
|
||||
ptp.mode,
|
||||
'platform::ptp::transport':
|
||||
ptp.transport,
|
||||
'platform::ptp::mechanism':
|
||||
ptp.mechanism,
|
||||
}
|
||||
|
||||
def _get_host_sysctl_config(self, host):
|
||||
config = {}
|
||||
|
||||
|
@ -1873,3 +1873,22 @@ class TestMigrations(BaseMigrationTestCase, WalkVersionsMixin):
|
||||
for col, coltype in ntps_col.items():
|
||||
self.assertTrue(isinstance(ntps.c[col].type,
|
||||
getattr(sqlalchemy.types, coltype)))
|
||||
|
||||
def _check_075(self, engine, data):
|
||||
# Assert data types for all columns in new table "ptp"
|
||||
ptp = db_utils.get_table(engine, 'ptp')
|
||||
ptp_cols = {
|
||||
'created_at': 'DateTime',
|
||||
'updated_at': 'DateTime',
|
||||
'deleted_at': 'DateTime',
|
||||
'id': 'Integer',
|
||||
'uuid': 'String',
|
||||
'enabled': 'Boolean',
|
||||
'mode': 'String',
|
||||
'transport': 'String',
|
||||
'mechanism': 'String',
|
||||
'system_id': 'Integer',
|
||||
}
|
||||
for col, coltype in ptp_cols.items():
|
||||
self.assertTrue(isinstance(ptp.c[col].type,
|
||||
getattr(sqlalchemy.types, coltype)))
|
||||
|
Loading…
Reference in New Issue
Block a user