Maintainability and config updates.

Use default openstack charm hooks where possible, implemented
2 functions to facilitate this: get_amqp_credentials() and get_database_setup().

Allow for additional trove configuration by the user:
    trove-volume-support: same name as in trove.conf
    trove-datastore-database: used to set datastore_registry_ext in
        trove-guestagent.conf
    trove-database-volume-support: corresponds to volume_support per DB in
        trove.conf
    default-neutron-networks: same name as in trove.conf

Add Newton version of the trove configuration files, remove verbose
setting as it's deprecated and ignored in Newton.

Change-Id: I0a9a33128e13ad232f28996b24fea48d081786f2
This commit is contained in:
hmlanigan 2017-03-02 15:14:15 -05:00 committed by Heather Lanigan
parent 8d6211a75c
commit abc294f463
17 changed files with 943 additions and 272 deletions

View File

@ -13,6 +13,7 @@ options:
cloud:<series>-<openstack-release>/proposed
For series=Xenial we support cloud archives for openstack-release:
* Mitaka
* Newton
NOTE: updating this setting to a source that is known to provide
a later version of OpenStack will trigger a software upgrade.
rabbit-user:
@ -26,11 +27,11 @@ options:
database-user:
default: trove
type: string
description: Username for Neutron database access (if enabled)
description: Username for Trove database access (if enabled)
database:
default: trove
type: string
description: Database name for Neutron (if enabled)
description: Database name for Trove (if enabled)
debug:
default: False
type: boolean
@ -38,12 +39,35 @@ options:
verbose:
default: False
type: boolean
description: Enable verbose logging
description: Enable verbose logging, deprecated in Newton
region:
default: RegionOne
type: string
description: OpenStack Region
keystone-api-version:
default: "3"
default: "2"
type: string
description: none, 2 or 3
description: none, 2 or 3
trove-volume-support:
default: True
type: boolean
description: A cinder volume will be provisioned for all datastores (if enabled)
trove-datastore-database:
default: mysql
type: string
description: |
One or more database type(s) for Trove datastores, list is comma separated,
options are: mysql, redis, cassandra, mongodb, vertica
trove-database-volume-support:
default: mysql
type: string
description: |
A cinder volume will be provisioned for individual datastores (if enabled)
One or more database type(s) for Trove instances, list is comma separated,
options are: mysql, redis, cassandra, mongodb, vertica
default-neutron-networks:
default:
type: string
description: |
List of IDs for management networks which should be attached to the
instance regardless of what NICs are specified in the create API call.

View File

@ -2,4 +2,5 @@ includes: ['layer:openstack-api']
options:
basic:
use_venv: True
include_system_packages: True
include_system_packages: True
repo: https://github.com/openstack/charm-trove.git

View File

@ -21,6 +21,7 @@ import collections
import charmhelpers.contrib.openstack.utils as ch_utils
import charmhelpers.core.unitdata as unitdata
import charmhelpers.core.hookenv as hookenv
import charms_openstack.charm
import charms_openstack.adapters
@ -31,6 +32,7 @@ TROVE_CONF = TROVE_DIR + "trove.conf"
TROVE_API_PASTE_CONF = TROVE_DIR + "api-paste.ini"
TROVE_CONDUCTOR = TROVE_DIR + "trove-conductor.conf"
TROVE_GUEST_AGENT = TROVE_DIR + "trove-guestagent.conf"
TROVE_LOGGING_GUEST_AGENT = TROVE_DIR + "trove-logging-guestagent.conf"
TROVE_TASK_MANAGER = TROVE_DIR + "trove-taskmanager.conf"
OPENSTACK_RELEASE_KEY = 'trove-charm.openstack-release-version'
@ -181,7 +183,8 @@ class TroveCharm(charms_openstack.charm.HAOpenStackCharm):
TROVE_API_PASTE_CONF: services,
TROVE_CONDUCTOR: services,
TROVE_TASK_MANAGER: services,
TROVE_GUEST_AGENT: services
TROVE_GUEST_AGENT: services,
TROVE_LOGGING_GUEST_AGENT: services
}
ha_resources = ['vips', 'haproxy']
@ -203,6 +206,39 @@ class TroveCharm(charms_openstack.charm.HAOpenStackCharm):
# and do the actual install
super(TroveCharm, self).install()
def get_amqp_credentials(self):
"""Provide the default amqp username and vhost as a tuple.
:returns (username, host): two strings to send to the amqp provider.
"""
return (self.config['rabbit-user'], self.config['rabbit-vhost'])
def get_database_setup(self):
"""Provide the default database credentials as a list of 3-tuples
returns a structure of:
[
{'database': <database>,
'username': <username>,
'hostname': <hostname of this unit>
'prefix': <the optional prefix for the database>, },
]
:returns [{'database': ...}, ...]: credentials for multiple databases
"""
host = None
try:
host = hookenv.network_get_primary_address('shared-db')
except NotImplementedError:
host = hookenv.unit_get('private-address')
return [
dict(
database=self.config['database'],
username=self.config['database-user'],
hostname=host, )
]
# Determine the charm class by the supported release
@charms_openstack.charm.register_os_release_selector

View File

@ -16,39 +16,20 @@
from __future__ import absolute_import
import charms.reactive as reactive
import charmhelpers.core.hookenv as hookenv
import charms_openstack.charm as charm
# This charm's library contains all of the handler code associated with trove
import charm.openstack.trove as trove
# use a synthetic state to ensure that it get it to be installed independent of
# the install hook.
@reactive.when_not('charm.installed')
def install_packages():
trove.install()
reactive.set_state('charm.installed')
@reactive.when('amqp.connected')
def setup_amqp_req(amqp):
"""
Use the amqp interface to request access to the amqp broker using our
local configuration.
"""
amqp.request_access(username='trove', vhost='openstack')
trove.assess_status()
@reactive.when('shared-db.connected')
def setup_database(database):
"""
Configure the database on the interface.
"""
database.configure(hookenv.config('database'),
hookenv.config('database-user'),
hookenv.unit_private_ip())
trove.assess_status()
charm.use_defaults(
'config.changed',
'amqp.connected',
'identity-service.available',
'charm.installed',
'upgrade-charm',
'update-status',
'shared-db.connected',
)
# this is to check if ha is running
@ -86,13 +67,3 @@ def run_db_migration():
def update_peers(cluster):
"""Inform designate peers about this unit"""
trove.update_peers(cluster)
@reactive.when('config.changed')
def config_changed():
trove.assess_status()
@reactive.when('identity-service.available')
def configure_ssl(keystone):
trove.configure_ssl(keystone)

