composable keystone services

Adds new puppet and puppet pacemaker specific services for
Keystone.

The puppet manifests for keystone now live in puppet-tripleo.
Hiera settings are driven by the nested stack heat templates
and used to control puppet-keystone and puppet-tripleo
directly.

The Pacemaker template extends the default keystone service and
swaps in the pacemaker specific puppet-tripleo profile instead.

Change-Id: I8b30438a27e9d5ec4e7d335e0bd1a931a20b03a2
Depends-On: I2faf5a78db802549053ec41678bf83bf28108189
This commit is contained in:
Dan Prince 2016-03-21 21:26:53 -04:00
parent 2198be8156
commit de0ac7b2d6
8 changed files with 178 additions and 280 deletions

View File

@ -4,3 +4,9 @@ resource_registry:
OS::TripleO::ControllerConfig: ../puppet/controller-config-pacemaker.yaml
OS::TripleO::Tasks::ControllerPrePuppet: ../extraconfig/tasks/pre_puppet_pacemaker.yaml
OS::TripleO::Tasks::ControllerPostPuppet: ../extraconfig/tasks/post_puppet_pacemaker.yaml
# custom pacemaker services
# NOTE: For now we will need to specify overrides to all services
# which use pacemaker. In the future (with upcoming HA light work) this
# list will hopefully be much smaller however.
OS::TripleO::Services::Keystone: ../puppet/services/pacemaker/keystone.yaml

View File

@ -123,6 +123,7 @@ resource_registry:
# services
OS::TripleO::Services: puppet/services/services.yaml
OS::TripleO::Services::Keystone: puppet/services/keystone.yaml
parameter_defaults:
EnablePackageInstall: false

View File

@ -355,10 +355,6 @@ parameters:
type: json
# Controller-specific params
AdminToken:
description: The keystone auth secret.
type: string
hidden: true
CinderLVMLoopDeviceSize:
default: 10280
description: The size of the loopback file used by the cinder LVM driver.
@ -499,38 +495,6 @@ parameters:
default: 'instance-%08x'
description: Template string to be used to generate instance names
type: string
KeystoneCACertificate:
default: ''
description: Keystone self-signed certificate authority certificate.
type: string
KeystoneSigningCertificate:
default: ''
description: Keystone certificate for verifying token validity.
type: string
KeystoneSigningKey:
default: ''
description: Keystone key for signing tokens.
type: string
hidden: true
KeystoneSSLCertificate:
default: ''
description: Keystone certificate for verifying token validity.
type: string
KeystoneSSLCertificateKey:
default: ''
description: Keystone key for signing tokens.
type: string
hidden: true
KeystoneNotificationDriver:
description: Comma-separated list of Oslo notification drivers used by Keystone
default: ['messaging']
type: comma_delimited_list
KeystoneNotificationFormat:
description: The Keystone notification format
default: 'basic'
type: string
constraints:
- allowed_values: [ 'basic', 'cadf' ]
ManageFirewall:
default: false
description: Whether to manage IPtables rules.
@ -712,7 +676,8 @@ parameters:
type: json
ControllerServices:
default: []
default:
- OS::TripleO::Services::Keystone
description: A list of service resources (configured in the Heat
resource_registry) which represent nested stacks
for each service that should get installed on the Controllers.
@ -916,7 +881,6 @@ resources:
type: OS::TripleO::Controller
properties:
AdminPassword: {get_param: AdminPassword}
AdminToken: {get_param: AdminToken}
AodhPassword: {get_param: AodhPassword}
CeilometerBackend: {get_param: CeilometerBackend}
CeilometerMeteringSecret: {get_param: CeilometerMeteringSecret}
@ -957,13 +921,6 @@ resources:
ImageUpdatePolicy: {get_param: ImageUpdatePolicy}
InstanceNameTemplate: {get_param: InstanceNameTemplate}
KeyName: {get_param: KeyName}
KeystoneCACertificate: {get_param: KeystoneCACertificate}
KeystoneSigningCertificate: {get_param: KeystoneSigningCertificate}
KeystoneSigningKey: {get_param: KeystoneSigningKey}
KeystoneSSLCertificate: {get_param: KeystoneSSLCertificate}
KeystoneSSLCertificateKey: {get_param: KeystoneSSLCertificateKey}
KeystoneNotificationDriver: {get_param: KeystoneNotificationDriver}
KeystoneNotificationFormat: {get_param: KeystoneNotificationFormat}
MemcachedIPv6: {get_param: MemcachedIPv6}
MysqlClusterUniquePart: {get_attr: [MysqlClusterUniquePart, value]}
MysqlInnodbBufferPoolSize: {get_param: MysqlInnodbBufferPoolSize}
@ -1043,8 +1000,6 @@ resources:
SwiftProxyVirtualIP: {get_attr: [VipMap, net_ip_map, {get_param: [ServiceNetMap, SwiftProxyNetwork]}]}
MysqlVirtualIP: {get_attr: [VipMap, net_ip_map, {get_param: [ServiceNetMap, MysqlNetwork]}]}
MysqlVirtualIPUri: {get_attr: [VipMap, net_ip_uri_map, {get_param: [ServiceNetMap, MysqlNetwork]}]}
KeystoneAdminApiVirtualIP: {get_attr: [VipMap, net_ip_map, {get_param: [ServiceNetMap, KeystoneAdminApiNetwork]}]}
KeystonePublicApiVirtualIP: {get_attr: [VipMap, net_ip_map, {get_param: [ServiceNetMap, KeystonePublicApiNetwork]}]}
NeutronApiVirtualIP: {get_attr: [VipMap, net_ip_map, {get_param: [ServiceNetMap, NeutronApiNetwork]}]}
NovaApiVirtualIP: {get_attr: [VipMap, net_ip_map, {get_param: [ServiceNetMap, NovaApiNetwork]}]}
SaharaApiVirtualIP: {get_attr: [VipMap, net_ip_map, {get_param: [ServiceNetMap, SaharaApiNetwork]}]}

