Support for NTP/PTP coexistence

The NTP/PTP selection will now be done per host. NTP will the default
selection.

This commit;

-Removes the enabled flag NTP and PTP API. Updates the CLI commands
and the database.

-Adds the parameter clock_synchronization to the host API. Valid
values are ‘ntp’ and ‘ptp’. Updates the host CLI commands and the
database.

-Updates puppet to set NTP/PTP per host .

-Updates the RestAPI documentation.

Story: 2006499
Task: 36464

Change-Id: I37bbb30a014301f8786cb02e35f0a1bd39d2f4aa
Signed-off-by: Kristine Bujold <kristine.bujold@windriver.com>
This commit is contained in:
Kristine Bujold 2019-09-13 13:27:45 -04:00
parent 6f04c28cd4
commit 2d17e9849f
21 changed files with 342 additions and 107 deletions

View File

@ -4311,6 +4311,162 @@ badMediaType (415)
"uuid":"81321749-5092-4faf-94ba-6a6853440725"
}
----
PTP
----
The PTP is the Precision Time Protocol entity for the system.
************************************
Shows attributes of the PTP object
************************************
.. rest_method:: GET /v1/ptp
**Normal response codes**
200
**Error response codes**
computeFault (400, 500, ...), serviceUnavailable (503), badRequest (400),
unauthorized (401), forbidden (403), badMethod (405), overLimit (413),
itemNotFound (404)
**Response parameters**
.. csv-table::
:header: "Parameter", "Style", "Type", "Description"
:widths: 20, 20, 20, 60
"mode (Optional)", "plain", "xsd:string", "PTP time stamping mode."
"transport (Optional)", "plain", "xsd:string", "PTP transport protocol."
"mechanism (Optional)", "plain", "xsd:string", "PTP delay mechanism."
"isystem_uuid (Optional)", "plain", "csapi:UUID", "The System UUID which the PTP belongs to."
"uuid (Optional)", "plain", "csapi:UUID", "The universally unique identifier for this object."
"links (Optional)", "plain", "xsd:list", "For convenience, resources contain links to themselves. This allows a client to easily obtain rather than construct resource URIs. The following types of link relations are associated with resources: a self link containing a versioned link to the resource, and a bookmark link containing a permanent link to a resource that is appropriate for long term storage."
"created_at (Optional)", "plain", "xsd:dateTime", "The time when the object was created."
"updated_at (Optional)", "plain", "xsd:dateTime", "The time when the object was last updated."
::
{
"ptp":[
{
"links":[
{
"href":"http://192.168.204.2:6385/v1/ptps/70649b44-b462-445a-9fa5-9233a1b5842d",
"rel":"self"
},
{
"href":"http://192.168.204.2:6385/ptps/70649b44-b462-445a-9fa5-9233a1b5842d",
"rel":"bookmark"
}
],
"created_at":"2019-09-30T14:42:16.693209+00:00",
"updated_at":"2019-10-01T17:33:43.169595+00:00",
"mechanism":"e2e",
"mode":"hardware",
"transport":"l2",
"isystem_uuid":"ce178041-2b2c-405d-bf87-f19334a35582",
"uuid":"70649b44-b462-445a-9fa5-9233a1b5842d"
}
]
}
This operation does not accept a request body.
***************************************
Modifies attributes of the PTP object
***************************************
.. rest_method:: PATCH /v1/ptp/{ptp_id}
The attributes of the PTP object that are configurable are:
- mode
- transport
- mechanism
**Normal response codes**
200
**Error response codes**
badMediaType (415)
**Request parameters**
.. csv-table::
:header: "Parameter", "Style", "Type", "Description"
:widths: 20, 20, 20, 60
"ptp_id", "URI", "csapi:UUID", "The unique identifier of the PTP for this system."
"mode (Optional)", "plain", "xsd:string", "PTP time stamping mode. Valid values are (is): ``hardware``, ``software`` or ``legacy``"
"transport (Optional)", "plain", "xsd:string", "PTP transport protocol. Valid values are (is): ``udp`` or ``l2``"
"mechanism (Optional)", "plain", "xsd:string", "PTP delay mechanism. Valid values are (is): ``e2e`` or ``p2p``"
**Response parameters**
.. csv-table::
:header: "Parameter", "Style", "Type", "Description"
:widths: 20, 20, 20, 60
"mode (Optional)", "plain", "xsd:string", "PTP time stamping mode."
"transport (Optional)", "plain", "xsd:string", "PTP transport protocol."
"mechanism (Optional)", "plain", "xsd:string", "PTP delay mechanism."
"isystem_uuid (Optional)", "plain", "csapi:UUID", "The System UUID which the NTP belongs to."
"uuid (Optional)", "plain", "csapi:UUID", "The universally unique identifier for this object."
"links (Optional)", "plain", "xsd:list", "For convenience, resources contain links to themselves. This allows a client to easily obtain rather than construct resource URIs. The following types of link relations are associated with resources: a self link containing a versioned link to the resource, and a bookmark link containing a permanent link to a resource that is appropriate for long term storage."
"created_at (Optional)", "plain", "xsd:dateTime", "The time when the object was created."
"updated_at (Optional)", "plain", "xsd:dateTime", "The time when the object was last updated."
::
[
{
"path": "/mode",
"value": "legacy",
"op": "replace"
},
{
"path": "/transport",
"value": "udp",
"op": "replace"
},
{
"path": "/mechanism",
"value": "p2p",
"op": "replace"
}
]
::
{
"links":[
{
"href":"http://192.168.204.2:6385/v1/ptps/70649b44-b462-445a-9fa5-9233a1b5842d",
"rel":"self"
},
{
"href":"http://192.168.204.2:6385/ptps/70649b44-b462-445a-9fa5-9233a1b5842d",
"rel":"bookmark"
}
],
"created_at":"2014-09-30T14:42:16.693209+00:00",
"updated_at":"2014-10-01T17:35:43.162472+00:00",
"mechanism": "p2p"
"mode": "legacy"
"transport": "udp"
"isystem_uuid":"ce178041-2b2c-405d-bf87-f19334a35582",
"forisystemid":1,
"uuid":"70649b44-b462-445a-9fa5-9233a1b5842d"
}
-------------
External OAM
-------------
@ -10896,7 +11052,7 @@ itemNotFound (404)
"registry_images": [
{
"tag": null,
"name": "docker.io/starlingx/ceph-config-helper"
"name": "docker.io/starlingx/ceph-config-helper"
},
{
"tag": null,
@ -10946,8 +11102,8 @@ itemNotFound (404)
{
"registry_images": [
{
"tag": "v1.15.0",
"name": "docker.io/starlingx/ceph-config-helper"
"tag": "v1.15.0",
"name": "docker.io/starlingx/ceph-config-helper"
}
]
}

View File

@ -1,2 +1,2 @@
SRC_DIR="cgts-client"
TIS_PATCH_VER=70
TIS_PATCH_VER=71

View File

@ -1,6 +1,6 @@
#!/usr/bin/env python
#
# Copyright (c) 2013-2017 Wind River Systems, Inc.
# Copyright (c) 2013-2019 Wind River Systems, Inc.
#
# SPDX-License-Identifier: Apache-2.0
#
@ -35,7 +35,7 @@ def _print_ihost_show(ihost):
'location', 'uptime', 'reserved', 'created_at', 'updated_at',
'boot_device', 'rootfs_device', 'install_output', 'console',
'tboot', 'vim_progress_status', 'software_load', 'install_state',
'install_state_info', 'inv_state']
'install_state_info', 'inv_state', 'clock_synchronization']
optional_fields = ['vsc_controllers', 'ttys_dcd']
if ihost.subfunctions != ihost.personality:
fields.append('subfunctions')
@ -141,13 +141,18 @@ def do_host_upgrade_list(cc, args):
@utils.arg('-D', '--ttys_dcd',
metavar='<true/false>',
help='Enable/disable serial console data carrier detection')
@utils.arg('-C', '--clock_synchronization',
metavar='<clock_synchronization>',
choices=['ntp', 'ptp'],
help='Clock synchronization, ntp or ptp. Default: ntp')
def do_host_add(cc, args):
"""Add a new host."""
field_list = ['hostname', 'personality', 'subfunctions',
'mgmt_mac', 'mgmt_ip',
'bm_ip', 'bm_type', 'bm_username', 'bm_password',
'boot_device', 'rootfs_device', 'install_output', 'console',
'vsc_controllers', 'location', 'ttys_dcd']
'vsc_controllers', 'location', 'ttys_dcd',
'clock_synchronization']
fields = dict((k, v) for (k, v) in vars(args).items()
if k in field_list and not (v is None))