View File

@ -1,13 +1,24 @@
###############################################################################
# [ WARNING ]
# trove configuration file maintained by Juju
# local changes may be overwritten.
###############################################################################
[DEFAULT]
verbose = {{ options.verbose }}
debug = {{ options.debug }}
#trove_auth_url = {{ identity_service.service_protocol }}://{{ identity_service.auth_host }}:{{ identity_service.auth_port }}/v{{ options.keystone_api_version }}.0
trove_auth_url = {{ identity_service.service_protocol }}://{{ identity_service.auth_host }}:{{ identity_service.auth_port }}/v2.0
{% if identity_service.api_version == '2' %}
trove_auth_url = {{ identity_service.service_protocol }}://{{ identity_service.auth_host }}:{{ identity_service.auth_port }}/v{{ identity_service.api_version }}.0
{% else %}
trove_auth_url = {{ identity_service.service_protocol }}://{{ identity_service.auth_host }}:{{ identity_service.auth_port }}/v{{ identity_service.api_version }}
{% endif %}
# The manager class to use for conductor. (string value)
# conductor_manager = trove.conductor.manager.Manager
taskmanager_manager = trove.taskmanager.manager.Manager
#===================== RPC Configuration =================================
# URL representing the messaging driver to use and its full configuration.

View File

@ -1,12 +1,22 @@
###############################################################################
# [ WARNING ]
# trove configuration file maintained by Juju
# version on trove unit may be overwritten.
###############################################################################
[DEFAULT]
use_syslog = False
debug = True
log_file = trove-guestagent.log
log_dir = /var/log/trove/
ignore_users = os_admin
control_exchange = trove
trove_auth_url = {{ identity_service.service_protocol }}://{{ identity_service.auth_host }}:{{ identity_service.service_port }}/v2.0
{% if identity_service.api_version == '2' %}
trove_auth_url = {{ identity_service.service_protocol }}://{{ identity_service.auth_host }}:{{ identity_service.auth_port }}/v{{ identity_service.api_version }}.0
{% else %}
trove_auth_url = {{ identity_service.service_protocol }}://{{ identity_service.auth_host }}:{{ identity_service.auth_port }}/v{{ identity_service.api_version }}
{% endif %}
#=========== RPC Configuration ======================
# URL representing the messaging driver to use and its full configuration.
@ -29,7 +39,7 @@ control_exchange = trove
# To fetch from Keystone, comment out swift_url, and uncomment the others.
# swift_url = http://10.0.0.1:8080/v1/AUTH_
# Region name of this node. Default value is None.
# os_region_name = RegionOne
os_region_name = {{ options.region }}
# Service type to use when searching catalog.
# swift_service_type = object-store
@ -39,6 +49,9 @@ control_exchange = trove
# Datastore manager implementations.
# Format: list of 'datastore-type:datastore.manager.implementation.module'
# datastore_registry_ext = mysql:trove.guestagent.datastore.mysql.manager.Manager, percona:trove.guestagent.datastore.mysql.manager.Manager
{% if options.trove_datastore_database == "mysql" %}
datastore_registry_ext = mysql:trove.guestagent.datastore.mysql.manager.Manager
{% endif %}
# ========== Default Users / DBs Configuration ==========
@ -74,13 +87,16 @@ root_grant_option = True
# ========== Sample Logging Configuration ==========
# Show more verbose log output (sets INFO log level output)
verbose = {{ options.verbose }}
# Show debugging output in logs (sets DEBUG log level output)
# debug = True
debug = {{ options.debug }}
# Directory and path for log files
log_dir = /var/log/trove/
log_file = logfile.txt
log_config_append = /etc/trove/trove-logging-guestagent.conf
#log_config_append = /etc/trove/trove-logging-guestagent.conf
[profiler]
# If False fully disable profiling feature.

View File

@ -0,0 +1,45 @@
###############################################################################
# [ WARNING ]
# trove configuration file maintained by Juju
# version on trove unit may be overwritten.
###############################################################################
[loggers]
keys=root
[handlers]
keys=file
[formatters]
keys=minimal,normal,debug
###########
# Loggers #
###########
[logger_root]
level=WARNING
handlers=file
################
# Log Handlers #
################
[handler_file]
class=logging.handlers.RotatingFileHandler
level=WARNING
formatter=normal
args=('/var/log/trove-guestagent.log', 'a', 100 * 1024 * 1024) # log file limit is 100MB
##################
# Log Formatters #
##################
[formatter_minimal]
format=%(message)s
[formatter_normal]
format=(%(name)s): %(asctime)s %(levelname)s %(message)s
[formatter_debug]
format=(%(name)s): %(asctime)s %(levelname)s %(module)s %(funcName)s %(message)s

View File