View File

@ -4,19 +4,10 @@ description: >
OpenStack controller node configured by Puppet.
parameters:
AdminEmail:
default: 'admin@example.com'
description: The email for the keystone admin account.
type: string
hidden: true
AdminPassword:
description: The password for the keystone admin account, used for monitoring, querying neutron etc.
type: string
hidden: true
AdminToken:
description: The keystone auth secret and db password.
type: string
hidden: true
AodhApiVirtualIP:
type: string
default: ''
@ -295,43 +286,6 @@ parameters:
type: string
constraints:
- custom_constraint: nova.keypair
KeystoneCACertificate:
default: ''
description: Keystone self-signed certificate authority certificate.
type: string
KeystoneEnableDBPurge:
default: true
description: |
Whether to create cron job for purging soft deleted rows in Keystone database.
type: boolean
KeystoneSigningCertificate:
default: ''
description: Keystone certificate for verifying token validity.
type: string
KeystoneSigningKey:
default: ''
description: Keystone key for signing tokens.
type: string
hidden: true
KeystoneSSLCertificate:
default: ''
description: Keystone certificate for verifying token validity.
type: string
KeystoneSSLCertificateKey:
default: ''
description: Keystone key for signing tokens.
type: string
hidden: true
KeystoneNotificationDriver:
description: Comma-separated list of Oslo notification drivers used by Keystone
default: ['messaging']
type: comma_delimited_list
KeystoneNotificationFormat:
description: The Keystone notification format
default: 'basic'
type: string
constraints:
- allowed_values: [ 'basic', 'cadf' ]
KeystoneRegion:
type: string
default: 'regionOne'
@ -348,10 +302,6 @@ parameters:
default: false
description: Whether IPtables rules should be purged before setting up the new ones.
type: boolean
KeystoneWorkers:
default: 0
description: Number of workers for Keystone service.
type: number
SaharaApiVirtualIP:
type: string
default: ''
@ -723,12 +673,6 @@ parameters:
MysqlVirtualIPUri:
type: string
default: ''
KeystoneAdminApiVirtualIP:
type: string
default: ''
KeystonePublicApiVirtualIP:
type: string
default: ''
NeutronApiVirtualIP:
type: string
default: ''
@ -956,7 +900,6 @@ resources:
cinder_workers: {get_param: CinderWorkers}
glance_workers: {get_param: GlanceWorkers}
heat_workers: {get_param: HeatWorkers}
keystone_workers: {get_param: KeystoneWorkers}
nova_workers: {get_param: NovaWorkers}
neutron_workers: {get_param: NeutronWorkers}
swift_workers: {get_param: SwiftWorkers}
@ -988,9 +931,7 @@ resources:
heat_enable_db_purge: {get_param: HeatEnableDBPurge}
horizon_allowed_hosts: {get_param: HorizonAllowedHosts}
horizon_secret: {get_param: HorizonSecret}
admin_email: {get_param: AdminEmail}
admin_password: {get_param: AdminPassword}
admin_token: {get_param: AdminToken}
neutron_public_interface_ip: {get_param: NeutronPublicInterfaceIP}
debug: {get_param: Debug}
cinder_enable_db_purge: {get_param: CinderEnableDBPurge}
@ -1042,26 +983,8 @@ resources:
- '@'
- {get_param: MysqlVirtualIPUri}
- '/heat'
keystone_ca_certificate: {get_param: KeystoneCACertificate}
keystone_signing_key: {get_param: KeystoneSigningKey}
keystone_signing_certificate: {get_param: KeystoneSigningCertificate}
keystone_ssl_certificate: {get_param: KeystoneSSLCertificate}
keystone_ssl_certificate_key: {get_param: KeystoneSSLCertificateKey}
keystone_notification_driver: {get_param: KeystoneNotificationDriver}
keystone_notification_format: {get_param: KeystoneNotificationFormat}
keystone_enable_db_purge: {get_param: KeystoneEnableDBPurge}
keystone_dsn:
list_join:
- ''
- - 'mysql+pymysql://keystone:'
- {get_param: AdminToken}
- '@'
- {get_param: MysqlVirtualIPUri}
- '/keystone'
keystone_identity_uri: { get_param: [EndpointMap, KeystoneAdmin, uri_no_suffix] }
keystone_auth_uri: {get_param: [EndpointMap, KeystoneInternal, uri] }
keystone_public_url: { get_param: [EndpointMap, KeystonePublic, uri_no_suffix] }
keystone_internal_url: { get_param: [EndpointMap, KeystoneInternal, uri_no_suffix] }
keystone_ec2_uri: { get_param: [EndpointMap, KeystoneEC2, uri] }
enable_fencing: {get_param: EnableFencing}
enable_galera: {get_param: EnableGalera}
@ -1446,38 +1369,11 @@ resources:
heat::db::mysql::password: {get_input: heat_password}
heat_enable_db_purge: {get_input: heat_enable_db_purge}
heat::keystone::domain::domain_password: {get_input: heat_stack_domain_admin_password}
# Keystone
keystone::admin_token: {get_input: admin_token}
keystone::roles::admin::password: {get_input: admin_password}
keystone_ca_certificate: {get_input: keystone_ca_certificate}
keystone_signing_key: {get_input: keystone_signing_key}
keystone_signing_certificate: {get_input: keystone_signing_certificate}
keystone_ssl_certificate: {get_input: keystone_ssl_certificate}
keystone_ssl_certificate_key: {get_input: keystone_ssl_certificate_key}
keystone::database_connection: {get_input: keystone_dsn}
keystone::admin_bind_host: {get_input: keystone_admin_api_network}
keystone::public_bind_host: {get_input: keystone_public_api_network}
keystone::wsgi::apache::bind_host: {get_input: keystone_public_api_network}
keystone::wsgi::apache::admin_bind_host: {get_input: keystone_admin_api_network}
keystone::debug: {get_input: debug}
keystone::db::mysql::password: {get_input: admin_token}
keystone::rabbit_userid: {get_input: rabbit_username}
keystone::rabbit_password: {get_input: rabbit_password}
keystone::rabbit_use_ssl: {get_input: rabbit_client_use_ssl}
keystone::rabbit_port: {get_input: rabbit_client_port}
keystone::notification_driver: {get_input: keystone_notification_driver}
keystone::notification_format: {get_input: keystone_notification_format}
keystone::roles::admin::email: {get_input: admin_email}
keystone::roles::admin::password: {get_input: admin_password}
keystone::endpoint::public_url: {get_input: keystone_public_url}
keystone::endpoint::internal_url: {get_input: keystone_internal_url}
keystone::endpoint::admin_url: {get_input: keystone_identity_uri}
keystone::endpoint::region: {get_input: keystone_region}
keystone::admin_workers: {get_input: keystone_workers}
keystone::public_workers: {get_input: keystone_workers}
keystone_enable_db_purge: {get_input: keystone_enable_db_purge}
keystone::public_endpoint: {get_input: keystone_public_url}
# MongoDB
mongodb::server::bind_ip: {get_input: mongo_db_network}
mongodb::server::nojournal: {get_input: mongodb_no_journal}