View File

@ -1,5 +1,5 @@
#
# Copyright (c) 2013-2015 Wind River Systems, Inc.
# Copyright (c) 2013-2019 Wind River Systems, Inc.
#
# SPDX-License-Identifier: Apache-2.0
#
@ -20,7 +20,7 @@ CREATION_ATTRIBUTES = ['hostname', 'personality', 'subfunctions',
'boot_device', 'rootfs_device', 'install_output',
'console', 'tboot', 'vsc_controllers', 'ttys_dcd',
'administrative', 'operational', 'availability',
'invprovision']
'invprovision', 'clock_synchronization']
class ihost(base.Resource):

View File

@ -1,6 +1,6 @@
#!/usr/bin/env python
#
# Copyright (c) 2013-2017 Wind River Systems, Inc.
# Copyright (c) 2013-2019 Wind River Systems, Inc.
#
# SPDX-License-Identifier: Apache-2.0
#
@ -14,8 +14,7 @@ from cgtsclient import exc
def _print_intp_show(intp):
fields = ['uuid', 'enabled', 'ntpservers',
'isystem_uuid', 'created_at', 'updated_at']
fields = ['uuid', 'ntpservers', 'isystem_uuid', 'created_at', 'updated_at']
data = [(f, getattr(intp, f, '')) for f in fields]
utils.print_tuple_list(data)
@ -34,8 +33,8 @@ def donot_config_ntp_list(cc, args):
intps = cc.intp.list()
field_labels = ['uuid', 'enabled', 'ntpservers']
fields = ['uuid', 'enabled', 'ntpservers']
field_labels = ['uuid', 'ntpservers']
fields = ['uuid', 'ntpservers']
utils.print_list(intps, fields, field_labels, sortby=1)
@ -70,9 +69,6 @@ def donot_ntp_add(cc, args):
_print_intp_show(intp)
@utils.arg('--enabled',
metavar='<true/false>',
help="NTP service enabled.")
@utils.arg('attributes',
metavar='<path=value>',
nargs='*',
@ -86,9 +82,6 @@ def do_ntp_modify(cc, args):
intp = intps[0]
op = "replace"
if args.enabled is not None:
args.attributes[0].append('enabled=%s' % args.enabled)
for attribute in args.attributes:
if 'ntpservers=' in attribute:
ntpservers = attribute[0].split('=')[1]

View File

@ -1,6 +1,6 @@
########################################################################
#
# Copyright (c) 2018 Wind River Systems, Inc.
# Copyright (c) 2018-2019 Wind River Systems, Inc.
#
# SPDX-License-Identifier: Apache-2.0
#
@ -11,7 +11,7 @@ from cgtsclient import exc
def _print_ptp_show(ptp):
fields = ['uuid', 'enabled', 'mode', 'transport', 'mechanism',
fields = ['uuid', 'mode', 'transport', 'mechanism',
'isystem_uuid', 'created_at', 'updated_at']
data = [(f, getattr(ptp, f, '')) for f in fields]
utils.print_tuple_list(data)
@ -30,14 +30,11 @@ def donot_config_ptp_list(cc, args):
ptps = cc.ptp.list()
field_labels = ['uuid', 'enabled', 'mode', 'transport', 'mechanism']
fields = ['uuid', 'enabled', 'mode', 'transport', 'mechanism']
field_labels = ['uuid', 'mode', 'transport', 'mechanism']
fields = ['uuid', '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,
@ -58,8 +55,7 @@ def do_ptp_modify(cc, args):
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:

View File

@ -1,2 +1,2 @@
SRC_DIR="sysinv"
TIS_PATCH_VER=331
TIS_PATCH_VER=332

View File

@ -422,6 +422,9 @@ class Host(base.APIBase):
config_target = wtypes.text
"Represent the configuration which needs to be applied to this ihost."
clock_synchronization = wtypes.text
"Represent the clock synchronization type of this ihost."
# Host uptime
uptime = int
@ -563,7 +566,7 @@ class Host(base.APIBase):
'software_load', 'target_load', 'peers', 'peer_id',
'install_state', 'install_state_info',
'iscsi_initiator_name',
'inv_state']
'inv_state', 'clock_synchronization']
fields = minimum_fields if not expand else None
uhost = Host.from_rpc_object(rpc_ihost, fields)
@ -1867,6 +1870,14 @@ class HostController(rest.RestController):
LOG.exception(e)
raise wsme.exc.ClientSideError(_("Patching Error: %s") % e)
if patched_ihost['clock_synchronization'] not in \
constants.CLOCK_SYNCHRONIZATION:
msg = _("Host update failed: clock_synchronization: "
"invalid choice: '%s', choose from %s" %
(patched_ihost['clock_synchronization'],
constants.CLOCK_SYNCHRONIZATION))
raise wsme.exc.ClientSideError(msg)
self._validate_capabilities(
ihost_dict['capabilities'], patched_ihost['capabilities'])
@ -2203,6 +2214,11 @@ class HostController(rest.RestController):
self._handle_ttys_dcd_change(hostupdate.ihost_orig,
hostupdate.ihost_patch['ttys_dcd'])
if 'clock_synchronization' in hostupdate.delta:
# perform rpc to conductor to perform config apply
pecan.request.rpcapi.update_clock_synchronization_config(
pecan.request.context, patched_ihost)
log_end = cutils.timestamped("ihost_patch_end")
if uptime_update:
LOG.debug("host %s %s patch" % (ihost_obj.hostname,
@ -2212,8 +2228,8 @@ class HostController(rest.RestController):
log_end))
if ('administrative' in hostupdate.delta and
hostupdate.ihost_patch['administrative'] ==
constants.ADMIN_LOCKED):
hostupdate.ihost_patch['administrative'] ==
constants.ADMIN_LOCKED):
LOG.info("Update host memory for (%s)" % ihost_obj['hostname'])
pecan.request.rpcapi.update_host_memory(pecan.request.context,
ihost_obj['uuid'])