@ -1,3 +1,9 @@
###############################################################################
# [ WARNING ]
# trove configuration file maintained by Juju
# local changes may be overwritten.
###############################################################################
[DEFAULT]
# Show more verbose log output (sets INFO log level output)
verbose = {{ options.verbose }}
@ -6,11 +12,20 @@ verbose = {{ options.verbose }}
debug = {{ options.debug }}
network_driver = trove.network.neutron.NeutronDriver
{% if options.default_neutron_networks %}
default_neutron_networks = {{ options.default_neutron_networks }}
{% else %}
default_neutron_networks =
{% endif %}
update_status_on_fail = True
log_dir = /var/log/trove
#trove_auth_url = {{ identity_service.service_protocol }}://{{ identity_service.auth_host }}:{{ identity_service.auth_port }}/v{{ options.keystone_api_version }}.0
trove_auth_url = {{ identity_service.service_protocol }}://{{ identity_service.auth_host }}:{{ identity_service.auth_port }}/v2.0
{% if identity_service.api_version == '2' %}
trove_auth_url = {{ identity_service.service_protocol }}://{{ identity_service.auth_host }}:{{ identity_service.auth_port }}/v{{ identity_service.api_version }}.0
{% else %}
trove_auth_url = {{ identity_service.service_protocol }}://{{ identity_service.auth_host }}:{{ identity_service.auth_port }}/v{{ identity_service.api_version }}
{% endif %}
notifier_queue_hostname = controller
backend = rabbit
@ -24,9 +39,9 @@ db_api_implementation = trove.db.sqlalchemy.api
taskmanager_manager = trove.taskmanager.manager.Manager
# Region name of this node. Used when searching catalog. Default value is None.
#os_region_name = RegionOne
os_region_name = {{ options.region }}
# Service type to use when searching catalog.
#nova_compute_service_type = compute
nova_compute_service_type = compute
# Service type to use when searching catalog.
#cinder_service_type = volumev2
# Service type to use when searching catalog.
@ -37,47 +52,97 @@ taskmanager_manager = trove.taskmanager.manager.Manager
#neutron_service_type = network
# Config options for enabling volume service
trove_volume_support = True
trove_volume_support = {{ options.trove_volume_support }}
block_device_mapping = vdb
device_path = /dev/vdb
mount_point = /var/lib/mysql
volume_time_out=30
server_delete_time_out=480
# Nova server boot options
# sets the --config-drive argument when doing a nova boot
# (controls how file injection is handled by nova)
use_nova_server_config_drive = False
# ================= Guestagent related ========================
guest_config = /etc/trove/conf.d/trove-guestagent.conf
# Use 'guest_info = /etc/guest_info' for pre-Kilo compatibility
#guest_info = guest_info.conf
# Use 'injected_config_location = /etc/trove' for pre-Kilo compatibility
injected_config_location = /etc/trove/conf.d
cloudinit_location = /etc/trove/cloudinit
{% if 'mysql' in options.trove_database %}
[mysql]
tcp_ports = 3306
{% if 'mysql' in options.trove_database_volume_support %}
volume_support = True
{% else %}
volume_support = False
{% endif %}
device_path = /dev/vdb
{% endif %}
{% if 'redis' in options.trove_database %}
#[redis]
[redis]
# Format (single port or port range): A, B-C
# where C greater than B
#tcp_ports = 6379
tcp_ports = 6379
{% if 'redis' in options.trove_database_volume_support %}
volume_support = True
{% else %}
volume_support = False
{% endif %}
# redis uses local storage
#volume_support = False
# default device_path = None
{% endif %}
{% if 'cassandra' in options.trove_database %}
#[cassandra]
#tcp_ports = 7000, 7001, 9042, 9160
#volume_support = True
#device_path = /dev/vdb
[cassandra]
tcp_ports = 7000, 7001, 9042, 9160
{% if 'cassandra' in options.trove_database_volume_support %}
volume_support = True
{% else %}
volume_support = False
{% endif %}
device_path = /dev/vdb
{% endif %}
{% if 'couchbase' in options.trove_database %}
#[couchbase]
#tcp_ports = 8091, 8092, 4369, 11209-11211, 21100-21199
#volume_support = True
#device_path = /dev/vdb
[couchbase]
tcp_ports = 8091, 8092, 4369, 11209-11211, 21100-21199
{% if 'couchbase' in options.trove_database_volume_support %}
volume_support = True
{% else %}
volume_support = False
{% endif %}
device_path = /dev/vdb
{% endif %}
{% if 'mongodb' in options.trove_database %}
#[mongodb]
#volume_support = True
#device_path = /dev/vdb
[mongodb]
{% if 'mongodb' in options.trove_database_volume_support %}
volume_support = True
{% else %}
volume_support = False
{% endif %}
device_path = /dev/vdb
{% endif %}
{% if 'vertica' in options.trove_database %}
#[vertica]
#tcp_ports = 5433, 5434, 22, 5444, 5450, 4803
#udp_ports = 5433, 4803, 4804, 6453
#volume_support = True
#device_path = /dev/vdb
#mount_point = /var/lib/vertica
#taskmanager_strategy = trove.common.strategies.cluster.experimental.vertica.taskmanager.VerticaTaskManagerStrategy
[vertica]
tcp_ports = 5433, 5434, 22, 5444, 5450, 4803
udp_ports = 5433, 4803, 4804, 6453
{% if 'vertica' in options.trove_database_volume_support %}
volume_support = True
{% else %}
volume_support = False
{% endif %}
device_path = /dev/vdb
mount_point = /var/lib/vertica
taskmanager_strategy = trove.common.strategies.cluster.experimental.vertica.taskmanager.VerticaTaskManagerStrategy
{% endif %}
{% include "parts/section-rabbitmq-oslo" %}

View File