View File

@ -212,42 +212,6 @@ if hiera('step') >= 2 {
if hiera('step') >= 4 {
include ::keystone
include ::keystone::config
include ::keystone::roles::admin
include ::keystone::endpoint
include ::keystone::wsgi::apache
#TODO: need a cleanup-keystone-tokens.sh solution here
file { [ '/etc/keystone/ssl', '/etc/keystone/ssl/certs', '/etc/keystone/ssl/private' ]:
ensure => 'directory',
owner => 'keystone',
group => 'keystone',
require => Package['keystone'],
}
file { '/etc/keystone/ssl/certs/signing_cert.pem':
content => hiera('keystone_signing_certificate'),
owner => 'keystone',
group => 'keystone',
notify => Service['keystone'],
require => File['/etc/keystone/ssl/certs'],
}
file { '/etc/keystone/ssl/private/signing_key.pem':
content => hiera('keystone_signing_key'),
owner => 'keystone',
group => 'keystone',
notify => Service['keystone'],
require => File['/etc/keystone/ssl/private'],
}
file { '/etc/keystone/ssl/certs/ca.pem':
content => hiera('keystone_ca_certificate'),
owner => 'keystone',
group => 'keystone',
notify => Service['keystone'],
require => File['/etc/keystone/ssl/certs'],
}
$glance_backend = downcase(hiera('glance_backend', 'swift'))
case $glance_backend {
'swift': { $backend_store = 'glance.store.swift.Store' }
@ -684,14 +648,10 @@ if hiera('step') >= 4 {
} #END STEP 4
if hiera('step') >= 5 {
$keystone_enable_db_purge = hiera('keystone_enable_db_purge', true)
$nova_enable_db_purge = hiera('nova_enable_db_purge', true)
$cinder_enable_db_purge = hiera('cinder_enable_db_purge', true)
$heat_enable_db_purge = hiera('heat_enable_db_purge', true)
if $keystone_enable_db_purge {
include ::keystone::cron::token_flush
}
if $nova_enable_db_purge {
include ::nova::cron::archive_deleted_rows
}
@ -703,7 +663,6 @@ if hiera('step') >= 5 {
}
if downcase(hiera('bootstrap_nodeid')) == $::hostname {
include ::keystone::roles::admin
# Class ::heat::keystone::domain has to run on bootstrap node
# because it creates DB entities via API calls.
include ::heat::keystone::domain

View File

@ -606,44 +606,6 @@ MYSQL_HOST=localhost\n",
if hiera('step') >= 4 {
class { '::keystone':
sync_db => $sync_db,
manage_service => false,
enabled => false,
enable_bootstrap => $pacemaker_master,
}
include ::keystone::config
#TODO: need a cleanup-keystone-tokens.sh solution here
file { [ '/etc/keystone/ssl', '/etc/keystone/ssl/certs', '/etc/keystone/ssl/private' ]:
ensure => 'directory',
owner => 'keystone',
group => 'keystone',
require => Package['keystone'],
}
file { '/etc/keystone/ssl/certs/signing_cert.pem':
content => hiera('keystone_signing_certificate'),
owner => 'keystone',
group => 'keystone',
notify => Service['keystone'],
require => File['/etc/keystone/ssl/certs'],
}
file { '/etc/keystone/ssl/private/signing_key.pem':
content => hiera('keystone_signing_key'),
owner => 'keystone',
group => 'keystone',
notify => Service['keystone'],
require => File['/etc/keystone/ssl/private'],
}
file { '/etc/keystone/ssl/certs/ca.pem':
content => hiera('keystone_ca_certificate'),
owner => 'keystone',
group => 'keystone',
notify => Service['keystone'],
require => File['/etc/keystone/ssl/certs'],
}
$glance_backend = downcase(hiera('glance_backend', 'swift'))
case $glance_backend {
'swift': { $backend_store = 'glance.store.swift.Store' }
@ -1127,7 +1089,6 @@ if hiera('step') >= 4 {
service_enable => false,
# service_manage => false, # <-- not supported with horizon&apache mod_wsgi?
}
include ::keystone::wsgi::apache
include ::apache::mod::status
if 'cisco_n1kv' in hiera('neutron::plugins::ml2::mechanism_drivers') {
$_profile_support = 'cisco'
@ -1189,14 +1150,10 @@ if hiera('step') >= 4 {
} #END STEP 4
if hiera('step') >= 5 {
$keystone_enable_db_purge = hiera('keystone_enable_db_purge', true)
$nova_enable_db_purge = hiera('nova_enable_db_purge', true)
$cinder_enable_db_purge = hiera('cinder_enable_db_purge', true)
$heat_enable_db_purge = hiera('heat_enable_db_purge', true)
if $keystone_enable_db_purge {
include ::keystone::cron::token_flush
}
if $nova_enable_db_purge {
include ::nova::cron::archive_deleted_rows
}
@ -1209,18 +1166,6 @@ if hiera('step') >= 5 {
if $pacemaker_master {
if $enable_load_balancer {
pacemaker::constraint::base { 'haproxy-then-keystone-constraint':
constraint_type => 'order',
first_resource => 'haproxy-clone',
second_resource => 'openstack-core-clone',
first_action => 'start',
second_action => 'start',
require => [Pacemaker::Resource::Service['haproxy'],
Pacemaker::Resource::Ocf['openstack-core']],
}
}
pacemaker::constraint::base { 'openstack-core-then-httpd-constraint':
constraint_type => 'order',
first_resource => 'openstack-core-clone',
@ -1230,15 +1175,6 @@ if hiera('step') >= 5 {
require => [Pacemaker::Resource::Service[$::apache::params::service_name],
Pacemaker::Resource::Ocf['openstack-core']],
}
pacemaker::constraint::base { 'rabbitmq-then-keystone-constraint':
constraint_type => 'order',
first_resource => 'rabbitmq-clone',
second_resource => 'openstack-core-clone',
first_action => 'start',
second_action => 'start',
require => [Pacemaker::Resource::Ocf['rabbitmq'],
Pacemaker::Resource::Ocf['openstack-core']],
}
pacemaker::constraint::base { 'memcached-then-openstack-core-constraint':
constraint_type => 'order',
first_resource => 'memcached-clone',
@ -1963,29 +1899,5 @@ if hiera('step') >= 5 {
} #END STEP 5
if hiera('step') >= 6 {
if $pacemaker_master {
class {'::keystone::roles::admin' :
require => Pacemaker::Resource::Service[$::apache::params::service_name],
} ->
class {'::keystone::endpoint' :
require => Pacemaker::Resource::Service[$::apache::params::service_name],
}
include ::heat::keystone::domain
Class['::keystone::roles::admin'] -> Class['::heat::keystone::domain']
} else {
# On non-master controller we don't need to create Keystone resources again
class { '::heat::keystone::domain':
manage_domain => false,
manage_user => false,
manage_role => false,
}
}
} #END STEP 6
$package_manifest_name = join(['/var/lib/tripleo/installed-packages/overcloud_controller_pacemaker', hiera('step')])
package_manifest{$package_manifest_name: ensure => present}

View File

@ -0,0 +1,135 @@
heat_template_version: 2016-04-08
description: >
OpenStack Keystone service configured with Puppet
parameters:
KeystoneCACertificate:
default: ''
description: Keystone self-signed certificate authority certificate.
type: string
KeystoneEnableDBPurge:
default: true
description: |
Whether to create cron job for purging soft deleted rows in Keystone database.
type: boolean
KeystoneSigningCertificate:
default: ''
description: Keystone certificate for verifying token validity.
type: string
KeystoneSigningKey:
default: ''
description: Keystone key for signing tokens.
type: string
hidden: true
KeystoneSSLCertificate:
default: ''
description: Keystone certificate for verifying token validity.
type: string
KeystoneSSLCertificateKey:
default: ''
description: Keystone key for signing tokens.
type: string
hidden: true
KeystoneNotificationDriver:
description: Comma-separated list of Oslo notification drivers used by Keystone
default: ['messaging']
type: comma_delimited_list
KeystoneNotificationFormat:
description: The Keystone notification format
default: 'basic'
type: string
constraints:
- allowed_values: [ 'basic', 'cadf' ]
KeystoneRegion:
type: string
default: 'regionOne'
description: Keystone region for endpoint
KeystoneWorkers:
default: 0
description: Number of workers for Keystone service.
type: number
EndpointMap:
default: {}
description: Mapping of service endpoint -> protocol. Typically set
via parameter_defaults in the resource registry.
type: json
MysqlVirtualIPUri:
type: string
default: ''
Debug:
type: string
default: ''
AdminEmail:
default: 'admin@example.com'
description: The email for the keystone admin account.
type: string
hidden: true
AdminPassword:
description: The password for the keystone admin account, used for monitoring, querying neutron etc.
type: string
hidden: true
AdminToken:
description: The keystone auth secret and db password.
type: string
hidden: true
RabbitPassword:
description: The password for RabbitMQ
type: string
hidden: true
RabbitUserName:
default: guest
description: The username for RabbitMQ
type: string
RabbitClientUseSSL:
default: false
description: >
Rabbit client subscriber parameter to specify
an SSL connection to the RabbitMQ host.
type: string
RabbitClientPort:
default: 5672
description: Set rabbit subscriber port, change this if using SSL
type: number
outputs:
role_data:
description: Role data for the Keystone role.
value:
config_settings:
keystone_dsn: &keystone_dsn
list_join:
- ''
- - 'mysql+pymysql://keystone:'
- {get_param: AdminToken}
- '@'
- {get_param: MysqlVirtualIPUri}
- '/keystone'
keystone::database_connection: *keystone_dsn
keystone::admin_token: {get_param: AdminToken}
keystone::roles::admin::password: {get_param: AdminPassword}
keystone_ca_certificate: {get_param: KeystoneCACertificate}
keystone_signing_key: {get_param: KeystoneSigningKey}
keystone_signing_certificate: {get_param: KeystoneSigningCertificate}
keystone_ssl_certificate: {get_param: KeystoneSSLCertificate}
keystone_ssl_certificate_key: {get_param: KeystoneSSLCertificateKey}
keystone::debug: {get_param: Debug}
keystone::db::mysql::password: {get_param: AdminToken}
keystone::rabbit_userid: {get_param: RabbitUserName}
keystone::rabbit_password: {get_param: RabbitPassword}
keystone::rabbit_use_ssl: {get_param: RabbitClientUseSSL}
keystone::rabbit_port: {get_param: RabbitClientPort}
keystone::notification_driver: {get_param: KeystoneNotificationDriver}
keystone::notification_format: {get_param: KeystoneNotificationFormat}
keystone::roles::admin::email: {get_param: AdminEmail}
keystone::roles::admin::password: {get_param: AdminPassword}
keystone::endpoint::public_url: {get_param: [EndpointMap, KeystonePublic, uri_no_suffix]}
keystone::endpoint::internal_url: {get_param: [EndpointMap, KeystoneInternal, uri_no_suffix]}
keystone::endpoint::admin_url: {get_param: [EndpointMap, KeystoneAdmin, uri_no_suffix]}
keystone::endpoint::region: {get_param: KeystoneRegion}
keystone::admin_workers: {get_param: KeystoneWorkers}
keystone::public_workers: {get_param: KeystoneWorkers}
keystone_enable_db_purge: {get_param: KeystoneEnableDBPurge}
keystone::public_endpoint: {get_param: [EndpointMap, KeystonePublic, uri_no_suffix]}
step_config: |
include ::tripleo::profile::base::keystone

View File

@ -0,0 +1,34 @@
heat_template_version: 2016-04-08
description: >
OpenStack Keystone service with Pacemaker configured with Puppet
parameters:
EndpointMap:
default: {}
description: Mapping of service endpoint -> protocol. Typically set
via parameter_defaults in the resource registry.
type: json
MysqlVirtualIPUri:
type: string
default: ''
resources:
KeystoneServiceBase:
type: ../keystone.yaml
properties:
EndpointMap: {get_param: EndpointMap}
MysqlVirtualIPUri: {get_param: MysqlVirtualIPUri}
outputs:
role_data:
description: Role data for the Keystone pacemaker role.
value:
config_settings:
map_merge:
- get_attr: [KeystoneServiceBase, role_data, config_settings]
#-
# custom keystone hiera goes here if we need it!?
step_config: |
include ::tripleo::profile::pacemaker::keystone