View File

@ -16,7 +16,7 @@
# License for the specific language governing permissions and limitations
# under the License.
#
# Copyright (c) 2013-2017 Wind River Systems, Inc.
# Copyright (c) 2013-2019 Wind River Systems, Inc.
#
@ -66,9 +66,6 @@ class NTP(base.APIBase):
uuid = types.uuid
"Unique UUID for this ntp"
enabled = types.boolean
"Represent the status of the intp."
ntpservers = wtypes.text
"Represent the ntpservers of the intp. csv list."
@ -105,7 +102,6 @@ class NTP(base.APIBase):
ntp = NTP(**rpc_ntp.as_dict())
if not expand:
ntp.unset_fields_except(['uuid',
'enabled',
'ntpservers',
'isystem_uuid',
'created_at',
@ -152,23 +148,12 @@ class intpCollection(collection.Collection):
##############
def _check_ntp_data(op, ntp):
# Get data
enabled = ntp['enabled']
ntpservers = ntp['ntpservers']
intp_ntpservers_list = []
ntp_ntpservers = ""
idns_nameservers_list = []
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:
@ -184,7 +169,8 @@ def _check_ntp_data(op, ntp):
except (AddrFormatError, ValueError):
if utils.is_valid_hostname(ntpserver):
# If server address in FQDN, and no DNS servers, raise error
# If server address in FQDN, and no DNS servers,
# raise error
if len(idns_nameservers_list) == 0 and ntpserver != 'NC':
raise wsme.exc.ClientSideError(_(
"A DNS server must be configured prior to "
@ -200,9 +186,9 @@ def _check_ntp_data(op, ntp):
raise wsme.exc.ClientSideError(_(
"Invalid NTP server %s "
"Please configure a valid NTP "
"IP address or hostname.") % (ntpserver))
"IP address or hostname.") % ntpserver)
if len(intp_ntpservers_list) == 0 and enabled is None:
if len(intp_ntpservers_list) == 0:
raise wsme.exc.ClientSideError(_("No NTP parameters provided."))
if len(intp_ntpservers_list) > MAX_S:
@ -270,7 +256,7 @@ class NTPController(rest.RestController):
"""Retrieve a list of ntps. Only one per system"""
return self._get_ntps_collection(isystem_uuid, marker, limit,
sort_key, sort_dir)
sort_key, sort_dir)
@wsme_pecan.wsexpose(intpCollection, types.uuid, types.uuid, int,
wtypes.text, wtypes.text)
@ -359,18 +345,13 @@ class NTPController(rest.RestController):
rpc_ntp[field] = ntp[field]
delta = rpc_ntp.obj_what_changed()
delta_handle = list(delta)
if delta:
rpc_ntp.save()
if 'enabled' in delta_handle:
service_change = True
else:
service_change = False
if action == constants.APPLY_ACTION:
# perform rpc to conductor to perform config apply
pecan.request.rpcapi.update_ntp_config(pecan.request.context,
service_change)
pecan.request.rpcapi.update_ntp_config(
pecan.request.context)
else:
LOG.info("No NTP config changes")

View File

@ -1,6 +1,6 @@
########################################################################
#
# Copyright (c) 2018 Wind River Systems, Inc.
# Copyright (c) 2018-2019 Wind River Systems, Inc.
#
# SPDX-License-Identifier: Apache-2.0
#
@ -48,9 +48,6 @@ class PTP(base.APIBase):
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."
@ -80,7 +77,6 @@ class PTP(base.APIBase):
ptp = PTP(**rpc_ptp.as_dict())
if not expand:
ptp.unset_fields_except(['uuid',
'enabled',
'mode',
'transport',
'mechanism',
@ -118,21 +114,6 @@ class ptpCollection(collection.Collection):
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'
@ -223,7 +204,7 @@ class PTPController(rest.RestController):
except utils.JSONPATCH_EXCEPTIONS as e:
raise exception.PatchError(patch=patch, reason=e)
ptp = _check_ptp_data("modify", ptp.as_dict())
ptp = ptp.as_dict()
try:
# Update only the fields that have changed
@ -242,9 +223,8 @@ class PTPController(rest.RestController):
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))
msg = _("PTP update failed: %s %s %s : patch %s" %
(ptp['mode'], ptp['transport'], ptp['mechanism'], patch))
raise wsme.exc.ClientSideError(msg)
@wsme_pecan.wsexpose(None, types.uuid, status_code=204)

View File

@ -1457,3 +1457,12 @@ DEFAULT_DNS_SERVICE_DOMAIN = 'cluster.local'
ANSIBLE_BOOTSTRAP_FLAG = os.path.join(tsc.VOLATILE_PATH, ".ansible_bootstrap")
UNLOCK_READY_FLAG = os.path.join(tsc.PLATFORM_CONF_PATH, ".unlock_ready")
INVENTORY_WAIT_TIMEOUT_IN_SECS = 90
# Clock synchronization types
NTP = 'ntp'
PTP = 'ptp'
CLOCK_SYNCHRONIZATION = [
NTP,
PTP
]

View File

@ -1621,7 +1621,7 @@ class ConductorManager(service.PeriodicService):
:param do_worker_apply: configure the worker subfunctions of the host.
"""
LOG.debug("configure_ihost %s" % host.hostname)
LOG.info("configure_ihost %s" % host.hostname)
# Generate system configuration files
# TODO(mpeters): remove this once all system reconfigurations properly
@ -5385,14 +5385,17 @@ class ConductorManager(service.PeriodicService):
config_uuid = self._config_update_hosts(context, personalities)
self._update_resolv_file(context, config_uuid, personalities)
def update_ntp_config(self, context, service_change=False):
def update_clock_synchronization_config(self, context, host):
"""Update clock_synchronization configuration of a host"""
personalities = [host.get('personality')]
self._config_update_hosts(context, personalities, [host.get('uuid')],
reboot=True)
def update_ntp_config(self, context):
"""Update the NTP configuration"""
if service_change:
personalities = [constants.CONTROLLER,
constants.WORKER,
constants.STORAGE]
else:
personalities = [constants.CONTROLLER]
personalities = [constants.CONTROLLER,
constants.WORKER,
constants.STORAGE]
self._config_update_hosts(context, personalities, reboot=True)
def update_ptp_config(self, context):

View File

@ -726,13 +726,23 @@ class ConductorAPI(sysinv.openstack.common.rpc.proxy.RpcProxy):
"""
return self.call(context, self.make_msg('update_dns_config'))
def update_ntp_config(self, context, service_change=False):
def update_clock_synchronization_config(self, context, host):
"""Synchronously, have the conductor update the
clock_synchronization configuration of a host.
:param context: request context.
:param host: the host to be modified.
"""
return self.call(context,
self.make_msg('update_clock_synchronization_config',
host=host))
def update_ntp_config(self, context):
"""Synchronously, have the conductor update the NTP configuration.
:param context: request context.
"""
return self.call(context, self.make_msg('update_ntp_config',
service_change=service_change))
return self.call(context, self.make_msg('update_ntp_config'))
def update_ptp_config(self, context):
"""Synchronously, have the conductor update the PTP configuration.

View File

@ -0,0 +1,43 @@
# vim: tabstop=4 shiftwidth=4 softtabstop=4
#
# Copyright (c) 2019 Wind River Systems, Inc.
#
# SPDX-License-Identifier: Apache-2.0
#
from sqlalchemy import Column, MetaData, Table
from sqlalchemy import String
from sysinv.common import constants
ENGINE = 'InnoDB'
CHARSET = 'utf8'
def upgrade(migrate_engine):
"""
This database upgrade creates a new host clock_synchronization attribute
for storing the clock_synchronization type (ntp/ptp) for a host.
"""
meta = MetaData()
meta.bind = migrate_engine
host = Table('i_host', meta, autoload=True)
host.create_column(Column('clock_synchronization', String(32)),
default=constants.NTP)
ntp = Table('i_ntp', meta, autoload=True)
ntp.drop_column(Column('enabled'))
ptp = Table('ptp', meta, autoload=True)
ptp.drop_column(Column('enabled'))
return True
def downgrade(migrate_engine):
meta = MetaData()
meta.bind = migrate_engine
# Downgrade is unsupported.
raise NotImplementedError('SysInv database downgrade is unsupported.')

View File

@ -223,6 +223,8 @@ class ihost(Base):
config_applied = Column(String(255))
config_target = Column(String(255))
clock_synchronization = Column(String(32), default=constants.NTP)
boot_device = Column(String(255), default="/dev/sda")
rootfs_device = Column(String(255), default="/dev/sda")
install_output = Column(String(255), default="text")
@ -777,7 +779,6 @@ class intp(Base):
id = Column(Integer, primary_key=True)
uuid = Column(String(36))
enabled = Column(Boolean, default=True)
ntpservers = Column(String(255)) # csv list of ntp servers
forisystemid = Column(Integer,
@ -792,13 +793,12 @@ class PTP(Base):
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'))
ForeignKey('i_system.id', ondelete='CASCADE'))
system = relationship("isystem", lazy="joined", join_depth=1)

View File

@ -1,5 +1,5 @@
#
# Copyright (c) 2013-2017 Wind River Systems, Inc.
# Copyright (c) 2013-2019 Wind River Systems, Inc.
#
# SPDX-License-Identifier: Apache-2.0
#
@ -75,6 +75,7 @@ class Host(base.SysinvObject):
'config_applied': utils.str_or_none,
'config_target': utils.str_or_none,
'capabilities': utils.dict_or_none,
'clock_synchronization': utils.str_or_none,
'boot_device': utils.str_or_none,
'rootfs_device': utils.str_or_none,

View File

@ -1,5 +1,5 @@
#
# Copyright (c) 2013-2016 Wind River Systems, Inc.
# Copyright (c) 2013-2019 Wind River Systems, Inc.
#
# SPDX-License-Identifier: Apache-2.0
#
@ -21,7 +21,6 @@ class NTP(base.SysinvObject):
'id': int,
'uuid': utils.str_or_none,
'enabled': utils.bool_or_none,
'ntpservers': utils.str_or_none,
'forisystemid': utils.int_or_none,

View File

@ -1,6 +1,6 @@
########################################################################
#
# Copyright (c) 2018 Wind River Systems, Inc.
# Copyright (c) 2018-2019 Wind River Systems, Inc.
#
# SPDX-License-Identifier: Apache-2.0
#
@ -19,7 +19,6 @@ class PTP(base.SysinvObject):
'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,

View File

@ -411,8 +411,13 @@ class PlatformPuppet(base.BasePuppet):
else:
ntpdate_timeout = "30"
if host.clock_synchronization == constants.NTP:
ntp_enabled = True
else:
ntp_enabled = False
return {
'platform::ntp::enabled': ntp.enabled,
'platform::ntp::enabled': ntp_enabled,
'platform::ntp::servers': servers,
'platform::ntp::ntpdate_timeout': ntpdate_timeout,
}
@ -420,9 +425,14 @@ class PlatformPuppet(base.BasePuppet):
def _get_host_ptp_config(self, host):
ptp = self.dbapi.ptp_get_one()
if host.clock_synchronization == constants.PTP:
ptp_enabled = True
else:
ptp_enabled = False
return {
'platform::ptp::enabled':
ptp.enabled,
ptp_enabled,
'platform::ptp::mode':
ptp.mode,
'platform::ptp::transport':

View File

@ -33,6 +33,7 @@ class FakeConductorAPI(object):
self.delete_barbican_secret = mock.MagicMock()
self.iplatform_update_by_ihost = mock.MagicMock()
self.evaluate_app_reapply = mock.MagicMock()
self.update_clock_synchronization_config = mock.MagicMock()
def create_ihost(self, context, values):
# Create the host in the DB as the code under test expects this
@ -691,6 +692,38 @@ class TestPatch(TestHost):
result = self.get_json('/ihosts/%s' % ndict['hostname'])
self.assertEqual(new_location, result['location'])
def test_update_clock_synchronization(self):
# Create controller-0
ndict = dbutils.post_get_test_ihost(hostname='controller-0',
mgmt_ip=None,
serialid='serial1')
self.post_json('/ihosts', ndict,
headers={'User-Agent': 'sysinv-test'})
# Update clock_synchronization
response = self.patch_json('/ihosts/%s' % ndict['hostname'],
[{'path': '/clock_synchronization',
'value': constants.PTP,
'op': 'replace'}],
headers={'User-Agent': 'sysinv-test'})
self.assertEqual(response.content_type, 'application/json')
self.assertEqual(response.status_code, http_client.OK)
# Verify that the host was configured
self.fake_conductor_api.configure_ihost.assert_called_once()
# Verify that the app reapply was checked
self.fake_conductor_api.evaluate_app_reapply.assert_not_called()
# Verify that update_clock_synchronization_config was called
self.fake_conductor_api.update_clock_synchronization_config.\
assert_called_once()
# Verify that the host was updated with the new clock_synchronization
result = self.get_json('/ihosts/%s' % ndict['hostname'])
self.assertEqual(constants.PTP, result['clock_synchronization'])
def test_unlock_action_controller(self):
self._configure_networks()
# Create controller-0
@ -768,7 +801,7 @@ class TestPatch(TestHost):
administrative=constants.ADMIN_LOCKED,
operational=constants.OPERATIONAL_ENABLED,
availability=constants.AVAILABILITY_ONLINE,
inv_state=None)
inv_state=None, clock_synchronization=constants.NTP)
# Unlock host
response = self._patch_host_action(c0_host['hostname'],

View File

@ -15,7 +15,7 @@
# License for the specific language governing permissions and limitations
# under the License.
#
# Copyright (c) 2013-2018 Wind River Systems, Inc.
# Copyright (c) 2013-2019 Wind River Systems, Inc.
#
"""Sysinv test utilities."""
@ -162,6 +162,7 @@ def get_test_ihost(**kw):
'install_state_info': kw.get('install_state_info', None),
'iscsi_initiator_name': kw.get('iscsi_initiator_name', None),
'inv_state': kw.get('inv_state', 'inventoried'),
'clock_synchronization': kw.get('clock_synchronization', constants.NTP),
}
return inv