@ -1,9 +1,26 @@
###############################################################################
# [ WARNING ]
# trove configuration file maintained by Juju
# local changes may be overwritten.
###############################################################################
[DEFAULT]
auth_strategy = keystone
log_dir = /var/log/trove
#trove_auth_url = {{ identity_service.service_protocol }}://{{ identity_service.auth_host }}:{{ identity_service.auth_port }}/v{{ identity_service.api_version }}
{% if identity_service.api_version == '2' %}
trove_auth_url = {{ identity_service.service_protocol }}://{{ identity_service.auth_host }}:{{ identity_service.auth_port }}/v{{ identity_service.api_version }}.0
{% else %}
trove_auth_url = {{ identity_service.service_protocol }}://{{ identity_service.auth_host }}:{{ identity_service.auth_port }}/v{{ identity_service.api_version }}
{% endif %}
notifier_queue_hostname = controller
network_driver = trove.network.neutron.NeutronDriver
{% if options.default_neutron_networks %}
default_neutron_networks = {{ options.default_neutron_networks }}
{% else %}
default_neutron_networks =
{% endif %}
# Show more verbose log output (sets INFO log level output)
verbose = {{ options.verbose }}
@ -29,7 +46,7 @@ control_exchange = trove
db_api_implementation = "trove.db.sqlalchemy.api"
# Region name of this node. Used when searching catalog. Default value is None.
#os_region_name = RegionOne
os_region_name = {{ options.region }}
# Service type to use when searching catalog.
#nova_compute_service_type = compute
# Service type to use when searching catalog.
@ -42,7 +59,7 @@ db_api_implementation = "trove.db.sqlalchemy.api"
#neutron_service_type = network
# Config options for enabling volume service
#trove_volume_support = True
trove_volume_support = {{ options.trove_volume_support }}
#block_device_mapping = vdb
#device_path = /dev/vdb
# Maximum volume size for an instance
@ -59,58 +76,84 @@ db_api_implementation = "trove.db.sqlalchemy.api"
{% include "parts/section-keystone-authtoken" %}
{% if 'mysql' in options.trove_database %}
[mysql]
root_on_create = False
# Format (single port or port range): A, B-C
# where C greater than B
tcp_ports = 3306
{% if 'mysql' in options.trove_database_volume_support %}
volume_support = True
{% else %}
volume_support = False
{% endif %}
device_path = /dev/vdb
# Users to ignore for user create/list/delete operations
ignore_users = os_admin, root
ignore_dbs = mysql, information_schema, performance_schema
[mysql]
root_on_create = False
# Format (single port or port range): A, B-C
# where C greater than B
tcp_ports = 3306
volume_support = True
device_path = /dev/vdb
{% endif %}
[percona]
{% if 'redis' in options.trove_database %}
#[redis]
#tcp_ports = 6379
[redis]
tcp_ports = 6379
#redis uses local storage
#volume_support = False
# default device_path = None
{% if 'redis' in options.trove_database_volume_support %}
volume_support = True
{% else %}
volume_support = False
{% endif %}
{% endif %}
{% if 'cassandra' in options.trove_database %}
#[cassandra]
#tcp_ports = 7000, 7001, 9042, 9160
#volume_support = True
#device_path = /dev/vdb
[cassandra]
tcp_ports = 7000, 7001, 9042, 9160
{% if 'cassandra' in options.trove_database_volume_support %}
volume_support = True
{% else %}
volume_support = False
{% endif %}
device_path = /dev/vdb
{% endif %}
{% if 'couchbase' in options.trove_database %}
#[couchbase]
#tcp_ports = 8091, 8092, 4369, 11209-11211, 21100-21199
#volume_support = True
#device_path = /dev/vdb
[couchbase]
tcp_ports = 8091, 8092, 4369, 11209-11211, 21100-21199
{% if 'couchbase' in options.trove_database_volume_support %}
volume_support = True
{% else %}
volume_support = False
{% endif %}
device_path = /dev/vdb
{% endif %}
{% if 'mongodb' in options.trove_database %}
#[mongodb]
#tcp_ports = 2500, 27017
#volume_support = True
#device_path = /dev/vdb
#num_config_servers_per_cluster = 1
#num_query_routers_per_cluster = 1
[mongodb]
tcp_ports = 2500, 27017
{% if 'mongodb' in options.trove_database_volume_support %}
volume_support = True
{% else %}
volume_support = False
{% endif %}
device_path = /dev/vdb
num_config_servers_per_cluster = 1
num_query_routers_per_cluster = 1
{% endif %}
{% if 'vertica' in options.trove_database %}
#[vertica]
#tcp_ports = 5433, 5434, 22, 5444, 5450, 4803
#udp_ports = 5433, 4803, 4804, 6453
#volume_support = True
#device_path = /dev/vdb
#cluster_support = True
#cluster_member_count = 3
#api_strategy = trove.common.strategies.cluster.experimental.vertica.api.VerticaAPIStrategy
[vertica]
tcp_ports = 5433, 5434, 22, 5444, 5450, 4803
udp_ports = 5433, 4803, 4804, 6453
{% if 'vertica' in options.trove_database_volume_support %}
volume_support = True
{% else %}
volume_support = False
{% endif %}
device_path = /dev/vdb
cluster_support = True
cluster_member_count = 3
api_strategy = trove.common.strategies.cluster.experimental.vertica.api.VerticaAPIStrategy
{% endif %}

View File

@ -1 +1 @@
These are the prototype config files for the Trove service.

View File

@ -0,0 +1,44 @@
###############################################################################
# [ WARNING ]
# trove configuration file maintained by Juju
# local changes may be overwritten.
###############################################################################
[DEFAULT]
debug = {{ options.debug }}
{% if identity_service.api_version == '2' %}
trove_auth_url = {{ identity_service.service_protocol }}://{{ identity_service.auth_host }}:{{ identity_service.auth_port }}/v{{ identity_service.api_version }}.0
{% else %}
trove_auth_url = {{ identity_service.service_protocol }}://{{ identity_service.auth_host }}:{{ identity_service.auth_port }}/v{{ identity_service.api_version }}
{% endif %}
# The manager class to use for conductor. (string value)
# conductor_manager = trove.conductor.manager.Manager
taskmanager_manager = trove.taskmanager.manager.Manager
#===================== RPC Configuration =================================
# URL representing the messaging driver to use and its full configuration.
# If not set, we fall back to the 'rpc_backend' option and driver specific
# configuration.
#transport_url=<None>
# The messaging driver to use. Options include rabbit, qpid and zmq.
# Default is rabbit. (string value)
rpc_backend=rabbit
# The default exchange under which topics are scoped. May be
# overridden by an exchange name specified in the 'transport_url option.
control_exchange = trove
[profiler]
# If False fully disable profiling feature.
#enabled = False
# If False doesn't trace SQL requests.
#trace_sqlalchemy = True
{% include "parts/section-database" %}
{% include "parts/section-rabbitmq-oslo" %}

View File

@ -0,0 +1,159 @@
###############################################################################
# [ WARNING ]
# trove configuration file maintained by Juju
# version on trove unit may be overwritten.
###############################################################################
[DEFAULT]
use_syslog = False
log_file = trove-guestagent.log
log_dir = /var/log/trove/
ignore_users = os_admin
control_exchange = trove
{% if identity_service.api_version == '2' %}
trove_auth_url = {{ identity_service.service_protocol }}://{{ identity_service.auth_host }}:{{ identity_service.auth_port }}/v{{ identity_service.api_version }}.0
{% else %}
trove_auth_url = {{ identity_service.service_protocol }}://{{ identity_service.auth_host }}:{{ identity_service.auth_port }}/v{{ identity_service.api_version }}
{% endif %}
#=========== RPC Configuration ======================
# URL representing the messaging driver to use and its full configuration.
# If not set, we fall back to the 'rpc_backend' option and driver specific
# configuration.
#transport_url=<None>
# The messaging driver to use. Options include rabbit, qpid and zmq.
# Default is rabbit. (string value)
rpc_backend=rabbit
# The default exchange under which topics are scoped. May be
# overridden by an exchange name specified in the 'transport_url option.
control_exchange = trove
# ========== Configuration options for Swift ==========
# The swift_url can be specified directly or fetched from Keystone catalog.
# To fetch from Keystone, comment out swift_url, and uncomment the others.
# swift_url = http://10.0.0.1:8080/v1/AUTH_
# Region name of this node. Default value is None.
os_region_name = {{ options.region }}
# Service type to use when searching catalog.
# swift_service_type = object-store
# ========== Datastore Manager Configurations ==========
# Datastore manager implementations.
# Format: list of 'datastore-type:datastore.manager.implementation.module'
# datastore_registry_ext = mysql:trove.guestagent.datastore.mysql.manager.Manager, percona:trove.guestagent.datastore.mysql.manager.Manager
{% if options.trove_datastore_database == "mysql" %}
datastore_registry_ext = mysql:trove.guestagent.datastore.mysql.manager.Manager
{% endif %}
# ========== Default Users / DBs Configuration ==========
# Permissions to grant "root" user by default
root_grant = ALL
root_grant_option = True
# root_grant = ALTER ROUTINE, CREATE, ALTER, CREATE ROUTINE, CREATE TEMPORARY TABLES, CREATE VIEW, CREATE USER, DELETE, DROP, EVENT, EXECUTE, INDEX, INSERT, LOCK TABLES, PROCESS, REFERENCES, SELECT, SHOW DATABASES, SHOW VIEW, TRIGGER, UPDATE, USAGE
# root_grant_option = False
# Default password Length for root password
# default_password_length = 36
# ========== Default Storage Options for backup ==========
# Default configuration for storage strategy and storage options
# for backups
# For storage to Swift, use the following as defaults:
# storage_strategy = SwiftStorage
# storage_namespace = trove.common.strategies.storage.swift
# Default config options for storing backups to swift
# backup_swift_container = database_backups
# backup_use_gzip_compression = True
# backup_use_openssl_encryption = True
# backup_aes_cbc_key = "default_aes_cbc_key"
# backup_use_snet = False
# backup_chunk_size = 65536
# backup_segment_max_size = 2147483648
# ========== Sample Logging Configuration ==========
# Show debugging output in logs (sets DEBUG log level output)
debug = {{ options.debug }}
# Directory and path for log files
log_dir = /var/log/trove/
log_file = logfile.txt
#log_config_append = /etc/trove/trove-logging-guestagent.conf
[profiler]
# If False fully disable profiling feature.
#enabled = False
# If False doesn't trace SQL requests.
#trace_sqlalchemy = True
{% include "parts/section-rabbitmq-oslo" %}
# ========== Datastore Specific Configuration Options ==========
[mysql]
# For mysql, the following are the defaults for backup, and restore:
# backup_strategy = InnoBackupEx
# backup_namespace = trove.guestagent.strategies.backup.mysql_impl
# restore_namespace = trove.guestagent.strategies.restore.mysql_impl
# Default configuration for mysql replication
# replication_strategy = MysqlBinlogReplication
# replication_namespace = trove.guestagent.strategies.replication.mysql_binlog
# replication_user = slave_user
# replication_password = slave_password
# Users to ignore for user create/list/delete operations
# ignore_users = os_admin
# Databases to ignore for db create/list/delete operations
# ignore_dbs = mysql, information_schema, performance_schema
#[vertica]
# For vertica, following are the defaults needed:
# mount_point = /var/lib/vertica
# readahead_size = 2048
# guestagent_strategy = trove.common.strategies.cluster.experimental.vertica.guestagent.VerticaGuestAgentStrategy
#[redis]
# For redis, the following are the defaults for backup, and restore:
# backup_strategy = RedisBackup
# backup_namespace = trove.guestagent.strategies.backup.experimental.redis_impl
# restore_namespace = trove.guestagent.strategies.restore.experimental.redis_impl
[percona]
backup_namespace = trove.guestagent.strategies.backup.mysql_impl
restore_namespace = trove.guestagent.strategies.restore.mysql_impl
#[couchbase]
#backup_namespace = trove.guestagent.strategies.backup.experimental.couchbase_impl
#restore_namespace = trove.guestagent.strategies.restore.experimental.couchbase_impl
#[cassandra]
#backup_namespace = trove.guestagent.strategies.backup.experimental.cassandra_impl
#restore_namespace = trove.guestagent.strategies.restore.experimental.cassandra_impl
#[db2]
# For db2, the following are the defaults for backup, and restore:
# backup_strategy = DB2Backup
# backup_namespace = trove.guestagent.strategies.backup.experimental.db2_impl
# restore_namespace = trove.guestagent.strategies.restore.experimental.db2_impl
#[couchdb]
#For CouchDB, the following are the defaults for backup and restore:
# backup_strategy = CouchDBBackup
# backup_namespace = trove.guestagent.strategies.backup.experimental.couchdb_impl
# restore_namespace = trove.guestagent.strategies.restore.experimental.couchdb_impl

View File

@ -0,0 +1,45 @@
###############################################################################
# [ WARNING ]
# trove configuration file maintained by Juju
# version on trove unit may be overwritten.
###############################################################################
[loggers]
keys=root
[handlers]
keys=file
[formatters]
keys=minimal,normal,debug
###########
# Loggers #
###########
[logger_root]
level=WARNING
handlers=file
################
# Log Handlers #
################
[handler_file]
class=logging.handlers.RotatingFileHandler
level=WARNING
formatter=normal
args=('/var/log/trove-guestagent.log', 'a', 100 * 1024 * 1024) # log file limit is 100MB
##################
# Log Formatters #
##################
[formatter_minimal]
format=%(message)s
[formatter_normal]
format=(%(name)s): %(asctime)s %(levelname)s %(message)s
[formatter_debug]
format=(%(name)s): %(asctime)s %(levelname)s %(module)s %(funcName)s %(message)s

View File

@ -0,0 +1,146 @@
###############################################################################
# [ WARNING ]
# trove configuration file maintained by Juju
# local changes may be overwritten.
###############################################################################
[DEFAULT]
# Show debugging output in logs (sets DEBUG log level output)
debug = {{ options.debug }}
network_driver = trove.network.neutron.NeutronDriver
{% if options.default_neutron_networks %}
default_neutron_networks = {{ options.default_neutron_networks }}
{% else %}
default_neutron_networks =
{% endif %}
update_status_on_fail = True
log_dir = /var/log/trove
{% if identity_service.api_version == '2' %}
trove_auth_url = {{ identity_service.service_protocol }}://{{ identity_service.auth_host }}:{{ identity_service.auth_port }}/v{{ identity_service.api_version }}.0
{% else %}
trove_auth_url = {{ identity_service.service_protocol }}://{{ identity_service.auth_host }}:{{ identity_service.auth_port }}/v{{ identity_service.api_version }}
{% endif %}
notifier_queue_hostname = controller
backend = rabbit
# The default exchange under which topics are scoped. May be
# overridden by an exchange name specified in the 'transport_url option.
control_exchange = trove
#DB Api Implementation
db_api_implementation = trove.db.sqlalchemy.api
taskmanager_manager = trove.taskmanager.manager.Manager
# Region name of this node. Used when searching catalog. Default value is None.
os_region_name = {{ options.region }}
# Service type to use when searching catalog.
nova_compute_service_type = compute
# Service type to use when searching catalog.
#cinder_service_type = volumev2
# Service type to use when searching catalog.
#swift_service_type = object-store
# Service type to use when searching catalog.
#heat_service_type = orchestration
# Service type to use when searching catalog.
#neutron_service_type = network
# Config options for enabling volume service
trove_volume_support = {{ options.trove_volume_support }}
block_device_mapping = vdb
device_path = /dev/vdb
mount_point = /var/lib/mysql
volume_time_out=30
server_delete_time_out=480
# Nova server boot options
# sets the --config-drive argument when doing a nova boot
# (controls how file injection is handled by nova)
use_nova_server_config_drive = False
# ================= Guestagent related ========================
guest_config = /etc/trove/conf.d/trove-guestagent.conf
# Use 'guest_info = /etc/guest_info' for pre-Kilo compatibility
#guest_info = guest_info.conf
# Use 'injected_config_location = /etc/trove' for pre-Kilo compatibility
injected_config_location = /etc/trove/conf.d
cloudinit_location = /etc/trove/cloudinit
{% if 'mysql' in options.trove_database %}
[mysql]
tcp_ports = 3306
{% if 'mysql' in options.trove_database_volume_support %}
volume_support = True
{% else %}
volume_support = False
{% endif %}
device_path = /dev/vdb
{% endif %}
{% if 'redis' in options.trove_database %}
[redis]
# Format (single port or port range): A, B-C
# where C greater than B
tcp_ports = 6379
{% if 'redis' in options.trove_database_volume_support %}
volume_support = True
{% else %}
volume_support = False
{% endif %}
# redis uses local storage
# default device_path = None
{% endif %}
{% if 'cassandra' in options.trove_database %}
[cassandra]
tcp_ports = 7000, 7001, 9042, 9160
{% if 'cassandra' in options.trove_database_volume_support %}
volume_support = True
{% else %}
volume_support = False
{% endif %}
device_path = /dev/vdb
{% endif %}
{% if 'couchbase' in options.trove_database %}
[couchbase]
tcp_ports = 8091, 8092, 4369, 11209-11211, 21100-21199
{% if 'couchbase' in options.trove_database_volume_support %}
volume_support = True
{% else %}
volume_support = False
{% endif %}
device_path = /dev/vdb
{% endif %}
{% if 'mongodb' in options.trove_database %}
[mongodb]
{% if 'mongodb' in options.trove_database_volume_support %}
volume_support = True
{% else %}
volume_support = False
{% endif %}
device_path = /dev/vdb
{% endif %}
{% if 'vertica' in options.trove_database %}
[vertica]
tcp_ports = 5433, 5434, 22, 5444, 5450, 4803
udp_ports = 5433, 4803, 4804, 6453
{% if 'vertica' in options.trove_database_volume_support %}
volume_support = True
{% else %}
volume_support = False
{% endif %}
device_path = /dev/vdb
mount_point = /var/lib/vertica
taskmanager_strategy = trove.common.strategies.cluster.experimental.vertica.taskmanager.VerticaTaskManagerStrategy
{% endif %}
{% include "parts/section-rabbitmq-oslo" %}
{% include "parts/section-database" %}

View File

@ -0,0 +1,156 @@
###############################################################################
# [ WARNING ]
# trove configuration file maintained by Juju
# local changes may be overwritten.
###############################################################################
[DEFAULT]
auth_strategy = keystone
log_dir = /var/log/trove
{% if identity_service.api_version == '2' %}
trove_auth_url = {{ identity_service.service_protocol }}://{{ identity_service.auth_host }}:{{ identity_service.auth_port }}/v{{ identity_service.api_version }}.0
{% else %}
trove_auth_url = {{ identity_service.service_protocol }}://{{ identity_service.auth_host }}:{{ identity_service.auth_port }}/v{{ identity_service.api_version }}
{% endif %}
notifier_queue_hostname = controller
network_driver = trove.network.neutron.NeutronDriver
{% if options.default_neutron_networks %}
default_neutron_networks = {{ options.default_neutron_networks }}
{% else %}
default_neutron_networks =
{% endif %}
# Show debugging output in logs (sets DEBUG log level output)
debug = {{ options.debug }}
# Address to bind the API server
bind_host = {{ options.service_listen_info.trove_api.ip }}
# Port the bind the API server to
#bind_port = 8779
bind_port = {{ options.service_listen_info.trove_api.port }}
rpc_backend = rabbit
# Config option for showing the IP address that nova doles out
add_addresses = True
api_paste_config = /etc/trove/api-paste.ini
control_exchange = trove
#DB Api Implementation
db_api_implementation = "trove.db.sqlalchemy.api"
# Region name of this node. Used when searching catalog. Default value is None.
os_region_name = {{ options.region }}
# Service type to use when searching catalog.
#nova_compute_service_type = compute
# Service type to use when searching catalog.
#cinder_service_type = volumev2
# Service type to use when searching catalog.
#swift_service_type = object-store
# Service type to use when searching catalog.
#heat_service_type = orchestration
# Service type to use when searching catalog.
#neutron_service_type = network
# Config options for enabling volume service
trove_volume_support = {{ options.trove_volume_support }}
#block_device_mapping = vdb
#device_path = /dev/vdb
# Maximum volume size for an instance
#max_accepted_volume_size = 10
#max_instances_per_tenant = 5
# Maximum volume capacity (in GB) spanning across all trove volumes per tenant
#max_volumes_per_tenant = 100
#max_backups_per_tenant = 5
#volume_time_out=30
{% include "parts/section-rabbitmq-oslo" %}
{% include "parts/section-database" %}
{% include "parts/section-keystone-authtoken" %}
{% if 'mysql' in options.trove_database %}
[mysql]
root_on_create = False
# Format (single port or port range): A, B-C
# where C greater than B
tcp_ports = 3306
{% if 'mysql' in options.trove_database_volume_support %}
volume_support = True
{% else %}
volume_support = False
{% endif %}
device_path = /dev/vdb
# Users to ignore for user create/list/delete operations
ignore_users = os_admin, root
ignore_dbs = mysql, information_schema, performance_schema
{% endif %}
[percona]
{% if 'redis' in options.trove_database %}
[redis]
tcp_ports = 6379
#redis uses local storage
# default device_path = None
{% if 'redis' in options.trove_database_volume_support %}
volume_support = True
{% else %}
volume_support = False
{% endif %}
{% endif %}
{% if 'cassandra' in options.trove_database %}
[cassandra]
tcp_ports = 7000, 7001, 9042, 9160
{% if 'cassandra' in options.trove_database_volume_support %}
volume_support = True
{% else %}
volume_support = False
{% endif %}
device_path = /dev/vdb
{% endif %}
{% if 'couchbase' in options.trove_database %}
[couchbase]
tcp_ports = 8091, 8092, 4369, 11209-11211, 21100-21199
{% if 'couchbase' in options.trove_database_volume_support %}
volume_support = True
{% else %}
volume_support = False
{% endif %}
device_path = /dev/vdb
{% endif %}
{% if 'mongodb' in options.trove_database %}
[mongodb]
tcp_ports = 2500, 27017
{% if 'mongodb' in options.trove_database_volume_support %}
volume_support = True
{% else %}
volume_support = False
{% endif %}
device_path = /dev/vdb
num_config_servers_per_cluster = 1
num_query_routers_per_cluster = 1
{% endif %}
{% if 'vertica' in options.trove_database %}
[vertica]
tcp_ports = 5433, 5434, 22, 5444, 5450, 4803
udp_ports = 5433, 4803, 4804, 6453
{% if 'vertica' in options.trove_database_volume_support %}
volume_support = True
{% else %}
volume_support = False
{% endif %}
device_path = /dev/vdb
cluster_support = True
cluster_member_count = 3
api_strategy = trove.common.strategies.cluster.experimental.vertica.api.VerticaAPIStrategy
{% endif %}

View File

@ -17,6 +17,8 @@ from __future__ import print_function
import mock
import charmhelpers
import charm.openstack.trove as trove
import charms_openstack.test_utils as test_utils
@ -31,6 +33,18 @@ class Helper(test_utils.PatchHelper):
class TestOpenStackTrove(Helper):
def _patch_config_and_charm(self, config):
self.patch_object(charmhelpers.core.hookenv, 'config')
def cf(key=None):
if key is not None:
return config[key]
return config
self.config.side_effect = cf
c = trove.TroveCharm()
return c
def test_install(self):
self.patch_object(trove.TroveCharm.singleton, 'install')
trove.install()
@ -99,3 +113,24 @@ class TestOpenStackTrove(Helper):
self.patch_object(trove.TroveCharm.singleton, 'assess_status')
trove.assess_status()
self.assess_status.assert_called_once_with()
def test_get_amqp_credentials(self):
config = {
'rabbit-user': 'rabbit1',
'rabbit-vhost': 'password'
}
c = self._patch_config_and_charm(config)
self.assertEqual(c.get_amqp_credentials(), ('rabbit1', 'password'))
def test_get_database_setup(self):
self.patch_object(charmhelpers.core.hookenv,
'network_get_primary_address')
self.network_get_primary_address.return_value = 'private_ip'
config = {
'database': 'db1',
'database-user': 'user1',
}
c = self._patch_config_and_charm(config)
self.assertEqual(
c.get_database_setup(),
[dict(database='db1', username='user1', hostname='private_ip')])

View File

@ -1,163 +1,37 @@
from __future__ import absolute_import
from __future__ import print_function
import unittest
import mock
import charms_openstack.test_utils as test_utils
import reactive.trove_handlers as handlers
_when_args = {}
_when_not_args = {}
class TestRegisteredHooks(test_utils.TestRegisteredHooks):
def mock_hook_factory(d):
def mock_hook(*args, **kwargs):
def inner(f):
# remember what we were passed. Note that we can't actually
# determine the class we're attached to, as the decorator only gets
# the function.
try:
d[f.__name__].append(dict(args=args, kwargs=kwargs))
except KeyError:
d[f.__name__] = [dict(args=args, kwargs=kwargs)]
return f
return inner
return mock_hook
class TestTroveHandlers(unittest.TestCase):
@classmethod
def setUpClass(cls):
cls._patched_when = mock.patch('charms.reactive.when',
mock_hook_factory(_when_args))
cls._patched_when_started = cls._patched_when.start()
cls._patched_when_not = mock.patch('charms.reactive.when_not',
mock_hook_factory(_when_not_args))
cls._patched_when_not_started = cls._patched_when_not.start()
# force requires to rerun the mock_hook decorator:
# try except is Python2/Python3 compatibility as Python3 has moved
# reload to importlib.
try:
reload(handlers)
except NameError:
import importlib
importlib.reload(handlers)
@classmethod
def tearDownClass(cls):
cls._patched_when.stop()
cls._patched_when_started = None
cls._patched_when = None
cls._patched_when_not.stop()
cls._patched_when_not_started = None
cls._patched_when_not = None
# and fix any breakage we did to the module
try:
reload(handlers)
except NameError:
import importlib
importlib.reload(handlers)
def setUp(self):
self._patches = {}
self._patches_start = {}
def tearDown(self):
for k, v in self._patches.items():
v.stop()
setattr(self, k, None)
self._patches = None
self._patches_start = None
def patch(self, obj, attr, return_value=None):
mocked = mock.patch.object(obj, attr)
self._patches[attr] = mocked
started = mocked.start()
started.return_value = return_value
self._patches_start[attr] = started
setattr(self, attr, started)
def test_registered_hooks(self):
# test that the hooks actually registered the relation expressions that
# are meaningful for this interface: this is to handle regressions.
# The keys are the function names that the hook attaches to.
when_patterns = {
'setup_amqp_req': [('amqp.connected', )],
'setup_database': [('shared-db.connected', )],
'setup_endpoint': [('identity-service.connected', )],
'configure_ssl': [('identity-service.available', )],
'update_peers': [('cluster.available', )],
'config_changed': [('config.changed', )],
'cluster_connected': [('ha.connected', )],
'render_stuff': [('amqp.available',),
('identity-service.available',),
('shared-db.available',)],
'run_db_migration': [('config.complete',)]
def test_hooks(self):
defaults = [
'charm.installed',
'amqp.connected',
'shared-db.connected',
'identity-service.connected',
'identity-service.available', # enables SSL support
'config.changed',
'config.complete',
'db.synced']
hook_set = {
'when': {
'render_stuff': ('shared-db.available',
'identity-service.available',
'amqp.available',),
'update_peers': ('cluster.available',),
'setup_endpoint': ('identity-service.connected',),
'cluster_connected': ('ha.connected',),
'run_db_migration': ('config.complete',),
},
'when_not': {
'run_db_migration': ('db.synced',),
},
}
when_not_patterns = {
'install_packages': [('charm.installed', )],
'run_db_migration': [('db.synced',)],
}
# check the when hooks are attached to the expected functions
for t, p in [(_when_args, when_patterns),
(_when_not_args, when_not_patterns)]:
for f, args in t.items():
# check that function is in patterns
print('f: {}'.format(f))
self.assertTrue(f in p.keys())
# check that the lists are equal
l = [a['args'] for a in args]
self.assertEqual(l, p[f])
def test_install_packages(self):
self.patch(handlers.trove, 'install')
self.patch(handlers.reactive, 'set_state')
handlers.install_packages()
self.install.assert_called_once_with()
self.set_state.assert_called_once_with('charm.installed')
def test_setup_amqp_req(self):
self.patch(handlers.trove, 'assess_status')
amqp = mock.MagicMock()
handlers.setup_amqp_req(amqp)
amqp.request_access.assert_called_once_with(
username='trove', vhost='openstack')
self.assess_status.assert_called_once_with()
def test_database(self):
self.patch(handlers.trove, 'assess_status')
database = mock.MagicMock()
self.patch(handlers.hookenv, 'unit_private_ip', 'private_ip')
handlers.setup_database(database)
calls = [
mock.call(
'trove',
'trove',
'private_ip',
prefix='trove'),
]
database.configure.has_calls(calls)
self.assess_status.assert_called_once_with()
def test_setup_endpoint(self):
self.patch(handlers.trove, 'assess_status')
self.patch(handlers.trove, 'setup_endpoint')
handlers.setup_endpoint('endpoint_object')
self.setup_endpoint.assert_called_once_with('endpoint_object')
self.assess_status.assert_called_once_with()
def test_update_peers(self):
cluster = mock.MagicMock()
self.patch(handlers.trove, 'update_peers')
handlers.update_peers(cluster)
self.update_peers.assert_called_once_with(cluster)
def test_config_changed(self):
self.patch(handlers.trove, 'assess_status')
handlers.config_changed()
self.assess_status.assert_called_once_with()
# test that the hooks were registered via the
# reactive.trove_handlers
self.registered_hooks_test_helper(handlers, hook_set, defaults)